Skip to content

Commit

Permalink
feat(@desktop/Wallet): use new status-go currency formatting API
Browse files Browse the repository at this point in the history
Fixes #9453
  • Loading branch information
dlipicar authored and iurimatias committed Feb 24, 2023
1 parent 4fe7de8 commit 2447272
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 113 deletions.
4 changes: 3 additions & 1 deletion src/app/boot/app_controller.nim
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.tokenService = token_service.newService(
statusFoundation.events, statusFoundation.threadpool, result.networkService
)
result.currencyService = currency_service.newService(result.tokenService, result.settingsService)
result.currencyService = currency_service.newService(
statusFoundation.events, statusFoundation.threadpool, result.tokenService, result.settingsService
)
result.collectibleService = collectible_service.newService(statusFoundation.events, statusFoundation.threadpool, result.networkService)
result.walletAccountService = wallet_account_service.newService(
statusFoundation.events, statusFoundation.threadpool, result.settingsService, result.accountsService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type
currentAccountIndex: int

proc onTokensRebuilt(self: Module, accountsTokens: OrderedTable[string, seq[WalletTokenDto]])
proc onCurrencyFormatsUpdated(self: Module)

proc newModule*(
delegate: delegate_interface.AccessInterface,
Expand Down Expand Up @@ -105,6 +106,9 @@ method load*(self: Module) =
let arg = TokensPerAccountArgs(e)
self.onTokensRebuilt(arg.accountsTokens)

self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
self.onCurrencyFormatsUpdated()

self.controller.init()
self.view.load()
self.switchAccount(0)
Expand All @@ -125,5 +129,10 @@ proc onTokensRebuilt(self: Module, accountsTokens: OrderedTable[string, seq[Wall
return
self.setAssets(accountsTokens[walletAccount.address])

proc onCurrencyFormatsUpdated(self: Module) =
# Update assets
let walletAccount = self.controller.getWalletAccount(self.currentAccountIndex)
self.setAssets(walletAccount.tokens)

method findTokenSymbolByAddress*(self: Module, address: string): string =
return self.controller.findTokenSymbolByAddress(address)
3 changes: 3 additions & 0 deletions src/app/modules/main/wallet_section/accounts/module.nim
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ method load*(self: Module) =
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
self.refreshWalletAccounts()

self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
self.refreshWalletAccounts()

self.events.on(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
let args = KeycardActivityArgs(e)
if not args.success:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type
currentAccountIndex: int

proc onTokensRebuilt(self: Module, accountsTokens: OrderedTable[string, seq[WalletTokenDto]])
proc onCurrencyFormatsUpdated(self: Module)

proc newModule*(
delegate: delegate_interface.AccessInterface,
Expand Down Expand Up @@ -68,6 +69,9 @@ method load*(self: Module) =
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
let arg = TokensPerAccountArgs(e)
self.onTokensRebuilt(arg.accountsTokens)

self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
self.onCurrencyFormatsUpdated()

self.controller.init()
self.view.load()
Expand Down Expand Up @@ -153,6 +157,11 @@ proc onTokensRebuilt(self: Module, accountsTokens: OrderedTable[string, seq[Wall
return
self.setAssetsAndBalance(accountsTokens[walletAccount.address])

proc onCurrencyFormatsUpdated(self: Module) =
# Update assets
let walletAccount = self.controller.getWalletAccount(self.currentAccountIndex)
self.setAssetsAndBalance(walletAccount.tokens)

method findTokenSymbolByAddress*(self: Module, address: string): string =
return self.controller.findTokenSymbolByAddress(address)

2 changes: 2 additions & 0 deletions src/app/modules/main/wallet_section/module.nim
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ method load*(self: Module) =
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
self.setTotalCurrencyBalance()
self.view.setTokensLoading(false)
self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
self.setTotalCurrencyBalance()

self.controller.init()
self.view.load()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ proc init*(self: Controller) =
let args = TransactionsLoadedArgs(e)
self.delegate.setHistoryFetchState(args.address, isFetching = false)

self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
# TODO: Rebuild Transaction items
discard

proc watchPendingTransactions*(self: Controller): seq[TransactionDto] =
return self.transactionService.watchPendingTransactions()

Expand Down
2 changes: 1 addition & 1 deletion src/app/modules/shared_models/currency_amount_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ proc currencyAmountToItem*(amount: float64, format: CurrencyFormatDto) : Currenc
return newCurrencyAmount(
amount,
format.symbol,
format.displayDecimals,
int(format.displayDecimals),
format.stripTrailingZeroes
)
8 changes: 8 additions & 0 deletions src/app_service/common/json_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ template getProp(obj: JsonNode, prop: string, value: var typedesc[int64]): bool

success

template getProp(obj: JsonNode, prop: string, value: var typedesc[uint]): bool =
var success = false
if (obj.kind == JObject and obj.contains(prop)):
value = uint(obj[prop].getInt)
success = true

success

template getProp(obj: JsonNode, prop: string, value: var typedesc[uint64]): bool =
var success = false
if (obj.kind == JObject and obj.contains(prop)):
Expand Down
16 changes: 16 additions & 0 deletions src/app_service/service/currency/async_tasks.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type
FetchAllCurrencyFormatsTaskArg = ref object of QObjectTaskArg
discard

const fetchAllCurrencyFormatsTaskArg: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[FetchAllCurrencyFormatsTaskArg](argEncoded)
let output = %* {
"formats": ""
}
try:
let response = backend.fetchAllCurrencyFormats()
output["formats"] = response.result
except Exception as e:
let errDesription = e.msg
error "error fetchAllCurrencyFormatsTaskArg: ", errDesription
arg.finish(output)
28 changes: 27 additions & 1 deletion src/app_service/service/currency/dto.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import json
include ../../common/json_utils

type
CurrencyFormatDto* = ref object
symbol*: string
displayDecimals*: int
displayDecimals*: uint
stripTrailingZeroes*: bool

proc newCurrencyFormatDto*(
symbol: string,
displayDecimals: uint,
stripTrailingZeroes: bool,
): CurrencyFormatDto =
return CurrencyFormatDto(
symbol: symbol,
displayDecimals: displayDecimals,
stripTrailingZeroes: stripTrailingZeroes
)

proc newCurrencyFormatDto*(symbol: string = ""): CurrencyFormatDto =
return CurrencyFormatDto(
symbol: symbol,
displayDecimals: 8,
stripTrailingZeroes: true
)

proc toCurrencyFormatDto*(jsonObj: JsonNode): CurrencyFormatDto =
result = CurrencyFormatDto()
discard jsonObj.getProp("symbol", result.symbol)
discard jsonObj.getProp("displayDecimals", result.displayDecimals)
discard jsonObj.getProp("stripTrailingZeroes", result.stripTrailingZeroes)
127 changes: 75 additions & 52 deletions src/app_service/service/currency/service.nim
Original file line number Diff line number Diff line change
@@ -1,79 +1,102 @@
import NimQml, strformat, strutils, tables
import NimQml, strformat, chronicles, strutils, tables, json

import ../../../backend/backend as backend

import ../../../app/core/eventemitter
import ../../../app/core/tasks/[qt, threadpool]
import ../../../app/core/signals/types

import ../settings/service as settings_service
import ../token/service as token_service
import ./dto, ./utils
import ../../common/cache
import ./dto

include ../../common/json_utils
include async_tasks

export dto

const DECIMALS_CALCULATION_CURRENCY = "USD"
# Signals which may be emitted by this service:
const SIGNAL_CURRENCY_FORMATS_UPDATED* = "currencyFormatsUpdated"

type
CurrencyFormatsUpdatedArgs* = ref object of Args
discard

QtObject:
type Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
tokenService: token_service.Service
settingsService: settings_service.Service
isCurrencyFiatCache: Table[string, bool] # Fiat info does not change, we can fetch/calculate once and
fiatCurrencyFormatCache: Table[string, CurrencyFormatDto] # keep the results forever.
tokenCurrencyFormatCache: TimedCache[CurrencyFormatDto] # Token format changes with price, so we use a timed cache.
currencyFormatCache: Table[string, CurrencyFormatDto]

# Forward declarations
proc fetchAllCurrencyFormats(self: Service)
proc getCachedCurrencyFormats(self: Service): Table[string, CurrencyFormatDto]

proc delete*(self: Service) =
self.QObject.delete

proc newService*(
events: EventEmitter,
threadpool: ThreadPool,
tokenService: token_service.Service,
settingsService: settings_service.Service,
): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
result.tokenService = tokenService
result.settingsService = settingsService
result.tokenCurrencyFormatCache = newTimedCache[CurrencyFormatDto]()

proc init*(self: Service) =
discard
self.events.on(SignalType.Wallet.event) do(e:Args):
var data = WalletSignal(e)
case data.eventType:
of "wallet-currency-tick-update-format":
self.fetchAllCurrencyFormats()
discard
# Load cache from DB
self.currencyFormatCache = self.getCachedCurrencyFormats()
# Trigger async fetch
self.fetchAllCurrencyFormats()

proc jsonToFormatsTable(node: JsonNode) : Table[string, CurrencyFormatDto] =
result = initTable[string, CurrencyFormatDto]()

for (symbol, formatObj) in node.pairs:
result[symbol] = formatObj.toCurrencyFormatDto()

proc getCachedCurrencyFormats(self: Service): Table[string, CurrencyFormatDto] =
try:
let response = backend.getCachedCurrencyFormats()
result = jsonToFormatsTable(response.result)
except Exception as e:
let errDesription = e.msg
error "error getCachedCurrencyFormats: ", errDesription

proc onAllCurrencyFormatsFetched(self: Service, response: string) {.slot.} =
try:
let responseObj = response.parseJson
if (responseObj.kind == JObject):
let formatsPerSymbol = jsonToFormatsTable(responseObj)
for symbol, format in formatsPerSymbol:
self.currencyFormatCache[symbol] = format
self.events.emit(SIGNAL_CURRENCY_FORMATS_UPDATED, CurrencyFormatsUpdatedArgs())
except Exception as e:
let errDescription = e.msg
error "error onAllCurrencyFormatsFetched: ", errDescription

proc isCurrencyFiat(self: Service, symbol: string): bool =
if not self.isCurrencyFiatCache.hasKey(symbol):
self.isCurrencyFiatCache[symbol] = isCurrencyFiat(symbol)
return self.isCurrencyFiatCache[symbol]

proc getFiatCurrencyFormat(self: Service, symbol: string): CurrencyFormatDto =
if not self.fiatCurrencyFormatCache.hasKey(symbol):
self.fiatCurrencyFormatCache[symbol] = CurrencyFormatDto(
symbol: toUpperAscii(symbol),
displayDecimals: getFiatDisplayDecimals(symbol),
stripTrailingZeroes: false
)
return self.fiatCurrencyFormatCache[symbol]

proc getTokenCurrencyFormat(self: Service, symbol: string): CurrencyFormatDto =
if self.tokenCurrencyFormatCache.isCached(symbol):
return self.tokenCurrencyFormatCache.get(symbol)

var updateCache = true
let pegSymbol = self.tokenService.getTokenPegSymbol(symbol)
if pegSymbol != "":
let currencyFormat = self.getFiatCurrencyFormat(pegSymbol)
result = CurrencyFormatDto(
symbol: symbol,
displayDecimals: currencyFormat.displayDecimals,
stripTrailingZeroes: currencyFormat.stripTrailingZeroes
)
updateCache = true
else:
let price = self.tokenService.getCachedTokenPrice(symbol, DECIMALS_CALCULATION_CURRENCY, true)
result = CurrencyFormatDto(
symbol: symbol,
displayDecimals: getTokenDisplayDecimals(price),
stripTrailingZeroes: true
)
updateCache = self.tokenService.isCachedTokenPriceRecent(symbol, DECIMALS_CALCULATION_CURRENCY)

if updateCache:
self.tokenCurrencyFormatCache.set(symbol, result)
proc fetchAllCurrencyFormats(self: Service) =
let arg = FetchAllCurrencyFormatsTaskArg(
tptr: cast[ByteAddress](fetchAllCurrencyFormatsTaskArg),
vptr: cast[ByteAddress](self.vptr),
slot: "onAllCurrencyFormatsFetched",
)
self.threadpool.start(arg)

proc getCurrencyFormat*(self: Service, symbol: string): CurrencyFormatDto =
if self.isCurrencyFiat(symbol):
return self.getFiatCurrencyFormat(symbol)
else:
return self.getTokenCurrencyFormat(symbol)
if not self.currencyFormatCache.hasKey(symbol):
return newCurrencyFormatDto(symbol)
return self.currencyFormatCache[symbol]
33 changes: 0 additions & 33 deletions src/app_service/service/currency/utils.nim

This file was deleted.

4 changes: 2 additions & 2 deletions src/app_service/service/settings/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export settings_dto
export stickers_dto

# Default values:
const DEFAULT_CURRENCY* = "usd"
const DEFAULT_CURRENCY* = "USD"
const DEFAULT_TELEMETRY_SERVER_URL* = "https://telemetry.status.im"
const DEFAULT_FLEET* = $Fleet.StatusProd

Expand Down Expand Up @@ -106,7 +106,7 @@ QtObject:
if(self.settings.currency.len == 0):
self.settings.currency = DEFAULT_CURRENCY

return self.settings.currency
return self.settings.currency.toUpperAscii()

proc saveDappsAddress*(self: Service, value: string): bool =
if(self.saveSetting(KEY_DAPPS_ADDRESS, value)):
Expand Down
Loading

0 comments on commit 2447272

Please sign in to comment.