refactor wallet views

add getSettings methods to src/status

fix issue with calling getSettings; document issue

remove most direct references to libstatus; document some common issues

remove most references to libstatus wallet

add mailserver layer to status lib; remove references to libstatus mailservers

remove libstatus accounts references

move types out of libstatus; remove libstatus types references

remove libstatus browser references

refactor libstatus utils references

remove more references to libstatus stickers

remove references to libstatus constants from src/app

remove more libstatus references from src/app

refactor token_list usage of libstatus

refactor stickers usage of libstatus

refactor chat usage of libstatus

remove libstatus references from the wallet view

remove logic from ens manager view

fix issue with import & namespace conflict

remove unnecessary imports

refactor provider view to not depend on libstatus

refactor provider view

refactor: move accounts specific code to its own section

fix account selection

move collectibles to their own module

update references to wallet transactions

refactor: move gas methods to their own file

refactor: extract tokens into their own file

refactor: extract ens to its own file

refactor: extract dappbrowser code to its own file

refactor: extract history related code to its own file

refactor: extract balance to its own file

refactor: extract utils to its own file

clean up wallet imports

fix: identicon for transaction commands
Fixes #2533
This commit is contained in:
Iuri Matias 2021-06-08 08:48:31 -04:00
parent d5dc7c29ca
commit 6bcdb9ca54
54 changed files with 1303 additions and 945 deletions

View File

@ -96,10 +96,23 @@ Typically this means the method has no return type and so `result =` isn't neces
This usually means a method that has no return type is being discarded
### required type for <variable>: <Type> but expression '<variable>' is of type: <Type>
### required type for <variable>: <Type> but expression '<variable>' is of type: <Type>
This tpyically means there is an import missing
### type mismatch: got <Type>
```
Error: type mismatch: got <WalletView>
but expected one of:
template `.`(a: Wrapnil; b): untyped
first type mismatch at position: 1
required type for a: Wrapnil
but expression 'self' is of type: WalletView
```
There is likely a typo or the method is not public
## Warnings
### QML anchor warnings

View File

@ -2,7 +2,6 @@ import NimQml, Tables, std/wrapnils
import ../../../status/[chat/chat, status, ens, accounts, settings]
from ../../../status/types import Setting
import ../../../status/utils as status_utils
# import ../../../status/settings as status_settings
import chat_members

View File

@ -9,7 +9,6 @@ import ../../utils/image_utils
import ../../../status/signals/types as signal_types
import ../../../status/types
logScope:
topics = "communities-view"

View File

@ -7,12 +7,6 @@ import nbaser
import stew/byteutils
from base32 import nil
const HTTPS_SCHEME = "https"
const IPFS_GATEWAY = ".infura.status.im"
const SWARM_GATEWAY = "swarm-gateways.net"
const base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
logScope:
topics = "provider-view"
@ -68,21 +62,8 @@ QtObject:
self.status.permissions.clearPermissions()
proc ensResourceURL*(self: Web3ProviderView, ens: string, url: string): string {.slot.} =
let contentHash = contenthash(ens)
if contentHash == "": # ENS does not have a content hash
return url_replaceHostAndAddPath(url, url_host(url), HTTPS_SCHEME)
let decodedHash = contentHash.decodeENSContentHash()
case decodedHash[0]:
of ENSType.IPFS:
let base32Hash = base32.encode(string.fromBytes(base58.decode(decodedHash[1]))).toLowerAscii().replace("=", "")
result = url_replaceHostAndAddPath(url, base32Hash & IPFS_GATEWAY, HTTPS_SCHEME)
of ENSType.SWARM:
result = url_replaceHostAndAddPath(url, SWARM_GATEWAY, HTTPS_SCHEME, "/bzz:/" & decodedHash[1] & "/")
of ENSType.IPNS:
result = url_replaceHostAndAddPath(url, decodedHash[1], HTTPS_SCHEME)
else:
warn "Unknown content for", ens, contentHash
let (url, base, http_scheme, path_prefix, hasContentHash) = self.status.provider.ensResourceURL(ens, url)
result = url_replaceHostAndAddPath(url, (if hasContentHash: base else: url_host(base)), http_scheme, path_prefix)
proc replaceHostByENS*(self: Web3ProviderView, url: string, ens: string): string {.slot.} =
result = url_replaceHostAndAddPath(url, ens)
@ -90,8 +71,5 @@ QtObject:
proc getHost*(self: Web3ProviderView, url: string): string {.slot.} =
result = url_host(url)
proc signMessage*(self: Web3ProviderView, payload: string, password: string) {.slot.} =
let jsonPayload = payload.parseJson
proc init*(self: Web3ProviderView) =
self.setDappsAddress(self.status.settings.getSetting[:string](Setting.DappsAddress))

View File

@ -40,7 +40,7 @@ proc init*(self: WalletController) =
self.status.events.on("newAccountAdded") do(e: Args):
var account = WalletTypes.AccountArgs(e)
self.view.accounts.addAccountToList(account.account)
self.view.addAccountToList(account.account)
self.view.updateView()
self.status.events.on("assetChanged") do(e: Args):

View File

