status-lib/status/wallet/balance_manager.nim

95 lines
3.8 KiB
Nim

import strformat, strutils, stint, httpclient, json, chronicles, net
import ../statusgo_backend/wallet as status_wallet
import ../statusgo_backend/eth as eth
import ../statusgo_backend/settings as status_settings
import ../statusgo_backend/network
import ../eth/tokens as status_tokens
import ../types/[rpc_response, network_type]
import ../utils/cache
import account
import options
logScope:
topics = "balance-manager"
type BalanceManager* = ref object
pricePairs: CachedValues
tokenBalances: CachedValues
proc newBalanceManager*(): BalanceManager =
result = BalanceManager()
result.pricePairs = newCachedValues()
result.tokenBalances = newCachedValues()
var balanceManager = newBalanceManager()
proc getPrice(crypto: string, fiat: string): string =
let secureSSLContext = newContext()
let client = newHttpClient(sslContext = secureSSLContext)
try:
let url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
let response = client.request(url)
result = $parseJson(response.body)[fiat.toUpper]
except Exception as e:
error "Error getting price", message = e.msg
result = "0.0"
finally:
client.close()
proc getEthBalance(address: string): string =
var balance = eth.getBalance(address)
result = status_wallet.hex2token(balance, 18)
proc getBalance*(symbol: string, accountAddress: string, tokenAddress: string, refreshCache: bool): string =
let cacheKey = fmt"{symbol}-{accountAddress}-{tokenAddress}"
if not refreshCache and balanceManager.tokenBalances.isCached(cacheKey):
return balanceManager.tokenBalances.get(cacheKey)
if symbol == "ETH":
let ethBalance = getEthBalance(accountAddress)
return ethBalance
let network = status_settings.getCurrentNetwork().toNetwork()
result = $status_tokens.getTokenBalance(network, tokenAddress, accountAddress)
balanceManager.tokenBalances.cacheValue(cacheKey, result)
proc convertValue*(balance: string, fromCurrency: string, toCurrency: string): float =
if balance == "0.0": return 0.0
let cacheKey = fmt"{fromCurrency}-{toCurrency}"
if balanceManager.pricePairs.isCached(cacheKey):
return parseFloat(balance) * parseFloat(balanceManager.pricePairs.get(cacheKey))
var fiat_crypto_price = getPrice(fromCurrency, toCurrency)
balanceManager.pricePairs.cacheValue(cacheKey, fiat_crypto_price)
parseFloat(balance) * parseFloat(fiat_crypto_price)
proc updateBalance*(asset: Asset, currency: string, refreshCache: bool): float =
var token_balance = getBalance(asset.symbol, asset.accountAddress, asset.address, refreshCache)
let fiat_balance = convertValue(token_balance, asset.symbol, currency)
asset.value = token_balance
asset.fiatBalanceDisplay = fmt"{fiat_balance:.2f} {currency}"
asset.fiatBalance = fmt"{fiat_balance:.2f}"
return fiat_balance
proc updateBalance*(account: WalletAccount, currency: string, refreshCache: bool = false) =
try:
var usd_balance = 0.0
for asset in account.assetList:
let assetFiatBalance = updateBalance(asset, currency, refreshCache)
usd_balance = usd_balance + assetFiatBalance
account.realFiatBalance = some(usd_balance)
account.balance = some(fmt"{usd_balance:.2f} {currency}")
except RpcException:
error "Error in updateBalance", message = getCurrentExceptionMsg()
proc storeBalances*(account: WalletAccount, ethBalance = "0", tokenBalance: JsonNode) =
let ethCacheKey = fmt"ETH-{account.address}-"
balanceManager.tokenBalances.cacheValue(ethCacheKey, ethBalance)
for asset in account.assetList:
if tokenBalance.hasKey(asset.address):
let cacheKey = fmt"{asset.symbol}-{account.address}-{asset.address}"
balanceManager.tokenBalances.cacheValue(cacheKey, tokenBalance{asset.address}.getStr())