mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-24 12:38:57 +00:00
parent
23426f184b
commit
570f312617
@ -54,16 +54,13 @@ proc getCurrencyAmount*(self: Controller, amount: float64, symbol: string): Curr
|
||||
proc updateCurrency*(self: Controller, currency: string) =
|
||||
self.walletAccountService.updateCurrency(currency)
|
||||
|
||||
# proc getIndex*(self: Controller, address: string): int =
|
||||
# return self.walletAccountService.getIndex(address)
|
||||
|
||||
proc getNetworks*(self: Controller): seq[NetworkDto] =
|
||||
return self.networkService.getNetworks()
|
||||
|
||||
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
|
||||
return self.walletAccountService.getWalletAccounts()
|
||||
|
||||
proc getEnabledChainIds*(self: Controller): seq[int] =
|
||||
proc getEnabledChainIds*(self: Controller): seq[int] =
|
||||
return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId)
|
||||
|
||||
proc toggleIncludeWatchOnlyAccount*(self: Controller) =
|
||||
|
File diff suppressed because it is too large
Load Diff
588
src/app_service/service/wallet_account/service_account.nim
Normal file
588
src/app_service/service/wallet_account/service_account.nim
Normal file
@ -0,0 +1,588 @@
|
||||
proc storeWatchOnlyAccount(self: Service, account: WalletAccountDto) =
|
||||
if self.watchOnlyAccounts.hasKey(account.address):
|
||||
error "trying to store an already existing watch only account"
|
||||
return
|
||||
self.watchOnlyAccounts[account.address] = account
|
||||
|
||||
proc storeKeypair(self: Service, keypair: KeypairDto) =
|
||||
if keypair.keyUid.len == 0:
|
||||
error "trying to store a keypair with empty keyUid"
|
||||
return
|
||||
if self.keypairs.hasKey(keypair.keyUid):
|
||||
error "trying to store an already existing keypair"
|
||||
return
|
||||
self.keypairs[keypair.keyUid] = keypair
|
||||
|
||||
proc storeAccountToKeypair(self: Service, account: WalletAccountDto) =
|
||||
if account.keyUid.len == 0:
|
||||
error "trying to store a keypair related account with empty keyUid"
|
||||
return
|
||||
if self.keypairs[account.keyUid].accounts.filter(acc => cmpIgnoreCase(acc.address, account.address) == 0).len != 0:
|
||||
error "trying to store an already existing keytpair related account"
|
||||
return
|
||||
self.keypairs[account.keyUid].accounts.add(account)
|
||||
|
||||
proc getKeypairs*(self: Service): seq[KeypairDto] =
|
||||
return toSeq(self.keypairs.values)
|
||||
|
||||
proc getKeypairByKeyUid*(self: Service, keyUid: string): KeypairDto =
|
||||
if not self.keypairs.hasKey(keyUid):
|
||||
return
|
||||
return self.keypairs[keyUid]
|
||||
|
||||
proc getWatchOnlyAccounts*(self: Service): seq[WalletAccountDto] =
|
||||
return toSeq(self.watchOnlyAccounts.values)
|
||||
|
||||
proc getWatchOnlyAccountByAddress*(self: Service, address: string): WalletAccountDto =
|
||||
if not self.watchOnlyAccounts.hasKey(address):
|
||||
return
|
||||
return self.watchOnlyAccounts[address]
|
||||
|
||||
proc getAccountByAddress*(self: Service, address: string): WalletAccountDto =
|
||||
result = self.getWatchOnlyAccountByAddress(address)
|
||||
if not result.isNil:
|
||||
return
|
||||
for _, kp in self.keypairs:
|
||||
let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, address) == 0)
|
||||
if accounts.len == 1:
|
||||
return accounts[0]
|
||||
|
||||
proc getAccountsByAddresses*(self: Service, addresses: seq[string]): seq[WalletAccountDto] =
|
||||
for address in addresses:
|
||||
let acc = self.getAccountByAddress(address)
|
||||
if acc.isNil:
|
||||
continue
|
||||
result.add(acc)
|
||||
|
||||
proc getWalletAccounts*(self: Service): seq[WalletAccountDto] =
|
||||
for _, kp in self.keypairs:
|
||||
if kp.keypairType == KeypairTypeProfile:
|
||||
for acc in kp.accounts:
|
||||
if acc.isChat:
|
||||
continue
|
||||
result.add(acc)
|
||||
continue
|
||||
result.add(kp.accounts)
|
||||
result.add(toSeq(self.watchOnlyAccounts.values))
|
||||
result.sort(walletAccountsCmp)
|
||||
|
||||
proc getWalletAddresses*(self: Service): seq[string] =
|
||||
return self.getWalletAccounts().map(a => a.address)
|
||||
|
||||
proc updateAssetsLoadingState(self: Service, address: string, loading: bool) =
|
||||
var acc = self.getAccountByAddress(address)
|
||||
if acc.isNil:
|
||||
return
|
||||
acc.assetsLoading = loading
|
||||
|
||||
#################################################
|
||||
# TODO: remove functions below
|
||||
#
|
||||
# The only need for a function `getIndex` below is for switching selected account.
|
||||
# Switching an account in UI by the index on which an account is stored in wallet settings service cache
|
||||
# is completely wrong approach we need to handle that properly, at least by using position.
|
||||
proc getIndex*(self: Service, address: string): int =
|
||||
let accounts = self.getWalletAccounts()
|
||||
for i in 0 ..< accounts.len:
|
||||
if cmpIgnoreCase(accounts[i].address, address) == 0:
|
||||
return i
|
||||
# The same for `getWalletAccount`, both of them need to be removed. Parts of the app which are using them
|
||||
# need refactor for sure.
|
||||
proc getWalletAccount*(self: Service, accountIndex: int): WalletAccountDto =
|
||||
let accounts = self.getWalletAccounts()
|
||||
if accountIndex < 0 or accountIndex >= accounts.len:
|
||||
return
|
||||
return accounts[accountIndex]
|
||||
#################################################
|
||||
|
||||
proc startWallet(self: Service) =
|
||||
if(not main_constants.WALLET_ENABLED):
|
||||
return
|
||||
discard backend.startWallet()
|
||||
|
||||
proc init*(self: Service) =
|
||||
try:
|
||||
let chainId = self.networkService.getNetworkForEns().chainId
|
||||
let woAccounts = getWatchOnlyAccountsFromDb()
|
||||
for acc in woAccounts:
|
||||
acc.ens = getEnsName(acc.address, chainId)
|
||||
self.storeWatchOnlyAccount(acc)
|
||||
let keypairs = getKeypairsFromDb()
|
||||
for kp in keypairs:
|
||||
for acc in kp.accounts:
|
||||
acc.ens = getEnsName(acc.address, chainId)
|
||||
self.storeKeypair(kp)
|
||||
|
||||
let addresses = self.getWalletAddresses()
|
||||
self.buildAllTokens(addresses, store = true)
|
||||
self.checkRecentHistory(addresses)
|
||||
self.startWallet()
|
||||
except Exception as e:
|
||||
let errDesription = e.msg
|
||||
error "error: ", errDesription
|
||||
return
|
||||
|
||||
self.events.on(SignalType.Message.event) do(e: Args):
|
||||
var receivedData = MessageSignal(e)
|
||||
if receivedData.watchOnlyAccounts.len > 0:
|
||||
for acc in receivedData.watchOnlyAccounts:
|
||||
self.handleWalletAccount(acc)
|
||||
if receivedData.keypairs.len > 0:
|
||||
for kp in receivedData.keypairs:
|
||||
self.handleKeypair(kp)
|
||||
if receivedData.accountsPositions.len > 0:
|
||||
self.updateAccountsPositions()
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_POSITION_UPDATED, Args())
|
||||
|
||||
self.events.on(SignalType.Wallet.event) do(e:Args):
|
||||
var data = WalletSignal(e)
|
||||
case data.eventType:
|
||||
of "wallet-tick-reload":
|
||||
let addresses = self.getWalletAddresses()
|
||||
self.buildAllTokens(addresses, store = true)
|
||||
self.checkRecentHistory(addresses)
|
||||
|
||||
self.events.on(SIGNAL_CURRENCY_UPDATED) do(e:Args):
|
||||
self.buildAllTokens(self.getWalletAddresses(), store = true)
|
||||
|
||||
proc addNewKeypairsAccountsToLocalStoreAndNotify(self: Service, notify: bool = true) =
|
||||
let chainId = self.networkService.getNetworkForEns().chainId
|
||||
let allLocalAaccounts = self.getWalletAccounts()
|
||||
# check if there is new watch only account
|
||||
let woAccountsDb = getWatchOnlyAccountsFromDb()
|
||||
for woAccDb in woAccountsDb:
|
||||
var found = false
|
||||
for localAcc in allLocalAaccounts:
|
||||
if cmpIgnoreCase(localAcc.address, woAccDb.address) == 0:
|
||||
found = true
|
||||
break
|
||||
if found:
|
||||
continue
|
||||
woAccDb.ens = getEnsName(woAccDb.address, chainId)
|
||||
self.storeWatchOnlyAccount(woAccDb)
|
||||
self.buildAllTokens(@[woAccDb.address], store = true)
|
||||
if notify:
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_SAVED, AccountArgs(account: woAccDb))
|
||||
# check if there is new keypair or any account added to an existing keypair
|
||||
let keypairsDb = getKeypairsFromDb()
|
||||
for kpDb in keypairsDb:
|
||||
var localKp = self.getKeypairByKeyUid(kpDb.keyUid)
|
||||
if localKp.isNil:
|
||||
self.storeKeypair(kpDb)
|
||||
let addresses = kpDb.accounts.map(a => a.address)
|
||||
self.buildAllTokens(addresses, store = true)
|
||||
for acc in kpDb.accounts:
|
||||
acc.ens = getEnsName(acc.address, chainId)
|
||||
if notify:
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_SAVED, AccountArgs(account: acc))
|
||||
else:
|
||||
for accDb in kpDb.accounts:
|
||||
var found = false
|
||||
for localAcc in allLocalAaccounts:
|
||||
if cmpIgnoreCase(localAcc.address, accDb.address) == 0:
|
||||
found = true
|
||||
break
|
||||
if found:
|
||||
continue
|
||||
accDb.ens = getEnsName(accDb.address, chainId)
|
||||
self.storeAccountToKeypair(accDb)
|
||||
self.buildAllTokens(@[accDb.address], store = true)
|
||||
if notify:
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_SAVED, AccountArgs(account: accDb))
|
||||
|
||||
proc removeAccountFromLocalStoreAndNotify(self: Service, address: string, notify: bool = true) =
|
||||
var acc = self.getAccountByAddress(address)
|
||||
if acc.isNil:
|
||||
return
|
||||
if acc.keyUid.len == 0:
|
||||
self.watchOnlyAccounts.del(acc.address)
|
||||
else:
|
||||
var index = -1
|
||||
for i in 0 ..< self.keypairs[acc.keyUid].accounts.len:
|
||||
if cmpIgnoreCase(self.keypairs[acc.keyUid].accounts[i].address, acc.address) == 0:
|
||||
index = i
|
||||
break
|
||||
if index == -1:
|
||||
error "cannot find account with the address to remove", address=address
|
||||
return
|
||||
self.keypairs[acc.keyUid].accounts.del(index)
|
||||
if self.keypairs[acc.keyUid].accounts.len == 0:
|
||||
self.keypairs.del(acc.keyUid)
|
||||
if notify:
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_DELETED, AccountArgs(account: acc))
|
||||
|
||||
proc updateAccountsPositions(self: Service) =
|
||||
let dbAccounts = getAccountsFromDb()
|
||||
for dbAcc in dbAccounts:
|
||||
var localAcc = self.getAccountByAddress(dbAcc.address)
|
||||
if localAcc.isNil:
|
||||
continue
|
||||
localAcc.position = dbAcc.position
|
||||
|
||||
proc updateAccountInLocalStoreAndNotify(self: Service, address, name, colorId, emoji: string,
|
||||
positionUpdated: Option[bool] = none(bool), notify: bool = true) =
|
||||
if address.len > 0:
|
||||
var account = self.getAccountByAddress(address)
|
||||
if account.isNil:
|
||||
return
|
||||
if name.len > 0 or colorId.len > 0 or emoji.len > 0:
|
||||
if name.len > 0 and name != account.name:
|
||||
account.name = name
|
||||
if colorId.len > 0 and colorId != account.colorId:
|
||||
account.colorId = colorId
|
||||
if emoji.len > 0 and emoji != account.emoji:
|
||||
account.emoji = emoji
|
||||
if notify:
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_UPDATED, AccountArgs(account: account))
|
||||
else:
|
||||
if not positionUpdated.isSome:
|
||||
return
|
||||
if positionUpdated.get:
|
||||
## if reordering was successfully stored, we need to update local storage
|
||||
self.updateAccountsPositions()
|
||||
if notify:
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_POSITION_UPDATED, Args())
|
||||
|
||||
proc updatePreferredSharingChainsAndNotify(self: Service, address, prodPreferredChains, testPreferredChains: string) =
|
||||
var account = self.getAccountByAddress(address)
|
||||
if account.isNil:
|
||||
error "account's address is not among known addresses: ", address=address, procName="updatePreferredSharingChainsAndNotify"
|
||||
return
|
||||
if testPreferredChains.len > 0 and testPreferredChains != account.testPreferredChainIds:
|
||||
account.testPreferredChainIds = testPreferredChains
|
||||
if prodPreferredChains.len > 0 and prodPreferredChains != account.prodPreferredChainIds:
|
||||
account.prodPreferredChainIds = prodPreferredChains
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_PREFERRED_SHARING_CHAINS_UPDATED, AccountArgs(account: account))
|
||||
|
||||
## if password is not provided local keystore file won't be created
|
||||
proc addWalletAccount*(self: Service, password: string, doPasswordHashing: bool, name, address, path, publicKey,
|
||||
keyUid, accountType, colorId, emoji: string): string =
|
||||
try:
|
||||
var response: RpcResponse[JsonNode]
|
||||
if password.len == 0:
|
||||
response = status_go_accounts.addAccountWithoutKeystoreFileCreation(name, address, path, publicKey, keyUid,
|
||||
accountType, colorId, emoji)
|
||||
else:
|
||||
var finalPassword = password
|
||||
if doPasswordHashing:
|
||||
finalPassword = utils.hashPassword(password)
|
||||
response = status_go_accounts.addAccount(finalPassword, name, address, path, publicKey, keyUid, accountType,
|
||||
colorId, emoji)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="addWalletAccount", errCode=response.error.code, errDesription=response.error.message
|
||||
return response.error.message
|
||||
self.addNewKeypairsAccountsToLocalStoreAndNotify()
|
||||
return ""
|
||||
except Exception as e:
|
||||
error "error: ", procName="addWalletAccount", errName=e.name, errDesription=e.msg
|
||||
return e.msg
|
||||
|
||||
## Mandatory fields for account: `address`, `keyUid`, `walletType`, `path`, `publicKey`, `name`, `emoji`, `colorId`
|
||||
proc addNewPrivateKeyKeypair*(self: Service, privateKey, password: string, doPasswordHashing: bool,
|
||||
keyUid, keypairName, rootWalletMasterKey: string, account: WalletAccountDto): string =
|
||||
if password.len == 0:
|
||||
error "for adding new private key account, password must be provided"
|
||||
return
|
||||
var finalPassword = password
|
||||
if doPasswordHashing:
|
||||
finalPassword = utils.hashPassword(password)
|
||||
try:
|
||||
var response = status_go_accounts.importPrivateKey(privateKey, finalPassword)
|
||||
if not response.error.isNil:
|
||||
error "status-go error importing private key", procName="addNewPrivateKeyKeypair", errCode=response.error.code, errDesription=response.error.message
|
||||
return response.error.message
|
||||
response = status_go_accounts.addKeypair(finalPassword, keyUid, keypairName, KeypairTypeKey, rootWalletMasterKey, @[account])
|
||||
if not response.error.isNil:
|
||||
error "status-go error adding keypair", procName="addNewPrivateKeyKeypair", errCode=response.error.code, errDesription=response.error.message
|
||||
return response.error.message
|
||||
self.addNewKeypairsAccountsToLocalStoreAndNotify()
|
||||
return ""
|
||||
except Exception as e:
|
||||
error "error: ", procName="addNewPrivateKeyKeypair", errName=e.name, errDesription=e.msg
|
||||
return e.msg
|
||||
|
||||
## Mandatory fields for all accounts: `address`, `keyUid`, `walletType`, `path`, `publicKey`, `name`, `emoji`, `colorId`
|
||||
proc addNewSeedPhraseKeypair*(self: Service, seedPhrase, password: string, doPasswordHashing: bool,
|
||||
keyUid, keypairName, rootWalletMasterKey: string, accounts: seq[WalletAccountDto]): string =
|
||||
var finalPassword = password
|
||||
if password.len > 0 and doPasswordHashing:
|
||||
finalPassword = utils.hashPassword(password)
|
||||
try:
|
||||
if seedPhrase.len > 0 and password.len > 0:
|
||||
let response = status_go_accounts.importMnemonic(seedPhrase, finalPassword)
|
||||
if not response.error.isNil:
|
||||
error "status-go error importing private key", procName="addNewSeedPhraseKeypair", errCode=response.error.code, errDesription=response.error.message
|
||||
return response.error.message
|
||||
let response = status_go_accounts.addKeypair(finalPassword, keyUid, keypairName, KeypairTypeSeed, rootWalletMasterKey, accounts)
|
||||
if not response.error.isNil:
|
||||
error "status-go error adding keypair", procName="addNewSeedPhraseKeypair", errCode=response.error.code, errDesription=response.error.message
|
||||
return response.error.message
|
||||
for i in 0 ..< accounts.len:
|
||||
self.addNewKeypairsAccountsToLocalStoreAndNotify()
|
||||
return ""
|
||||
except Exception as e:
|
||||
error "error: ", procName="addNewSeedPhraseKeypair", errName=e.name, errDesription=e.msg
|
||||
return e.msg
|
||||
|
||||
proc getRandomMnemonic*(self: Service): string =
|
||||
try:
|
||||
let response = status_go_accounts.getRandomMnemonic()
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="getRandomMnemonic", errCode=response.error.code, errDesription=response.error.message
|
||||
return ""
|
||||
return response.result.getStr
|
||||
except Exception as e:
|
||||
error "error: ", procName="getRandomMnemonic", errName=e.name, errDesription=e.msg
|
||||
return ""
|
||||
|
||||
proc deleteAccount*(self: Service, address: string) =
|
||||
try:
|
||||
let response = status_go_accounts.deleteAccount(address)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="deleteAccount", errCode=response.error.code, errDesription=response.error.message
|
||||
return
|
||||
self.removeAccountFromLocalStoreAndNotify(address)
|
||||
except Exception as e:
|
||||
error "error: ", procName="deleteAccount", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc deleteKeypair*(self: Service, keyUid: string) =
|
||||
try:
|
||||
let kp = self.getKeypairByKeyUid(keyUid)
|
||||
if kp.isNil:
|
||||
error "there is no known keypair", keyUid=keyUid, procName="deleteKeypair"
|
||||
return
|
||||
let response = status_go_accounts.deleteKeypair(keyUid)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="deleteKeypair", errCode=response.error.code, errDesription=response.error.message
|
||||
return
|
||||
self.updateAccountsPositions()
|
||||
for acc in kp.accounts:
|
||||
self.removeAccountFromLocalStoreAndNotify(acc.address)
|
||||
except Exception as e:
|
||||
error "error: ", procName="deleteKeypair", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc updateCurrency*(self: Service, newCurrency: string) =
|
||||
discard self.settingsService.saveCurrency(newCurrency)
|
||||
|
||||
proc setNetworksState*(self: Service, chainIds: seq[int], enabled: bool) =
|
||||
self.networkService.setNetworksState(chainIds, enabled)
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED, Args())
|
||||
|
||||
proc toggleTestNetworksEnabled*(self: Service) =
|
||||
discard self.settingsService.toggleTestNetworksEnabled()
|
||||
let addresses = self.getWalletAddresses()
|
||||
self.buildAllTokens(addresses, store = true)
|
||||
self.tokenService.loadData()
|
||||
self.checkRecentHistory(addresses)
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED, Args())
|
||||
|
||||
proc updateWalletAccount*(self: Service, address: string, accountName: string, colorId: string, emoji: string): bool =
|
||||
try:
|
||||
var account = self.getAccountByAddress(address)
|
||||
if account.isNil:
|
||||
error "account's address is not among known addresses: ", address=address, procName="updateWalletAccount"
|
||||
return false
|
||||
let response = status_go_accounts.updateAccount(accountName, account.address, account.path, account.publicKey,
|
||||
account.keyUid, account.walletType, colorId, emoji, account.isWallet, account.isChat, account.prodPreferredChainIds, account.testPreferredChainIds)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="updateWalletAccount", errCode=response.error.code, errDesription=response.error.message
|
||||
return false
|
||||
self.updateAccountInLocalStoreAndNotify(address, accountName, colorId, emoji)
|
||||
return true
|
||||
except Exception as e:
|
||||
error "error: ", procName="updateWalletAccount", errName=e.name, errDesription=e.msg
|
||||
return false
|
||||
|
||||
proc updateWalletAccountProdPreferredChains*(self: Service, address, preferredChainIds: string): bool =
|
||||
try:
|
||||
var account = self.getAccountByAddress(address)
|
||||
if account.isNil:
|
||||
error "account's address is not among known addresses: ", address=address, procName="updateWalletAccountProdPreferredChains"
|
||||
return false
|
||||
let response = status_go_accounts.updateAccount(account.name, account.address, account.path, account.publicKey,
|
||||
account.keyUid, account.walletType, account.colorId, account.emoji, account.isWallet, account.isChat, preferredChainIds, account.testPreferredChainIds)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="updateWalletAccountProdPreferredChains", errCode=response.error.code, errDesription=response.error.message
|
||||
return false
|
||||
self.updatePreferredSharingChainsAndNotify(address, prodPreferredChains = preferredChainIds, testPreferredChains = "")
|
||||
return true
|
||||
except Exception as e:
|
||||
error "error: ", procName="updateWalletAccountProdPreferredChains", errName=e.name, errDesription=e.msg
|
||||
return false
|
||||
|
||||
proc updateWalletAccountTestPreferredChains*(self: Service, address, preferredChainIds: string): bool =
|
||||
try:
|
||||
var account = self.getAccountByAddress(address)
|
||||
if account.isNil:
|
||||
error "account's address is not among known addresses: ", address=address, procName="updateWalletAccountTestPreferredChains"
|
||||
return false
|
||||
let response = status_go_accounts.updateAccount(account.name, account.address, account.path, account.publicKey,
|
||||
account.keyUid, account.walletType, account.colorId, account.emoji, account.isWallet, account.isChat, account.prodPreferredChainIds, preferredChainIds)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="updateWalletAccountTestPreferredChains", errCode=response.error.code, errDesription=response.error.message
|
||||
return false
|
||||
self.updatePreferredSharingChainsAndNotify(address, prodPreferredChains = "", testPreferredChains = preferredChainIds)
|
||||
return true
|
||||
except Exception as e:
|
||||
error "error: ", procName="updateWalletAccountTestPreferredChains", errName=e.name, errDesription=e.msg
|
||||
return false
|
||||
|
||||
proc moveAccountFinally*(self: Service, fromPosition: int, toPosition: int) =
|
||||
var updated = false
|
||||
try:
|
||||
let response = backend.moveWalletAccount(fromPosition, toPosition)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="moveAccountFinally", errCode=response.error.code, errDesription=response.error.message
|
||||
updated = true
|
||||
except Exception as e:
|
||||
error "error: ", procName="moveAccountFinally", errName=e.name, errDesription=e.msg
|
||||
self.updateAccountInLocalStoreAndNotify(address = "", name = "", colorId = "", emoji = "", some(updated))
|
||||
|
||||
proc updateKeypairName*(self: Service, keyUid: string, name: string) =
|
||||
try:
|
||||
let kp = self.getKeypairByKeyUid(keyUid)
|
||||
if kp.isNil:
|
||||
error "there is no known keypair", keyUid=keyUid, procName="updateKeypairName"
|
||||
return
|
||||
let response = backend.updateKeypairName(keyUid, name)
|
||||
if not response.error.isNil:
|
||||
error "status-go error", procName="updateKeypairName", errCode=response.error.code, errDesription=response.error.message
|
||||
return
|
||||
var data = KeypairArgs(
|
||||
keypair: KeypairDto(
|
||||
keyUid: keyUid,
|
||||
name: name
|
||||
),
|
||||
oldKeypairName: kp.name
|
||||
)
|
||||
kp.name = name
|
||||
self.events.emit(SIGNAL_KEYPAIR_NAME_CHANGED, data)
|
||||
except Exception as e:
|
||||
error "error: ", procName="updateKeypairName", errName=e.name, errDesription=e.msg
|
||||
|
||||
proc fetchDerivedAddresses*(self: Service, password: string, derivedFrom: string, paths: seq[string], hashPassword: bool) =
|
||||
let arg = FetchDerivedAddressesTaskArg(
|
||||
password: if hashPassword: utils.hashPassword(password) else: password,
|
||||
derivedFrom: derivedFrom,
|
||||
paths: paths,
|
||||
tptr: cast[ByteAddress](fetchDerivedAddressesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onDerivedAddressesFetched",
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc onDerivedAddressesFetched*(self: Service, jsonString: string) {.slot.} =
|
||||
let response = parseJson(jsonString)
|
||||
var derivedAddress: seq[DerivedAddressDto] = @[]
|
||||
derivedAddress = response["derivedAddresses"].getElems().map(x => x.toDerivedAddressDto())
|
||||
let error = response["error"].getStr()
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESSES_FETCHED, DerivedAddressesArgs(
|
||||
derivedAddresses: derivedAddress,
|
||||
error: error
|
||||
))
|
||||
|
||||
proc fetchDerivedAddressesForMnemonic*(self: Service, mnemonic: string, paths: seq[string])=
|
||||
let arg = FetchDerivedAddressesForMnemonicTaskArg(
|
||||
mnemonic: mnemonic,
|
||||
paths: paths,
|
||||
tptr: cast[ByteAddress](fetchDerivedAddressesForMnemonicTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onDerivedAddressesForMnemonicFetched",
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc onDerivedAddressesForMnemonicFetched*(self: Service, jsonString: string) {.slot.} =
|
||||
let response = parseJson(jsonString)
|
||||
var derivedAddress: seq[DerivedAddressDto] = @[]
|
||||
derivedAddress = response["derivedAddresses"].getElems().map(x => x.toDerivedAddressDto())
|
||||
let error = response["error"].getStr()
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESSES_FROM_MNEMONIC_FETCHED, DerivedAddressesArgs(
|
||||
derivedAddresses: derivedAddress,
|
||||
error: error
|
||||
))
|
||||
|
||||
proc fetchDetailsForAddresses*(self: Service, uniqueId: string, addresses: seq[string]) =
|
||||
let network = self.networkService.getNetworkForActivityCheck()
|
||||
let arg = FetchDetailsForAddressesTaskArg(
|
||||
uniqueId: uniqueId,
|
||||
chainId: network.chainId,
|
||||
addresses: addresses,
|
||||
tptr: cast[ByteAddress](fetchDetailsForAddressesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAddressDetailsFetched",
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc onAddressDetailsFetched*(self: Service, jsonString: string) {.slot.} =
|
||||
var data = DerivedAddressesArgs()
|
||||
try:
|
||||
let response = parseJson(jsonString)
|
||||
data.uniqueId = response["uniqueId"].getStr()
|
||||
let addrDto = response{"details"}.toDerivedAddressDto()
|
||||
data.derivedAddresses.add(addrDto)
|
||||
data.error = response["error"].getStr()
|
||||
except Exception as e:
|
||||
error "error: ", procName="fetchAddressDetails", errName = e.name, errDesription = e.msg
|
||||
data.error = e.msg
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_ADDRESS_DETAILS_FETCHED, data)
|
||||
|
||||
proc handleWalletAccount(self: Service, account: WalletAccountDto, notify: bool = true) =
|
||||
if account.removed:
|
||||
self.updateAccountsPositions()
|
||||
self.removeAccountFromLocalStoreAndNotify(account.address, notify)
|
||||
else:
|
||||
var localAcc = self.getAccountByAddress(account.address)
|
||||
if not localAcc.isNil:
|
||||
self.updateAccountInLocalStoreAndNotify(account.address, account.name, account.colorId, account.emoji,
|
||||
none(bool), notify)
|
||||
else:
|
||||
self.addNewKeypairsAccountsToLocalStoreAndNotify(notify)
|
||||
|
||||
proc handleKeypair(self: Service, keypair: KeypairDto) =
|
||||
let localKp = self.getKeypairByKeyUid(keypair.keyUid)
|
||||
if not localKp.isNil:
|
||||
# - first remove removed accounts from the UI
|
||||
for localAcc in localKp.accounts:
|
||||
let accAddress = localAcc.address
|
||||
if keypair.accounts.filter(a => cmpIgnoreCase(a.address, accAddress) == 0).len == 0:
|
||||
self.handleWalletAccount(WalletAccountDto(address: accAddress, removed: true), notify = false)
|
||||
# - second add/update new/existing accounts
|
||||
for acc in keypair.accounts:
|
||||
self.handleWalletAccount(acc, notify = false)
|
||||
else:
|
||||
self.addNewKeypairsAccountsToLocalStoreAndNotify(notify = false)
|
||||
|
||||
# notify all interested parts about the keypair change
|
||||
self.events.emit(SIGNAL_KEYPAIR_SYNCED, KeypairArgs(keypair: keypair))
|
||||
|
||||
proc isIncludeWatchOnlyAccount*(self: Service): bool =
|
||||
return self.settingsService.isIncludeWatchOnlyAccount()
|
||||
|
||||
proc toggleIncludeWatchOnlyAccount*(self: Service) =
|
||||
self.settingsService.toggleIncludeWatchOnlyAccount()
|
||||
|
||||
proc onFetchChainIdForUrl*(self: Service, jsonString: string) {.slot.} =
|
||||
let response = parseJson(jsonString)
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_CHAIN_ID_FOR_URL_FETCHED, ChainIdForUrlArgs(
|
||||
chainId: response{"chainId"}.getInt,
|
||||
success: response{"success"}.getBool,
|
||||
url: response{"url"}.getStr,
|
||||
))
|
||||
|
||||
proc fetchChainIdForUrl*(self: Service, url: string) =
|
||||
let arg = FetchChainIdForUrlTaskArg(
|
||||
tptr: cast[ByteAddress](fetchChainIdForUrlTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onFetchChainIdForUrl",
|
||||
url: url
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc getEnabledChainIds*(self: Service): seq[int] =
|
||||
return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId)
|
||||
|
||||
proc getCurrencyFormat*(self: Service, symbol: string): CurrencyFormatDto =
|
||||
return self.currencyService.getCurrencyFormat(symbol)
|
||||
|
||||
proc areTestNetworksEnabled*(self: Service): bool =
|
||||
return self.settingsService.areTestNetworksEnabled()
|
184
src/app_service/service/wallet_account/service_keycard.nim
Normal file
184
src/app_service/service/wallet_account/service_keycard.nim
Normal file
@ -0,0 +1,184 @@
|
||||
proc addKeycardOrAccountsAsync*(self: Service, keycard: KeycardDto, accountsComingFromKeycard: bool = false) =
|
||||
let arg = SaveOrUpdateKeycardTaskArg(
|
||||
tptr: cast[ByteAddress](saveOrUpdateKeycardTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onKeycardAdded",
|
||||
keycard: keycard,
|
||||
accountsComingFromKeycard: accountsComingFromKeycard
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc emitAddKeycardAddAccountsChange(self: Service, success: bool, keycard: KeycardDto) =
|
||||
let data = KeycardArgs(
|
||||
success: success,
|
||||
keycard: keycard
|
||||
)
|
||||
self.events.emit(SIGNAL_NEW_KEYCARD_SET, data)
|
||||
|
||||
proc onKeycardAdded*(self: Service, response: string) {.slot.} =
|
||||
var keycard = KeycardDto()
|
||||
var success = false
|
||||
try:
|
||||
let responseObj = response.parseJson
|
||||
discard responseObj.getProp("success", success)
|
||||
var kpJson: JsonNode
|
||||
if responseObj.getProp("keycard", kpJson):
|
||||
keycard = kpJson.toKeycardDto()
|
||||
except Exception as e:
|
||||
error "error handilng migrated keycard response", errDesription=e.msg
|
||||
self.emitAddKeycardAddAccountsChange(success, keycard)
|
||||
|
||||
proc addKeycardOrAccounts*(self: Service, keycard: KeycardDto, accountsComingFromKeycard: bool = false): bool =
|
||||
var success = false
|
||||
try:
|
||||
let response = backend.saveOrUpdateKeycard(
|
||||
%* {
|
||||
"keycard-uid": keycard.keycardUid,
|
||||
"keycard-name": keycard.keycardName,
|
||||
# "keycard-locked" - no need to set it here, cause it will be set to false by the status-go
|
||||
"key-uid": keycard.keyUid,
|
||||
"accounts-addresses": keycard.accountsAddresses,
|
||||
# "position": - no need to set it here, cause it is fully maintained by the status-go
|
||||
},
|
||||
accountsComingFromKeycard
|
||||
)
|
||||
success = responseHasNoErrors("addKeycardOrAccounts", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="addKeycardOrAccounts", errName = e.name, errDesription = e.msg
|
||||
self.emitAddKeycardAddAccountsChange(success = success, keycard)
|
||||
return success
|
||||
|
||||
proc removeMigratedAccountsForKeycard*(self: Service, keyUid: string, keycardUid: string, accountsToRemove: seq[string]) =
|
||||
let arg = DeleteKeycardAccountsTaskArg(
|
||||
tptr: cast[ByteAddress](deleteKeycardAccountsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onMigratedAccountsForKeycardRemoved",
|
||||
keycard: KeycardDto(keyUid: keyUid, keycardUid: keycardUid, accountsAddresses: accountsToRemove)
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc onMigratedAccountsForKeycardRemoved*(self: Service, response: string) {.slot.} =
|
||||
var data = KeycardArgs(
|
||||
success: false,
|
||||
)
|
||||
try:
|
||||
let responseObj = response.parseJson
|
||||
discard responseObj.getProp("success", data.success)
|
||||
var kpJson: JsonNode
|
||||
if responseObj.getProp("keycard", kpJson):
|
||||
data.keycard = kpJson.toKeycardDto()
|
||||
except Exception as e:
|
||||
error "error handilng migrated keycard response", errDesription=e.msg
|
||||
self.events.emit(SIGNAL_KEYCARD_ACCOUNTS_REMOVED, data)
|
||||
|
||||
proc getAllKnownKeycards*(self: Service): seq[KeycardDto] =
|
||||
try:
|
||||
let response = backend.getAllKnownKeycards()
|
||||
if responseHasNoErrors("getAllKnownKeycards", response):
|
||||
return map(response.result.getElems(), proc(x: JsonNode): KeycardDto = toKeycardDto(x))
|
||||
except Exception as e:
|
||||
error "error: ", procName="getAllKnownKeycards", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc getKeycardByKeycardUid*(self: Service, keycardUid: string): KeycardDto =
|
||||
try:
|
||||
let response = backend.getKeycardByKeycardUID(keycardUid)
|
||||
if responseHasNoErrors("getKeycardByKeycardUid", response):
|
||||
return response.result.toKeycardDto()
|
||||
except Exception as e:
|
||||
error "error: ", procName="getKeycardByKeycardUid", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc getKeycardsWithSameKeyUid*(self: Service, keyUid: string): seq[KeycardDto] =
|
||||
try:
|
||||
let response = backend.getKeycardsWithSameKeyUID(keyUid)
|
||||
if responseHasNoErrors("getKeycardsWithSameKeyUid", response):
|
||||
return map(response.result.getElems(), proc(x: JsonNode): KeycardDto = toKeycardDto(x))
|
||||
except Exception as e:
|
||||
error "error: ", procName="getKeycardsWithSameKeyUid", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc isKeycardAccount*(self: Service, account: WalletAccountDto): bool =
|
||||
if account.isNil or
|
||||
account.keyUid.len == 0 or
|
||||
account.path.len == 0 or
|
||||
utils.isPathOutOfTheDefaultStatusDerivationTree(account.path):
|
||||
return false
|
||||
let keycards = self.getKeycardsWithSameKeyUid(account.keyUid)
|
||||
return keycards.len > 0
|
||||
|
||||
proc updateKeycardName*(self: Service, keycardUid: string, name: string): bool =
|
||||
var data = KeycardArgs(
|
||||
success: false,
|
||||
keycard: KeycardDto(keycardUid: keycardUid, keycardName: name)
|
||||
)
|
||||
try:
|
||||
let response = backend.setKeycardName(keycardUid, name)
|
||||
data.success = responseHasNoErrors("updateKeycardName", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="updateKeycardName", errName = e.name, errDesription = e.msg
|
||||
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, data)
|
||||
return data.success
|
||||
|
||||
proc setKeycardLocked*(self: Service, keyUid: string, keycardUid: string): bool =
|
||||
var data = KeycardArgs(
|
||||
success: false,
|
||||
keycard: KeycardDto(keyUid: keyUid, keycardUid: keycardUid)
|
||||
)
|
||||
try:
|
||||
let response = backend.keycardLocked(keycardUid)
|
||||
data.success = responseHasNoErrors("setKeycardLocked", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="setKeycardLocked", errName = e.name, errDesription = e.msg
|
||||
self.events.emit(SIGNAL_KEYCARD_LOCKED, data)
|
||||
return data.success
|
||||
|
||||
proc setKeycardUnlocked*(self: Service, keyUid: string, keycardUid: string): bool =
|
||||
var data = KeycardArgs(
|
||||
success: false,
|
||||
keycard: KeycardDto(keyUid: keyUid, keycardUid: keycardUid)
|
||||
)
|
||||
try:
|
||||
let response = backend.keycardUnlocked(keycardUid)
|
||||
data.success = responseHasNoErrors("setKeycardUnlocked", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="setKeycardUnlocked", errName = e.name, errDesription = e.msg
|
||||
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, data)
|
||||
return data.success
|
||||
|
||||
proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: string): bool =
|
||||
var data = KeycardArgs(
|
||||
success: false,
|
||||
oldKeycardUid: oldKeycardUid,
|
||||
keycard: KeycardDto(keycardUid: newKeycardUid)
|
||||
)
|
||||
try:
|
||||
let response = backend.updateKeycardUID(oldKeycardUid, newKeycardUid)
|
||||
data.success = responseHasNoErrors("updateKeycardUid", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="updateKeycardUid", errName = e.name, errDesription = e.msg
|
||||
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, data)
|
||||
return data.success
|
||||
|
||||
proc deleteKeycard*(self: Service, keycardUid: string): bool =
|
||||
var data = KeycardArgs(
|
||||
success: false,
|
||||
keycard: KeycardDto(keycardUid: keycardUid)
|
||||
)
|
||||
try:
|
||||
let response = backend.deleteKeycard(keycardUid)
|
||||
data.success = responseHasNoErrors("deleteKeycard", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg
|
||||
self.events.emit(SIGNAL_KEYCARD_DELETED, data)
|
||||
return data.success
|
||||
|
||||
proc deleteAllKeycardsWithKeyUid*(self: Service, keyUid: string): bool =
|
||||
var data = KeycardArgs(
|
||||
success: false,
|
||||
keycard: KeycardDto(keyUid: keyUid)
|
||||
)
|
||||
try:
|
||||
let response = backend.deleteAllKeycardsWithKeyUID(keyUid)
|
||||
data.success = responseHasNoErrors("deleteAllKeycardsWithKeyUid", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="deleteAllKeycardsWithKeyUid", errName = e.name, errDesription = e.msg
|
||||
self.events.emit(SIGNAL_ALL_KEYCARDS_DELETED, data)
|
||||
return data.success
|
178
src/app_service/service/wallet_account/service_token.nim
Normal file
178
src/app_service/service/wallet_account/service_token.nim
Normal file
@ -0,0 +1,178 @@
|
||||
proc storeTokensForAccount*(self: Service, address: string, tokens: seq[WalletTokenDto], areBalancesCached: bool, areMarketValuesCached: bool) =
|
||||
let acc = self.getAccountByAddress(address)
|
||||
if acc.isNil:
|
||||
return
|
||||
self.accountsTokens[address] = tokens
|
||||
acc.hasBalanceCache = areBalancesCached
|
||||
acc.hasMarketValuesCache = areMarketValuesCached
|
||||
|
||||
proc updateReceivedTokens*(self: Service, address: string, tokens: var seq[WalletTokenDto]) =
|
||||
let acc = self.getAccountByAddress(address)
|
||||
if acc.isNil or not self.accountsTokens.hasKey(address):
|
||||
return
|
||||
let allBalancesForAllTokensHaveError = allBalancesForAllTokensHaveError(tokens)
|
||||
let allMarketValuesForAllTokensHaveError = allMarketValuesForAllTokensHaveError(tokens)
|
||||
|
||||
for storedToken in self.accountsTokens[address]:
|
||||
for token in tokens.mitems:
|
||||
if storedToken.name == token.name:
|
||||
if allBalancesForAllTokensHaveError:
|
||||
token.balancesPerChain = storedToken.balancesPerChain
|
||||
if allMarketValuesForAllTokensHaveError:
|
||||
token.marketValuesPerCurrency = storedToken.marketValuesPerCurrency
|
||||
|
||||
proc getTokensByAddress*(self: Service, address: string): seq[WalletTokenDto] =
|
||||
if not self.accountsTokens.hasKey(address):
|
||||
return
|
||||
return self.accountsTokens[address]
|
||||
|
||||
proc getTokensByAddresses*(self: Service, addresses: seq[string]): seq[WalletTokenDto] =
|
||||
var tokens = initTable[string, WalletTokenDto]()
|
||||
for address in addresses:
|
||||
if not self.accountsTokens.hasKey(address):
|
||||
continue
|
||||
for token in self.accountsTokens[address]:
|
||||
if not tokens.hasKey(token.symbol):
|
||||
let newToken = token.copyToken()
|
||||
tokens[token.symbol] = newToken
|
||||
continue
|
||||
|
||||
for chainId, balanceDto in token.balancesPerChain:
|
||||
if not tokens[token.symbol].balancesPerChain.hasKey(chainId):
|
||||
tokens[token.symbol].balancesPerChain[chainId] = balanceDto
|
||||
continue
|
||||
|
||||
tokens[token.symbol].balancesPerChain[chainId].balance += balanceDto.balance
|
||||
|
||||
result = toSeq(tokens.values)
|
||||
result.sort(priorityTokenCmp)
|
||||
|
||||
proc onAllTokensBuilt*(self: Service, response: string) {.slot.} =
|
||||
try:
|
||||
var visibleSymbols: seq[string]
|
||||
let chainIds = self.networkService.getNetworks().map(n => n.chainId)
|
||||
|
||||
let responseObj = response.parseJson
|
||||
var storeResult: bool
|
||||
var resultObj: JsonNode
|
||||
discard responseObj.getProp("storeResult", storeResult)
|
||||
discard responseObj.getProp("result", resultObj)
|
||||
|
||||
var data = TokensPerAccountArgs()
|
||||
data.accountsTokens = initOrderedTable[string, seq[WalletTokenDto]]()
|
||||
data.hasBalanceCache = false
|
||||
data.hasMarketValuesCache = false
|
||||
if resultObj.kind == JObject:
|
||||
for wAddress, tokensDetailsObj in resultObj:
|
||||
if tokensDetailsObj.kind == JArray:
|
||||
var tokens: seq[WalletTokenDto]
|
||||
tokens = map(tokensDetailsObj.getElems(), proc(x: JsonNode): WalletTokenDto = x.toWalletTokenDto())
|
||||
tokens.sort(priorityTokenCmp)
|
||||
self.updateReceivedTokens(wAddress, tokens)
|
||||
let hasBalanceCache = anyTokenHasBalanceForAnyChain(tokens)
|
||||
let hasMarketValuesCache = anyTokenHasMarketValuesForAnyChain(tokens)
|
||||
data.accountsTokens[wAddress] = @[]
|
||||
deepCopy(data.accountsTokens[wAddress], tokens)
|
||||
data.hasBalanceCache = data.hasBalanceCache or hasBalanceCache
|
||||
data.hasMarketValuesCache = data.hasMarketValuesCache or hasMarketValuesCache
|
||||
|
||||
# set assetsLoading to false once the tokens are loaded
|
||||
self.updateAssetsLoadingState(wAddress, false)
|
||||
|
||||
if storeResult:
|
||||
self.storeTokensForAccount(wAddress, tokens, hasBalanceCache, hasMarketValuesCache)
|
||||
self.tokenService.updateTokenPrices(tokens) # For efficiency. Will be removed when token info fetching gets moved to the tokenService
|
||||
# Gather symbol for visible tokens
|
||||
for token in tokens:
|
||||
if token.getVisibleForNetworkWithPositiveBalance(chainIds) and find(visibleSymbols, token.symbol) == -1:
|
||||
visibleSymbols.add(token.symbol)
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT, data)
|
||||
if visibleSymbols.len > 0:
|
||||
discard backend.updateVisibleTokens(visibleSymbols)
|
||||
except Exception as e:
|
||||
error "error: ", procName="onAllTokensBuilt", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc buildAllTokens(self: Service, accounts: seq[string], store: bool) =
|
||||
if not main_constants.WALLET_ENABLED or
|
||||
accounts.len == 0:
|
||||
return
|
||||
|
||||
# set assetsLoading to true as the tokens are being loaded
|
||||
for waddress in accounts:
|
||||
self.updateAssetsLoadingState(waddress, true)
|
||||
|
||||
let arg = BuildTokensTaskArg(
|
||||
tptr: cast[ByteAddress](prepareTokensTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAllTokensBuilt",
|
||||
accounts: accounts,
|
||||
storeResult: store
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc checkRecentHistory*(self: Service, addresses: seq[string]) =
|
||||
if(not main_constants.WALLET_ENABLED):
|
||||
return
|
||||
try:
|
||||
let chainIds = self.networkService.getNetworks().map(a => a.chainId)
|
||||
status_go_transactions.checkRecentHistory(chainIds, addresses)
|
||||
except Exception as e:
|
||||
let errDescription = e.msg
|
||||
error "error: ", errDescription
|
||||
|
||||
proc reloadAccountTokens*(self: Service) =
|
||||
let addresses = self.getWalletAddresses()
|
||||
self.buildAllTokens(addresses, store = true)
|
||||
self.checkRecentHistory(addresses)
|
||||
|
||||
proc getCurrency*(self: Service): string =
|
||||
return self.settingsService.getCurrency()
|
||||
|
||||
proc getCurrentCurrencyIfEmpty(self: Service, currency = ""): string =
|
||||
if currency != "":
|
||||
return currency
|
||||
else:
|
||||
return self.getCurrency()
|
||||
|
||||
proc getCurrencyBalance*(self: Service, address: string, chainIds: seq[int], currency: string): float64 =
|
||||
if not self.accountsTokens.hasKey(address):
|
||||
return
|
||||
return self.accountsTokens[address].map(t => t.getCurrencyBalance(chainIds, currency)).foldl(a + b, 0.0)
|
||||
|
||||
proc getTotalCurrencyBalance*(self: Service, addresses: seq[string], currency: string = ""): float64 =
|
||||
let chainIds = self.networkService.getNetworks().filter(a => a.enabled).map(a => a.chainId)
|
||||
let accounts = self.getWalletAccounts().filter(w => addresses.contains(w.address))
|
||||
return accounts.map(a => self.getCurrencyBalance(a.address, chainIds, self.getCurrentCurrencyIfEmpty(currency))).foldl(a + b, 0.0)
|
||||
|
||||
proc findTokenSymbolByAddress*(self: Service, address: string): string =
|
||||
return self.tokenService.findTokenSymbolByAddress(address)
|
||||
|
||||
proc getOrFetchBalanceForAddressInPreferredCurrency*(self: Service, address: string): tuple[balance: float64, fetched: bool] =
|
||||
let acc = self.getAccountByAddress(address)
|
||||
if acc.isNil:
|
||||
self.buildAllTokens(@[address], store = false)
|
||||
result.balance = 0.0
|
||||
result.fetched = false
|
||||
return
|
||||
let chainIds = self.networkService.getNetworks().map(n => n.chainId)
|
||||
result.balance = self.getCurrencyBalance(acc.address, chainIds, self.getCurrentCurrencyIfEmpty())
|
||||
result.fetched = true
|
||||
|
||||
proc getTokenBalanceOnChain*(self: Service, address: string, chainId: int, symbol: string): float64 =
|
||||
if not self.accountsTokens.hasKey(address):
|
||||
return 0.0
|
||||
for token in self.accountsTokens[address]:
|
||||
if token.symbol == symbol and token.balancesPerChain.hasKey(chainId):
|
||||
return token.balancesPerChain[chainId].balance
|
||||
return 0.0
|
||||
|
||||
proc allAccountsTokenBalance*(self: Service, symbol: string): float64 =
|
||||
var totalTokenBalance = 0.0
|
||||
for walletAccount in self.getWalletAccounts():
|
||||
if walletAccount.walletType == WalletTypeWatch or
|
||||
not self.accountsTokens.hasKey(walletAccount.address):
|
||||
continue
|
||||
for token in self.accountsTokens[walletAccount.address]:
|
||||
if token.symbol == symbol:
|
||||
totalTokenBalance += token.getTotalBalanceOfSupportedChains()
|
||||
return totalTokenBalance
|
@ -0,0 +1,66 @@
|
||||
#################################################
|
||||
# Signals emitted by wallet account service
|
||||
#################################################
|
||||
|
||||
const SIGNAL_WALLET_ACCOUNT_SAVED* = "walletAccount/accountSaved"
|
||||
const SIGNAL_WALLET_ACCOUNT_DELETED* = "walletAccount/accountDeleted"
|
||||
const SIGNAL_WALLET_ACCOUNT_UPDATED* = "walletAccount/walletAccountUpdated"
|
||||
const SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED* = "walletAccount/networkEnabledUpdated"
|
||||
const SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT* = "walletAccount/tokensRebuilt"
|
||||
const SIGNAL_WALLET_ACCOUNT_TOKENS_BEING_FETCHED* = "walletAccount/tokenFetching"
|
||||
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESSES_FETCHED* = "walletAccount/derivedAddressesFetched"
|
||||
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESSES_FROM_MNEMONIC_FETCHED* = "walletAccount/derivedAddressesFromMnemonicFetched"
|
||||
const SIGNAL_WALLET_ACCOUNT_ADDRESS_DETAILS_FETCHED* = "walletAccount/addressDetailsFetched"
|
||||
const SIGNAL_WALLET_ACCOUNT_POSITION_UPDATED* = "walletAccount/positionUpdated"
|
||||
const SIGNAL_WALLET_ACCOUNT_OPERABILITY_UPDATED* = "walletAccount/operabilityUpdated"
|
||||
const SIGNAL_WALLET_ACCOUNT_CHAIN_ID_FOR_URL_FETCHED* = "walletAccount/chainIdForUrlFetched"
|
||||
const SIGNAL_WALLET_ACCOUNT_PREFERRED_SHARING_CHAINS_UPDATED* = "walletAccount/preferredSharingChainsUpdated"
|
||||
|
||||
const SIGNAL_KEYPAIR_SYNCED* = "keypairSynced"
|
||||
const SIGNAL_KEYPAIR_NAME_CHANGED* = "keypairNameChanged"
|
||||
|
||||
const SIGNAL_NEW_KEYCARD_SET* = "newKeycardSet"
|
||||
const SIGNAL_KEYCARD_REBUILD* = "keycardRebuild"
|
||||
const SIGNAL_KEYCARD_DELETED* = "keycardDeleted"
|
||||
const SIGNAL_ALL_KEYCARDS_DELETED* = "allKeycardsDeleted"
|
||||
const SIGNAL_KEYCARD_ACCOUNTS_REMOVED* = "keycardAccountsRemoved"
|
||||
const SIGNAL_KEYCARD_LOCKED* = "keycardLocked"
|
||||
const SIGNAL_KEYCARD_UNLOCKED* = "keycardUnlocked"
|
||||
const SIGNAL_KEYCARD_UID_UPDATED* = "keycardUidUpdated"
|
||||
const SIGNAL_KEYCARD_NAME_CHANGED* = "keycardNameChanged"
|
||||
|
||||
#################################################
|
||||
# Payload sent via above defined signals
|
||||
#################################################
|
||||
|
||||
type AccountArgs* = ref object of Args
|
||||
account*: WalletAccountDto
|
||||
|
||||
type KeypairArgs* = ref object of Args
|
||||
keypair*: KeypairDto
|
||||
oldKeypairName*: string
|
||||
|
||||
type KeycardArgs* = ref object of Args
|
||||
success*: bool
|
||||
oldKeycardUid*: string
|
||||
keycard*: KeycardDto
|
||||
|
||||
type DerivedAddressesArgs* = ref object of Args
|
||||
uniqueId*: string
|
||||
derivedAddresses*: seq[DerivedAddressDto]
|
||||
error*: string
|
||||
|
||||
type TokensPerAccountArgs* = ref object of Args
|
||||
accountsTokens*: OrderedTable[string, seq[WalletTokenDto]] # [wallet address, list of tokens]
|
||||
hasBalanceCache*: bool
|
||||
hasMarketValuesCache*: bool
|
||||
|
||||
type KeycardActivityArgs* = ref object of Args
|
||||
success*: bool
|
||||
oldKeycardUid*: string
|
||||
keycard*: KeycardDto
|
||||
|
||||
type ChainIdForUrlArgs* = ref object of Args
|
||||
chainId*: int
|
||||
success*: bool
|
||||
url*: string
|
109
src/app_service/service/wallet_account/utils.nim
Normal file
109
src/app_service/service/wallet_account/utils.nim
Normal file
@ -0,0 +1,109 @@
|
||||
#################################################
|
||||
# Common functions
|
||||
#################################################
|
||||
|
||||
proc priorityTokenCmp(a, b: WalletTokenDto): int =
|
||||
for symbol in @["ETH", "SNT", "DAI", "STT"]:
|
||||
if a.symbol == symbol:
|
||||
return -1
|
||||
if b.symbol == symbol:
|
||||
return 1
|
||||
cmp(a.name, b.name)
|
||||
|
||||
proc walletAccountsCmp(x, y: WalletAccountDto): int =
|
||||
cmp(x.position, y.position)
|
||||
|
||||
proc hex2Balance*(input: string, decimals: int): string =
|
||||
var value = fromHex(Stuint[256], input)
|
||||
if decimals == 0:
|
||||
return fmt"{value}"
|
||||
var p = u256(10).pow(decimals)
|
||||
var i = value.div(p)
|
||||
var r = value.mod(p)
|
||||
var leading_zeros = "0".repeat(decimals - ($r).len)
|
||||
var d = fmt"{leading_zeros}{$r}"
|
||||
result = $i
|
||||
if(r > 0): result = fmt"{result}.{d}"
|
||||
|
||||
proc responseHasNoErrors(procName: string, response: RpcResponse[JsonNode]): bool =
|
||||
var errMsg = ""
|
||||
if not response.error.isNil:
|
||||
errMsg = "(" & $response.error.code & ") " & response.error.message
|
||||
elif response.result.kind == JObject and response.result.contains("error"):
|
||||
errMsg = response.result["error"].getStr
|
||||
if(errMsg.len == 0):
|
||||
return true
|
||||
error "error: ", procName=procName, errDesription = errMsg
|
||||
return false
|
||||
|
||||
proc allBalancesForAllTokensHaveError(tokens: seq[WalletTokenDto]): bool =
|
||||
for token in tokens:
|
||||
for chainId, balanceDto in token.balancesPerChain:
|
||||
if not balanceDto.hasError:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc anyTokenHasBalanceForAnyChain(tokens: seq[WalletTokenDto]): bool =
|
||||
for token in tokens:
|
||||
if len(token.balancesPerChain) > 0:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc allMarketValuesForAllTokensHaveError(tokens: seq[WalletTokenDto]): bool =
|
||||
for token in tokens:
|
||||
for currency, marketDto in token.marketValuesPerCurrency:
|
||||
if not marketDto.hasError:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc anyTokenHasMarketValuesForAnyChain(tokens: seq[WalletTokenDto]): bool =
|
||||
for token in tokens:
|
||||
if len(token.marketValuesPerCurrency) > 0:
|
||||
return true
|
||||
return false
|
||||
|
||||
#################################################
|
||||
# Remote functions
|
||||
#################################################
|
||||
|
||||
proc getAccountsFromDb(): seq[WalletAccountDto] =
|
||||
try:
|
||||
let response = status_go_accounts.getAccounts()
|
||||
return response.result.getElems().map(
|
||||
x => x.toWalletAccountDto()
|
||||
).filter(a => not a.isChat)
|
||||
except Exception as e:
|
||||
error "error: ", procName="getAccounts", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc getWatchOnlyAccountsFromDb(): seq[WalletAccountDto] =
|
||||
try:
|
||||
let response = status_go_accounts.getWatchOnlyAccounts()
|
||||
return response.result.getElems().map(x => x.toWalletAccountDto())
|
||||
except Exception as e:
|
||||
error "error: ", procName="getWatchOnlyAccounts", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc getKeypairsFromDb(): seq[KeypairDto] =
|
||||
try:
|
||||
let response = status_go_accounts.getKeypairs()
|
||||
return response.result.getElems().map(x => x.toKeypairDto())
|
||||
except Exception as e:
|
||||
error "error: ", procName="getKeypairs", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc getKeypairByKeyUidFromDb(keyUid: string): KeypairDto =
|
||||
if keyUid.len == 0:
|
||||
return
|
||||
try:
|
||||
let response = status_go_accounts.getKeypairByKeyUid(keyUid)
|
||||
if not response.error.isNil:
|
||||
return
|
||||
return response.result.toKeypairDto()
|
||||
except Exception as e:
|
||||
info "no known keypair", keyUid=keyUid, procName="getKeypairByKeyUid", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc getEnsName(address: string, chainId: int): string =
|
||||
try:
|
||||
let response = backend.getName(chainId, address)
|
||||
return response.result.getStr
|
||||
except Exception as e:
|
||||
let errDesription = e.msg
|
||||
error "error: ", errDesription
|
Loading…
x
Reference in New Issue
Block a user