@ -1,630 +1,153 @@
import # std libs
algorithm, atomics, sequtils, strformat, strutils, sugar, sequtils, json,
parseUtils, std/wrapnils, tables
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables
import NimQml, chronicles, stint
import # vendor libs
NimQml, chronicles, stint
import # status-desktop libs
../../status/[status, wallet, settings, tokens],
../../status/wallet/collectibles as status_collectibles,
../../status/wallet as status_wallet,
../../status/types,
../../status/utils as status_utils,
../../status/tokens as status_tokens,
../../status/ens as status_ens,
views/[asset_list, account_list, account_item, token_list, transaction_list, collectibles_list],
../../status/tasks/[qt, task_runner_impl], ../../status/signals/types as signal_types
const ZERO_ADDRESS* = "0x0000000000000000000000000000000000000000"
type
SendTransactionTaskArg = ref object of QObjectTaskArg
from_addr: string
to: string
assetAddress: string
value: string
gas: string
gasPrice: string
password: string
uuid: string
InitBalancesTaskArg = ref object of QObjectTaskArg
address: string
tokenList: seq[string]
LoadCollectiblesTaskArg = ref object of QObjectTaskArg
address: string
collectiblesType: string
running*: ByteAddress # pointer to threadpool's `.running` Atomic[bool]
GasPredictionsTaskArg = ref object of QObjectTaskArg
LoadTransactionsTaskArg = ref object of QObjectTaskArg
address: string
toBlock: Uint256
limit: int
loadMore: bool
ResolveEnsTaskArg = ref object of QObjectTaskArg
ens: string
uuid: string
WatchTransactionTaskArg = ref object of QObjectTaskArg
transactionHash: string
const sendTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[SendTransactionTaskArg](argEncoded)
var
success: bool
response: string
if arg.assetAddress != ZERO_ADDRESS and not arg.assetAddress.isEmptyOrWhitespace:
response = wallet.sendTokenTransaction(arg.from_addr, arg.to, arg.assetAddress, arg.value, arg.gas, arg.gasPrice, arg.password, success)
else:
response = wallet.sendTransaction(arg.from_addr, arg.to, arg.value, arg.gas, arg.gasPrice, arg.password, success)
let output = %* { "result": %response, "success": %success, "uuid": %arg.uuid }
arg.finish(output)
proc sendTransaction[T](self: T, slot: string, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) =
let arg = SendTransactionTaskArg(
tptr: cast[ByteAddress](sendTransactionTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
from_addr: from_addr,
to: to,
assetAddress: assetAddress,
value: value,
gas: gas,
gasPrice: gasPrice,
password: password,
uuid: uuid
)
self.status.tasks.threadpool.start(arg)
const initBalancesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[InitBalancesTaskArg](argEncoded)
var tokenBalances = initTable[string, string]()
for token in arg.tokenList:
tokenBalances[token] = status_tokens.getTokenBalance(token, arg.address)
let output = %* {
"address": arg.address,
"eth": getEthBalance(arg.address),
"tokens": tokenBalances
}
arg.finish(output)
proc initBalances[T](self: T, slot: string, address: string, tokenList: seq[string]) =
let arg = InitBalancesTaskArg(
tptr: cast[ByteAddress](initBalancesTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
address: address,
tokenList: tokenList
)
self.status.tasks.threadpool.start(arg)
const loadCollectiblesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[LoadCollectiblesTaskArg](argEncoded)
var running = cast[ptr Atomic[bool]](arg.running)
var collectiblesOrError = ""
case arg.collectiblesType:
of status_collectibles.CRYPTOKITTY:
collectiblesOrError = status_collectibles.getCryptoKitties(arg.address)
of status_collectibles.KUDO:
collectiblesOrError = status_collectibles.getKudos(arg.address)
of status_collectibles.ETHERMON:
collectiblesOrError = status_collectibles.getEthermons(arg.address)
of status_collectibles.STICKER:
collectiblesOrError = status_collectibles.getStickers(arg.address, running[])
let output = %*{
"address": arg.address,
"collectibleType": arg.collectiblesType,
"collectiblesOrError": collectiblesOrError
}
arg.finish(output)
proc loadCollectibles[T](self: T, slot: string, address: string, collectiblesType: string) =
let arg = LoadCollectiblesTaskArg(
tptr: cast[ByteAddress](loadCollectiblesTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
address: address,
collectiblesType: collectiblesType,
running: cast[ByteAddress](addr self.status.tasks.threadpool.running)
)
self.status.tasks.threadpool.start(arg)
const getGasPredictionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[GasPredictionsTaskArg](argEncoded)
output = %getGasPricePredictions()
arg.finish(output)
proc getGasPredictions[T](self: T, slot: string) =
let arg = GasPredictionsTaskArg(
tptr: cast[ByteAddress](getGasPredictionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot
)
self.status.tasks.threadpool.start(arg)
const loadTransactionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[LoadTransactionsTaskArg](argEncoded)
output = %*{
"address": arg.address,
"history": status_wallet.getTransfersByAddress(arg.address, arg.toBlock, arg.limit, arg.loadMore),
"loadMore": arg.loadMore
}
arg.finish(output)
proc loadTransactions[T](self: T, slot: string, address: string, toBlock: Uint256, limit: int, loadMore: bool) =
let arg = LoadTransactionsTaskArg(
tptr: cast[ByteAddress](loadTransactionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
address: address,
toBlock: toBlock,
limit: limit,
loadMore: loadMore
)
self.status.tasks.threadpool.start(arg)
const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[ResolveEnsTaskArg](argEncoded)
output = %* { "address": status_ens.address(arg.ens), "uuid": arg.uuid }
arg.finish(output)
proc resolveEns[T](self: T, slot: string, ens: string, uuid: string) =
let arg = ResolveEnsTaskArg(
tptr: cast[ByteAddress](resolveEnsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
ens: ens,
uuid: uuid
)
self.status.tasks.threadpool.start(arg)
const watchTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[WatchTransactionTaskArg](argEncoded)
response = status_wallet.watchTransaction(arg.transactionHash)
output = %* { "result": response }
arg.finish(output)
proc watchTransaction[T](self: T, slot: string, transactionHash: string) =
let arg = WatchTransactionTaskArg(
tptr: cast[ByteAddress](watchTransactionTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
transactionHash: transactionHash
)
self.status.tasks.threadpool.start(arg)
import
../../status/[status, wallet],
views/[accounts, collectibles, transactions, tokens, gas, ens, dapp_browser, history, balance, utils, asset_list, account_list]
QtObject:
type
WalletView* = ref object of QAbstractListModel
accounts*: AccountList
currentAssetList*: AssetList
currentCollectiblesLists*: CollectiblesList
currentAccount: AccountItemView
focusedAccount: AccountItemView
dappBrowserAccount: AccountItemView
currentTransactions: TransactionList
defaultTokenList: TokenList
customTokenList: TokenList
status: Status
totalFiatBalance: string
etherscanLink: string
safeLowGasPrice: string
standardGasPrice: string
fastGasPrice: string
fastestGasPrice: string
defaultGasLimit: string
signingPhrase: string
fetchingHistoryState: Table[string, bool]
accountsView: AccountsView
collectiblesView: CollectiblesView
transactionsView*: TransactionsView
tokensView*: TokensView
dappBrowserView*: DappBrowserView
gasView*: GasView
ensView*: EnsView
historyView*: HistoryView
balanceView*: BalanceView
utilsView*: UtilsView
isNonArchivalNode: bool
proc delete(self: WalletView) =
self.accounts.delete
self.currentAssetList.delete
self.currentAccount.delete
self.focusedAccount.delete
self.dappBrowserAccount.delete
self.currentTransactions.delete
self.defaultTokenList.delete
self.customTokenList.delete
self.accountsView.delete
self.gasView.delete
self.ensView.delete
self.utilsView.delete
self.QAbstractListModel.delete
proc setup(self: WalletView) =
self.QAbstractListModel.setup
proc setDappBrowserAddress*(self: WalletView)
proc newWalletView*(status: Status): WalletView =
new(result, delete)
result.status = status
result.accounts = newAccountList()
result.currentAccount = newAccountItemView()
result.focusedAccount = newAccountItemView()
result.dappBrowserAccount = newAccountItemView()
result.currentAssetList = newAssetList()
result.currentTransactions = newTransactionList()
result.currentCollectiblesLists = newCollectiblesList()
result.defaultTokenList = newTokenList(status)
result.customTokenList = newTokenList(status)
result.totalFiatBalance = ""
result.etherscanLink = ""
result.safeLowGasPrice = "0"
result.standardGasPrice = "0"
result.fastGasPrice = "0"
result.fastestGasPrice = "0"
result.defaultGasLimit = "21000"
result.signingPhrase = ""
result.fetchingHistoryState = initTable[string, bool]()
result.accountsView = newAccountsView(status)
result.collectiblesView = newCollectiblesView(status, result.accountsView)
result.transactionsView = newTransactionsView(status, result.accountsView)
result.tokensView = newTokensView(status, result.accountsView)
result.gasView = newGasView(status)
result.ensView = newEnsView(status)
result.dappBrowserView = newDappBrowserView(status, result.accountsView)
result.historyView = newHistoryView(status, result.accountsView, result.transactionsView)
result.balanceView = newBalanceView(status, result.accountsView, result.transactionsView, result.historyView)
result.utilsView = newUtilsView()
result.isNonArchivalNode = false
result.setup
proc etherscanLinkChanged*(self: WalletView) {.signal.}
proc getAccounts(self: WalletView): QVariant {.slot.} = newQVariant(self.accountsView)
QtProperty[QVariant] accountsView:
read = getAccounts
proc getEtherscanLink*(self: WalletView): QVariant {.slot.} =
newQVariant(self.etherscanLink.replace("/address", "/tx"))
proc getCollectibles(self: WalletView): QVariant {.slot.} = newQVariant(self.collectiblesView)
QtProperty[QVariant] collectiblesView:
read = getCollectibles
proc setEtherscanLink*(self: WalletView, link: string) =
self.etherscanLink = link
self.etherscanLinkChanged()
proc getTransactions(self: WalletView): QVariant {.slot.} = newQVariant(self.transactionsView)
QtProperty[QVariant] transactionsView:
read = getTransactions
proc signingPhraseChanged*(self: WalletView) {.signal.}
proc getGas(self: WalletView): QVariant {.slot.} = newQVariant(self.gasView)
QtProperty[QVariant] gasView:
read = getGas
proc getSigningPhrase*(self: WalletView): QVariant {.slot.} =
newQVariant(self.signingPhrase)
proc getTokens(self: WalletView): QVariant {.slot.} = newQVariant(self.tokensView)
QtProperty[QVariant] tokensView:
read = getTokens
proc setSigningPhrase*(self: WalletView, signingPhrase: string) =
self.signingPhrase = signingPhrase
self.signingPhraseChanged()
proc getEns(self: WalletView): QVariant {.slot.} = newQVariant(self.ensView)
QtProperty[QVariant] ensView:
read = getEns
QtProperty[QVariant] etherscanLink:
read = getEtherscanLink
notify = etherscanLinkChanged
proc getHistory(self: WalletView): QVariant {.slot.} = newQVariant(self.historyView)
QtProperty[QVariant] historyView:
read = getHistory
QtProperty[QVariant] signingPhrase:
read = getSigningPhrase
notify = signingPhraseChanged
proc getBalance(self: WalletView): QVariant {.slot.} = newQVariant(self.balanceView)
QtProperty[QVariant] balanceView:
read = getBalance
proc getStatusToken*(self: WalletView): string {.slot.} = self.status.wallet.getStatusToken
proc setCurrentAssetList*(self: WalletView, assetList: seq[Asset])
proc currentCollectiblesListsChanged*(self: WalletView) {.signal.}
proc getCurrentCollectiblesLists(self: WalletView): QVariant {.slot.} =
return newQVariant(self.currentCollectiblesLists)
proc setCurrentCollectiblesLists*(self: WalletView, collectiblesLists: seq[CollectibleList]) =
self.currentCollectiblesLists.setNewData(collectiblesLists)
self.currentCollectiblesListsChanged()
QtProperty[QVariant] collectiblesLists:
read = getCurrentCollectiblesLists
write = setCurrentCollectiblesLists
notify = currentCollectiblesListsChanged
proc currentTransactionsChanged*(self: WalletView) {.signal.}
proc getCurrentTransactions*(self: WalletView): QVariant {.slot.} =
return newQVariant(self.currentTransactions)
proc setCurrentTransactions*(self: WalletView, transactionList: seq[Transaction]) =
self.currentTransactions.setNewData(transactionList)
self.currentTransactionsChanged()
QtProperty[QVariant] transactions:
read = getCurrentTransactions
write = setCurrentTransactions
notify = currentTransactionsChanged
proc loadCollectiblesForAccount*(self: WalletView, address: string, currentCollectiblesList: seq[CollectibleList])
proc currentAccountChanged*(self: WalletView) {.signal.}
proc setCurrentAccountByIndex*(self: WalletView, index: int) {.slot.} =
if(self.accounts.rowCount() == 0): return
let selectedAccount = self.accounts.getAccount(index)
if self.currentAccount.address == selectedAccount.address: return
self.currentAccount.setAccountItem(selectedAccount)
self.currentAccountChanged()
self.setCurrentAssetList(selectedAccount.assetList)
# Display currently known collectibles, and get latest from API/Contracts
self.setCurrentCollectiblesLists(selectedAccount.collectiblesLists)
self.loadCollectiblesForAccount(selectedAccount.address, selectedAccount.collectiblesLists)
self.currentTransactions.setHasMore(selectedAccount.transactions.hasMore)
self.setCurrentTransactions(selectedAccount.transactions.data)
proc getCurrentAccount*(self: WalletView): QVariant {.slot.} =
result = newQVariant(self.currentAccount)
QtProperty[QVariant] currentAccount:
read = getCurrentAccount
write = setCurrentAccountByIndex
notify = currentAccountChanged
proc focusedAccountChanged*(self: WalletView) {.signal.}
proc setFocusedAccountByAddress*(self: WalletView, address: string) {.slot.} =
if(self.accounts.rowCount() == 0): return
var index = self.accounts.getAccountindexByAddress(address)
if index == -1: index = 0
let selectedAccount = self.accounts.getAccount(index)
if self.focusedAccount.address == selectedAccount.address: return
self.focusedAccount.setAccountItem(selectedAccount)
self.focusedAccountChanged()
proc getFocusedAccount*(self: WalletView): QVariant {.slot.} =
result = newQVariant(self.focusedAccount)
QtProperty[QVariant] focusedAccount:
read = getFocusedAccount
write = setFocusedAccountByAddress
notify = focusedAccountChanged
proc currentAssetListChanged*(self: WalletView) {.signal.}
proc getCurrentAssetList(self: WalletView): QVariant {.slot.} =
return newQVariant(self.currentAssetList)
proc setCurrentAssetList*(self: WalletView, assetList: seq[Asset]) =
self.currentAssetList.setNewData(assetList)
self.currentAssetListChanged()
QtProperty[QVariant] assets:
read = getCurrentAssetList
write = setCurrentAssetList
notify = currentAssetListChanged
proc totalFiatBalanceChanged*(self: WalletView) {.signal.}
proc getTotalFiatBalance(self: WalletView): string {.slot.} =
self.status.wallet.getTotalFiatBalance()
proc setTotalFiatBalance*(self: WalletView, newBalance: string) =
self.totalFiatBalance = newBalance
self.totalFiatBalanceChanged()
QtProperty[string] totalFiatBalance:
read = getTotalFiatBalance
write = setTotalFiatBalance
notify = totalFiatBalanceChanged
proc accountListChanged*(self: WalletView) {.signal.}
proc addAccountToList*(self: WalletView, account: WalletAccount) =
self.accounts.addAccountToList(account)
# If it's the first account we ever get, use its list as our first lists
if (self.accounts.rowCount == 1):
self.setCurrentAssetList(account.assetList)
self.setCurrentAccountByIndex(0)
self.accountListChanged()
proc getFiatValue*(self: WalletView, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string {.slot.} =
if (cryptoBalance == "" or cryptoSymbol == "" or fiatSymbol == ""): return "0.00"
let val = self.status.wallet.convertValue(cryptoBalance, cryptoSymbol, fiatSymbol)
result = fmt"{val:.2f}"
proc getCryptoValue*(self: WalletView, fiatBalance: string, fiatSymbol: string, cryptoSymbol: string): string {.slot.} =
result = fmt"{self.status.wallet.convertValue(fiatBalance, fiatSymbol, cryptoSymbol)}"
proc getGasEthValue*(self: WalletView, gweiValue: string, gasLimit: string): string {.slot.} =
var gweiValueInt:int
var gasLimitInt:int
discard gweiValue.parseInt(gweiValueInt)
discard gasLimit.parseInt(gasLimitInt)
let weiValue = gweiValueInt.u256 * 1000000000.u256 * gasLimitInt.u256
let ethValue = wei2Eth(weiValue)
result = fmt"{ethValue}"
proc generateNewAccount*(self: WalletView, password: string, accountName: string, color: string): string {.slot.} =
try:
self.status.wallet.generateNewAccount(password, accountName, color)
except StatusGoException as e:
result = StatusGoError(error: e.msg).toJson
proc addAccountsFromSeed*(self: WalletView, seed: string, password: string, accountName: string, color: string): string {.slot.} =
try:
self.status.wallet.addAccountsFromSeed(seed.strip(), password, accountName, color)
except StatusGoException as e:
result = StatusGoError(error: e.msg).toJson
proc addAccountsFromPrivateKey*(self: WalletView, privateKey: string, password: string, accountName: string, color: string): string {.slot.} =
try:
self.status.wallet.addAccountsFromPrivateKey(privateKey, password, accountName, color)
except StatusGoException as e:
result = StatusGoError(error: e.msg).toJson
proc addWatchOnlyAccount*(self: WalletView, address: string, accountName: string, color: string): string {.slot.} =
self.status.wallet.addWatchOnlyAccount(address, accountName, color)
proc changeAccountSettings*(self: WalletView, address: string, accountName: string, color: string): string {.slot.} =
result = self.status.wallet.changeAccountSettings(address, accountName, color)
if (result == ""):
self.currentAccountChanged()
self.accountListChanged()
self.accounts.forceUpdate()
proc deleteAccount*(self: WalletView, address: string): string {.slot.} =
result = self.status.wallet.deleteAccount(address)
if (result == ""):
let index = self.accounts.getAccountindexByAddress(address)
if (index == -1):
return fmt"Unable to find the account with the address {address}"
self.accounts.deleteAccountAtIndex(index)
self.accountListChanged()
self.accounts.forceUpdate()
proc getAccountList(self: WalletView): QVariant {.slot.} =
return newQVariant(self.accounts)
QtProperty[QVariant] accounts:
read = getAccountList
notify = accountListChanged
proc estimateGas*(self: WalletView, from_addr: string, to: string, assetAddress: string, value: string, data: string = ""): string {.slot.} =
var
response: string
success: bool
if assetAddress != ZERO_ADDRESS and not assetAddress.isEmptyOrWhitespace:
response = self.status.wallet.estimateTokenGas(from_addr, to, assetAddress, value, success)
else:
response = self.status.wallet.estimateGas(from_addr, to, value, data, success)
if success == true:
let res = fromHex[int](response)
result = $(%* { "result": %res, "success": %success })
else:
result = $(%* { "result": "-1", "success": %success, "error": { "message": %response } })
proc transactionWasSent*(self: WalletView, txResult: string) {.signal.}
proc transactionSent(self: WalletView, txResult: string) {.slot.} =
self.transactionWasSent(txResult)
let jTxRes = txResult.parseJSON()
let txHash = jTxRes{"result"}.getStr()
if txHash != "":
self.watchTransaction("transactionWatchResultReceived", txHash)
proc sendTransaction*(self: WalletView, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) {.slot.} =
self.sendTransaction("transactionSent", from_addr, to, assetAddress, value, gas, gasPrice, password, uuid)
proc getDefaultAccount*(self: WalletView): string {.slot.} =
self.currentAccount.address
proc defaultCurrency*(self: WalletView): string {.slot.} =
self.status.wallet.getDefaultCurrency()
proc defaultCurrencyChanged*(self: WalletView) {.signal.}
proc setDefaultCurrency*(self: WalletView, currency: string) {.slot.} =
self.status.wallet.setDefaultCurrency(currency)
self.defaultCurrencyChanged()
QtProperty[string] defaultCurrency:
read = defaultCurrency
write = setDefaultCurrency
notify = defaultCurrencyChanged
proc hasAsset*(self: WalletView, symbol: string): bool {.slot.} =
self.status.wallet.hasAsset(symbol)
proc toggleAsset*(self: WalletView, symbol: string) {.slot.} =
self.status.wallet.toggleAsset(symbol)
for account in self.status.wallet.accounts:
if account.address == self.currentAccount.address:
self.currentAccount.setAccountItem(account)
else:
self.accounts.updateAssetsInList(account.address, account.assetList)
self.accountListChanged()
self.currentAccountChanged()
proc removeCustomToken*(self: WalletView, tokenAddress: string) {.slot.} =
let t = self.status.tokens.getCustomTokens().getErc20ContractByAddress(parseAddress(tokenAddress))
if t == nil: return
self.status.wallet.hideAsset(t.symbol)
self.status.tokens.removeCustomToken(tokenAddress)
self.customTokenList.loadCustomTokens()
for account in self.status.wallet.accounts:
if account.address == self.currentAccount.address:
self.currentAccount.setAccountItem(account)
else:
self.accounts.updateAssetsInList(account.address, account.assetList)
self.accountListChanged()
self.currentAccountChanged()
proc addCustomToken*(self: WalletView, address: string, name: string, symbol: string, decimals: string) {.slot.} =
self.status.wallet.addCustomToken(symbol, true, address, name, parseInt(decimals), "")
proc getUtils(self: WalletView): QVariant {.slot.} = newQVariant(self.utilsView)
QtProperty[QVariant] utilsView:
read = getUtils
proc updateView*(self: WalletView) =
self.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance())
self.totalFiatBalanceChanged()
self.currentAccount.assetList.setNewData(self.currentAccount.account.assetList)
self.currentAccountChanged()
self.accountListChanged()
self.accounts.forceUpdate()
self.setCurrentAssetList(self.currentAccount.account.assetList)
self.balanceView.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance())
self.balanceView.totalFiatBalanceChanged()
proc checkRecentHistory*(self:WalletView) {.slot.} =
var addresses:seq[string] = @[]
for acc in self.status.wallet.accounts:
addresses.add(acc.address)
discard self.status.wallet.checkRecentHistory(addresses)
self.accountsView.currentAccount.assetList.setNewData(self.accountsView.currentAccount.account.assetList)
self.accountsView.triggerUpdateAccounts()
proc transactionWatchResultReceived(self: WalletView, watchResult: string) {.slot.} =
let wTxRes = watchResult.parseJSON()["result"].getStr().parseJson(){"result"}
if wTxRes.kind == JNull:
self.checkRecentHistory()
else:
discard #TODO: Ask Simon if should we show an error popup indicating the trx wasn't mined in 10m or something
self.tokensView.setCurrentAssetList(self.accountsView.currentAccount.account.assetList)
proc getAccountBalanceSuccess*(self: WalletView, jsonResponse: string) {.slot.} =
let jsonObj = jsonResponse.parseJson()
self.status.wallet.update(jsonObj["address"].getStr(), jsonObj["eth"].getStr(), jsonObj["tokens"])
self.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance())
self.accounts.forceUpdate()
self.currentAccountChanged()
self.balanceView.getAccountBalanceSuccess(jsonResponse)
self.updateView()
proc getDefaultAddress*(self: WalletView): string {.slot.} =
result = $self.status.wallet.getWalletAccounts()[0].address
proc loadCollectiblesForAccount*(self: WalletView, address: string, currentCollectiblesList: seq[CollectibleList]) =
if (currentCollectiblesList.len > 0):
return
# Add loading state if it is the current account
if address == self.currentAccount.address:
for collectibleType in status_collectibles.COLLECTIBLE_TYPES:
self.currentCollectiblesLists.addCollectibleListToList(CollectibleList(
collectibleType: collectibleType,
collectiblesJSON: "[]",
error: "",
loading: 1
))
proc setInitialRange*(self: WalletView) {.slot.} =
discard self.status.wallet.setInitialBlocksRange()
# TODO find a way to use a loop to streamline this code
# Create a thread in the threadpool for each collectible. They can end in whichever order
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.CRYPTOKITTY)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.KUDO)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.ETHERMON)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.STICKER)
proc setCurrentAccountByIndex*(self: WalletView, index: int) {.slot.} =
if self.accountsView.setCurrentAccountByIndex(index):
let selectedAccount = self.accountsView.accounts.getAccount(index)
proc setCollectiblesResult(self: WalletView, collectiblesJSON: string) {.slot.} =
let collectibleData = parseJson(collectiblesJSON)
let address = collectibleData["address"].getStr
let collectibleType = collectibleData["collectibleType"].getStr
var collectibles: JSONNode
try:
collectibles = parseJson(collectibleData["collectiblesOrError"].getStr)
except Exception as e:
# We failed parsing, this means the result is an error string
self.currentCollectiblesLists.setErrorByType(
collectibleType,
$collectibleData["collectiblesOrError"]
)
return
self.tokensView.setCurrentAssetList(selectedAccount.assetList)
# Add the collectibles to the WalletAccount
let index = self.accounts.getAccountindexByAddress(address)
if index == -1: return
self.accounts.addCollectibleListToAccount(index, collectibleType, $collectibles)
if address == self.currentAccount.address:
# Add CollectibleListJSON to the right list
self.currentCollectiblesLists.setCollectiblesJSONByType(
collectibleType,
$collectibles
)
self.collectiblesView.setCurrentCollectiblesLists(selectedAccount.collectiblesLists)
self.collectiblesView.loadCollectiblesForAccount(selectedAccount.address, selectedAccount.collectiblesLists)
proc reloadCollectible*(self: WalletView, collectibleType: string) {.slot.} =
let address = self.currentAccount.address
self.loadCollectibles("setCollectiblesResult", address, collectibleType)
self.currentCollectiblesLists.setLoadingByType(collectibleType, 1)
self.transactionsView.setCurrentTransactions(selectedAccount.transactions.data)
proc addAccountToList*(self: WalletView, account: WalletAccount) =
self.accountsView.addAccountToList(account)
# If it's the first account we ever get, use its list as our first lists
if (self.accountsView.accounts.rowCount == 1):
self.tokensView.setCurrentAssetList(account.assetList)
discard self.accountsView.setCurrentAccountByIndex(0)
proc transactionCompleted*(self: WalletView, success: bool, txHash: string, revertReason: string = "") {.signal.}
proc setDappBrowserAddress*(self: WalletView) {.slot.} =
self.dappBrowserView.setDappBrowserAddress()
proc loadTransactionsForAccount*(self: WalletView, address: string) {.slot.} =
self.historyView.loadTransactionsForAccount(address)
proc setHistoryFetchState*(self: WalletView, accounts: seq[string], isFetching: bool) =
self.historyView.setHistoryFetchState(accounts, isFetching)
proc initBalances*(self: WalletView, loadTransactions: bool = true) =
self.balanceView.initBalances(loadTransactions)
proc setSigningPhrase*(self: WalletView, signingPhrase: string) =
self.utilsView.setSigningPhrase(signingPhrase)
proc setEtherscanLink*(self: WalletView, link: string) =
self.utilsView.setEtherscanLink(link)
proc checkRecentHistory*(self: WalletView) =
self.transactionsView.checkRecentHistory()
proc initBalances*(self: WalletView, accounts: seq[string], loadTransactions: bool = true) =
for acc in accounts:
self.balanceView.initBalance(acc, loadTransactions)
proc isNonArchivalNodeChanged*(self: WalletView) {.signal.}
@ -637,184 +160,3 @@ QtObject:
read = isNonArchivalNode
write = setNonArchivalNode
notify = isNonArchivalNodeChanged
proc gasPricePredictionsChanged*(self: WalletView) {.signal.}
proc getGasPricePredictions*(self: WalletView) {.slot.} =
self.getGasPredictions("getGasPricePredictionsResult")
proc getGasPricePredictionsResult(self: WalletView, gasPricePredictionsJson: string) {.slot.} =
let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction)
self.safeLowGasPrice = fmt"{prediction.safeLow:.3f}"
self.standardGasPrice = fmt"{prediction.standard:.3f}"
self.fastGasPrice = fmt"{prediction.fast:.3f}"
self.fastestGasPrice = fmt"{prediction.fastest:.3f}"
self.gasPricePredictionsChanged()
proc safeLowGasPrice*(self: WalletView): string {.slot.} = result = ?.self.safeLowGasPrice
QtProperty[string] safeLowGasPrice:
read = safeLowGasPrice
notify = gasPricePredictionsChanged
proc standardGasPrice*(self: WalletView): string {.slot.} = result = ?.self.standardGasPrice
QtProperty[string] standardGasPrice:
read = standardGasPrice
notify = gasPricePredictionsChanged
proc fastGasPrice*(self: WalletView): string {.slot.} = result = ?.self.fastGasPrice
QtProperty[string] fastGasPrice:
read = fastGasPrice
notify = gasPricePredictionsChanged
proc fastestGasPrice*(self: WalletView): string {.slot.} = result = ?.self.fastestGasPrice
QtProperty[string] fastestGasPrice:
read = fastestGasPrice
notify = gasPricePredictionsChanged
proc defaultGasLimit*(self: WalletView): string {.slot.} = result = ?.self.defaultGasLimit
QtProperty[string] defaultGasLimit:
read = defaultGasLimit
proc getDefaultAddress*(self: WalletView): string {.slot.} =
result = $self.status.wallet.getWalletAccounts()[0].address
proc getDefaultTokenList(self: WalletView): QVariant {.slot.} =
self.defaultTokenList.loadDefaultTokens()
result = newQVariant(self.defaultTokenList)
QtProperty[QVariant] defaultTokenList:
read = getDefaultTokenList
proc loadCustomTokens(self: WalletView) {.slot.} =
self.customTokenList.loadCustomTokens()
proc getCustomTokenList(self: WalletView): QVariant {.slot.} =
result = newQVariant(self.customTokenList)
QtProperty[QVariant] customTokenList:
read = getCustomTokenList
proc isKnownTokenContract*(self: WalletView, address: string): bool {.slot.} =
return self.status.wallet.getKnownTokenContract(parseAddress(address)) != nil
proc decodeTokenApproval*(self: WalletView, tokenAddress: string, data: string): string {.slot.} =
let amount = data[74..len(data)-1]
let token = self.status.tokens.getToken(tokenAddress)
if(token != nil):
let amountDec = $self.status.wallet.hex2Token(amount, token.decimals)
return $(%* {"symbol": token.symbol, "amount": amountDec})
return """{"error":"Unknown token address"}""";
proc isFetchingHistory*(self: WalletView): bool {.slot.} =
if self.fetchingHistoryState.hasKey(self.currentAccount.address):
return self.fetchingHistoryState[self.currentAccount.address]
return false
proc loadingTrxHistoryChanged*(self: WalletView, isLoading: bool, address: string) {.signal.}
proc loadTransactionsForAccount*(self: WalletView, address: string, toBlock: string = "0x0", limit: int = 20, loadMore: bool = false) {.slot.} =
self.loadingTrxHistoryChanged(true, address)
let toBlockParsed = stint.fromHex(Uint256, toBlock)
self.loadTransactions("setTrxHistoryResult", address, toBlockParsed, limit, loadMore)
proc setHistoryFetchState*(self: WalletView, accounts: seq[string], isFetching: bool) =
for acc in accounts:
self.fetchingHistoryState[acc] = isFetching
self.loadingTrxHistoryChanged(isFetching, acc)
proc initBalance(self: WalletView, acc: WalletAccount, loadTransactions: bool = true) =
let
accountAddress = acc.address
tokenList = acc.assetList.filter(proc(x:Asset): bool = x.address != "").map(proc(x: Asset): string = x.address)
self.initBalances("getAccountBalanceSuccess", accountAddress, tokenList)
if loadTransactions:
self.loadTransactionsForAccount(accountAddress)
proc initBalance(self: WalletView, accountAddress: string, loadTransactions: bool = true) =
var found = false
let acc = self.status.wallet.accounts.find(acc => acc.address.toLowerAscii == accountAddress.toLowerAscii, found)
if not found:
error "Failed to init balance: could not find account", account=accountAddress
return
self.initBalance(acc, loadTransactions)
proc initBalances*(self: WalletView, loadTransactions: bool = true) =
for acc in self.status.wallet.accounts:
self.initBalance(acc, loadTransactions)
proc initBalances*(self: WalletView, accounts: seq[string], loadTransactions: bool = true) =
for acc in accounts:
self.initBalance(acc, loadTransactions)
proc setTrxHistoryResult(self: WalletView, historyJSON: string) {.slot.} =
let
historyData = parseJson(historyJSON)
transactions = historyData["history"].to(seq[Transaction])
address = historyData["address"].getStr
wasFetchMore = historyData["loadMore"].getBool
isCurrentAccount = address.toLowerAscii == self.currentAccount.address.toLowerAscii
index = self.accounts.getAccountindexByAddress(address)
if index == -1: return
let account = self.accounts.getAccount(index)
# concatenate the new page of txs to existing account transactions,
# sort them by block number and nonce, then deduplicate them based on their
# transaction id.
let existingAcctTxIds = account.transactions.data.map(tx => tx.id)
let hasNewTxs = transactions.len > 0 and transactions.any(tx => not existingAcctTxIds.contains(tx.id))
if hasNewTxs or not wasFetchMore:
var allTxs: seq[Transaction] = account.transactions.data.concat(transactions)
allTxs.sort(cmpTransactions, SortOrder.Descending)
allTxs.deduplicate(tx => tx.id)
account.transactions.data = allTxs
account.transactions.hasMore = true
if isCurrentAccount:
self.currentTransactions.setHasMore(true)
self.setCurrentTransactions(allTxs)
else:
account.transactions.hasMore = false
if isCurrentAccount:
self.currentTransactions.setHasMore(false)
self.currentTransactionsChanged()
self.loadingTrxHistoryChanged(false, address)
proc resolveENS*(self: WalletView, ens: string, uuid: string) {.slot.} =
self.resolveEns("ensResolved", ens, uuid)
proc ensWasResolved*(self: WalletView, resolvedAddress: string, uuid: string) {.signal.}
proc ensResolved(self: WalletView, addressUuidJson: string) {.slot.} =
var
parsed = addressUuidJson.parseJson
address = parsed["address"].to(string)
uuid = parsed["uuid"].to(string)
if address == "0x":
address = ""
self.ensWasResolved(address, uuid)
proc transactionCompleted*(self: WalletView, success: bool, txHash: string, revertReason: string = "") {.signal.}
proc dappBrowserAccountChanged*(self: WalletView) {.signal.}
proc setDappBrowserAddress*(self: WalletView) {.slot.} =
if(self.accounts.rowCount() == 0): return
let dappAddress = self.status.settings.getSetting[:string](Setting.DappsAddress)
var index = self.accounts.getAccountIndexByAddress(dappAddress)
if index == -1: index = 0
let selectedAccount = self.accounts.getAccount(index)
if self.dappBrowserAccount.address == selectedAccount.address: return
self.dappBrowserAccount.setAccountItem(selectedAccount)
self.dappBrowserAccountChanged()
proc getDappBrowserAccount*(self: WalletView): QVariant {.slot.} =
result = newQVariant(self.dappBrowserAccount)
QtProperty[QVariant] dappBrowserAccount:
read = getDappBrowserAccount
notify = dappBrowserAccountChanged
proc setInitialRange*(self: WalletView) {.slot.} =
discard self.status.wallet.setInitialBlocksRange()

