mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-21 20:09:37 +00:00
get token balance
get token balance fix token balance call Fix assets on load add token list display images cleanup cleanup cleanup clean up
This commit is contained in:
parent
761be22bfa
commit
826c0ed46d
@ -9,6 +9,7 @@ type
|
||||
Value = UserRole + 3,
|
||||
FiatValue = UserRole + 4,
|
||||
Image = UserRole + 5
|
||||
HasIcon = UserRole + 6
|
||||
|
||||
QtObject:
|
||||
type AssetList* = ref object of QAbstractListModel
|
||||
@ -41,13 +42,15 @@ QtObject:
|
||||
of AssetRoles.Value: result = newQVariant(asset.value)
|
||||
of AssetRoles.FiatValue: result = newQVariant(asset.fiatValue)
|
||||
of AssetRoles.Image: result = newQVariant(asset.image)
|
||||
of AssetRoles.HasIcon: result = newQVariant(asset.hasIcon)
|
||||
|
||||
method roleNames(self: AssetList): Table[int, string] =
|
||||
{ AssetRoles.Name.int:"name",
|
||||
AssetRoles.Symbol.int:"symbol",
|
||||
AssetRoles.Value.int:"value",
|
||||
AssetRoles.FiatValue.int:"fiatValue",
|
||||
AssetRoles.Image.int:"image" }.toTable
|
||||
AssetRoles.Image.int:"image",
|
||||
AssetRoles.Image.int:"hasIcon" }.toTable
|
||||
|
||||
proc addAssetToList*(self: AssetList, asset: Asset) =
|
||||
self.beginInsertRows(newQModelIndex(), self.assets.len, self.assets.len)
|
||||
|
@ -183,29 +183,32 @@ proc MultiAccountImportPrivateKey*(privateKey: string): GeneratedAccount =
|
||||
result = Json.decode(importResult, GeneratedAccount)
|
||||
|
||||
proc saveAccount*(account: GeneratedAccount, password: string, color: string, accountType: string, isADerivedAccount = true): DerivedAccount =
|
||||
# Only store derived accounts. Private key accounts are not multiaccounts
|
||||
if (isADerivedAccount):
|
||||
discard storeDerivedAccounts(account, password)
|
||||
try:
|
||||
# Only store derived accounts. Private key accounts are not multiaccounts
|
||||
if (isADerivedAccount):
|
||||
discard storeDerivedAccounts(account, password)
|
||||
|
||||
var address = account.derived.defaultWallet.address
|
||||
var publicKey = account.derived.defaultWallet.publicKey
|
||||
var address = account.derived.defaultWallet.address
|
||||
var publicKey = account.derived.defaultWallet.publicKey
|
||||
|
||||
if (address == ""):
|
||||
address = account.address
|
||||
publicKey = account.publicKey
|
||||
if (address == ""):
|
||||
address = account.address
|
||||
publicKey = account.publicKey
|
||||
|
||||
discard callPrivateRPC("accounts_saveAccounts", %* [
|
||||
[{
|
||||
"color": color,
|
||||
"name": account.name,
|
||||
"address": address,
|
||||
"public-key": publicKey,
|
||||
"type": accountType,
|
||||
"path": "m/44'/60'/0'/0/1"
|
||||
}]
|
||||
])
|
||||
discard callPrivateRPC("accounts_saveAccounts", %* [
|
||||
[{
|
||||
"color": color,
|
||||
"name": account.name,
|
||||
"address": address,
|
||||
"public-key": publicKey,
|
||||
"type": accountType,
|
||||
"path": "m/44'/60'/0'/0/1"
|
||||
}]
|
||||
])
|
||||
|
||||
result = DerivedAccount(address: address, publicKey: publicKey)
|
||||
result = DerivedAccount(address: address, publicKey: publicKey)
|
||||
except:
|
||||
error "Error storing the new account. Bad password?"
|
||||
|
||||
proc deriveAccounts*(accountId: string): MultiAccounts =
|
||||
let deriveJson = %* {
|
||||
|
@ -1,6 +1,10 @@
|
||||
import core as status
|
||||
import json
|
||||
import chronicles
|
||||
import strformat
|
||||
import stint
|
||||
import strutils
|
||||
import wallet
|
||||
|
||||
logScope:
|
||||
topics = "wallet"
|
||||
@ -12,13 +16,13 @@ proc getCustomTokens*(): JsonNode =
|
||||
return %* []
|
||||
return response["result"]
|
||||
|
||||
proc addCustomToken*(address: string, name: string, symbol: string, decimals: int, color: string): string =
|
||||
proc addCustomToken*(address: string, name: string, symbol: string, decimals: int, color: string) =
|
||||
let payload = %* [{"address": address, "name": name, "symbol": symbol, "decimals": decimals, "color": color}]
|
||||
status.callPrivateRPC("wallet_addCustomToken", payload)
|
||||
discard status.callPrivateRPC("wallet_addCustomToken", payload)
|
||||
|
||||
proc removeCustomToken*(address: string): string =
|
||||
proc removeCustomToken*(address: string) =
|
||||
let payload = %* [address]
|
||||
status.callPrivateRPC("wallet_deleteCustomToken", payload)
|
||||
discard status.callPrivateRPC("wallet_deleteCustomToken", payload)
|
||||
|
||||
proc getTokensBalances*(accounts: openArray[string], tokens: openArray[string]): JsonNode =
|
||||
let payload = %* [accounts, tokens]
|
||||
@ -26,3 +30,20 @@ proc getTokensBalances*(accounts: openArray[string], tokens: openArray[string]):
|
||||
if response["result"].kind == JNull:
|
||||
return %* {}
|
||||
response["result"]
|
||||
|
||||
proc getTokenBalance*(tokenAddress: string, account: string): string =
|
||||
var postfixedAccount: string = account
|
||||
postfixedAccount.removePrefix("0x")
|
||||
let payload = %* [{
|
||||
"to": tokenAddress, "from": account, "data": fmt"0x70a08231000000000000000000000000{postfixedAccount}"
|
||||
}, "latest"]
|
||||
var response = status.callPrivateRPC("eth_call", payload)
|
||||
var balance = response.parseJson["result"].getStr
|
||||
result = $hex2Eth(balance)
|
||||
|
||||
proc addOrRemoveToken*(enable: bool, address: string, name: string, symbol: string, decimals: int, color: string): JsonNode =
|
||||
if enable:
|
||||
addCustomToken(address, name, symbol, decimals, color)
|
||||
else:
|
||||
removeCustomToken(address)
|
||||
getCustomTokens()
|
||||
|
@ -34,7 +34,6 @@ proc getWalletAccounts*(): seq[WalletAccount] =
|
||||
let msg = getCurrentExceptionMsg()
|
||||
error "Failed getting wallet accounts", msg
|
||||
|
||||
|
||||
proc sendTransaction*(from_address: string, to: string, value: string, password: string): string =
|
||||
var args = %* {
|
||||
"value": fmt"0x{toHex(value)}",
|
||||
@ -44,17 +43,6 @@ proc sendTransaction*(from_address: string, to: string, value: string, password:
|
||||
var response = status.sendTransaction($args, password)
|
||||
result = response
|
||||
|
||||
proc getPrice*(crypto: string, fiat: string): string =
|
||||
var url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
|
||||
let client = newHttpClient()
|
||||
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||
|
||||
try:
|
||||
let response = client.request(url)
|
||||
result = $parseJson(response.body)[fiat.toUpper]
|
||||
except:
|
||||
echo "error getting price"
|
||||
|
||||
proc getBalance*(address: string): string =
|
||||
let payload = %* [address, "latest"]
|
||||
parseJson(status.callPrivateRPC("eth_getBalance", payload))["result"].str
|
||||
|
@ -1,28 +1,14 @@
|
||||
import eventemitter
|
||||
import json
|
||||
import strformat
|
||||
import strutils
|
||||
import libstatus/wallet as status_wallet
|
||||
import libstatus/settings as status_settings
|
||||
import eventemitter, json, strformat, strutils, tables, chronicles, sequtils
|
||||
import libstatus/accounts as status_accounts
|
||||
import libstatus/accounts/constants as constants
|
||||
import libstatus/types
|
||||
import chronicles
|
||||
import libstatus/tokens as status_tokens
|
||||
|
||||
type CurrencyArgs* = ref object of Args
|
||||
currency*: string
|
||||
|
||||
type Asset* = ref object
|
||||
name*, symbol*, value*, fiatValue*, image*: string
|
||||
|
||||
type Account* = ref object
|
||||
name*, address*, iconColor*, balance*: string
|
||||
realFiatBalance*: float
|
||||
assetList*: seq[Asset]
|
||||
|
||||
type AccountArgs* = ref object of Args
|
||||
account*: Account
|
||||
import libstatus/settings as status_settings
|
||||
import libstatus/wallet as status_wallet
|
||||
import libstatus/accounts/constants as constants
|
||||
from libstatus/types import GeneratedAccount, DerivedAccount
|
||||
import wallet/token_list
|
||||
import wallet/balance_manager
|
||||
import wallet/account
|
||||
export account
|
||||
|
||||
type WalletModel* = ref object
|
||||
events*: EventEmitter
|
||||
@ -30,7 +16,6 @@ type WalletModel* = ref object
|
||||
defaultCurrency*: string
|
||||
tokens*: JsonNode
|
||||
|
||||
proc updateBalance*(self: Account)
|
||||
proc getDefaultCurrency*(self: WalletModel): string
|
||||
|
||||
proc newWalletModel*(events: EventEmitter): WalletModel =
|
||||
@ -44,7 +29,7 @@ proc initEvents*(self: WalletModel) =
|
||||
self.events.on("currencyChanged") do(e: Args):
|
||||
self.defaultCurrency = self.getDefaultCurrency()
|
||||
for account in self.accounts:
|
||||
account.updateBalance()
|
||||
updateBalance(account, self.getDefaultCurrency())
|
||||
self.events.emit("accountsUpdated", Args())
|
||||
|
||||
proc delete*(self: WalletModel) =
|
||||
@ -53,80 +38,34 @@ proc delete*(self: WalletModel) =
|
||||
proc sendTransaction*(self: WalletModel, from_value: string, to: string, value: string, password: string): string =
|
||||
status_wallet.sendTransaction(from_value, to, value, password)
|
||||
|
||||
proc getEthBalance*(address: string): string =
|
||||
var balance = status_wallet.getBalance(address)
|
||||
echo(fmt"balance in hex: {balance}")
|
||||
|
||||
# 2. convert balance to eth
|
||||
var eth_value = status_wallet.hex2Eth(balance)
|
||||
echo(fmt"balance in eth: {eth_value}")
|
||||
eth_value
|
||||
|
||||
proc getDefaultCurrency*(): string =
|
||||
status_settings.getSettings().parseJSON()["result"]["currency"].getStr
|
||||
|
||||
proc getDefaultCurrency*(self: WalletModel): string =
|
||||
getDefaultCurrency()
|
||||
status_settings.getSettings().parseJSON()["result"]["currency"].getStr
|
||||
|
||||
proc setDefaultCurrency*(self: WalletModel, currency: string) =
|
||||
discard status_settings.saveSettings("currency", currency)
|
||||
self.events.emit("currencyChanged", CurrencyArgs(currency: currency))
|
||||
|
||||
proc getFiatValue*(eth_balance: string, symbol: string, fiat_symbol: string): float =
|
||||
if eth_balance == "0.0": return 0.0
|
||||
# 3. get usd price of 1 eth
|
||||
var fiat_eth_price = status_wallet.getPrice("ETH", fiat_symbol)
|
||||
echo(fmt"fiat_price: {fiat_eth_price}")
|
||||
|
||||
# 4. convert balance to usd
|
||||
var fiat_balance = parseFloat(eth_balance) * parseFloat(fiat_eth_price)
|
||||
echo(fmt"balance in usd: {fiat_balance}")
|
||||
fiat_balance
|
||||
|
||||
proc hasAsset*(self: WalletModel, account: string, symbol: string): bool =
|
||||
for token in self.tokens:
|
||||
if symbol == token["symbol"].getStr:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc updateBalance*(self: Account) =
|
||||
let defaultCurrency = getDefaultCurrency()
|
||||
const symbol = "ETH"
|
||||
let eth_balance = getEthBalance(self.address)
|
||||
let usd_balance = getFiatValue(eth_balance, symbol, defaultCurrency)
|
||||
var totalAccountBalance = usd_balance
|
||||
self.balance = fmt"{totalAccountBalance:.2f} {defaultCurrency}"
|
||||
|
||||
proc generateAccountConfiguredAssets*(self: WalletModel): seq[Asset] =
|
||||
var assets: seq[Asset] = @[]
|
||||
var symbol = "ETH"
|
||||
var asset = Asset(name:"Ethereum", symbol: symbol, value: fmt"0.0", fiatValue: "$" & fmt"0.0", image: fmt"../../img/token-icons/{toLowerAscii(symbol)}.svg")
|
||||
var asset = Asset(name:"Ethereum", symbol: "ETH", value: "0.0", fiatValue: "0.0", image: fmt"../../img/token-icons/eth.svg", hasIcon: true)
|
||||
assets.add(asset)
|
||||
for token in self.tokens:
|
||||
var symbol = token["symbol"].getStr
|
||||
var existingToken = Asset(name: token["name"].getStr, symbol: symbol, value: fmt"0.0", fiatValue: "$0.0", image: fmt"../../img/token-icons/{toLowerAscii(symbol)}.svg")
|
||||
var existingToken = Asset(name: token["name"].getStr, symbol: symbol, value: fmt"0.0", fiatValue: "$0.0", image: fmt"../../img/token-icons/{toLowerAscii(symbol)}.svg", hasIcon: true)
|
||||
assets.add(existingToken)
|
||||
assets
|
||||
|
||||
proc newAccount*(self: WalletModel, name: string, address: string, iconColor: string, balance: string): Account =
|
||||
var assets: seq[Asset] = self.generateAccountConfiguredAssets()
|
||||
var account = Account(name: name, address: address, iconColor: iconColor, balance: fmt"{balance} {self.defaultCurrency}", assetList: assets, realFiatBalance: 0.0)
|
||||
updateBalance(account, self.getDefaultCurrency())
|
||||
account
|
||||
|
||||
proc initAccounts*(self: WalletModel) =
|
||||
self.tokens = status_tokens.getCustomTokens()
|
||||
let accounts = status_wallet.getWalletAccounts()
|
||||
|
||||
var totalAccountBalance: float = 0
|
||||
const symbol = "ETH"
|
||||
let defaultCurrency = getDefaultCurrency()
|
||||
|
||||
for account in accounts:
|
||||
let address = account.address
|
||||
let eth_balance = getEthBalance(address)
|
||||
let usd_balance = getFiatValue(eth_balance, symbol, defaultCurrency)
|
||||
|
||||
totalAccountBalance = totalAccountBalance + usd_balance
|
||||
|
||||
var assets: seq[Asset] = self.generateAccountConfiguredAssets()
|
||||
|
||||
var account = Account(name: account.name, address: address, iconColor: account.color, balance: "", assetList: assets, realFiatBalance: totalAccountBalance)
|
||||
account.updateBalance()
|
||||
var account = self.newAccount(account.name, account.address, account.color, "")
|
||||
self.accounts.add(account)
|
||||
|
||||
proc getTotalFiatBalance*(self: WalletModel): string =
|
||||
@ -135,21 +74,8 @@ proc getTotalFiatBalance*(self: WalletModel): string =
|
||||
|
||||
proc addNewGeneratedAccount(self: WalletModel, generatedAccount: GeneratedAccount, password: string, accountName: string, color: string, accountType: string, isADerivedAccount = true) =
|
||||
generatedAccount.name = accountName
|
||||
|
||||
var derivedAccount: DerivedAccount
|
||||
try:
|
||||
derivedAccount = status_accounts.saveAccount(generatedAccount, password, color, accountType, isADerivedAccount)
|
||||
except:
|
||||
error "Error storing the new account. Bad password?"
|
||||
return
|
||||
|
||||
# TODO actually fetch the balance for accounts that are not generated
|
||||
var symbol = "SNT"
|
||||
var asset = Asset(name:"Status", symbol: symbol, value: fmt"0.0", fiatValue: "$" & fmt"0.0", image: fmt"../../img/token-icons/{toLowerAscii(symbol)}.svg")
|
||||
|
||||
var assets: seq[Asset] = self.generateAccountConfiguredAssets()
|
||||
var account = Account(name: accountName, address: derivedAccount.address, iconColor: color, balance: fmt"0.00 {self.defaultCurrency}", assetList: assets, realFiatBalance: 0.0)
|
||||
|
||||
var derivedAccount: DerivedAccount = status_accounts.saveAccount(generatedAccount, password, color, accountType, isADerivedAccount)
|
||||
var account = self.newAccount(accountName, derivedAccount.address, color, fmt"0.00 {self.defaultCurrency}")
|
||||
self.accounts.add(account)
|
||||
self.events.emit("newAccountAdded", AccountArgs(account: account))
|
||||
|
||||
@ -171,13 +97,12 @@ proc addWatchOnlyAccount*(self: WalletModel, address: string, accountName: strin
|
||||
let account = GeneratedAccount(address: address)
|
||||
self.addNewGeneratedAccount(account, "", accountName, color, constants.WATCH, false)
|
||||
|
||||
proc hasAsset*(self: WalletModel, account: string, symbol: string): bool =
|
||||
self.tokens.anyIt(it["symbol"].getStr == symbol)
|
||||
|
||||
proc toggleAsset*(self: WalletModel, symbol: string, enable: bool, address: string, name: string, decimals: int, color: string) =
|
||||
if enable:
|
||||
discard status_tokens.addCustomToken(address, name, symbol, decimals, color)
|
||||
else:
|
||||
discard status_tokens.removeCustomToken(address)
|
||||
self.tokens = status_tokens.getCustomTokens()
|
||||
self.tokens = addOrRemoveToken(enable, address, name, symbol, decimals, color)
|
||||
for account in self.accounts:
|
||||
var assets: seq[Asset] = self.generateAccountConfiguredAssets()
|
||||
account.assetList = assets
|
||||
account.assetList = self.generateAccountConfiguredAssets()
|
||||
updateBalance(account, self.getDefaultCurrency())
|
||||
self.events.emit("assetChanged", Args())
|
||||
|
16
src/status/wallet/account.nim
Normal file
16
src/status/wallet/account.nim
Normal file
@ -0,0 +1,16 @@
|
||||
import eventemitter
|
||||
|
||||
type CurrencyArgs* = ref object of Args
|
||||
currency*: string
|
||||
|
||||
type Asset* = ref object
|
||||
name*, symbol*, value*, fiatValue*, image*: string
|
||||
hasIcon*: bool
|
||||
|
||||
type Account* = ref object
|
||||
name*, address*, iconColor*, balance*: string
|
||||
realFiatBalance*: float
|
||||
assetList*: seq[Asset]
|
||||
|
||||
type AccountArgs* = ref object of Args
|
||||
account*: Account
|
67
src/status/wallet/balance_manager.nim
Normal file
67
src/status/wallet/balance_manager.nim
Normal file
@ -0,0 +1,67 @@
|
||||
import tables, strformat, strutils, stint, httpclient, json
|
||||
import ../libstatus/wallet as status_wallet
|
||||
import ../libstatus/tokens as status_tokens
|
||||
import account
|
||||
import token_list
|
||||
|
||||
type BalanceManager* = ref object
|
||||
pricePairs: Table[string, string]
|
||||
tokenBalances: Table[string, string]
|
||||
|
||||
proc newBalanceManager*(): BalanceManager =
|
||||
result = BalanceManager()
|
||||
result.pricePairs = initTable[string, string]()
|
||||
result.tokenBalances = initTable[string, string]()
|
||||
|
||||
var balanceManager = newBalanceManager()
|
||||
|
||||
proc getPrice(crypto: string, fiat: string): string =
|
||||
try:
|
||||
if balanceManager.pricePairs.hasKey(fiat):
|
||||
return balanceManager.pricePairs[fiat]
|
||||
var url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
|
||||
echo url
|
||||
let client = newHttpClient()
|
||||
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||
|
||||
let response = client.request(url)
|
||||
echo $response.body
|
||||
result = $parseJson(response.body)[fiat.toUpper]
|
||||
balanceManager.pricePairs[fiat] = result
|
||||
except Exception as e:
|
||||
echo "error getting price"
|
||||
echo e.msg
|
||||
|
||||
proc getEthBalance(address: string): string =
|
||||
var balance = status_wallet.getBalance(address)
|
||||
result = status_wallet.hex2Eth(balance)
|
||||
balanceManager.tokenBalances["ETH"] = result
|
||||
|
||||
proc getBalance*(symbol: string, accountAddress: string): string =
|
||||
if balanceManager.tokenBalances.hasKey(symbol):
|
||||
return balanceManager.tokenBalances[symbol]
|
||||
|
||||
if symbol == "ETH":
|
||||
return getEthBalance(accountAddress)
|
||||
var token: AssetConfig = getTokenConfig(symbol)
|
||||
result = $status_tokens.getTokenBalance(token.address, accountAddress)
|
||||
balanceManager.tokenBalances[symbol] = result
|
||||
|
||||
proc getFiatValue*(crypto_balance: string, crypto_symbol: string, fiat_symbol: string): float =
|
||||
if crypto_balance == "0.0": return 0.0
|
||||
var fiat_crypto_price = getPrice(crypto_symbol, fiat_symbol)
|
||||
parseFloat(crypto_balance) * parseFloat(fiat_crypto_price)
|
||||
|
||||
proc updateBalance*(asset: Asset, currency: string) =
|
||||
var token_balance = getBalance(asset.symbol, "0xf977814e90da44bfa03b6295a0616a897441acec")
|
||||
let fiat_balance = getFiatValue(token_balance, asset.symbol, currency)
|
||||
asset.value = token_balance
|
||||
asset.fiatValue = fmt"{fiat_Balance:.2f} {currency}"
|
||||
|
||||
proc updateBalance*(account: Account, currency: string) =
|
||||
let eth_balance = getBalance("ETH", account.address)
|
||||
let usd_balance = getFiatValue(eth_balance, "ETH", currency)
|
||||
var totalAccountBalance = usd_balance
|
||||
account.balance = fmt"{totalAccountBalance:.2f} {currency}"
|
||||
for asset in account.assetList:
|
||||
updateBalance(asset, currency)
|
1333
src/status/wallet/token_list.nim
Normal file
1333
src/status/wallet/token_list.nim
Normal file
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ Item {
|
||||
id: assetInfoImage
|
||||
width: 36
|
||||
height: 36
|
||||
source: image
|
||||
source: hasIcon ? "../../img/tokens/" + symbol + ".png" : "../../img/tokens/0-native.png"
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
BIN
ui/app/img/tokens/ETH.png
Normal file
BIN
ui/app/img/tokens/ETH.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 870 B |
BIN
ui/app/img/tokens/ETH@2x.png
Normal file
BIN
ui/app/img/tokens/ETH@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
ui/app/img/tokens/ETH@3x.png
Normal file
BIN
ui/app/img/tokens/ETH@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
Loading…
x
Reference in New Issue
Block a user