View File

@ -0,0 +1,141 @@
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import
../../../status/[status, settings, types],
../../../status/signals/types as signal_types,
../../../status/wallet as status_wallet
import account_list, account_item
logScope:
topics = "accounts-view"
QtObject:
type AccountsView* = ref object of QObject
status: Status
accounts*: AccountList
currentAccount*: AccountItemView
focusedAccount*: AccountItemView
proc setup(self: AccountsView) = self.QObject.setup
proc delete(self: AccountsView) =
self.accounts.delete
self.currentAccount.delete
self.focusedAccount.delete
self.QObject.delete
proc newAccountsView*(status: Status): AccountsView =
new(result, delete)
result.status = status
result.accounts = newAccountList()
result.currentAccount = newAccountItemView()
result.focusedAccount = newAccountItemView()
result.setup
proc generateNewAccount*(self: AccountsView, password: string, accountName: string, color: string): string {.slot.} =
try:
self.status.wallet.generateNewAccount(password, accountName, color)
except StatusGoException as e:
result = StatusGoError(error: e.msg).toJson
proc addAccountsFromSeed*(self: AccountsView, seed: string, password: string, accountName: string, color: string): string {.slot.} =
try:
self.status.wallet.addAccountsFromSeed(seed.strip(), password, accountName, color)
except StatusGoException as e:
result = StatusGoError(error: e.msg).toJson
proc addAccountsFromPrivateKey*(self: AccountsView, privateKey: string, password: string, accountName: string, color: string): string {.slot.} =
try:
self.status.wallet.addAccountsFromPrivateKey(privateKey, password, accountName, color)
except StatusGoException as e:
result = StatusGoError(error: e.msg).toJson
proc addWatchOnlyAccount*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} =
self.status.wallet.addWatchOnlyAccount(address, accountName, color)
proc currentAccountChanged*(self: AccountsView) {.signal.}
proc accountListChanged*(self: AccountsView) {.signal.}
proc addAccountToList*(self: AccountsView, account: WalletAccount) =
self.accounts.addAccountToList(account)
self.accountListChanged()
proc changeAccountSettings*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} =
result = self.status.wallet.changeAccountSettings(address, accountName, color)
if (result == ""):
self.currentAccountChanged()
self.accountListChanged()
self.accounts.forceUpdate()
proc deleteAccount*(self: AccountsView, address: string): string {.slot.} =
result = self.status.wallet.deleteAccount(address)
if (result == ""):
let index = self.accounts.getAccountindexByAddress(address)
if (index == -1):
return fmt"Unable to find the account with the address {address}"
self.accounts.deleteAccountAtIndex(index)
self.accountListChanged()
self.accounts.forceUpdate()
proc getCurrentAccount*(self: AccountsView): QVariant {.slot.} =
result = newQVariant(self.currentAccount)
proc focusedAccountChanged*(self: AccountsView) {.signal.}
proc setFocusedAccountByAddress*(self: AccountsView, address: string) {.slot.} =
if (self.accounts.rowCount() == 0): return
var index = self.accounts.getAccountindexByAddress(address)
if index == -1: index = 0
let selectedAccount = self.accounts.getAccount(index)
if self.focusedAccount.address == selectedAccount.address: return
self.focusedAccount.setAccountItem(selectedAccount)
self.focusedAccountChanged()
proc getFocusedAccount*(self: AccountsView): QVariant {.slot.} =
result = newQVariant(self.focusedAccount)
QtProperty[QVariant] focusedAccount:
read = getFocusedAccount
write = setFocusedAccountByAddress
notify = focusedAccountChanged
#TODO: use an Option here
proc setCurrentAccountByIndex*(self: AccountsView, index: int): bool =
if(self.accounts.rowCount() == 0): return false
let selectedAccount = self.accounts.getAccount(index)
if self.currentAccount.address == selectedAccount.address: return false
self.currentAccount.setAccountItem(selectedAccount)
self.currentAccountChanged()
return true
QtProperty[QVariant] currentAccount:
read = getCurrentAccount
write = setCurrentAccountByIndex
notify = currentAccountChanged
proc getAccountList(self: AccountsView): QVariant {.slot.} =
return newQVariant(self.accounts)
QtProperty[QVariant] accounts:
read = getAccountList
notify = accountListChanged
proc getDefaultAccount*(self: AccountsView): string {.slot.} =
self.currentAccount.address
proc setAccountItems*(self: AccountsView) =
for account in self.status.wallet.accounts:
if account.address == self.currentAccount.address:
self.currentAccount.setAccountItem(account)
else:
self.accounts.updateAssetsInList(account.address, account.assetList)
self.accountListChanged()
self.currentAccountChanged()
proc triggerUpdateAccounts*(self: AccountsView) =
self.currentAccountChanged()
self.accountListChanged()
self.accounts.forceUpdate()

View File

@ -0,0 +1,117 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint
from sugar import `=>`, `->`
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import
../../../status/[status, settings, wallet, tokens],
../../../status/tokens as status_tokens,
../../../status/tasks/[qt, task_runner_impl]
import account_list, account_item, transaction_list, accounts, asset_list, token_list, transactions, history
logScope:
topics = "balance-view"
type
InitBalancesTaskArg = ref object of QObjectTaskArg
address: string
tokenList: seq[string]
const initBalancesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[InitBalancesTaskArg](argEncoded)
var tokenBalances = initTable[string, string]()
for token in arg.tokenList:
tokenBalances[token] = status_tokens.getTokenBalance(token, arg.address)
let output = %* {
"address": arg.address,
"eth": getEthBalance(arg.address),
"tokens": tokenBalances
}
arg.finish(output)
proc initBalances[T](self: T, slot: string, address: string, tokenList: seq[string]) =
let arg = InitBalancesTaskArg(
tptr: cast[ByteAddress](initBalancesTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot, address: address, tokenList: tokenList
)
self.status.tasks.threadpool.start(arg)
QtObject:
type BalanceView* = ref object of QObject
status: Status
totalFiatBalance: string
accountsView: AccountsView
transactionsView*: TransactionsView
historyView*: HistoryView
proc setup(self: BalanceView) = self.QObject.setup
proc delete(self: BalanceView) = self.QObject.delete
proc newBalanceView*(status: Status, accountsView: AccountsView, transactionsView: TransactionsView, historyView: HistoryView): BalanceView =
new(result, delete)
result.status = status
result.totalFiatBalance = ""
result.accountsView = accountsView
result.transactionsView = transactionsView
result.historyView = historyView
result.setup
proc totalFiatBalanceChanged*(self: BalanceView) {.signal.}
proc getTotalFiatBalance(self: BalanceView): string {.slot.} =
self.status.wallet.getTotalFiatBalance()
proc setTotalFiatBalance*(self: BalanceView, newBalance: string) =
self.totalFiatBalance = newBalance
self.totalFiatBalanceChanged()
QtProperty[string] totalFiatBalance:
read = getTotalFiatBalance
write = setTotalFiatBalance
notify = totalFiatBalanceChanged
proc getFiatValue*(self: BalanceView, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string {.slot.} =
if (cryptoBalance == "" or cryptoSymbol == "" or fiatSymbol == ""): return "0.00"
let val = self.status.wallet.convertValue(cryptoBalance, cryptoSymbol, fiatSymbol)
result = fmt"{val:.2f}"
proc getCryptoValue*(self: BalanceView, fiatBalance: string, fiatSymbol: string, cryptoSymbol: string): string {.slot.} =
result = fmt"{self.status.wallet.convertValue(fiatBalance, fiatSymbol, cryptoSymbol)}"
proc defaultCurrency*(self: BalanceView): string {.slot.} =
self.status.wallet.getDefaultCurrency()
proc defaultCurrencyChanged*(self: BalanceView) {.signal.}
proc setDefaultCurrency*(self: BalanceView, currency: string) {.slot.} =
self.status.wallet.setDefaultCurrency(currency)
self.defaultCurrencyChanged()
QtProperty[string] defaultCurrency:
read = defaultCurrency
write = setDefaultCurrency
notify = defaultCurrencyChanged
proc initBalances*(self: BalanceView, loadTransactions: bool = true) =
for acc in self.status.wallet.accounts:
let accountAddress = acc.address
let tokenList = acc.assetList.filter(proc(x:Asset): bool = x.address != "").map(proc(x: Asset): string = x.address)
self.initBalances("getAccountBalanceSuccess", accountAddress, tokenList)
if loadTransactions:
self.historyView.loadTransactionsForAccount(accountAddress)
proc initBalance*(self: BalanceView, accountAddress: string, loadTransactions: bool = true) =
echo "initBalance"
#var found = false
#let acc = self.status.wallet.accounts.find(acc => acc.address.toLowerAscii == accountAddress.toLowerAscii, found)
#if not found:
# error "Failed to init balance: could not find account", account=accountAddress
# return
#self.initBalance(acc, loadTransactions)
proc getAccountBalanceSuccess*(self: BalanceView, jsonResponse: string) {.slot.} =
let jsonObj = jsonResponse.parseJson()
self.status.wallet.update(jsonObj["address"].getStr(), jsonObj["eth"].getStr(), jsonObj["tokens"])
self.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance())
self.accountsView.triggerUpdateAccounts()

View File

@ -0,0 +1,133 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import
../../../status/[status, settings, wallet, tokens, utils, types],
../../../status/wallet/collectibles as status_collectibles,
../../../status/tasks/[qt, task_runner_impl]
import collectibles_list, accounts, account_list, account_item
logScope:
topics = "collectibles-view"
type
LoadCollectiblesTaskArg = ref object of QObjectTaskArg
address: string
collectiblesType: string
running*: ByteAddress # pointer to threadpool's `.running` Atomic[bool]
const loadCollectiblesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[LoadCollectiblesTaskArg](argEncoded)
var running = cast[ptr Atomic[bool]](arg.running)
var collectiblesOrError = ""
case arg.collectiblesType:
of status_collectibles.CRYPTOKITTY:
collectiblesOrError = status_collectibles.getCryptoKitties(arg.address)
of status_collectibles.KUDO:
collectiblesOrError = status_collectibles.getKudos(arg.address)
of status_collectibles.ETHERMON:
collectiblesOrError = status_collectibles.getEthermons(arg.address)
of status_collectibles.STICKER:
collectiblesOrError = status_collectibles.getStickers(arg.address, running[])
let output = %*{
"address": arg.address,
"collectibleType": arg.collectiblesType,
"collectiblesOrError": collectiblesOrError
}
arg.finish(output)
proc loadCollectibles[T](self: T, slot: string, address: string, collectiblesType: string) =
let arg = LoadCollectiblesTaskArg(
tptr: cast[ByteAddress](loadCollectiblesTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot, address: address, collectiblesType: collectiblesType,
running: cast[ByteAddress](addr self.status.tasks.threadpool.running)
)
self.status.tasks.threadpool.start(arg)
QtObject:
type CollectiblesView* = ref object of QObject
status: Status
accountsView*: AccountsView
currentCollectiblesLists*: CollectiblesList
proc setup(self: CollectiblesView) = self.QObject.setup
proc delete(self: CollectiblesView) =
self.currentCollectiblesLists.delete
self.QObject.delete
proc newCollectiblesView*(status: Status, accountsView: AccountsView): CollectiblesView =
new(result, delete)
result.status = status
result.currentCollectiblesLists = newCollectiblesList()
result.accountsView = accountsView # TODO: not ideal but a solution for now
result.setup
proc currentCollectiblesListsChanged*(self: CollectiblesView) {.signal.}
proc getCurrentCollectiblesLists(self: CollectiblesView): QVariant {.slot.} =
return newQVariant(self.currentCollectiblesLists)
proc setCurrentCollectiblesLists*(self: CollectiblesView, collectiblesLists: seq[CollectibleList]) =
self.currentCollectiblesLists.setNewData(collectiblesLists)
self.currentCollectiblesListsChanged()
QtProperty[QVariant] collectiblesLists:
read = getCurrentCollectiblesLists
write = setCurrentCollectiblesLists
notify = currentCollectiblesListsChanged
proc loadCollectiblesForAccount*(self: CollectiblesView, address: string, currentCollectiblesList: seq[CollectibleList]) =
if (currentCollectiblesList.len > 0):
return
# Add loading state if it is the current account
if address == self.accountsView.currentAccount.address:
for collectibleType in status_collectibles.COLLECTIBLE_TYPES:
self.currentCollectiblesLists.addCollectibleListToList(CollectibleList(
collectibleType: collectibleType,
collectiblesJSON: "[]",
error: "",
loading: 1
))
# TODO find a way to use a loop to streamline this code
# Create a thread in the threadpool for each collectible. They can end in whichever order
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.CRYPTOKITTY)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.KUDO)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.ETHERMON)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.STICKER)
proc setCollectiblesResult(self: CollectiblesView, collectiblesJSON: string) {.slot.} =
let collectibleData = parseJson(collectiblesJSON)
let address = collectibleData["address"].getStr
let collectibleType = collectibleData["collectibleType"].getStr
var collectibles: JSONNode
try:
collectibles = parseJson(collectibleData["collectiblesOrError"].getStr)
except Exception as e:
# We failed parsing, this means the result is an error string
self.currentCollectiblesLists.setErrorByType(
collectibleType,
$collectibleData["collectiblesOrError"]
)
return
# Add the collectibles to the WalletAccount
let index = self.accountsView.accounts.getAccountindexByAddress(address)
if index == -1: return
self.accountsView.accounts.addCollectibleListToAccount(index, collectibleType, $collectibles)
if address == self.accountsView.currentAccount.address:
# Add CollectibleListJSON to the right list
self.currentCollectiblesLists.setCollectiblesJSONByType(
collectibleType,
$collectibles
)
proc reloadCollectible*(self: CollectiblesView, collectibleType: string) {.slot.} =
let address = self.accountsView.currentAccount.address
self.loadCollectibles("setCollectiblesResult", address, collectibleType)
self.currentCollectiblesLists.setLoadingByType(collectibleType, 1)

View File

@ -0,0 +1,47 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import ../../../status/[status, settings, wallet, tokens, types]
import account_list, account_item, transaction_list, accounts, asset_list, token_list
logScope:
topics = "ens-view"
QtObject:
type DappBrowserView* = ref object of QObject
status: Status
accountsView: AccountsView
dappBrowserAccount*: AccountItemView
proc setup(self: DappBrowserView) = self.QObject.setup
proc delete(self: DappBrowserView) =
self.dappBrowserAccount.delete
self.QObject.delete
proc newDappBrowserView*(status: Status, accountsView: AccountsView): DappBrowserView =
new(result, delete)
result.status = status
result.accountsView = accountsView
result.dappBrowserAccount = newAccountItemView()
result.setup
proc dappBrowserAccountChanged*(self: DappBrowserView) {.signal.}
proc setDappBrowserAddress*(self: DappBrowserView) {.slot.} =
if(self.accountsView.accounts.rowCount() == 0): return
let dappAddress = self.status.settings.getSetting[:string](Setting.DappsAddress)
var index = self.accountsView.accounts.getAccountIndexByAddress(dappAddress)
if index == -1: index = 0
let selectedAccount = self.accountsView.accounts.getAccount(index)
if self.dappBrowserAccount.address == selectedAccount.address: return
self.dappBrowserAccount.setAccountItem(selectedAccount)
self.dappBrowserAccountChanged()
proc getDappBrowserAccount*(self: DappBrowserView): QVariant {.slot.} =
result = newQVariant(self.dappBrowserAccount)
QtProperty[QVariant] dappBrowserAccount:
read = getDappBrowserAccount
notify = dappBrowserAccountChanged

View File

@ -0,0 +1,57 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import
../../../status/[status, settings, wallet, tokens],
../../../status/ens as status_ens,
../../../status/tasks/[qt, task_runner_impl]
import account_list, account_item, transaction_list, accounts, asset_list, token_list
logScope:
topics = "ens-view"
type
ResolveEnsTaskArg = ref object of QObjectTaskArg
ens: string
uuid: string
const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[ResolveEnsTaskArg](argEncoded)
output = %* { "address": status_ens.address(arg.ens), "uuid": arg.uuid }
arg.finish(output)
proc resolveEns[T](self: T, slot: string, ens: string, uuid: string) =
let arg = ResolveEnsTaskArg(
tptr: cast[ByteAddress](resolveEnsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot, ens: ens, uuid: uuid
)
self.status.tasks.threadpool.start(arg)
QtObject:
type EnsView* = ref object of QObject
status: Status
proc setup(self: EnsView) = self.QObject.setup
proc delete(self: EnsView) = self.QObject.delete
proc newEnsView*(status: Status): EnsView =
new(result, delete)
result.status = status
result.setup
proc resolveENS*(self: EnsView, ens: string, uuid: string) {.slot.} =
self.resolveEns("ensResolved", ens, uuid)
proc ensWasResolved*(self: EnsView, resolvedAddress: string, uuid: string) {.signal.}
proc ensResolved(self: EnsView, addressUuidJson: string) {.slot.} =
var
parsed = addressUuidJson.parseJson
address = parsed["address"].to(string)
uuid = parsed["uuid"].to(string)
if address == "0x":
address = ""
self.ensWasResolved(address, uuid)

View File

@ -0,0 +1,115 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import
../../../status/[status, settings, wallet, tokens, utils, types],
../../../status/tasks/[qt, task_runner_impl]
import account_list, account_item, transaction_list, accounts
const ZERO_ADDRESS* = "0x0000000000000000000000000000000000000000"
type
GasPredictionsTaskArg = ref object of QObjectTaskArg
const getGasPredictionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[GasPredictionsTaskArg](argEncoded)
output = %getGasPricePredictions()
arg.finish(output)
proc getGasPredictions[T](self: T, slot: string) =
let arg = GasPredictionsTaskArg(
tptr: cast[ByteAddress](getGasPredictionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot
)
self.status.tasks.threadpool.start(arg)
logScope:
topics = "gas-view"
QtObject:
type GasView* = ref object of QObject
status: Status
safeLowGasPrice: string
standardGasPrice: string
fastGasPrice: string
fastestGasPrice: string
defaultGasLimit: string
proc setup(self: GasView) = self.QObject.setup
proc delete(self: GasView) = self.QObject.delete
proc newGasView*(status: Status): GasView =
new(result, delete)
result.status = status
result.safeLowGasPrice = "0"
result.standardGasPrice = "0"
result.fastGasPrice = "0"
result.fastestGasPrice = "0"
result.defaultGasLimit = "21000"
result.setup
proc getGasEthValue*(self: GasView, gweiValue: string, gasLimit: string): string {.slot.} =
var gweiValueInt:int
var gasLimitInt:int
discard gweiValue.parseInt(gweiValueInt)
discard gasLimit.parseInt(gasLimitInt)
let weiValue = gweiValueInt.u256 * 1000000000.u256 * gasLimitInt.u256
let ethValue = wei2Eth(weiValue)
result = fmt"{ethValue}"
proc estimateGas*(self: GasView, from_addr: string, to: string, assetAddress: string, value: string, data: string = ""): string {.slot.} =
var
response: string
success: bool
if assetAddress != ZERO_ADDRESS and not assetAddress.isEmptyOrWhitespace:
response = self.status.wallet.estimateTokenGas(from_addr, to, assetAddress, value, success)
else:
response = self.status.wallet.estimateGas(from_addr, to, value, data, success)
if success == true:
let res = fromHex[int](response)
result = $(%* { "result": %res, "success": %success })
else:
result = $(%* { "result": "-1", "success": %success, "error": { "message": %response } })
proc gasPricePredictionsChanged*(self: GasView) {.signal.}
proc getGasPricePredictions*(self: GasView) {.slot.} =
self.getGasPredictions("getGasPricePredictionsResult")
proc getGasPricePredictionsResult(self: GasView, gasPricePredictionsJson: string) {.slot.} =
let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction)
self.safeLowGasPrice = fmt"{prediction.safeLow:.3f}"
self.standardGasPrice = fmt"{prediction.standard:.3f}"
self.fastGasPrice = fmt"{prediction.fast:.3f}"
self.fastestGasPrice = fmt"{prediction.fastest:.3f}"
self.gasPricePredictionsChanged()
proc safeLowGasPrice*(self: GasView): string {.slot.} = result = ?.self.safeLowGasPrice
QtProperty[string] safeLowGasPrice:
read = safeLowGasPrice
notify = gasPricePredictionsChanged
proc standardGasPrice*(self: GasView): string {.slot.} = result = ?.self.standardGasPrice
QtProperty[string] standardGasPrice:
read = standardGasPrice
notify = gasPricePredictionsChanged
proc fastGasPrice*(self: GasView): string {.slot.} = result = ?.self.fastGasPrice
QtProperty[string] fastGasPrice:
read = fastGasPrice
notify = gasPricePredictionsChanged
proc fastestGasPrice*(self: GasView): string {.slot.} = result = ?.self.fastestGasPrice
QtProperty[string] fastestGasPrice:
read = fastestGasPrice
notify = gasPricePredictionsChanged
proc defaultGasLimit*(self: GasView): string {.slot.} = result = ?.self.defaultGasLimit
QtProperty[string] defaultGasLimit:
read = defaultGasLimit

View File

@ -0,0 +1,92 @@
import algorithm, atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint, sugar
from sugar import `=>`, `->`
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import
../../../status/[status, settings, wallet, tokens, types, utils],
../../../status/tasks/[qt, task_runner_impl]
import account_list, account_item, transaction_list, accounts, asset_list, token_list, transactions
logScope:
topics = "history-view"
QtObject:
type HistoryView* = ref object of QObject
status: Status
accountsView: AccountsView
transactionsView*: TransactionsView
fetchingHistoryState: Table[string, bool]
proc setup(self: HistoryView) = self.QObject.setup
proc delete(self: HistoryView) = self.QObject.delete
proc newHistoryView*(status: Status, accountsView: AccountsView, transactionsView: TransactionsView): HistoryView =
new(result, delete)
result.status = status
result.fetchingHistoryState = initTable[string, bool]()
result.accountsView = accountsView
result.transactionsView = transactionsView
result.setup
proc historyWasFetched*(self: HistoryView) {.signal.}
proc setHistoryFetchState*(self: HistoryView, accounts: seq[string], isFetching: bool) =
for acc in accounts:
self.fetchingHistoryState[acc] = isFetching
if not isFetching: self.historyWasFetched()
proc isFetchingHistory*(self: HistoryView, address: string): bool {.slot.} =
if self.fetchingHistoryState.hasKey(address):
return self.fetchingHistoryState[address]
return true
proc isHistoryFetched*(self: HistoryView, address: string): bool {.slot.} =
return self.transactionsView.currentTransactions.rowCount() > 0
proc loadingTrxHistoryChanged*(self: HistoryView, isLoading: bool, address: string) {.signal.}
# proc loadTransactionsForAccount*(self: HistoryView, address: string) {.slot.} =
# self.loadingTrxHistoryChanged(true)
# self.transactionsView.loadTransactions("setTrxHistoryResult", address)
proc loadTransactionsForAccount*(self: HistoryView, address: string, toBlock: string = "0x0", limit: int = 20, loadMore: bool = false) {.slot.} =
self.loadingTrxHistoryChanged(true, address)
let toBlockParsed = stint.fromHex(Uint256, toBlock)
self.loadTransactions("setTrxHistoryResult", address, toBlockParsed, limit, loadMore)
# proc getLatestTransactionHistory*(self: HistoryView, accounts: seq[string]) =
# for acc in accounts:
# self.loadTransactionsForAccount(acc)
proc setTrxHistoryResult(self: HistoryView, historyJSON: string) {.slot.} =
let
historyData = parseJson(historyJSON)
transactions = historyData["history"].to(seq[Transaction])
address = historyData["address"].getStr
wasFetchMore = historyData["loadMore"].getBool
isCurrentAccount = address.toLowerAscii == self.accountsView.currentAccount.address.toLowerAscii
index = self.accountsView.accounts.getAccountindexByAddress(address)
if index == -1: return
let account = self.accountsView.accounts.getAccount(index)
# concatenate the new page of txs to existing account transactions,
# sort them by block number and nonce, then deduplicate them based on their
# transaction id.
let existingAcctTxIds = account.transactions.data.map(tx => tx.id)
let hasNewTxs = transactions.len > 0 and transactions.any(tx => not existingAcctTxIds.contains(tx.id))
if hasNewTxs or not wasFetchMore:
var allTxs: seq[Transaction] = account.transactions.data.concat(transactions)
allTxs.sort(cmpTransactions, SortOrder.Descending)
allTxs.deduplicate(tx => tx.id)
account.transactions.data = allTxs
account.transactions.hasMore = true
if isCurrentAccount:
self.transactionsView.currentTransactions.setHasMore(true)
self.transactionsView.setCurrentTransactions(allTxs)
else:
account.transactions.hasMore = false
if isCurrentAccount:
self.transactionsView.currentTransactions.setHasMore(false)
self.transactionsView.currentTransactionsChanged()
self.loadingTrxHistoryChanged(false, address)

View File

@ -0,0 +1,96 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint
import NimQml, json, sequtils, chronicles, strutils, strformat, json
import ../../../status/[status, settings, wallet, tokens, utils, types]
import account_list, account_item, transaction_list, accounts, asset_list, token_list
logScope:
topics = "tokens-view"
QtObject:
type TokensView* = ref object of QObject
status: Status
accountsView: AccountsView
currentAssetList*: AssetList
defaultTokenList: TokenList
customTokenList: TokenList
proc setup(self: TokensView) = self.QObject.setup
proc delete(self: TokensView) =
self.currentAssetList.delete
self.defaultTokenList.delete
self.customTokenList.delete
self.QObject.delete
proc newTokensView*(status: Status, accountsView: AccountsView): TokensView =
new(result, delete)
result.status = status
result.accountsView = accountsView
result.currentAssetList = newAssetList()
result.defaultTokenList = newTokenList(status)
result.customTokenList = newTokenList(status)
result.setup
proc hasAsset*(self: TokensView, symbol: string): bool {.slot.} =
self.status.wallet.hasAsset(symbol)
proc toggleAsset*(self: TokensView, symbol: string) {.slot.} =
self.status.wallet.toggleAsset(symbol)
self.accountsView.setAccountItems()
proc removeCustomToken*(self: TokensView, tokenAddress: string) {.slot.} =
let t = self.status.tokens.getCustomTokens().getErc20ContractByAddress(parseAddress(tokenAddress))
if t == nil: return
self.status.wallet.hideAsset(t.symbol)
self.status.tokens.removeCustomToken(tokenAddress)
self.customTokenList.loadCustomTokens()
self.accountsView.setAccountItems()
proc addCustomToken*(self: TokensView, address: string, name: string, symbol: string, decimals: string) {.slot.} =
self.status.wallet.addCustomToken(symbol, true, address, name, parseInt(decimals), "")
proc getDefaultTokenList(self: TokensView): QVariant {.slot.} =
self.defaultTokenList.loadDefaultTokens()
result = newQVariant(self.defaultTokenList)
QtProperty[QVariant] defaultTokenList:
read = getDefaultTokenList
proc loadCustomTokens(self: TokensView) {.slot.} =
self.customTokenList.loadCustomTokens()
proc getCustomTokenList(self: TokensView): QVariant {.slot.} =
result = newQVariant(self.customTokenList)
QtProperty[QVariant] customTokenList:
read = getCustomTokenList
proc isKnownTokenContract*(self: TokensView, address: string): bool {.slot.} =
return self.status.wallet.getKnownTokenContract(parseAddress(address)) != nil
proc decodeTokenApproval*(self: TokensView, tokenAddress: string, data: string): string {.slot.} =
let amount = data[74..len(data)-1]
let token = self.status.tokens.getToken(tokenAddress)
if(token != nil):
let amountDec = $self.status.wallet.hex2Token(amount, token.decimals)
return $(%* {"symbol": token.symbol, "amount": amountDec})
return """{"error":"Unknown token address"}""";
proc getStatusToken*(self: TokensView): string {.slot.} = self.status.wallet.getStatusToken
proc currentAssetListChanged*(self: TokensView) {.signal.}
proc getCurrentAssetList(self: TokensView): QVariant {.slot.} =
return newQVariant(self.currentAssetList)
proc setCurrentAssetList*(self: TokensView, assetList: seq[Asset]) =
self.currentAssetList.setNewData(assetList)
self.currentAssetListChanged()
QtProperty[QVariant] assets:
read = getCurrentAssetList
write = setCurrentAssetList
notify = currentAssetListChanged

View File

@ -0,0 +1,148 @@
import algorithm, atomics, sequtils, strformat, strutils, sugar, sequtils, json, parseUtils, std/wrapnils, tables
import NimQml, json, sequtils, chronicles, strutils, strformat, json, stint
import
../../../status/[status, settings, wallet, tokens],
../../../status/wallet as status_wallet,
../../../status/tasks/[qt, task_runner_impl]
import account_list, account_item, transaction_list, accounts
const ZERO_ADDRESS* = "0x0000000000000000000000000000000000000000"
logScope:
topics = "transactions-view"
type
SendTransactionTaskArg = ref object of QObjectTaskArg
from_addr: string
to: string
assetAddress: string
value: string
gas: string
gasPrice: string
password: string
uuid: string
LoadTransactionsTaskArg = ref object of QObjectTaskArg
address: string
toBlock: Uint256
limit: int
loadMore: bool
WatchTransactionTaskArg = ref object of QObjectTaskArg
transactionHash: string
const sendTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[SendTransactionTaskArg](argEncoded)
var
success: bool
response: string
if arg.assetAddress != ZERO_ADDRESS and not arg.assetAddress.isEmptyOrWhitespace:
response = wallet.sendTokenTransaction(arg.from_addr, arg.to, arg.assetAddress, arg.value, arg.gas, arg.gasPrice, arg.password, success)
else:
response = wallet.sendTransaction(arg.from_addr, arg.to, arg.value, arg.gas, arg.gasPrice, arg.password, success)
let output = %* { "result": %response, "success": %success, "uuid": %arg.uuid }
arg.finish(output)
proc sendTransaction[T](self: T, slot: string, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) =
let arg = SendTransactionTaskArg(
tptr: cast[ByteAddress](sendTransactionTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot, from_addr: from_addr, to: to,
assetAddress: assetAddress, value: value, gas: gas,
gasPrice: gasPrice, password: password, uuid: uuid
)
self.status.tasks.threadpool.start(arg)
const loadTransactionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[LoadTransactionsTaskArg](argEncoded)
output = %*{
"address": arg.address,
"history": status_wallet.getTransfersByAddress(arg.address, arg.toBlock, arg.limit, arg.loadMore),
"loadMore": arg.loadMore
}
arg.finish(output)
proc loadTransactions*[T](self: T, slot: string, address: string, toBlock: Uint256, limit: int, loadMore: bool) =
let arg = LoadTransactionsTaskArg(
tptr: cast[ByteAddress](loadTransactionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot, address: address,
toBlock: toBlock, limit: limit, loadMore: loadMore
)
self.status.tasks.threadpool.start(arg)
const watchTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[WatchTransactionTaskArg](argEncoded)
response = status_wallet.watchTransaction(arg.transactionHash)
output = %* { "result": response }
arg.finish(output)
proc watchTransaction[T](self: T, slot: string, transactionHash: string) =
let arg = WatchTransactionTaskArg(
tptr: cast[ByteAddress](watchTransactionTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot, transactionHash: transactionHash
)
self.status.tasks.threadpool.start(arg)
QtObject:
type TransactionsView* = ref object of QObject
status: Status
accountsView*: AccountsView
transactionsView*: TransactionsView
currentTransactions*: TransactionList
proc setup(self: TransactionsView) = self.QObject.setup
proc delete(self: TransactionsView) =
self.currentTransactions.delete
self.QObject.delete
proc newTransactionsView*(status: Status, accountsView: AccountsView): TransactionsView =
new(result, delete)
result.status = status
result.accountsView = accountsView # TODO: not ideal but a solution for now
result.currentTransactions = newTransactionList()
result.setup
proc currentTransactionsChanged*(self: TransactionsView) {.signal.}
proc getCurrentTransactions*(self: TransactionsView): QVariant {.slot.} =
return newQVariant(self.currentTransactions)
proc setCurrentTransactions*(self: TransactionsView, transactionList: seq[Transaction]) =
self.currentTransactions.setNewData(transactionList)
self.currentTransactionsChanged()
QtProperty[QVariant] transactions:
read = getCurrentTransactions
write = setCurrentTransactions
notify = currentTransactionsChanged
proc transactionWasSent*(self: TransactionsView, txResult: string) {.signal.}
proc transactionSent(self: TransactionsView, txResult: string) {.slot.} =
self.transactionWasSent(txResult)
let jTxRes = txResult.parseJSON()
let txHash = jTxRes{"result"}.getStr()
if txHash != "":
self.watchTransaction("transactionWatchResultReceived", txHash)
proc sendTransaction*(self: TransactionsView, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) {.slot.} =
self.sendTransaction("transactionSent", from_addr, to, assetAddress, value, gas, gasPrice, password, uuid)
proc checkRecentHistory*(self: TransactionsView) {.slot.} =
var addresses:seq[string] = @[]
for acc in self.status.wallet.accounts:
addresses.add(acc.address)
discard self.status.wallet.checkRecentHistory(addresses)
proc transactionWatchResultReceived(self: TransactionsView, watchResult: string) {.slot.} =
let wTxRes = watchResult.parseJSON()["result"].getStr().parseJson(){"result"}
if wTxRes.kind == JNull:
self.checkRecentHistory()
else:
discard #TODO: Ask Simon if should we show an error popup indicating the trx wasn't mined in 10m or something
proc transactionCompleted*(self: TransactionsView, success: bool, txHash: string, revertReason: string = "") {.signal.}

View File

@ -0,0 +1,45 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint
import NimQml, json, sequtils, chronicles, strutils, strformat, json
logScope:
topics = "utils-view"
QtObject:
type UtilsView* = ref object of QObject
etherscanLink: string
signingPhrase: string
proc setup(self: UtilsView) = self.QObject.setup
proc delete(self: UtilsView) = self.QObject.delete
proc newUtilsView*(): UtilsView =
new(result, delete)
result.etherscanLink = ""
result.signingPhrase = ""
result.setup
proc etherscanLinkChanged*(self: UtilsView) {.signal.}
proc getEtherscanLink*(self: UtilsView): QVariant {.slot.} =
newQVariant(self.etherscanLink.replace("/address", "/tx"))
proc setEtherscanLink*(self: UtilsView, link: string) =
self.etherscanLink = link
self.etherscanLinkChanged()
proc signingPhraseChanged*(self: UtilsView) {.signal.}
proc getSigningPhrase*(self: UtilsView): QVariant {.slot.} =
newQVariant(self.signingPhrase)
proc setSigningPhrase*(self: UtilsView, signingPhrase: string) =
self.signingPhrase = signingPhrase
self.signingPhraseChanged()
QtProperty[QVariant] etherscanLink:
read = getEtherscanLink
notify = etherscanLinkChanged
QtProperty[QVariant] signingPhrase:
read = getSigningPhrase
notify = signingPhraseChanged

View File

@ -10,6 +10,12 @@ import nbaser
import stew/byteutils
from base32 import nil
const HTTPS_SCHEME = "https"
const IPFS_GATEWAY = ".infura.status.im"
const SWARM_GATEWAY = "swarm-gateways.net"
const base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
logScope:
topics = "provider-model"
@ -238,3 +244,20 @@ proc postMessage*(self: ProviderModel, message: string): string =
of RequestTypes.HistoryStateChanged: """{"type":"TODO-IMPLEMENT-THIS"}""" ############# TODO:
of RequestTypes.APIRequest: self.process(message.toAPIRequest())
else: """{"type":"TODO-IMPLEMENT-THIS"}""" ##################### TODO:
proc ensResourceURL*(self: ProviderModel, ens: string, url: string): (string, string, string, string, bool) =
let contentHash = contenthash(ens)
if contentHash == "": # ENS does not have a content hash
return (url, url, HTTPS_SCHEME, "", false)
let decodedHash = contentHash.decodeENSContentHash()
case decodedHash[0]:
of ENSType.IPFS:
let base32Hash = base32.encode(string.fromBytes(base58.decode(decodedHash[1]))).toLowerAscii().replace("=", "")
result = (url, base32Hash & IPFS_GATEWAY, HTTPS_SCHEME, "", true)
of ENSType.SWARM:
result = (url, SWARM_GATEWAY, HTTPS_SCHEME, "/bzz:/" & decodedHash[1] & "/", true)
of ENSType.IPNS:
result = (url, decodedHash[1], HTTPS_SCHEME, "", true)
else:
warn "Unknown content for", ens, contentHash

View File

@ -117,9 +117,9 @@ Popup {
width: 190
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
showAccountDetails: false
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
selectedAccount: walletModel.dappBrowserAccount
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
onSelectedAccountChanged: {
if (!root.currentAddress) {
// We just set the account for the first time. Nothing to do here

View File

@ -212,7 +212,7 @@ property Component sendTransactionModalComponent: SignTransactionModal {}
toastMessage.source = "../../img/loading.svg"
toastMessage.iconColor = Style.current.primary
toastMessage.iconRotates = true
toastMessage.link = `${_walletModel.etherscanLink}/${responseObj.result.result}`
toastMessage.link = `${_walletModel.utilsView.etherscanLink}/${responseObj.result.result}`
toastMessage.open()
} catch (e) {
if (Utils.isInvalidPasswordMessage(e.message)){
@ -229,7 +229,7 @@ property Component sendTransactionModalComponent: SignTransactionModal {}
}
sendDialog.open();
walletModel.getGasPricePredictions()
walletModel.gasView.getGasPricePredictions()
} else if (request.type === Constants.web3SendAsyncReadOnly && ["eth_sign", "personal_sign", "eth_signTypedData", "eth_signTypedData_v3"].indexOf(request.payload.method) > -1) {
const signDialog = signMessageModalComponent.createObject(browserWindow, {
request,

View File

@ -109,9 +109,9 @@ Popup {
anchors.left: parent.left
anchors.right: copyBtn.left
anchors.rightMargin: Style.current.padding
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
selectedAccount: walletModel.dappBrowserAccount
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
onSelectedAccountChanged: {
if (!accountSelectorRow.currentAddress) {
// We just set the account for the first time. Nothing to do here

View File

@ -72,7 +72,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: parent.width
signingPhrase: walletModel.signingPhrase
signingPhrase: walletModel.utilsView.signingPhrase
visible: showSigningPhrase
}

View File

@ -439,7 +439,7 @@ StackLayout {
return {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
alias: chatsModel.activeChannel.alias,
identicon: activeChatIdenticon,
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
@ -466,7 +466,7 @@ StackLayout {
return {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
alias: chatsModel.activeChannel.alias,
identicon: activeChatIdenticon,
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
@ -480,7 +480,7 @@ StackLayout {
SendModal {
id: sendTransactionWithEns
onOpened: {
walletModel.getGasPricePredictions()
walletModel.gasView.getGasPricePredictions()
}
onClosed: {
txModalLoader.closed()
@ -490,7 +490,7 @@ StackLayout {
return {
address: "",
alias: chatsModel.activeChannel.alias,
identicon: activeChatIdenticon,
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact,
ensVerified: true

View File

@ -35,15 +35,15 @@ ModalPopup {
AccountSelector {
id: selectFromAccount
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
selectedAccount: {
const currAcc = walletModel.currentAccount
const currAcc = walletModel.accountsView.currentAccount
if (currAcc.walletType !== Constants.watchWalletType) {
return currAcc
}
return null
}
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
width: stack.width
label: {
return root.isRequested ?
@ -73,7 +73,7 @@ ModalPopup {
RecipientSelector {
id: selectRecipient
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
contacts: profileModel.contacts.addedContacts
label: root.isRequested ?
//% "From"
@ -97,9 +97,9 @@ ModalPopup {
AssetAndAmountInput {
id: txtAmount
selectedAccount: selectFromAccount.selectedAccount
defaultCurrency: walletModel.defaultCurrency
getFiatValue: walletModel.getFiatValue
getCryptoValue: walletModel.getCryptoValue
defaultCurrency: walletModel.balanceView.defaultCurrency
getFiatValue: walletModel.balanceView.getFiatValue
getCryptoValue: walletModel.balanceView.getCryptoValue
validateBalance: !root.isRequested
width: stack.width
}
@ -121,7 +121,7 @@ ModalPopup {
asset: txtAmount.selectedAsset
amount: { "value": txtAmount.selectedAmount, "fiatValue": txtAmount.selectedFiatAmount }
toWarn: addressRequiredValidator.isWarn
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
}
AddressRequiredValidator {

View File

@ -19,7 +19,7 @@ ModalPopup {
property alias transactionSigner: transactionSigner
property var sendTransaction: function(selectedGasLimit, selectedGasPrice, enteredPassword) {
let responseStr = walletModel.sendTransaction(selectFromAccount.selectedAccount.address,
let responseStr = walletModel.transactionsView.sendTransaction(selectFromAccount.selectedAccount.address,
selectRecipient.selectedRecipient.address,
root.selectedAsset.address,
root.selectedAmount,
@ -64,7 +64,7 @@ ModalPopup {
id: groupSelectAcct
headerText: {
if(trxData.startsWith("0x095ea7b3")){
const approveData = JSON.parse(walletModel.decodeTokenApproval(selectedRecipient.address, trxData))
const approveData = JSON.parse(walletModel.tokensView.decodeTokenApproval(selectedRecipient.address, trxData))
if(approveData.symbol)
//% "Authorize %1 %2"
return qsTrId("authorize--1--2").arg(approveData.amount).arg(approveData.symbol)
@ -81,8 +81,8 @@ ModalPopup {
}
AccountSelector {
id: selectFromAccount
accounts: walletModel.accounts
currency: walletModel.defaultCurrency
accounts: walletModel.accountsView.accounts
currency: walletModel.balanceView.defaultCurrency
width: stack.width
selectedAccount: root.selectedAccount
//% "Choose account"
@ -94,7 +94,7 @@ ModalPopup {
RecipientSelector {
id: selectRecipient
visible: false
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
contacts: profileModel.contacts.addedContacts
selectedRecipient: root.selectedRecipient
readOnly: true
@ -113,11 +113,11 @@ ModalPopup {
GasSelector {
id: gasSelector
anchors.topMargin: Style.current.bigPadding
slowestGasPrice: parseFloat(walletModel.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.fastestGasPrice)
getGasEthValue: walletModel.getGasEthValue
getFiatValue: walletModel.getFiatValue
defaultCurrency: walletModel.defaultCurrency
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency
width: stack.width
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
@ -129,7 +129,7 @@ ModalPopup {
return
}
let gasEstimate = JSON.parse(walletModel.estimateGas(
let gasEstimate = JSON.parse(walletModel.gasView.estimateGas(
selectFromAccount.selectedAccount.address,
selectRecipient.selectedRecipient.address,
root.selectedAsset.address,
@ -183,7 +183,7 @@ ModalPopup {
toAccount: selectRecipient.selectedRecipient
asset: root.selectedAsset
amount: { "value": root.selectedAmount, "fiatValue": root.selectedFiatAmount }
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
isFromEditable: false
trxData: root.trxData
isGasEditable: true
@ -224,7 +224,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: walletModel.signingPhrase
signingPhrase: walletModel.utilsView.signingPhrase
}
}
}
@ -273,7 +273,7 @@ ModalPopup {
}
Connections {
target: walletModel
target: walletModel.transactionsView
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
@ -297,7 +297,7 @@ ModalPopup {
toastMessage.source = "../../../../img/loading.svg"
toastMessage.iconColor = Style.current.primary
toastMessage.iconRotates = true
toastMessage.link = `${walletModel.etherscanLink}/${response.result}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${response.result}`
toastMessage.open()
root.close()
} catch (e) {

View File

@ -31,8 +31,8 @@ Item {
if (!tokenAmount || !token.symbol) {
return "0"
}
var defaultFiatSymbol = walletModel.defaultCurrency
return walletModel.getFiatValue(tokenAmount, token.symbol, defaultFiatSymbol) + " " + defaultFiatSymbol.toUpperCase()
var defaultFiatSymbol = walletModel.balanceView.defaultCurrency
return walletModel.balanceView.getFiatValue(tokenAmount, token.symbol, defaultFiatSymbol) + " " + defaultFiatSymbol.toUpperCase()
}
property int state: commandParametersObject.commandState
property bool outgoing: {

View File

@ -89,7 +89,7 @@ Item {
id: signTxComponent
SignTransactionModal {
onOpened: {
walletModel.getGasPricePredictions()
walletModel.gasView.getGasPricePredictions()
}
onClosed: {
destroy();

View File

@ -20,8 +20,8 @@ ModalPopup {
anchors.rightMargin: Style.current.padding
AccountSelector {
id: selectFromAccount
accounts: walletModel.accounts
currency: walletModel.defaultCurrency
accounts: walletModel.accountsView.accounts
currency: walletModel.balanceView.defaultCurrency
width: parent.width
//% "Choose account"
//% "Select account to share and receive assets"

View File

@ -30,8 +30,8 @@ Item {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
walletModel.setFocusedAccountByAddress(commandParametersObject.fromAddress)
var acc = walletModel.focusedAccount
walletModel.accountsView.setFocusedAccountByAddress(commandParametersObject.fromAddress)
var acc = walletModel.accountsView.focusedAccount
openPopup(signTxComponent, {selectedAccount: {
name: acc.name,
address: commandParametersObject.fromAddress,
@ -46,7 +46,7 @@ Item {
id: signTxComponent
SignTransactionModal {
onOpened: {
walletModel.getGasPricePredictions()
walletModel.gasView.getGasPricePredictions()
}
onClosed: {
destroy();

View File

@ -63,15 +63,15 @@ ModalPopup {
AccountSelector {
id: selectFromAccount
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
selectedAccount: {
const currAcc = walletModel.currentAccount
const currAcc = walletModel.accountsView.currentAccount
if (currAcc.walletType !== Constants.watchWalletType) {
return currAcc
}
return null
}
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
width: stack.width
//% "Choose account"
label: qsTrId("choose-account")
@ -82,7 +82,7 @@ ModalPopup {
RecipientSelector {
id: selectRecipient
visible: false
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
contacts: profileModel.contacts.addedContacts
selectedRecipient: { "address": utilsModel.ensRegisterAddress, "type": RecipientSelector.Type.Address }
readOnly: true
@ -91,11 +91,11 @@ ModalPopup {
GasSelector {
id: gasSelector
visible: false
slowestGasPrice: parseFloat(walletModel.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.fastestGasPrice)
getGasEthValue: walletModel.getGasEthValue
getFiatValue: walletModel.getFiatValue
defaultCurrency: walletModel.defaultCurrency
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
if (!(root.ensUsername !== "" && selectFromAccount.selectedAccount)) {
selectedGasLimit = 380000
@ -132,9 +132,9 @@ ModalPopup {
}
toAccount: selectRecipient.selectedRecipient
asset: root.asset
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
amount: {
const fiatValue = walletModel.getFiatValue(root.ensPrice || 0, root.asset.symbol, currency)
const fiatValue = walletModel.balanceView.getFiatValue(root.ensPrice || 0, root.asset.symbol, currency)
return { "value": root.ensPrice, "fiatValue": fiatValue }
}
}
@ -149,7 +149,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: walletModel.signingPhrase
signingPhrase: walletModel.utilsView.signingPhrase
}
}
}

View File

@ -68,15 +68,15 @@ ModalPopup {
AccountSelector {
id: selectFromAccount
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
selectedAccount: {
const currAcc = walletModel.currentAccount
const currAcc = walletModel.accountsView.currentAccount
if (currAcc.walletType !== Constants.watchWalletType) {
return currAcc
}
return null
}
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
width: stack.width
//% "Choose account"
label: qsTrId("choose-account")
@ -87,7 +87,7 @@ ModalPopup {
RecipientSelector {
id: selectRecipient
visible: false
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
contacts: profileModel.contacts.addedContacts
selectedRecipient: { "address": utilsModel.ensRegisterAddress, "type": RecipientSelector.Type.Address }
readOnly: true
@ -96,11 +96,11 @@ ModalPopup {
GasSelector {
id: gasSelector
visible: false
slowestGasPrice: parseFloat(walletModel.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.fastestGasPrice)
getGasEthValue: walletModel.getGasEthValue
getFiatValue: walletModel.getFiatValue
defaultCurrency: walletModel.defaultCurrency
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
if (!(root.ensUsername !== "" && selectFromAccount.selectedAccount)) {
selectedGasLimit = 80000;
@ -137,9 +137,9 @@ ModalPopup {
}
toAccount: selectRecipient.selectedRecipient
asset: root.asset
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
amount: {
const fiatValue = walletModel.getFiatValue(0, root.asset.symbol, currency)
const fiatValue = walletModel.balanceView.getFiatValue(0, root.asset.symbol, currency)
return { "value": 0, "fiatValue": fiatValue }
}
}
@ -154,7 +154,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: walletModel.signingPhrase
signingPhrase: walletModel.utilsView.signingPhrase
}
}
}

View File

@ -136,7 +136,7 @@ Item {
StyledText {
//% "<a href='%1%2'>Look up on Etherscan</a>"
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-").arg(walletModel.etherscanLink.replace("/tx", "/address")).arg(profileModel.ens.getUsernameRegistrar())
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-").arg(walletModel.utilsView.etherscanLink.replace("/tx", "/address")).arg(profileModel.ens.getUsernameRegistrar())
anchors.left: parent.left
anchors.right: parent.right
onLinkActivated: appMain.openLink(link)
@ -158,7 +158,7 @@ Item {
StyledText {
//% "<a href='%1%2'>Look up on Etherscan</a>"
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-").arg(walletModel.etherscanLink.replace("/tx", "/address")).arg(profileModel.ens.getENSRegistry())
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-").arg(walletModel.utilsView.etherscanLink.replace("/tx", "/address")).arg(profileModel.ens.getENSRegistry())
anchors.left: parent.left
anchors.right: parent.right
onLinkActivated: appMain.openLink(link)

View File

@ -283,7 +283,7 @@ Item {
toastMessage.source = "../../../img/loading.svg"
toastMessage.iconColor = Style.current.primary
toastMessage.iconRotates = true
toastMessage.link = `${walletModel.etherscanLink}/${txResult}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txResult}`
toastMessage.open()
}
onTransactionCompleted: {
@ -314,7 +314,7 @@ Item {
toastMessage.iconColor = Style.current.danger
}
toastMessage.link = `${walletModel.etherscanLink}/${txHash}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
toastMessage.open()
}
}

View File

@ -7,7 +7,7 @@ import "../../../shared"
import "../../../shared/status"
ModalPopup {
property var currentAccount: walletModel.currentAccount
property var currentAccount: walletModel.accountsView.currentAccount
property var changeSelectedAccount
id: popup
// TODO add icon when we have that feature
@ -138,7 +138,7 @@ ModalPopup {
icon: StandardIcon.Warning
standardButtons: StandardButton.Yes | StandardButton.No
onAccepted: {
const error = walletModel.deleteAccount(currentAccount.address);
const error = walletModel.accountsView.deleteAccount(currentAccount.address);
if (error) {
errorSound.play()
deleteError.text = error
@ -178,7 +178,7 @@ ModalPopup {
return
}
const error = walletModel.changeAccountSettings(currentAccount.address, accountNameInput.text, accountColorInput.selectedColor);
const error = walletModel.accountsView.changeAccountSettings(currentAccount.address, accountNameInput.text, accountColorInput.selectedColor);
if (error) {
errorSound.play()

View File

@ -51,7 +51,7 @@ ModalPopup {
}
property var getTokenDetails: Backpressure.debounce(popup, 500, function (tokenAddress){
walletModel.customTokenList.getTokenDetails(tokenAddress)
walletModel.tokensView.customTokenList.getTokenDetails(tokenAddress)
});
function onKeyReleased(){
@ -64,7 +64,7 @@ ModalPopup {
Item {
Connections {
target: walletModel.customTokenList
target: walletModel.tokensView.customTokenList
onTokenDetailsWereResolved: {
const jsonObj = JSON.parse(tokenDetails)
if (jsonObj.error) {
@ -153,7 +153,7 @@ ModalPopup {
enabled: validationError === "" && addressInput.text !== "" && nameInput.text !== "" && symbolInput.text !== "" && decimalsInput.text !== ""
onClicked : {
const error = walletModel.addCustomToken(addressInput.text, nameInput.text, symbolInput.text, decimalsInput.text);
const error = walletModel.tokensView.addCustomToken(addressInput.text, nameInput.text, symbolInput.text, decimalsInput.text);
if (error) {
errorSound.play()
@ -162,7 +162,7 @@ ModalPopup {
return
}
walletModel.loadCustomTokens()
walletModel.tokensView.loadCustomTokens()
popup.close();
}
}

View File

@ -59,7 +59,7 @@ Item {
StyledText {
id: assetFiatValue
color: Style.current.secondaryText
text: Utils.toLocaleString(fiatBalance, globalSettings.locale) + " " + walletModel.defaultCurrency.toUpperCase()
text: Utils.toLocaleString(fiatBalance, globalSettings.locale) + " " + walletModel.balanceView.defaultCurrency.toUpperCase()
anchors.right: parent.right
anchors.rightMargin: 0
anchors.bottom: parent.bottom
@ -93,7 +93,7 @@ Item {
spacing: Style.current.padding * 2
anchors.fill: parent
// model: exampleModel
model: walletModel.assets
model: walletModel.tokensView.assets
delegate: assetViewDelegate
boundsBehavior: Flickable.StopAtBounds
}

View File

@ -45,7 +45,7 @@ Item {
Repeater {
id: collectiblesRepeater
model: walletModel.collectiblesLists
model: walletModel.collectiblesView.collectiblesLists
CollectiblesContainer {
property var collectibleData: CollectiblesData.collectiblesData[model.collectibleType]
@ -63,7 +63,7 @@ Item {
}
Connections {
target: walletModel.collectiblesLists
target: walletModel.collectiblesView.collectiblesLists
onDataChanged: {
checkCollectiblesVisibility()
}

View File

@ -11,12 +11,12 @@ import "../../../shared/status"
Item {
property int pageSize: 20 // number of transactions per page
property var tokens: {
let count = walletModel.defaultTokenList.rowCount()
const count = walletModel.tokensView.defaultTokenList.rowCount()
const toks = []
for (var i = 0; i < count; i++) {
toks.push({
"address": walletModel.defaultTokenList.rowData(i, 'address'),
"symbol": walletModel.defaultTokenList.rowData(i, 'symbol')
"address": walletModel.tokensView.defaultTokenList.rowData(i, 'address'),
"symbol": walletModel.tokensView.defaultTokenList.rowData(i, 'symbol')
})
}
count = walletModel.customTokenList.rowCount()
@ -41,6 +41,17 @@ Item {
}
}
// function checkIfHistoryIsBeingFetched() {
// loadMoreButton.loadedMore = false;
// // prevent history from being fetched everytime you click on
// // the history tab
// if (walletModel.historyView.isHistoryFetched(walletModel.accountsView.currentAccount.account))
// return;
// fetchHistory();
// }
id: root
Loader {
@ -58,7 +69,8 @@ Item {
}
Connections {
target: walletModel
target: walletModel.historyView
// onHistoryWasFetched: checkIfHistoryIsBeingFetched()
onLoadingTrxHistoryChanged: {
if (walletModel.currentAccount.address.toLowerCase() === address.toLowerCase()) {
loadingImg.active = isLoading
@ -73,7 +85,7 @@ Item {
id: transactionListItem
property bool isHovered: false
property string symbol: ""
property bool isIncoming: to === walletModel.currentAccount.address
property bool isIncoming: to === walletModel.accountsView.currentAccount.address
anchors.right: parent.right
anchors.left: parent.left
height: 64
@ -237,7 +249,7 @@ Item {
width: parent.width
clip: true
boundsBehavior: Flickable.StopAtBounds
model: walletModel.transactions
model: walletModel.transactionsView.transactions
delegate: transactionListItemCmp
ScrollBar.vertical: ScrollBar {
id: scrollBar

View File

@ -9,7 +9,7 @@ import "./components"
Rectangle {
property int selectedAccount: 0
property var changeSelectedAccount: function(newIndex) {
if (newIndex > walletModel.accounts) {
if (newIndex > walletModel.accountsView.accounts) {
return
}
selectedAccount = newIndex
@ -43,7 +43,7 @@ Rectangle {
StyledTextEdit {
id: walletAmountValue
color: Style.current.textColor
text: Utils.toLocaleString(walletModel.totalFiatBalance, globalSettings.locale) + " " + walletModel.defaultCurrency.toUpperCase()
text: Utils.toLocaleString(walletModel.balanceView.totalFiatBalance, globalSettings.locale) + " " + walletModel.balanceView.defaultCurrency.toUpperCase()
selectByMouse: true
cursorVisible: true
readOnly: true
@ -141,7 +141,7 @@ Rectangle {
}
StyledText {
id: walletBalance
text: isLoading ? "..." : Utils.toLocaleString(fiatBalance, globalSettings.locale) + " " + walletModel.defaultCurrency.toUpperCase()
text: isLoading ? "..." : Utils.toLocaleString(fiatBalance, globalSettings.locale) + " " + walletModel.balanceView.defaultCurrency.toUpperCase()
anchors.top: parent.top
anchors.topMargin: Style.current.smallPadding
anchors.right: parent.right
@ -210,7 +210,7 @@ Rectangle {
}
}
model: walletModel.accounts
model: walletModel.accountsView.accounts
// model: exampleWalletModel
}
}

View File

@ -40,8 +40,8 @@ ModalPopup {
id: accountSelector
label: ""
showAccountDetails: false
accounts: walletModel.accounts
currency: walletModel.defaultCurrency
accounts: walletModel.accountsView.accounts
currency: walletModel.balanceView.defaultCurrency
anchors.top: qrCodeBox.bottom
anchors.topMargin: Style.current.padding
anchors.horizontalCenter: parent.horizontalCenter

View File

@ -27,7 +27,7 @@ ModalPopup {
function sendTransaction() {
stack.currentGroup.isPending = true
walletModel.sendTransaction(selectFromAccount.selectedAccount.address,
walletModel.transactionsView.sendTransaction(selectFromAccount.selectedAccount.address,
selectRecipient.selectedRecipient.address,
txtAmount.selectedAsset.address,
txtAmount.selectedAmount,
@ -55,15 +55,15 @@ ModalPopup {
AccountSelector {
id: selectFromAccount
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
selectedAccount: {
const currAcc = walletModel.currentAccount
const currAcc = walletModel.accountsView.currentAccount
if (currAcc.walletType !== Constants.watchWalletType) {
return currAcc
}
return null
}
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
width: stack.width
//% "From account"
label: qsTrId("from-account")
@ -76,7 +76,7 @@ ModalPopup {
}
RecipientSelector {
id: selectRecipient
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
contacts: profileModel.contacts.addedContacts
//% "Recipient"
label: qsTrId("recipient")
@ -96,9 +96,9 @@ ModalPopup {
AssetAndAmountInput {
id: txtAmount
selectedAccount: selectFromAccount.selectedAccount
defaultCurrency: walletModel.defaultCurrency
getFiatValue: walletModel.getFiatValue
getCryptoValue: walletModel.getCryptoValue
defaultCurrency: walletModel.balanceView.defaultCurrency
getFiatValue: walletModel.balanceView.getFiatValue
getCryptoValue: walletModel.balanceView.getCryptoValue
width: stack.width
onSelectedAssetChanged: if (isValid) { gasSelector.estimateGas() }
onSelectedAmountChanged: if (isValid) { gasSelector.estimateGas() }
@ -107,11 +107,11 @@ ModalPopup {
id: gasSelector
anchors.top: txtAmount.bottom
anchors.topMargin: Style.current.bigPadding * 2
slowestGasPrice: parseFloat(walletModel.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.fastestGasPrice)
getGasEthValue: walletModel.getGasEthValue
getFiatValue: walletModel.getFiatValue
defaultCurrency: walletModel.defaultCurrency
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency
width: stack.width
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address &&
@ -119,7 +119,7 @@ ModalPopup {
txtAmount.selectedAsset && txtAmount.selectedAsset.address &&
txtAmount.selectedAmount)) return
let gasEstimate = JSON.parse(walletModel.estimateGas(
let gasEstimate = JSON.parse(walletModel.gasView.estimateGas(
selectFromAccount.selectedAccount.address,
selectRecipient.selectedRecipient.address,
txtAmount.selectedAsset.address,
@ -163,7 +163,7 @@ ModalPopup {
toAccount: selectRecipient.selectedRecipient
asset: txtAmount.selectedAsset
amount: { "value": txtAmount.selectedAmount, "fiatValue": txtAmount.selectedFiatAmount }
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
}
SendToContractWarning {
id: sendToContractWarning
@ -181,7 +181,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: walletModel.signingPhrase
signingPhrase: walletModel.utilsView.signingPhrase
}
}
}
@ -221,7 +221,7 @@ ModalPopup {
}
Connections {
target: walletModel
target: walletModel.transactionsView
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
@ -245,7 +245,7 @@ ModalPopup {
toastMessage.source = "../../img/loading.svg"
toastMessage.iconColor = Style.current.primary
toastMessage.iconRotates = true
toastMessage.link = `${walletModel.etherscanLink}/${response.result}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${response.result}`
toastMessage.open()
root.close()
} catch (e) {
@ -264,7 +264,7 @@ ModalPopup {
toastMessage.source = "../../img/block-icon.svg"
toastMessage.iconColor = Style.current.danger
}
toastMessage.link = `${walletModel.etherscanLink}/${txHash}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
toastMessage.open()
}
}

View File

@ -47,7 +47,7 @@ ModalPopup {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 15
text: walletModel.signingPhrase
text: walletModel.utilsView.signingPhrase
}
}

View File

@ -7,7 +7,7 @@ import "../../../shared/status"
import "./components"
Item {
property var currentAccount: walletModel.currentAccount
property var currentAccount: walletModel.accountsView.currentAccount
property var changeSelectedAccount
id: walletHeader
@ -175,7 +175,7 @@ Item {
icon.height: 16
onTriggered: {
openPopup(tokenSettingsModalComponent)
walletModel.loadCustomTokens()
walletModel.tokensView.loadCustomTokens()
}
}
Action {
@ -186,7 +186,7 @@ Item {
icon.height: 16
onTriggered: {
openPopup(setCurrencyModalComponent, {
defaultCurrency: walletModel.defaultCurrency
defaultCurrency: walletModel.balanceView.defaultCurrency
})
}
}

View File

@ -23,6 +23,7 @@ ColumnLayout {
onboardingModel.firstTimeLogin = false
walletModel.setInitialRange()
}
walletModel.transactionsView.checkRecentHistory()
}
Timer {
@ -30,7 +31,7 @@ ColumnLayout {
interval: Constants.walletFetchRecentHistoryInterval
running: true
repeat: true
onTriggered: walletModel.checkRecentHistory()
onTriggered: walletModel.transactionsView.checkRecentHistory()
}
SeedPhraseBackupWarning { }

View File

@ -127,7 +127,7 @@ ModalPopup {
return loading = false
}
const result = walletModel.addAccountsFromPrivateKey(accountPKeyInput.text, passwordInput.text, accountNameInput.text, accountColorInput.selectedColor)
const result = walletModel.accountsView.addAccountsFromPrivateKey(accountPKeyInput.text, passwordInput.text, accountNameInput.text, accountColorInput.selectedColor)
loading = false
if (result) {

View File

@ -129,7 +129,7 @@ ModalPopup {
return loading = false
}
const result = walletModel.addAccountsFromSeed(seedPhraseTextArea.textArea.text, passwordInput.text, accountNameInput.text, accountColorInput.selectedColor)
const result = walletModel.accountsView.addAccountsFromSeed(seedPhraseTextArea.textArea.text, passwordInput.text, accountNameInput.text, accountColorInput.selectedColor)
loading = false
if (result) {
let resultJson = JSON.parse(result);

View File

@ -101,7 +101,7 @@ ModalPopup {
return loading = false
}
const error = walletModel.addWatchOnlyAccount(addressInput.text, accountNameInput.text, accountColorInput.selectedColor);
const error = walletModel.accountsView.addWatchOnlyAccount(addressInput.text, accountNameInput.text, accountColorInput.selectedColor);
loading = false
if (error) {
errorSound.play()

View File

@ -101,7 +101,7 @@ ModalPopup {
return loading = false
}
const result = walletModel.generateNewAccount(passwordInput.text, accountNameInput.text, accountColorInput.selectedColor)
const result = walletModel.accountsView.generateNewAccount(passwordInput.text, accountNameInput.text, accountColorInput.selectedColor)
loading = false
if (result) {
let resultJson = JSON.parse(result);

View File

@ -59,7 +59,7 @@ Item {
anchors.rightMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter
ButtonGroup.group: currencyGroup
onClicked: { walletModel.setDefaultCurrency(key) }
onClicked: { walletModel.balanceView.setDefaultCurrency(key) }
}
MouseArea {
@ -75,7 +75,7 @@ Item {
onClicked: {
currencyRadioBtn.checked = !currencyRadioBtn.checked
modalBody.currency = key
walletModel.setDefaultCurrency(key)
walletModel.balanceView.setDefaultCurrency(key)
}
}
}

View File

@ -62,10 +62,10 @@ Item {
}
StatusCheckBox {
id: assetCheck
checked: walletModel.hasAsset(symbol)
checked: walletModel.tokensView.hasAsset(symbol)
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
onClicked: walletModel.toggleAsset(symbol)
onClicked: walletModel.tokensView.toggleAsset(symbol)
anchors.verticalCenter: parent.verticalCenter
}
@ -79,7 +79,7 @@ Item {
return contextMenu.popup(assetSymbol.x - 100, assetSymbol.y + 25)
}
assetCheck.checked = !assetCheck.checked
walletModel.toggleAsset(symbol)
walletModel.tokensView.toggleAsset(symbol)
}
onEntered: {
tokenContainer.hovered = true
@ -101,7 +101,7 @@ Item {
enabled: isCustom
//% "Remove token"
text: qsTrId("remove-token")
onTriggered: walletModel.removeCustomToken(address)
onTriggered: walletModel.tokensView.removeCustomToken(address)
}
}
}
@ -142,14 +142,14 @@ Item {
Repeater {
id: customTokensRepeater
model: walletModel.customTokenList
model: walletModel.tokensView.customTokenList
delegate: tokenComponent
anchors.top: customLbl.bottom
anchors.topMargin: Style.current.smallPadding
}
Connections {
target: walletModel.customTokenList
target: walletModel.tokensView.customTokenList
onTokensLoaded: {
customLbl.visible = cnt > 0
}
@ -172,7 +172,7 @@ Item {
}
Repeater {
model: walletModel.defaultTokenList
model: walletModel.tokensView.defaultTokenList
delegate: tokenComponent
anchors.top: defaultLbl.bottom
anchors.topMargin: Style.current.smallPadding

View File

@ -57,7 +57,7 @@ ScrollView {
anchors.top: somethingWentWrongText.bottom
anchors.topMargin: Style.current.halfPadding
onClicked: {
walletModel.reloadCollectible(collectibleType)
walletModel.collectiblesView.reloadCollectible(collectibleType)
}
}
}

View File

@ -311,7 +311,7 @@ RowLayout {
}
sourceComponent: SendModal {
onOpened: {
walletModel.getGasPricePredictions()
walletModel.gasView.getGasPricePredictions()
}
onClosed: {
sendModal.closed()

View File

@ -14,7 +14,7 @@ Item {
readonly property var validateAsync: Backpressure.debounce(inpAddress, debounceDelay, function (inputValue) {
root.isPending = true
var name = inputValue.startsWith("@") ? inputValue.substring(1) : inputValue
walletModel.resolveENS(name, uuid)
walletModel.ensView.resolveENS(name, uuid)
});
signal resolved(string resolvedAddress)
@ -41,7 +41,7 @@ Item {
}
Connections {
target: walletModel
target: walletModel.ensView
onEnsWasResolved: {
if (uuid !== root.uuid) {
return

View File

@ -21,7 +21,7 @@ Item {
return root.isValid
}
txtValidationError.text = ""
if (walletModel.isKnownTokenContract(selectedRecipient.address)) {
if (walletModel.tokensView.isKnownTokenContract(selectedRecipient.address)) {
// do not set isValid = false here because it would make the
// TransactionStackGroup invalid and therefore not let the user
// continue in the modal

View File

@ -14,7 +14,7 @@ ModalPopup {
property bool showBackBtn: false
Component.onCompleted: {
walletModel.getGasPricePredictions()
walletModel.gasView.getGasPricePredictions()
}
//% "Authorize %1 %2"
@ -72,15 +72,15 @@ ModalPopup {
AccountSelector {
id: selectFromAccount
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
selectedAccount: {
const currAcc = walletModel.currentAccount
const currAcc = walletModel.accountsView.currentAccount
if (currAcc.walletType !== Constants.watchWalletType) {
return currAcc
}
return null
}
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
width: stack.width
//% "Choose account"
label: qsTrId("choose-account")
@ -91,7 +91,7 @@ ModalPopup {
RecipientSelector {
id: selectRecipient
visible: false
accounts: walletModel.accounts
accounts: walletModel.accountsView.accounts
contacts: profileModel.contacts.addedContacts
selectedRecipient: { "address": utilsModel.stickerMarketAddress, "type": RecipientSelector.Type.Address }
readOnly: true
@ -100,11 +100,11 @@ ModalPopup {
GasSelector {
id: gasSelector
visible: false
slowestGasPrice: parseFloat(walletModel.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.fastestGasPrice)
getGasEthValue: walletModel.getGasEthValue
getFiatValue: walletModel.getFiatValue
defaultCurrency: walletModel.defaultCurrency
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
if (!(root.stickerPackId > -1 && selectFromAccount.selectedAccount && root.packPrice && parseFloat(root.packPrice) > 0)) {
selectedGasLimit = 325000
@ -153,9 +153,9 @@ ModalPopup {
}
toAccount: selectRecipient.selectedRecipient
asset: root.asset
currency: walletModel.defaultCurrency
currency: walletModel.balanceView.defaultCurrency
amount: {
const fiatValue = walletModel.getFiatValue(root.packPrice || 0, root.asset.symbol, currency)
const fiatValue = walletModel.balanceView.getFiatValue(root.packPrice || 0, root.asset.symbol, currency)
return { "value": root.packPrice, "fiatValue": fiatValue }
}
}
@ -170,7 +170,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: walletModel.signingPhrase
signingPhrase: walletModel.utilsView.signingPhrase
}
}
}
@ -220,7 +220,7 @@ ModalPopup {
toastMessage.source = "../../../img/loading.svg"
toastMessage.iconColor = Style.current.primary
toastMessage.iconRotates = true
toastMessage.link = `${walletModel.etherscanLink}/${txResult}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txResult}`
toastMessage.open()
}
onTransactionCompleted: {
@ -238,7 +238,7 @@ ModalPopup {
toastMessage.iconColor = Style.current.danger
}
toastMessage.link = `${walletModel.etherscanLink}/${txHash}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
toastMessage.open()
}
}