feat(@desktop/wallet): Updated UI for bridge and multi routing support

fixes #7334
This commit is contained in:
Khushboo Mehta 2022-10-17 12:17:25 +02:00 committed by Khushboo-dev-cpp
parent 68fbaeadc1
commit aec7a30d03
74 changed files with 2232 additions and 2169 deletions

View File

@ -24,4 +24,7 @@ proc getWalletAccount*(self: Controller, accountIndex: int): wallet_account_serv
return self.walletAccountService.getWalletAccount(accountIndex)
proc getIndex*(self: Controller, address: string): int =
return self.walletAccountService.getIndex(address)
return self.walletAccountService.getIndex(address)
method findTokenSymbolByAddress*(self: Controller, address: string): string =
return self.walletAccountService.findTokenSymbolByAddress(address)

View File

@ -14,6 +14,9 @@ method isLoaded*(self: AccessInterface): bool {.base.} =
method switchAccountByAddress*(self: AccessInterface, address: string) {.base.} =
raise newException(ValueError, "No implementation available")
method findTokenSymbolByAddress*(self: AccessInterface, address: string): string {.base.} =
raise newException(ValueError, "No implementation available")
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.

View File

@ -62,6 +62,7 @@ proc setAssets(self: Module, tokens: seq[WalletTokenDto]) =
t.changePct24hour,
t.change24hour,
t.currencyPrice,
t.decimals,
)
items.add(item)
@ -104,3 +105,6 @@ proc onTokensRebuilt(self: Module, accountsTokens: OrderedTable[string, seq[Wall
if not accountsTokens.contains(walletAccount.address):
return
self.setAssets(accountsTokens[walletAccount.address])
method findTokenSymbolByAddress*(self: Module, address: string): string =
return self.controller.findTokenSymbolByAddress(address)

View File

@ -134,6 +134,15 @@ QtObject:
proc connectedAccountDeleted*(self: View) {.signal.}
proc findTokenSymbolByAddress*(self: View, address: string): string {.slot.} =
return self.delegate.findTokenSymbolByAddress(address)
proc hasGas*(self: View, chainId: int, nativeGasSymbol: string, requiredGas: float): bool {.slot.} =
return self.assets.hasGas(chainId, nativeGasSymbol, requiredGas)
proc getTokenBalanceOnChain*(self: View, chainId: int, tokenSymbol: string): string {.slot.} =
return self.assets.getTokenBalanceOnChain(chainId, tokenSymbol)
proc setData*(self: View, dto: wallet_account_service.WalletAccountDto) =
self.name = dto.name
self.nameChanged()

View File

@ -6,6 +6,9 @@ import ../../../../../app_service/service/network/service as network_service
import ../../../../../app_service/service/settings/service as settings_service
import ../../../../../app_service/service/provider/service as provider_service
import ../../../../../app_service/service/wallet_account/service
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
const UNIQUE_BROWSER_SECTION_TRANSACTION_MODULE_IDENTIFIER* = "BrowserSection-TransactionModule"
type
Controller* = ref object of RootObj
@ -43,6 +46,12 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED) do(e: Args):
self.delegate.updateNetwork(self.getNetwork())
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_BROWSER_SECTION_TRANSACTION_MODULE_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.password)
proc getDappsAddress*(self: Controller): string =
return self.settingsService.getDappsAddress()
@ -55,3 +64,10 @@ proc postMessage*(self: Controller, payloadMethod: string, requestType: string,
proc ensResourceURL*(self: Controller, ens: string, url: string): (string, string, string, string, bool) =
return self.providerService.ensResourceURL(ens, url)
proc authenticateUser*(self: Controller, keyUid = "", bip44Path = "", txHash = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_BROWSER_SECTION_TRANSACTION_MODULE_IDENTIFIER,
keyUid: keyUid,
bip44Path: bip44Path,
txHash: txHash)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)

View File

@ -34,4 +34,10 @@ method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method updateNetwork*(self: AccessInterface, network: NetworkDto) {.base.} =
raise newException(ValueError, "No implementation available")
raise newException(ValueError, "No implementation available")
method authenticateToPostMessage*(self: AccessInterface, payloadMethod: string, requestType: string, message: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -2,6 +2,7 @@ import NimQml
import io_interface
import view
import controller
import std/json
import ../../../../core/eventemitter
import ../io_interface as delegate_interface
@ -11,6 +12,12 @@ import ../../../../../app_service/service/provider/service as provider_service
import ../../../../global/global_singleton
export io_interface
# Shouldn't be public ever, user only within this module.
type TmpSendTransactionDetails = object
payloadMethod: string
requestType: string
message: string
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
@ -18,6 +25,7 @@ type
viewVariant: QVariant
moduleLoaded: bool
controller: Controller
tmpSendTransactionDetails: TmpSendTransactionDetails
proc newModule*(
delegate: delegate_interface.AccessInterface,
@ -72,3 +80,24 @@ method ensResourceURL*(self: Module, ens: string, url: string): (string, string,
method updateNetwork*(self: Module, network: NetworkDto) =
self.view.chainId = network.chainId
self.view.chainName = network.chainName
method authenticateToPostMessage*(self: Module, payloadMethod: string, requestType: string, message: string) {.slot.} =
self.tmpSendTransactionDetails.payloadMethod = payloadMethod
self.tmpSendTransactionDetails.requestType = requestType
self.tmpSendTransactionDetails.message = message
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
method onUserAuthenticated*(self: Module, password: string) =
let jsonNode = parseJson(self.tmpSendTransactionDetails.message)
if jsonNode.kind == JObject and jsonNode.contains("payload"):
jsonNode["payload"]["password"] = %* password
self.tmpSendTransactionDetails.message = $jsonNode
self.postMessage(self.tmpSendTransactionDetails.payloadMethod,
self.tmpSendTransactionDetails.requestType,
self.tmpSendTransactionDetails.message)

View File

@ -85,3 +85,7 @@ QtObject:
newHost = base
result = url_replaceHostAndAddPath(url, newHost, http_scheme, "")
proc authenticateToPostMessage*(self: View, payloadMethod: string, requestType: string, message: string) {.slot.} =
self.delegate.authenticateToPostMessage(payloadMethod, requestType, message)

View File

@ -109,6 +109,26 @@ QtObject:
of ModelRole.Balance:
result = newQVariant(item.getBalance())
proc rowData(self: Model, index: int, column: string): string {.slot.} =
if (index >= self.items.len):
return
let item = self.items[index]
case column:
of "chainId": result = $item.getChainId()
of "nativeCurrencyDecimals": result = $item.getNativeCurrencyDecimals()
of "layer": result = $item.getLayer()
of "chainName": result = $item.getChainName()
of "rpcURL": result = $item.getRpcURL()
of "blockExplorerURL": result = $item.getBlockExplorerURL()
of "nativeCurrencyName": result = $item.getNativeCurrencyName()
of "nativeCurrencySymbol": result = $item.getNativeCurrencySymbol()
of "isTest": result = $item.getIsTest()
of "isEnabled": result = $item.getIsEnabled()
of "iconUrl": result = $item.getIconURL()
of "chainColor": result = $item.getChainColor()
of "shortName": result = $item.getShortName()
of "balance": result = $item.getBalance()
proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel()
self.items = items
@ -143,4 +163,16 @@ QtObject:
for item in self.items:
if(item.getShortName() == toLowerAscii(shortName)):
return item.getChainName()
return ""
proc getNetworkColor*(self: Model, shortName: string): string {.slot.} =
for item in self.items:
if(item.getShortName() == toLowerAscii(shortName)):
return item.getChainColor()
return ""
proc getNetworkChainId*(self: Model, shortName: string): int {.slot.} =
for item in self.items:
if(item.getShortName() == toLowerAscii(shortName)):
return item.getChainId()
return 0

View File

@ -8,10 +8,13 @@ import ../../../../../app_service/service/ens/service as ens_service
import ../../../../../app_service/service/network/service as network_service
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../../../app_service/service/token/dto
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
logScope:
topics = "profile-section-ens-usernames-module-controller"
const UNIQUE_ENS_SECTION_TRANSACTION_MODULE_IDENTIFIER* = "EnsSection-TransactionModule"
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
@ -54,6 +57,12 @@ proc init*(self: Controller) =
let args = EnsTransactionArgs(e)
self.delegate.ensTransactionReverted(args.transactionType, args.ensUsername, args.transactionHash, args.revertReason)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_ENS_SECTION_TRANSACTION_MODULE_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.password)
proc checkEnsUsernameAvailability*(self: Controller, desiredEnsUsername: string, statusDomain: bool) =
self.ensService.checkEnsUsernameAvailability(desiredEnsUsername, statusDomain)
@ -129,4 +138,11 @@ proc getStatusToken*(self: Controller): string =
return $jsonObj
proc getNetwork*(self: Controller): NetworkDto =
return self.networkService.getNetworkForEns()
return self.networkService.getNetworkForEns()
proc authenticateUser*(self: Controller, keyUid = "", bip44Path = "", txHash = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_ENS_SECTION_TRANSACTION_MODULE_IDENTIFIER,
keyUid: keyUid,
bip44Path: bip44Path,
txHash: txHash)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)

View File

@ -45,15 +45,15 @@ method fetchDetailsForEnsUsername*(self: AccessInterface, ensUsername: string) {
method setPubKeyGasEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method setPubKey*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.base.} =
method authenticateAndSetPubKey*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method releaseEnsEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method release*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.base.} =
method authenticateAndReleaseEns*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method connectOwnedUsername*(self: AccessInterface, ensUsername: string, isStatus: bool) {.base.} =
@ -65,8 +65,8 @@ method getEnsRegisteredAddress*(self: AccessInterface): string {.base.} =
method registerEnsGasEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method registerEns*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.base.} =
method authenticateAndRegisterEns*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method getSNTBalance*(self: AccessInterface): string {.base.} =
@ -93,3 +93,6 @@ method getChainIdForEns*(self: AccessInterface): int {.base.} =
method setPrefferedEnsUsername*(self: AccessInterface, ensUsername: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -4,6 +4,7 @@ import io_interface
import ../io_interface as delegate_interface
import view, controller, model
import ../../../../global/global_singleton
import ../../../../core/eventemitter
import ../../../../../app_service/common/conversion as service_conversion
import ../../../../../app_service/service/settings/service as settings_service
@ -19,6 +20,19 @@ logScope:
include ../../../../../app_service/common/json_utils
# Shouldn't be public ever, user only within this module.
type TmpSendEnsTransactionDetails = object
ensUsername: string
address: string
gas: string
gasPrice: string
maxPriorityFeePerGas: string
maxFeePerGas: string
eip1559Enabled: bool
isRegistration: bool
isRelease: bool
isSetPubKey: bool
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
@ -26,6 +40,7 @@ type
viewVariant: QVariant
controller: Controller
moduleLoaded: bool
tmpSendEnsTransactionDetails: TmpSendEnsTransactionDetails
proc newModule*(
delegate: delegate_interface.AccessInterface, events: EventEmitter,
@ -90,9 +105,36 @@ method onDetailsForEnsUsername*(self: Module, ensUsername: string, address: stri
method setPubKeyGasEstimate*(self: Module, ensUsername: string, address: string): int =
return self.controller.setPubKeyGasEstimate(ensUsername, address)
method setPubKey*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string =
let response = self.controller.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
method authenticateAndSetPubKey*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) =
self.tmpSendEnsTransactionDetails.ensUsername = ensUsername
self.tmpSendEnsTransactionDetails.address = address
self.tmpSendEnsTransactionDetails.gas = gas
self.tmpSendEnsTransactionDetails.gasPrice = gasPrice
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendEnsTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendEnsTransactionDetails.eip1559Enabled = eip1559Enabled
self.tmpSendEnsTransactionDetails.isRegistration = false
self.tmpSendEnsTransactionDetails.isRelease = false
self.tmpSendEnsTransactionDetails.isSetPubKey = true
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
method setPubKey*(self: Module, password: string) =
let response = self.controller.setPubKey(
self.tmpSendEnsTransactionDetails.ensUsername,
self.tmpSendEnsTransactionDetails.address,
self.tmpSendEnsTransactionDetails.gas,
self.tmpSendEnsTransactionDetails.gasPrice,
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas,
self.tmpSendEnsTransactionDetails.maxFeePerGas,
password,
self.tmpSendEnsTransactionDetails.eip1559Enabled
)
if(response.len == 0):
info "expected response is empty", methodName="setPubKey"
return
@ -103,23 +145,48 @@ method setPubKey*(self: Module, ensUsername: string, address: string, gas: strin
return
var success: bool
if(not responseObj.getProp("success", success) or not success):
if(not responseObj.getProp("success", success)):
info "remote call is not executed with success", methodName="setPubKey"
return response
var respResult: string
if(responseObj.getProp("result", respResult)):
self.view.model().addItem(Item(ensUsername: ensUsername, isPending: true))
self.view.emitTransactionWasSentSignal(respResult)
return response
self.view.model().addItem(Item(ensUsername: self.tmpSendEnsTransactionDetails.ensUsername, isPending: true))
self.view.emitTransactionWasSentSignal(response)
method releaseEnsEstimate*(self: Module, ensUsername: string, address: string): int =
return self.controller.releaseEnsEstimate(ensUsername, address)
method release*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string =
let response = self.controller.release(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
method authenticateAndReleaseEns*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) =
self.tmpSendEnsTransactionDetails.ensUsername = ensUsername
self.tmpSendEnsTransactionDetails.address = address
self.tmpSendEnsTransactionDetails.gas = gas
self.tmpSendEnsTransactionDetails.gasPrice = gasPrice
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendEnsTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendEnsTransactionDetails.eip1559Enabled = eip1559Enabled
self.tmpSendEnsTransactionDetails.isRegistration = false
self.tmpSendEnsTransactionDetails.isRelease = true
self.tmpSendEnsTransactionDetails.isSetPubKey = false
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
method releaseEns*(self: Module, password: string) =
let response = self.controller.release(
self.tmpSendEnsTransactionDetails.ensUsername,
self.tmpSendEnsTransactionDetails.address,
self.tmpSendEnsTransactionDetails.gas,
self.tmpSendEnsTransactionDetails.gasPrice,
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas,
self.tmpSendEnsTransactionDetails.maxFeePerGas,
password,
self.tmpSendEnsTransactionDetails.eip1559Enabled
)
if(response.len == 0):
info "expected response is empty", methodName="release"
return
@ -130,17 +197,15 @@ method release*(self: Module, ensUsername: string, address: string, gas: string,
return
var success: bool
if(not responseObj.getProp("success", success) or not success):
if(not responseObj.getProp("success", success)):
info "remote call is not executed with success", methodName="release"
return
var result: string
if(responseObj.getProp("result", result)):
self.controller.setPreferredName("")
self.view.model().removeItemByEnsUsername(ensUsername)
self.view.emitTransactionWasSentSignal(result)
return response
self.view.model().removeItemByEnsUsername(self.tmpSendEnsTransactionDetails.ensUsername)
self.view.emitTransactionWasSentSignal(response)
proc formatUsername(self: Module, ensUsername: string, isStatus: bool): string =
result = ensUsername
@ -178,10 +243,58 @@ method getEnsRegisteredAddress*(self: Module): string =
method registerEnsGasEstimate*(self: Module, ensUsername: string, address: string): int =
return self.controller.registerEnsGasEstimate(ensUsername, address)
method registerEns*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string =
let response = self.controller.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
method authenticateAndRegisterEns*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) =
self.tmpSendEnsTransactionDetails.ensUsername = ensUsername
self.tmpSendEnsTransactionDetails.address = address
self.tmpSendEnsTransactionDetails.gas = gas
self.tmpSendEnsTransactionDetails.gasPrice = gasPrice
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendEnsTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendEnsTransactionDetails.eip1559Enabled = eip1559Enabled
self.tmpSendEnsTransactionDetails.isRegistration = true
self.tmpSendEnsTransactionDetails.isRelease = false
self.tmpSendEnsTransactionDetails.isSetPubKey = false
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
##################################
## Do Not Delete
##
## Once we start with signing a transactions we shold check if the address we want to send a transaction from is migrated
## or not. In case it's not we should just authenticate logged in user, otherwise we should use one of the keycards that
## address (key pair) is migrated to and sign the transaction using it.
##
## The code bellow is an example how we can achieve that in future, when we start with signing transactions.
##
## let acc = self.controller.getAccountByAddress(from_addr)
## if acc.isNil:
## echo "error: selected account to send a transaction from is not known"
## return
## let keyPair = self.controller.getMigratedKeyPairByKeyUid(acc.keyUid)
## if keyPair.len == 0:
## self.controller.authenticateUser()
## else:
## self.controller.authenticateUser(acc.keyUid, acc.path)
##
##################################
method registerEns(self: Module, password: string) =
let response = self.controller.registerEns(
self.tmpSendEnsTransactionDetails.ensUsername,
self.tmpSendEnsTransactionDetails.address,
self.tmpSendEnsTransactionDetails.gas,
self.tmpSendEnsTransactionDetails.gasPrice,
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas,
self.tmpSendEnsTransactionDetails.maxFeePerGas,
password,
self.tmpSendEnsTransactionDetails.eip1559Enabled
)
let responseObj = response.parseJson
if (responseObj.kind != JObject):
info "expected response is not a json object", methodName="registerEns"
@ -189,10 +302,8 @@ method registerEns*(self: Module, ensUsername: string, address: string, gas: str
var respResult: string
if(responseObj.getProp("result", respResult) and responseObj{"success"}.getBool == true):
self.view.model().addItem(Item(ensUsername: self.formatUsername(ensUsername, true), isPending: true))
self.view.emitTransactionWasSentSignal(respResult)
return response
self.view.model().addItem(Item(ensUsername: self.formatUsername(self.tmpSendEnsTransactionDetails.ensUsername, true), isPending: true))
self.view.emitTransactionWasSentSignal(response)
method getSNTBalance*(self: Module): string =
return self.controller.getSNTBalance()
@ -203,13 +314,19 @@ method getWalletDefaultAddress*(self: Module): string =
method getCurrentCurrency*(self: Module): string =
return self.controller.getCurrentCurrency()
method getFiatValue*(self: Module, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string =
method getFiatValue*(self: Module, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string =
var floatCryptoBalance: float = 0
try:
floatCryptoBalance = parseFloat(cryptoBalance)
except ValueError:
return "0.00"
if (cryptoBalance == "" or cryptoSymbol == "" or fiatSymbol == ""):
return "0.00"
let price = self.controller.getPrice(cryptoSymbol, fiatSymbol)
let value = parseFloat(cryptoBalance) * price
return fmt"{value:.2f}"
let value = floatCryptoBalance * price
return fmt"{value}"
method getGasEthValue*(self: Module, gweiValue: string, gasLimit: string): string {.slot.} =
var gasLimitInt:int
@ -240,3 +357,12 @@ method getChainIdForEns*(self: Module): int =
method setPrefferedEnsUsername*(self: Module, ensUsername: string) =
self.controller.setPreferredName(ensUsername)
method onUserAuthenticated*(self: Module, password: string) =
if self.tmpSendEnsTransactionDetails.isRegistration:
self.registerEns(password)
elif self.tmpSendEnsTransactionDetails.isRelease:
self.releaseEns(password)
elif self.tmpSendEnsTransactionDetails.isSetPubKey:
self.setPubKey(password)

View File

@ -71,9 +71,9 @@ QtObject:
proc setPubKeyGasEstimate*(self: View, ensUsername: string, address: string): int {.slot.} =
return self.delegate.setPubKeyGasEstimate(ensUsername, address)
proc setPubKey*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.slot.} =
return self.delegate.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
proc authenticateAndSetPubKey*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) {.slot.} =
self.delegate.authenticateAndSetPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled)
proc getEtherscanLink*(self: View): string {.slot.} =
return self.etherscanLink
@ -94,9 +94,9 @@ QtObject:
proc releaseEnsEstimate*(self: View, ensUsername: string, address: string): int {.slot.} =
return self.delegate.releaseEnsEstimate(ensUsername, address)
proc releaseEns*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.slot.} =
return self.delegate.release(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
proc authenticateAndReleaseEns*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) {.slot.} =
self.delegate.authenticateAndReleaseEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled)
proc connectOwnedUsername*(self: View, ensUsername: string, isStatus: bool) {.slot.} =
self.delegate.connectOwnedUsername(ensUsername, isStatus)
@ -107,9 +107,9 @@ QtObject:
proc registerEnsGasEstimate*(self: View, ensUsername: string, address: string): int {.slot.} =
return self.delegate.registerEnsGasEstimate(ensUsername, address)
proc registerEns*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.slot.} =
return self.delegate.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
proc authenticateAndRegisterEns*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) {.slot.} =
self.delegate.authenticateAndRegisterEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled)
proc getSNTBalance*(self: View): string {.slot.} =
return self.delegate.getSNTBalance()

View File

@ -10,7 +10,9 @@ import ../../../../app_service/service/settings/service as settings_service
import ../../../../app_service/service/network/service as network_service
import ../../../../app_service/service/eth/utils as eth_utils
import ../../../../app_service/service/wallet_account/service as wallet_account_service
import ../../shared_modules/keycard_popup/io_interface as keycard_shared_module
const UNIQUE_BUY_STICKER_TRANSACTION_MODULE_IDENTIFIER* = "StickersSection-TransactionModule"
type
Controller* = ref object of RootObj
@ -92,6 +94,12 @@ proc init*(self: Controller) =
let args = StickerTransactionArgs(e)
self.delegate.stickerTransactionReverted(args.transactionType, args.packID, args.transactionHash, args.revertReason)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_BUY_STICKER_TRANSACTION_MODULE_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.password)
proc buy*(self: Controller, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): tuple[response: string, success: bool] =
self.stickerService.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
@ -156,4 +164,11 @@ proc getStatusToken*(self: Controller): string =
"symbol": token.symbol,
"address": token.addressAsString()
}
return $jsonObj
return $jsonObj
proc authenticateUser*(self: Controller, keyUid = "", bip44Path = "", txHash = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_BUY_STICKER_TRANSACTION_MODULE_IDENTIFIER,
keyUid: keyUid,
bip44Path: bip44Path,
txHash: txHash)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)

View File

@ -19,7 +19,7 @@ method isLoaded*(self: AccessInterface): bool {.base.} =
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method buy*(self: AccessInterface, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): tuple[response: string, success: bool] {.base.} =
method authenticateAndBuy*(self: AccessInterface, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool){.base.} =
raise newException(ValueError, "No implementation available")
method getInstalledStickerPacks*(self: AccessInterface): Table[string, StickerPackDto] {.base.} =
@ -95,3 +95,6 @@ method stickerTransactionConfirmed*(self: AccessInterface, trxType: string, pack
method stickerTransactionReverted*(self: AccessInterface, trxType: string, packID: string, transactionHash: string,
revertReason: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -11,6 +11,17 @@ import ../../../../app_service/service/wallet_account/service as wallet_account_
export io_interface
# Shouldn't be public ever, user only within this module.
type TmpBuyStickersTransactionDetails = object
packId: string
address: string
gas: string
gasPrice: string
maxPriorityFeePerGas: string
maxFeePerGas: string
eip1559Enabled: bool
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
@ -18,6 +29,7 @@ type
view: View
viewVariant: QVariant
moduleLoaded: bool
tmpBuyStickersTransactionDetails: TmpBuyStickersTransactionDetails
proc newModule*(
delegate: delegate_interface.AccessInterface,
@ -52,8 +64,58 @@ method viewDidLoad*(self: Module) =
self.moduleLoaded = true
self.delegate.stickersDidLoad()
method buy*(self: Module, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): tuple[response: string, success: bool] =
return self.controller.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
method authenticateAndBuy*(self: Module, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) =
self.tmpBuyStickersTransactionDetails.packId = packId
self.tmpBuyStickersTransactionDetails.address = address
self.tmpBuyStickersTransactionDetails.gas = gas
self.tmpBuyStickersTransactionDetails.gasPrice = gasPrice
self.tmpBuyStickersTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpBuyStickersTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpBuyStickersTransactionDetails.eip1559Enabled = eip1559Enabled
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
##################################
## Do Not Delete
##
## Once we start with signing a transactions we shold check if the address we want to send a transaction from is migrated
## or not. In case it's not we should just authenticate logged in user, otherwise we should use one of the keycards that
## address (key pair) is migrated to and sign the transaction using it.
##
## The code bellow is an example how we can achieve that in future, when we start with signing transactions.
##
## let acc = self.controller.getAccountByAddress(from_addr)
## if acc.isNil:
## echo "error: selected account to send a transaction from is not known"
## return
## let keyPair = self.controller.getMigratedKeyPairByKeyUid(acc.keyUid)
## if keyPair.len == 0:
## self.controller.authenticateUser()
## else:
## self.controller.authenticateUser(acc.keyUid, acc.path)
##
##################################
method onUserAuthenticated*(self: Module, password: string) =
let responseTuple = self.controller.buy(
self.tmpBuyStickersTransactionDetails.packId,
self.tmpBuyStickersTransactionDetails.address,
self.tmpBuyStickersTransactionDetails.gas,
self.tmpBuyStickersTransactionDetails.gasPrice,
self.tmpBuyStickersTransactionDetails.maxPriorityFeePerGas,
self.tmpBuyStickersTransactionDetails.maxFeePerGas,
password,
self.tmpBuyStickersTransactionDetails.eip1559Enabled
)
let response = responseTuple.response
let success = responseTuple.success
if success:
self.view.stickerPacks.updateStickerPackInList(self.tmpBuyStickersTransactionDetails.packId, false, true)
self.view.transactionWasSent($(%*{"success": success, "result": response}))
method getInstalledStickerPacks*(self: Module): Table[string, StickerPackDto] =
self.controller.getInstalledStickerPacks()

View File

@ -61,18 +61,8 @@ QtObject:
proc gasEstimateReturned*(self: View, estimate: int, uuid: string) {.signal.}
proc buy*(self: View, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.slot.} =
let responseTuple = self.delegate.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
let response = responseTuple.response
let success = responseTuple.success
if success:
self.stickerPacks.updateStickerPackInList(packId, false, true)
self.transactionWasSent(response)
result = $(%*{
"success": success,
"result": response
})
proc authenticateAndBuy*(self: View, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) {.slot.} =
self.delegate.authenticateAndBuy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled)
proc stickerPacksLoaded*(self: View) {.signal.}

View File

@ -68,6 +68,7 @@ method refreshWalletAccounts*(self: Module) =
t.changePct24hour,
t.change24hour,
t.currencyPrice,
t.decimals,
))
)

View File

@ -93,6 +93,7 @@ proc setAssetsAndBalance(self: Module, tokens: seq[WalletTokenDto]) =
t.changePct24hour,
t.change24hour,
t.currencyPrice,
t.decimals,
)
items.add(item)
totalCurrencyBalanceForAllAssets += t.enabledNetworkBalance.currencybalance

View File

@ -224,3 +224,9 @@ QtObject:
proc findTokenSymbolByAddress*(self: View, address: string): string {.slot.} =
return self.delegate.findTokenSymbolByAddress(address)
proc hasGas*(self: View, chainId: int, nativeGasSymbol: string, requiredGas: float): bool {.slot.} =
return self.assets.hasGas(chainId, nativeGasSymbol, requiredGas)
proc getTokenBalanceOnChain*(self: View, chainId: int, tokenSymbol: string): string {.slot.} =
return self.assets.getTokenBalanceOnChain(chainId, tokenSymbol)

View File

@ -78,6 +78,9 @@ proc init*(self: Controller) =
return
self.delegate.onUserAuthenticated(args.password)
self.events.on(SIGNAL_SUGGESTED_ROUTES_READY) do(e:Args):
self.delegate.suggestedRoutesReady(SuggestedRoutesArgs(e).suggestedRoutes)
proc checkPendingTransactions*(self: Controller) =
self.transactionService.checkPendingTransactions()
@ -106,17 +109,15 @@ proc estimateGas*(self: Controller, from_addr: string, to: string, assetSymbol:
result = "0"
proc transfer*(self: Controller, from_addr: string, to_addr: string, tokenSymbol: string,
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,maxFeePerGas: string,
password: string, chainId: string, uuid: string, eip1559Enabled: bool) =
discard self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, gas,
gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
value: string, uuid: string, priority: int, selectedRoutes: string, password: string) =
self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, uuid, priority, selectedRoutes, password)
proc suggestedFees*(self: Controller, chainId: int): string =
let suggestedFees = self.transactionService.suggestedFees(chainId)
return suggestedFees.toJson()
proc suggestedRoutes*(self: Controller, account: string, amount: float64, token: string, disabledChainIDs: seq[uint64]): string =
let suggestedRoutes = self.transactionService.suggestedRoutes(account, amount, token, disabledChainIDs)
proc suggestedRoutes*(self: Controller, account: string, amount: Uint256, token: string, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[uint64], priority: int, sendType: int): string =
let suggestedRoutes = self.transactionService.suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, priority, sendType)
return suggestedRoutes.toJson()
proc getChainIdForChat*(self: Controller): int =
@ -137,4 +138,4 @@ proc authenticateUser*(self: Controller, keyUid = "", bip44Path = "", txHash = "
keyUid: keyUid,
bip44Path: bip44Path,
txHash: txHash)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)

View File

@ -47,9 +47,8 @@ method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")
method authenticateAndTransfer*(self: AccessInterface, from_addr: string, to_addr: string,
tokenSymbol: string, value: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, chainId: string, uuid: string,
eip1559Enabled: bool) {.base.} =
tokenSymbol: string, value: string, uuid: string,
priority: int, selectedRoutes: string) {.base.} =
raise newException(ValueError, "No implementation available")
method transactionWasSent*(self: AccessInterface, result: string) {.base.} =
@ -58,7 +57,7 @@ method transactionWasSent*(self: AccessInterface, result: string) {.base.} =
method suggestedFees*(self: AccessInterface, chainId: int): string {.base.} =
raise newException(ValueError, "No implementation available")
method suggestedRoutes*(self: AccessInterface, account: string, amount: float64, token: string, disabledChainIDs: seq[uint64]): string {.base.} =
method suggestedRoutes*(self: AccessInterface, account: string, amount: UInt256, token: string, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[uint64], priority: int, sendType: int): string {.base.} =
raise newException(ValueError, "No implementation available")
method getChainIdForChat*(self: AccessInterface): int =
@ -78,3 +77,6 @@ method viewDidLoad*(self: AccessInterface) {.base.} =
method getLastTxBlockNumber*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method suggestedRoutesReady*(self: AccessInterface, suggestedRoutes: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -15,14 +15,10 @@ type TmpSendTransactionDetails = object
fromAddr: string
toAddr: string
tokenSymbol: string
value: string
gas: string
gasPrice: string
maxPriorityFeePerGas: string
maxFeePerGas: string
chainId: string
value: string
uuid: string
eip1559Enabled: bool
priority: int
selectedRoutes: string
type
Module* = ref object of io_interface.AccessInterface
@ -105,21 +101,16 @@ method estimateGas*(self: Module, from_addr: string, to: string, assetSymbol: st
method setIsNonArchivalNode*(self: Module, isNonArchivalNode: bool) =
self.view.setIsNonArchivalNode(isNonArchivalNode)
method authenticateAndTransfer*(self: Module, from_addr: string, to_addr: string, tokenSymbol: string,
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
maxFeePerGas: string, chainId: string, uuid: string, eip1559Enabled: bool) =
method authenticateAndTransfer*(self: Module, from_addr: string, to_addr: string,
tokenSymbol: string, value: string, uuid: string,
priority: int, selectedRoutes: string) =
self.tmpSendTransactionDetails.fromAddr = from_addr
self.tmpSendTransactionDetails.toAddr = to_addr
self.tmpSendTransactionDetails.tokenSymbol = tokenSymbol
self.tmpSendTransactionDetails.value = value
self.tmpSendTransactionDetails.gas = gas
self.tmpSendTransactionDetails.gasPrice = gasPrice
self.tmpSendTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendTransactionDetails.chainId = chainId
self.tmpSendTransactionDetails.uuid = uuid
self.tmpSendTransactionDetails.eip1559Enabled = eip1559Enabled
self.tmpSendTransactionDetails.priority = priority
self.tmpSendTransactionDetails.selectedRoutes = selectedRoutes
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
@ -149,11 +140,9 @@ method authenticateAndTransfer*(self: Module, from_addr: string, to_addr: string
##################################
method onUserAuthenticated*(self: Module, password: string) =
self.controller.transfer(self.tmpSendTransactionDetails.fromAddr, self.tmpSendTransactionDetails.toAddr,
self.tmpSendTransactionDetails.tokenSymbol, self.tmpSendTransactionDetails.value, self.tmpSendTransactionDetails.gas,
self.tmpSendTransactionDetails.gasPrice, self.tmpSendTransactionDetails.maxPriorityFeePerGas,
self.tmpSendTransactionDetails.maxFeePerGas, password, self.tmpSendTransactionDetails.chainId, self.tmpSendTransactionDetails.uuid,
self.tmpSendTransactionDetails.eip1559Enabled)
self.controller.transfer(self.tmpSendTransactionDetails.fromAddr, self.tmpSendTransactionDetails.toAddr,
self.tmpSendTransactionDetails.tokenSymbol, self.tmpSendTransactionDetails.value, self.tmpSendTransactionDetails.uuid,
self.tmpSendTransactionDetails.priority, self.tmpSendTransactionDetails.selectedRoutes, password)
method transactionWasSent*(self: Module, result: string) =
self.view.transactionWasSent(result)
@ -161,8 +150,8 @@ method transactionWasSent*(self: Module, result: string) =
method suggestedFees*(self: Module, chainId: int): string =
return self.controller.suggestedFees(chainId)
method suggestedRoutes*(self: Module, account: string, amount: float64, token: string, disabledChainIDs: seq[uint64]): string =
return self.controller.suggestedRoutes(account, amount, token, disabledChainIDs)
method suggestedRoutes*(self: Module, account: string, amount: UInt256, token: string, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[uint64], priority: int, sendType: int): string =
return self.controller.suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, priority, sendType)
method getChainIdForChat*(self: Module): int =
return self.controller.getChainIdForChat()
@ -175,3 +164,6 @@ method getEstimatedTime*(self: Module, chainId: int, maxFeePerGas: string): int
method getLastTxBlockNumber*(self: Module): string =
return self.controller.getLastTxBlockNumber()
method suggestedRoutesReady*(self: Module, suggestedRoutes: string) =
self.view.suggestedRoutesReady(suggestedRoutes)

View File

@ -117,30 +117,42 @@ QtObject:
self.transactionSent(txResult)
proc authenticateAndTransfer*(self: View, from_addr: string, to_addr: string, tokenSymbol: string,
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
maxFeePerGas: string, chainId: string, uuid: string, eip1559Enabled: bool) {.slot.} =
self.delegate.authenticateAndTransfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice,
maxPriorityFeePerGas, maxFeePerGas, chainId, uuid, eip1559Enabled)
value: string, uuid: string, priority: int, selectedRoutes: string) {.slot.} =
self.delegate.authenticateAndTransfer(from_addr, to_addr, tokenSymbol, value, uuid, priority, selectedRoutes)
proc suggestedFees*(self: View, chainId: int): string {.slot.} =
return self.delegate.suggestedFees(chainId)
proc suggestedRoutes*(self: View, account: string, amount: string, token: string, disabledChainIDs: string): string {.slot.} =
var parsedAmount = 0.0
var seqDisabledChainIds = seq[uint64] : @[]
proc suggestedRoutes*(self: View, account: string, amount: string, token: string, disabledFromChainIDs: string, disabledToChainIDs: string, preferredChainIDs: string, priority: int, sendType: int): string {.slot.} =
var parsedAmount = stint.u256("0")
var seqPreferredChainIDs = seq[uint64] : @[]
var seqDisabledFromChainIDs = seq[uint64] : @[]
var seqDisabledToChainIDs = seq[uint64] : @[]
try:
for chainID in disabledChainIDs.split(','):
seqDisabledChainIds.add(parseUInt(chainID))
for chainID in disabledFromChainIDs.split(','):
seqDisabledFromChainIDs.add(parseUInt(chainID))
except:
discard
try:
parsedAmount = parsefloat(amount)
for chainID in disabledToChainIDs.split(','):
seqDisabledToChainIDs.add(parseUInt(chainID))
except:
discard
return self.delegate.suggestedRoutes(account, parsedAmount, token, seqDisabledChainIds)
try:
for chainID in preferredChainIDs.split(','):
seqPreferredChainIDs.add(parseUInt(chainID))
except:
discard
try:
parsedAmount = fromHex(Stuint[256], amount)
except Exception as e:
discard
return self.delegate.suggestedRoutes(account, parsedAmount, token, seqDisabledFromChainIDs, seqDisabledToChainIDs, seqPreferredChainIDs, priority, sendType)
proc getChainIdForChat*(self: View): int {.slot.} =
return self.delegate.getChainIdForChat()
@ -153,3 +165,5 @@ QtObject:
proc getLastTxBlockNumber*(self: View): string {.slot.} =
return self.delegate.getLastTxBlockNumber()
proc suggestedRoutesReady*(self: View, suggestedRoutes: string) {.signal.}

View File

@ -25,6 +25,7 @@ type
changePct24hour: string
change24hour: string
currencyPrice: float
decimals: int
proc initItem*(
name, symbol: string,
@ -45,7 +46,8 @@ proc initItem*(
changePctDay: string,
changePct24hour: string,
change24hour: string,
currencyPrice: float
currencyPrice: float,
decimals: int,
): Item =
result.name = name
result.symbol = symbol
@ -68,6 +70,7 @@ proc initItem*(
result.changePct24hour = changePct24hour
result.change24hour = change24hour
result.currencyPrice = currencyPrice
result.decimals = decimals
proc `$`*(self: Item): string =
result = fmt"""AllTokensItem(
@ -90,6 +93,7 @@ proc `$`*(self: Item): string =
changePct24hour: {self.changePct24hour},
change24hour: {self.change24hour},
currencyPrice: {self.currencyPrice},
decimals: {self.decimals},
]"""
proc getName*(self: Item): string =
@ -151,3 +155,6 @@ proc getChange24hour*(self: Item): string =
proc getCurrencyPrice*(self: Item): float =
return self.currencyPrice
proc getDecimals*(self: Item): int =
return self.decimals

View File

@ -24,6 +24,7 @@ type
ChangePct24hour
Change24hour
CurrencyPrice
Decimals
QtObject:
type
@ -79,6 +80,7 @@ QtObject:
ModelRole.ChangePct24hour.int:"changePct24hour",
ModelRole.Change24hour.int:"change24hour",
ModelRole.CurrencyPrice.int:"currencyPrice",
ModelRole.Decimals.int:"decimals",
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -132,6 +134,8 @@ QtObject:
result = newQVariant(item.getChange24hour())
of ModelRole.CurrencyPrice:
result = newQVariant(item.getCurrencyPrice())
of ModelRole.Decimals:
result = newQVariant(item.getDecimals())
proc rowData(self: Model, index: int, column: string): string {.slot.} =
if (index >= self.items.len):
@ -157,6 +161,7 @@ QtObject:
of "changePct24hour": result = $item.getChangePct24hour()
of "change24hour": result = $item.getChange24hour()
of "currencyPrice": result = $item.getCurrencyPrice()
of "decimals": result = $item.getDecimals()
proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel()
@ -172,16 +177,30 @@ QtObject:
return false
proc hasGas*(self: Model, chainId: int, nativeGasSymbol: string, requiredGas: float): bool {.slot.} =
proc hasGas*(self: Model, chainId: int, nativeGasSymbol: string, requiredGas: float): bool =
for item in self.items:
if(item.getSymbol() != nativeGasSymbol):
continue
if(item.getSymbol() != nativeGasSymbol):
continue
for balance in item.getBalances().items:
if (balance.chainId != chainId):
continue
for balance in item.getBalances().items:
if (balance.chainId != chainId):
continue
if(balance.balance >= requiredGas):
return true
if(balance.balance >= requiredGas):
return true
return false
proc getTokenBalanceOnChain*(self: Model, chainId: int, tokenSymbol: string): string =
var tokenBalance: float64 = 0.0
for item in self.items:
if(item.getSymbol() != tokenSymbol):
continue
for balance in item.getBalances().items:
if (balance.chainId != chainId):
continue
tokenBalance = balance.balance
return $tokenBalance

View File

@ -324,7 +324,7 @@ QtObject:
self.pendingEnsUsernames.excl(ensUsername)
result = $(%* { "result": hash, "success": true })
except RpcException as e:
except Exception as e:
error "error occurred", procName="release", msg = e.msg
result = $(%* { "result": e.msg, "success": false })
@ -395,4 +395,4 @@ QtObject:
return (response.result{"Scheme"}.getStr, response.result{"Host"}.getStr, response.result{"Path"}.getStr)
except Exception as e:
error "Error getting ENS resourceUrl", username=username, exception=e.msg
raise
raise

View File

@ -12,9 +12,17 @@ type
maxFeePerGas*: Option[Uint256]
value*: Option[Uint256] # (optional) integer of the value sent with this transaction.
data*: string # the compiled code of a contract OR the hash of the invoked proc signature and encoded parameters. For details see Ethereum Contract ABI.
input*: string
nonce*: Option[Nonce] # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce
txType*: string
chainID*: Option[int] # (optional) chainID in case of a bridge hop transaction
symbol*: Option[string] # (optional) symbol in case of a bridge hop transaction
recipient*: Option[Address] # (optional) recipient in case of a bridge hop transaction
amount*: Option[UInt256] # (optional) amount in case of a bridge hop transaction
amountOutMin*: Option[UInt256] # (optional) amountOutMin in case of a bridge hop transaction
bonderFee*: Option[string] # (optional) bonderFee in case of a bridge hop transaction
proc `%`*(x: TransactionDataDto): JsonNode =
result = newJobject()
result["from"] = %x.source
@ -32,5 +40,31 @@ proc `%`*(x: TransactionDataDto): JsonNode =
if x.value.isSome:
result["value"] = %("0x" & x.value.unsafeGet.toHex)
result["data"] = %x.data
result["input"] = %x.input
if x.nonce.isSome:
result["nonce"] = %x.nonce.unsafeGet
if x.chainID.isSome:
result["chainId"] = %x.chainID.unsafeGet
if x.symbol.isSome:
result["symbol"] = %x.symbol.unsafeGet
if x.recipient.isSome:
result["recipient"] = %x.recipient.unsafeGet
if x.amount.isSome:
result["amount"] = %x.amount.unsafeGet
if x.amountOutMin.isSome:
result["amountOutMin"] = %x.amountOutMin.unsafeGet
if x.bonderFee.isSome:
result["bonderFee"] = %x.bonderFee.unsafeGet
type TransactionBridgeDto* = object
bridgeName*: string
chainID*: int
simpleTx*: TransactionDataDto
hopTx*: TransactionDataDto
proc `%`*(x: TransactionBridgeDto): JsonNode =
result = newJobject()
result["bridgeName"] = %x.bridgeName
result["chainID"] = %x.chainID
result["simpleTx"] = %x.simpleTx
result["hopTx"] = %x.hopTx

View File

@ -2,6 +2,9 @@
# Async load transactions
#################################################
import stint
import ../../common/conversion as service_conversion
type
LoadTransactionsTaskArg* = ref object of QObjectTaskArg
chainId: int
@ -21,3 +24,72 @@ const loadTransactionsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.}
"loadMore": arg.loadMore
}
arg.finish(output)
type
GetSuggestedRoutesTaskArg* = ref object of QObjectTaskArg
account: string
amount: Uint256
token: string
disabledFromChainIDs: seq[uint64]
disabledToChainIDs: seq[uint64]
preferredChainIDs: seq[uint64]
priority: int
sendType: int
proc getGasEthValue*(gweiValue: float, gasLimit: uint64): float =
let weiValue = service_conversion.gwei2Wei(gweiValue) * u256(gasLimit)
let ethValue = parseFloat(service_conversion.wei2Eth(weiValue))
return ethValue
proc getFeesTotal*(paths: seq[TransactionPathDto]): seq[Fees] =
if(paths.len == 0):
return @[]
var fees: seq[Fees] = @[Fees(), Fees(), Fees()]
for path in paths:
var slowPrice = path.gasFees.gasPrice
var optimalPrice = path.gasFees.gasPrice
var fastPrice = path.gasFees.gasPrice
if path.gasFees.eip1559Enabled:
slowPrice = path.gasFees.maxFeePerGasL
optimalPrice = path.gasFees.maxFeePerGasM
fastPrice = path.gasFees.maxFeePerGasH
fees[0].totalFeesInEth += getGasEthValue(slowPrice, path.gasAmount)
fees[1].totalFeesInEth += getGasEthValue(optimalPrice, path.gasAmount)
fees[2].totalFeesInEth += getGasEthValue(fastPrice, path.gasAmount)
fees[0].totalTokenFees += path.tokenFees
fees[1].totalTokenFees += path.tokenFees
fees[2].totalTokenFees += path.tokenFees
# keeping it same for all as path will change when priority changes changing the time needed and so on
fees[0].totalTime += path.estimatedTime
fees[1].totalTime += path.estimatedTime
fees[2].totalTime += path.estimatedTime
return fees
const getSuggestedRoutesTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[GetSuggestedRoutesTaskArg](argEncoded)
try:
let amountAsHex = "0x" & eth_utils.stripLeadingZeros(arg.amount.toHex)
let response = eth.suggestedRoutes(arg.account, amountAsHex, arg.token, arg.disabledFromChainIDs, arg.disabledToChainIDs, arg.preferredChainIDs, arg.priority, arg.sendType).result
let bestPaths = response["Best"].getElems().map(x => x.toTransactionPathDto())
let output = %*{
"suggestedRoutes": SuggestedRoutesDto(
best: bestPaths,
candidates: response["Candidates"].getElems().map(x => x.toTransactionPathDto()),
gasTimeEstimates: getFeesTotal(bestPaths)),
"error": ""
}
arg.finish(output)
except Exception as e:
let output = %* {
"suggestedRoutes": SuggestedRoutesDto(best: @[], candidates: @[],gasTimeEstimates: @[]),
"error": fmt"Error getting suggested routes: {e.msg}"
}
arg.finish(output)

View File

@ -1,5 +1,6 @@
import json, strutils, stint, json_serialization
import json, strutils, stint, json_serialization, strformat
include ../../common/json_utils
import ../network/dto
type
PendingTransactionTypeDto* {.pure.} = enum
@ -99,3 +100,109 @@ proc cmpTransactions*(x, y: TransactionDto): int =
if result == 0:
result = cmp(x.nonce, y.nonce)
type
SuggestedFeesDto* = ref object
gasPrice*: float
baseFee*: float
maxPriorityFeePerGas*: float
maxFeePerGasL*: float
maxFeePerGasM*: float
maxFeePerGasH*: float
eip1559Enabled*: bool
proc toSuggestedFeesDto*(jsonObj: JsonNode): SuggestedFeesDto =
result = SuggestedFeesDto()
result.gasPrice = parseFloat(jsonObj["gasPrice"].getStr)
result.baseFee = parseFloat(jsonObj["baseFee"].getStr)
result.maxPriorityFeePerGas = parseFloat(jsonObj{"maxPriorityFeePerGas"}.getStr)
result.maxFeePerGasL = parseFloat(jsonObj{"maxFeePerGasLow"}.getStr)
result.maxFeePerGasM = parseFloat(jsonObj{"maxFeePerGasMedium"}.getStr)
result.maxFeePerGasH = parseFloat(jsonObj{"maxFeePerGasHigh"}.getStr)
result.eip1559Enabled = jsonObj{"eip1559Enabled"}.getbool
proc `$`*(self: SuggestedFeesDto): string =
return fmt"""SuggestedFees(
gasPrice:{self.gasPrice},
baseFee:{self.baseFee},
maxPriorityFeePerGas:{self.maxPriorityFeePerGas},
maxFeePerGasL:{self.maxFeePerGasL},
maxFeePerGasM:{self.maxFeePerGasM},
maxFeePerGasH:{self.maxFeePerGasH},
eip1559Enabled:{self.eip1559Enabled}
)"""
type
TransactionPathDto* = ref object
bridgeName*: string
fromNetwork*: NetworkDto
toNetwork*: NetworkDto
maxAmountIn* : UInt256
amountIn*: UInt256
amountOut*: UInt256
gasAmount*: uint64
gasFees*: SuggestedFeesDto
tokenFees*: float
bonderFees*: string
cost*: float
preferred*: bool
estimatedTime*: int
proc `$`*(self: TransactionPathDto): string =
return fmt"""TransactionPath(
bridgeName:{self.bridgeName},
fromNetwork:{self.fromNetwork},
toNetwork:{self.toNetwork},
maxAmountIn:{self.maxAmountIn},
amountIn:{self.amountIn},
amountOut:{self.amountOut},
gasAmount:{self.gasAmount},
tokenFees:{self.tokenFees},
bonderFees:{self.bonderFees},
cost:{self.cost},
preferred:{self.preferred},
estimatedTime:{self.estimatedTime}
)"""
proc toTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =
result = TransactionPathDto()
discard jsonObj.getProp("BridgeName", result.bridgeName)
result.fromNetwork = Json.decode($jsonObj["From"], NetworkDto, allowUnknownFields = true)
result.toNetwork = Json.decode($jsonObj["To"], NetworkDto, allowUnknownFields = true)
result.gasFees = jsonObj["GasFees"].toSuggestedFeesDto()
result.cost = parseFloat(jsonObj{"Cost"}.getStr)
result.tokenFees = parseFloat(jsonObj{"TokenFees"}.getStr)
result.bonderFees = jsonObj{"BonderFees"}.getStr
result.maxAmountIn = stint.fromHex(UInt256, jsonObj{"MaxAmountIn"}.getStr)
result.amountIn = stint.fromHex(UInt256, jsonObj{"AmountIn"}.getStr)
result.amountOut = stint.fromHex(UInt256, jsonObj{"AmountOut"}.getStr)
result.estimatedTime = jsonObj{"EstimatedTime"}.getInt
discard jsonObj.getProp("GasAmount", result.gasAmount)
discard jsonObj.getProp("Preferred", result.preferred)
proc convertToTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =
result = TransactionPathDto()
discard jsonObj.getProp("bridgeName", result.bridgeName)
result.fromNetwork = Json.decode($jsonObj["fromNetwork"], NetworkDto, allowUnknownFields = true)
result.toNetwork = Json.decode($jsonObj["toNetwork"], NetworkDto, allowUnknownFields = true)
result.gasFees = Json.decode($jsonObj["gasFees"], SuggestedFeesDto, allowUnknownFields = true)
discard jsonObj.getProp("cost", result.cost)
discard jsonObj.getProp("tokenFees", result.tokenFees)
discard jsonObj.getProp("bonderFees", result.bonderFees)
result.maxAmountIn = stint.u256(jsonObj{"maxAmountIn"}.getStr)
result.amountIn = stint.u256(jsonObj{"amountIn"}.getStr)
result.amountOut = stint.u256(jsonObj{"amountOut"}.getStr)
result.estimatedTime = jsonObj{"estimatedTime"}.getInt
discard jsonObj.getProp("gasAmount", result.gasAmount)
discard jsonObj.getProp("preferred", result.preferred)
type
Fees* = ref object
totalFeesInEth*: float
totalTokenFees*: float
totalTime*: int
type
SuggestedRoutesDto* = ref object
best*: seq[TransactionPathDto]
candidates*: seq[TransactionPathDto]
gasTimeEstimates*: seq[Fees]

View File

@ -32,6 +32,7 @@ include ../../common/json_utils
# Signals which may be emitted by this service:
const SIGNAL_TRANSACTIONS_LOADED* = "transactionsLoaded"
const SIGNAL_TRANSACTION_SENT* = "transactionSent"
const SIGNAL_SUGGESTED_ROUTES_READY* = "suggestedRoutesReady"
type
EstimatedTime* {.pure.} = enum
@ -59,18 +60,9 @@ type
TransactionSentArgs* = ref object of Args
result*: string
type SuggestedFees = object
gasPrice: float
baseFee: float
maxPriorityFeePerGas: float
maxFeePerGasL: float
maxFeePerGasM: float
maxFeePerGasH: float
eip1559Enabled: bool
# Initial version of suggested routes is a list of network where the tx is possible
type SuggestedRoutes = object
networks: seq[NetworkDto]
type
SuggestedRoutesArgs* = ref object of Args
suggestedRoutes*: string
QtObject:
type Service* = ref object of QObject
@ -253,145 +245,131 @@ QtObject:
error "Error estimating gas", msg = e.msg
return $(%* { "result": "-1", "success": false, "error": { "message": e.msg } })
proc transferEth(
self: Service,
from_addr: string,
to_addr: string,
value: string,
gas: string,
gasPrice: string,
maxPriorityFeePerGas: string,
maxFeePerGas: string,
password: string,
chainId: string,
uuid: string,
eip1559Enabled: bool,
): bool {.slot.} =
try:
eth_utils.validateTransactionInput(from_addr, to_addr, assetAddress = "", value, gas,
gasPrice, data = "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid)
var tx = ens_utils.buildTransaction(parseAddress(from_addr), eth2Wei(parseFloat(value), 18),
gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
tx.to = parseAddress(to_addr).some
let response = transactions.createMultiTransaction(
MultiTransactionDto(
fromAddress: from_addr,
toAddress: to_addr,
fromAsset: "ETH",
toAsset: "ETH",
fromAmount: "0x" & tx.value.unsafeGet.toHex,
multiTxtype: MultiTransactionType.MultiTransactionSend,
),
{chainId: @[tx]}.toTable,
password,
)
let output = %* { "result": response.result{"hashes"}{chainId}[0].getStr, "success": %(response.error.isNil), "uuid": %uuid }
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(result: $output))
except Exception as e:
error "Error sending eth transfer transaction", msg = e.msg
return false
return true
proc transferTokens*(
proc transfer*(
self: Service,
from_addr: string,
to_addr: string,
tokenSymbol: string,
value: string,
gas: string,
gasPrice: string,
maxPriorityFeePerGas: string,
maxFeePerGas: string,
password: string,
chainId: string,
uuid: string,
eip1559Enabled: bool,
): bool =
# TODO move this to another thread
priority: int,
selectedRoutes: string,
password: string,
) =
try:
let network = self.networkService.getNetwork(parseInt(chainId))
let token = self.tokenService.findTokenBySymbol(network, tokenSymbol)
let selRoutes = parseJson(selectedRoutes)
let routes = selRoutes.getElems().map(x => x.convertToTransactionPathDto())
eth_utils.validateTransactionInput(from_addr, to_addr, token.addressAsString(), value, gas,
gasPrice, data = "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid)
var tx = ens_utils.buildTokenTransaction(parseAddress(from_addr), token.address,
gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
var paths: seq[TransactionBridgeDto] = @[]
var isEthTx = false
var chainID = 0
var amountToSend: UInt256
var toAddress: Address
var data = ""
let weiValue = conversion.eth2Wei(parseFloat(value), token.decimals)
let transfer = Transfer(
to: parseAddress(to_addr),
value: weiValue,
)
if(routes.len > 0):
chainID = routes[0].fromNetwork.chainID
let network = self.networkService.getNetwork(chainID)
if network.nativeCurrencySymbol == tokenSymbol:
isEthTx = true
if(isEthTx):
amountToSend = conversion.eth2Wei(parseFloat(value), 18)
toAddress = parseAddress(to_addr)
else:
let token = self.tokenService.findTokenBySymbol(network, tokenSymbol)
amountToSend = conversion.eth2Wei(parseFloat(value), token.decimals)
toAddress = token.address
let transfer = Transfer(
to: parseAddress(to_addr),
value: amountToSend,
)
data = ERC20_procS.toTable["transfer"].encodeAbi(transfer)
for route in routes:
var simpleTx = TransactionDataDto()
var hopTx = TransactionDataDto()
var txData = TransactionDataDto()
var maxFees: float = 0
var gasFees: string = ""
case(priority):
of 0: maxFees = route.gasFees.maxFeePerGasL
of 1: maxFees = route.gasFees.maxFeePerGasM
of 2: maxFees = route.gasFees.maxFeePerGasH
else: maxFees = 0
if( not route.gasFees.eip1559Enabled):
gasFees = $route.gasFees.gasPrice
if(isEthTx) :
txData = ens_utils.buildTransaction(parseAddress(from_addr), eth2Wei(parseFloat(value), 18),
$route.gasAmount, gasFees, route.gasFees.eip1559Enabled, $route.gasFees.maxPriorityFeePerGas, $maxFees)
txData.to = parseAddress(to_addr).some
else:
txData = ens_utils.buildTokenTransaction(parseAddress(from_addr), toAddress,
$route.gasAmount, gasFees, route.gasFees.eip1559Enabled, $route.gasFees.maxPriorityFeePerGas, $maxFees)
txData.data = data
var path = TransactionBridgeDto(bridgeName: route.bridgeName, chainID: route.fromNetwork.chainId)
if(route.bridgeName == "Simple"):
path.simpleTx = txData
else:
hopTx = txData
hopTx.chainID = route.toNetwork.chainId.some
hopTx.symbol = tokenSymbol.some
hopTx.recipient = parseAddress(to_addr).some
hopTx.amount = route.amountIn.some
hopTx.bonderFee = route.bonderFees.some
path.hopTx = hopTx
paths.add(path)
tx.data = ERC20_procS.toTable["transfer"].encodeAbi(transfer)
let response = transactions.createMultiTransaction(
MultiTransactionDto(
fromAddress: from_addr,
toAddress: to_addr,
fromAsset: tokenSymbol,
toAsset: tokenSymbol,
fromAmount: "0x" & weiValue.toHex,
fromAmount: "0x" & amountToSend.toHex,
multiTxtype: MultiTransactionType.MultiTransactionSend,
),
{chainId: @[tx]}.toTable,
),
paths,
password,
)
let txHash = response.result.getStr
let output = %* { "result": response.result{"hashes"}{chainId}[0].getStr, "success":true, "uuid": %uuid }
let output = %* {"result": response.result{"hashes"}, "success":true, "uuid": %uuid }
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(result: $output))
except Exception as e:
error "Error sending token transfer transaction", msg = e.msg
return false
return true
let err = fmt"Error sending token transfer transaction: {e.msg}"
let output = %* {"success":false, "uuid": %uuid, "error":fmt"Error sending token transfer transaction: {e.msg}"}
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(result: $output))
proc transfer*(
self: Service,
from_addr: string,
to_addr: string,
assetSymbol: string,
value: string,
gas: string,
gasPrice: string,
maxPriorityFeePerGas: string,
maxFeePerGas: string,
password: string,
chainId: string,
uuid: string,
eip1559Enabled: bool,
): bool =
let network = self.networkService.getNetwork(parseInt(chainId))
if network.nativeCurrencySymbol == assetSymbol:
return self.transferEth(from_addr, to_addr, value, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
return self.transferTokens(from_addr, to_addr, assetSymbol, value, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
proc suggestedFees*(self: Service, chainId: int): SuggestedFees =
proc suggestedFees*(self: Service, chainId: int): SuggestedFeesDto =
try:
let response = eth.suggestedFees(chainId).result
return SuggestedFees(
gasPrice: parseFloat(response{"gasPrice"}.getStr),
baseFee: parseFloat(response{"baseFee"}.getStr),
maxPriorityFeePerGas: parseFloat(response{"maxPriorityFeePerGas"}.getStr),
maxFeePerGasL: parseFloat(response{"maxFeePerGasLow"}.getStr),
maxFeePerGasM: parseFloat(response{"maxFeePerGasMedium"}.getStr),
maxFeePerGasH: parseFloat(response{"maxFeePerGasHigh"}.getStr),
eip1559Enabled: response{"eip1559Enabled"}.getbool,
)
return response.toSuggestedFeesDto()
except Exception as e:
error "Error getting suggested fees", msg = e.msg
proc suggestedRoutes*(self: Service, account: string, amount: float64, token: string, disabledChainIDs: seq[uint64]): SuggestedRoutes =
try:
let response = eth.suggestedRoutes(account, amount, token, disabledChainIDs)
return SuggestedRoutes(
networks: Json.decode($response.result{"networks"}, seq[NetworkDto], allowUnknownFields = true)
)
except Exception as e:
error "Error getting suggested routes", msg = e.msg
proc suggestedRoutesReady*(self: Service, suggestedRoutes: string) {.slot.} =
self.events.emit(SIGNAL_SUGGESTED_ROUTES_READY, SuggestedRoutesArgs(suggestedRoutes: suggestedRoutes))
proc suggestedRoutes*(self: Service, account: string, amount: Uint256, token: string, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[uint64], priority: int, sendType: int): SuggestedRoutesDto =
let arg = GetSuggestedRoutesTaskArg(
tptr: cast[ByteAddress](getSuggestedRoutesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "suggestedRoutesReady",
account: account,
amount: amount,
token: token,
disabledFromChainIDs: disabledFromChainIDs,
disabledToChainIDs: disabledToChainIDs,
preferredChainIDs: preferredChainIDs,
priority: priority,
sendType: sendType
)
self.threadpool.start(arg)
proc fetchCryptoServices*(self: Service): seq[CryptoRampDto] =
try:

View File

@ -1,4 +1,4 @@
import json
import json, stint
import ./core, ./response_type
export response_type
@ -28,6 +28,6 @@ proc suggestedFees*(chainId: int): RpcResponse[JsonNode] {.raises: [Exception].}
let payload = %* [chainId]
return core.callPrivateRPC("wallet_getSuggestedFees", payload)
proc suggestedRoutes*(account: string, amount: float64, token: string, disabledChainIDs: seq[uint64]): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [account, amount, token, disabledChainIDs]
proc suggestedRoutes*(account: string, amount: string, token: string, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[uint64], priority: int, sendType: int): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [sendType, account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, priority]
return core.callPrivateRPC("wallet_getSuggestedRoutes", payload)

View File

@ -45,7 +45,7 @@ proc getPendingOutboundTransactionsByAddress*(chainIds: seq[int], address: strin
proc fetchCryptoServices*(): RpcResponse[JsonNode] {.raises: [Exception].} =
result = core.callPrivateRPC("wallet_getCryptoOnRamps", %* [])
proc createMultiTransaction*(multiTransaction: MultiTransactionDto, data: Table[string, seq[TransactionDataDto]], password: string): RpcResponse[JsonNode] {.raises: [Exception].} =
proc createMultiTransaction*(multiTransaction: MultiTransactionDto, data: seq[TransactionBridgeDto], password: string): RpcResponse[JsonNode] {.raises: [Exception].} =
var hashed_password = "0x" & $keccak_256.digest(password)
let payload = %* [multiTransaction, data, hashed_password]
result = core.callPrivateRPC("wallet_createMultiTransaction", payload)
result = core.callPrivateRPC("wallet_createMultiTransaction", payload)

View File

@ -33,9 +33,6 @@ mainWallet_Send_Popup_Password_Input = {"container": statusDesktop_mainWindow, "
mainWallet_Send_Popup_Asset_Selector = {"container": statusDesktop_mainWindow, "objectName": "assetSelectorButton", "type": "StatusComboBox"}
mainWallet_Send_Popup_Asset_List = {"container": statusDesktop_mainWindow, "objectName": "assetSelectorList", "type": "StatusListView"}
mainWallet_Send_Popup_GasPrice_Input = {"container": statusDesktop_mainWindow, "objectName": "gasPriceSelectorInput", "type": "StyledTextField"}
mainWallet_Send_Popup_GasSelector_LowGas_Button = {"container": statusDesktop_mainWindow, "objectName": "GasSelector_lowGasButton", "type": "GasSelectorButton"}
mainWallet_Send_Popup_GasSelector_OptimalGas_Button = {"container": statusDesktop_mainWindow, "objectName": "GasSelector_optimalGasButton", "type": "GasSelectorButton"}
mainWallet_Send_Popup_GasSelector_HighGas_Button = {"container": statusDesktop_mainWindow, "objectName": "GasSelector_highGasButton", "type": "GasSelectorButton"}
# Add account popup:
mainWallet_Add_Account_Popup_Main = {"container": statusDesktop_mainWindow, "objectName": "AddAccountModalContent", "type": "StatusScrollView", "visible": True}

View File

@ -178,6 +178,8 @@ Rectangle {
id: basicInput
StatusBaseText {
id: secondaryLabel
Layout.maximumWidth: root.width - 12
elide: Text.ElideRight
font.pixelSize: 13
font.weight: Font.Medium
}
@ -246,6 +248,8 @@ Rectangle {
}
StatusBaseText {
id: tertiaryText
Layout.maximumWidth: root.width - 12
elide: Text.ElideRight
font.pixelSize: 10
}
}

View File

@ -0,0 +1,3 @@
<svg width="20" height="15" viewBox="0 0 20 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 1.00413H6.22C5.11 -0.225866 3.21 -0.315866 2 0.804134C1.36 1.36413 1 2.16413 1 3.00413V12.0041C0.734784 12.0041 0.48043 12.1095 0.292893 12.297C0.105357 12.4846 0 12.7389 0 13.0041V14.0041H8V13.0041C8 12.7389 7.89464 12.4846 7.70711 12.297C7.51957 12.1095 7.26522 12.0041 7 12.0041V5.00413H18C18.5304 5.00413 19.0391 4.79342 19.4142 4.41835C19.7893 4.04327 20 3.53457 20 3.00413C20 2.4737 19.7893 1.96499 19.4142 1.58992C19.0391 1.21485 18.5304 1.00413 18 1.00413ZM5.5 12.0041H2.5V5.60413C3.43 6.14413 4.57 6.14413 5.5 5.60413V12.0041ZM4 4.50413C3.60218 4.50413 3.22064 4.3461 2.93934 4.06479C2.65804 3.78349 2.5 3.40196 2.5 3.00413C2.5 2.60631 2.65804 2.22478 2.93934 1.94347C3.22064 1.66217 3.60218 1.50413 4 1.50413C4.39782 1.50413 4.77936 1.66217 5.06066 1.94347C5.34196 2.22478 5.5 2.60631 5.5 3.00413C5.5 3.40196 5.34196 3.78349 5.06066 4.06479C4.77936 4.3461 4.39782 4.50413 4 4.50413V4.50413ZM8.5 4.00413L7 2.00413H8.5L10 4.00413H8.5ZM12.5 4.00413L11 2.00413H12.5L14 4.00413H12.5ZM16.5 4.00413L15 2.00413H16.5L18 4.00413H16.5Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 0.00390625C1.67893 0.00390625 0 1.68284 0 3.75391V15.7539C0 16.7204 0.783502 17.5039 1.75 17.5039H7.75C8.7165 17.5039 9.5 16.7204 9.5 15.7539V10.0039H9.75C10.4404 10.0039 11 10.5635 11 11.2539V14.7539C11 16.2727 12.2312 17.5039 13.75 17.5039C15.2688 17.5039 16.5 16.2727 16.5 14.7539V6.11521C16.5 5.03385 16.0332 4.00513 15.2194 3.29305L12.2439 0.689474C11.9322 0.416712 11.4583 0.4483 11.1856 0.760028C10.9128 1.07176 10.9444 1.54558 11.2561 1.81834L12.744 3.12024C11.7328 3.44065 11 4.3867 11 5.50391C11 6.88462 12.1193 8.00391 13.5 8.00391C14.0628 8.00391 14.5822 7.81793 15 7.50409V14.7539C15 15.4443 14.4404 16.0039 13.75 16.0039C13.0596 16.0039 12.5 15.4443 12.5 14.7539V11.2539C12.5 9.73512 11.2688 8.50391 9.75 8.50391H9.5V3.75391C9.5 1.68284 7.82107 0.00390625 5.75 0.00390625H3.75ZM1.5 3.75391C1.5 2.51127 2.50736 1.50391 3.75 1.50391H5.75C6.99264 1.50391 8 2.51127 8 3.75391V6.25391L1.5 6.25391V3.75391ZM1.5 7.75391V15.7539C1.5 15.892 1.61193 16.0039 1.75 16.0039H7.75C7.88807 16.0039 8 15.892 8 15.7539V7.75391L1.5 7.75391ZM13.5 4.50391C12.9477 4.50391 12.5 4.95162 12.5 5.50391C12.5 6.05619 12.9477 6.50391 13.5 6.50391C14.0523 6.50391 14.5 6.05619 14.5 5.50391C14.5 4.95162 14.0523 4.50391 13.5 4.50391Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -59,12 +59,10 @@ StatusSectionLayout {
}
}
// TODO we'll need a new dialog at one point because this one is not using the same call, but it's good for now
property Component sendTransactionModalComponent: SignTransactionModal {
property Component sendTransactionModalComponent: SendModal {
anchors.centerIn: parent
store: root.globalStore
contactsStore: root.globalStore.profileSectionStore.contactsStore
chainId: root.globalStore.getChainIdForBrowser()
selectedAccount: WalletStore.dappBrowserAccount
preSelectedAsset: _internal.getEthAsset()
}
property Component signMessageModalComponent: SignMessageModal {}
@ -142,6 +140,22 @@ StatusSectionLayout {
findBar.reset();
browserHeader.addressBar.text = Web3ProviderStore.obtainAddress(currentWebView.url)
}
function getEthAsset() {
let assetsList = WalletStore.dappBrowserAccount.assets
for(var i=0; i< assetsList.count;i++) {
if("ETH" === assetsList.rowData(i, "symbol"))
return {
name: assetsList.rowData(i, "name"),
symbol: assetsList.rowData(i, "symbol"),
totalBalance: assetsList.rowData(i, "totalBalance"),
totalCurrencyBalance: assetsList.rowData(i, "totalCurrencyBalance"),
balances: assetsList.rowData(i, "balances"),
decimals: assetsList.rowData(i, "decimals")
}
}
return {}
}
}
centerPanel: Rectangle {
@ -156,26 +170,8 @@ StatusSectionLayout {
}
createSendTransactionModalComponent: function(request) {
return _internal.sendTransactionModalComponent.createObject(root, {
trxData: request.payload.params[0].data || "",
selectedAccount: {
name: WalletStore.dappBrowserAccount.name,
address: request.payload.params[0].from,
iconColor: WalletStore.dappBrowserAccount.color,
assets: WalletStore.dappBrowserAccount.assets
},
selectedRecipient: {
address: request.payload.params[0].to,
identicon: "",
name: RootStore.activeChannelName,
type: RecipientSelector.Type.Address
},
selectedAsset: {
name: "ETH",
symbol: "ETH",
address: Constants.zeroAddress
},
selectedFiatAmount: "42", // TODO calculate that
selectedAmount: RootStore.getWei2Eth(request.payload.params[0].value, 18)
preSelectedRecipient: request.payload.params[0].to,
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(RootStore.getWei2Eth(request.payload.params[0].value, 18)),
})
}
createSignMessageModalComponent: function(request) {

View File

@ -91,22 +91,27 @@ QtObject {
request.payload.method === "eth_sendTransaction") {
var acc = WalletStore.dappBrowserAccount
const value = RootStore.getWei2Eth(request.payload.params[0].value, 18);
const sendDialog = createSendTransactionModalComponent(request)
const sendDialog = createSendTransactionModalComponent(request, requestType)
sendDialog.sendTransaction = function (selectedGasLimit, selectedGasPrice, selectedTipLimit, selectedOverallLimit, enteredPassword) {
let trx = request.payload.params[0]
// TODO: use bignumber instead of floats
trx.value = RootStore.getEth2Hex(parseFloat(value))
trx.gas = "0x" + parseInt(selectedGasLimit, 10).toString(16)
trx.maxPriorityFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedTipLimit))
trx.maxFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedOverallLimit))
sendDialog.sendTransaction = function () {
if(sendDialog.bestRoutes.length === 1) {
let path = sendDialog.bestRoutes[0]
let eip1559Enabled = path.gasFees.eip1559Enabled
let maxFeePerGas = (sendDialog.selectedPriority === 0) ? path.gasFees.maxFeePerGasL:
(sendDialog.selectedPriority === 1) ? path.gasFees.maxFeePerGasM:
path.gasFees.maxFeePerGasH
let trx = request.payload.params[0]
// TODO: use bignumber instead of floats
trx.value = RootStore.getEth2Hex(parseFloat(value))
trx.gas = "0x" + parseInt(path.gasAmount, 10).toString(16)
trx.maxPriorityFeePerGas = RootStore.getGwei2Hex(parseFloat(eip1559Enabled ? path.gasFees.maxPriorityFeePerGas : "0"))
trx.maxFeePerGas = RootStore.getGwei2Hex(parseFloat(eip1559Enabled ? maxFeePerGas : path.gasFees.gasPrice))
request.payload.password = enteredPassword
request.payload.params[0] = trx
request.payload.params[0] = trx
Web3ProviderStore.web3ProviderInst.postMessage(request.payload.method, requestType, JSON.stringify(request))
sendDialog.close()
sendDialog.destroy()
Web3ProviderStore.web3ProviderInst.authenticateToPostMessage(request.payload.method, requestType, JSON.stringify(request))
sendDialog.close()
}
}
sendDialog.open();

View File

@ -2,6 +2,7 @@ import QtQuick 2.13
import utils 1.0
import StatusQ.Core.Utils 0.1 as StatusQUtils
import shared.stores 1.0
QtObject {
id: root
@ -138,10 +139,6 @@ QtObject {
property var userProfileInst: userProfile
property var accounts: walletSectionAccounts.model
property var currentAccount: walletSectionCurrent
property string currentCurrency: walletSection.currentCurrency
property string signingPhrase: walletSection.signingPhrase
property string channelEmoji: chatCommunitySectionModule && chatCommunitySectionModule.emoji ? chatCommunitySectionModule.emoji : ""
@ -475,38 +472,46 @@ QtObject {
return userProfile.getPubKey()
}
// Needed for TX in chat for stickers and via contact
property var accounts: walletSectionAccounts.model
property var currentAccount: walletSectionCurrent
property string currentCurrency: walletSection.currentCurrency
property CurrenciesStore currencyStore: CurrenciesStore { }
property var allNetworks: networksModule.all
property var savedAddressesModel: walletSectionSavedAddresses.model
property var disabledChainIds: []
property var disabledChainIdsFromList: []
property var disabledChainIdsToList: []
function addRemoveDisabledChain(suggestedRoutes, chainID, isDisbaled) {
if(isDisbaled) {
for(var i = 0; i < suggestedRoutes.length;i++) {
if(suggestedRoutes[i].chainId === chainID) {
disabledChainIds.push(suggestedRoutes[i].chainId)
}
}
function addRemoveDisabledFromChain(chainID, isDisabled) {
if(isDisabled) {
disabledChainIdsFromList.push(chainID)
}
else {
for(var i = 0; i < disabledChainIds.length;i++) {
if(disabledChainIds[i] === chainID) {
disabledChainIds.splice(i, 1)
for(var i = 0; i < disabledChainIdsFromList.length;i++) {
if(disabledChainIdsFromList[i] === chainID) {
disabledChainIdsFromList.splice(i, 1)
}
}
}
}
function checkIfDisabledByUser(chainID) {
for(var i = 0; i < disabledChainIds.length;i++) {
if(disabledChainIds[i] === chainID) {
return true
function addRemoveDisabledToChain(chainID, isDisabled) {
if(isDisabled) {
disabledChainIdsToList.push(chainID)
}
else {
for(var i = 0; i < disabledChainIdsToList.length;i++) {
if(disabledChainIdsToList[i] === chainID) {
disabledChainIdsToList.splice(i, 1)
}
}
}
return false
}
function getFiatValue(balance, cryptoSymbo, fiatSymbol) {
return profileSectionModule.ensUsernamesModule.getFiatValue(balance, cryptoSymbo, fiatSymbol)
function getFiatValue(balance, cryptoSymbol, fiatSymbol) {
return profileSectionModule.ensUsernamesModule.getFiatValue(balance, cryptoSymbol, fiatSymbol)
}
function acceptRequestTransaction(transactionHash, messageId, signature) {
@ -533,12 +538,8 @@ QtObject {
return walletSectionTransactions.estimateGas(from_addr, to, assetSymbol, value === "" ? "0.00" : value, chainId, data)
}
function transfer(from, to, address, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, chainId, uuid, eip1559Enabled) {
return walletSectionTransactions.authenticateAndTransfer(
from, to, address, tokenSymbol, amount, gasLimit,
gasPrice, tipLimit, overallLimit, chainId, uuid,
eip1559Enabled
);
function authenticateAndTransfer(from, to, tokenSymbol, amount, uuid, priority, selectedRoutes) {
walletSectionTransactions.authenticateAndTransfer(from, to, tokenSymbol, amount, uuid, priority, selectedRoutes)
}
function getAccountNameByAddress(address) {
@ -558,7 +559,34 @@ QtObject {
return JSON.parse(walletSectionTransactions.suggestedFees(chainId))
}
function suggestedRoutes(account, amount, token, disabledChainIds) {
return JSON.parse(walletSectionTransactions.suggestedRoutes(account, amount, token, disabledChainIds)).networks
function suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIds, priority, sendType) {
walletSectionTransactions.suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIds, priority, sendType)
}
function resolveENS(value) {
mainModuleInst.resolveENS(value, "")
}
function getWei2Eth(wei) {
return globalUtils.wei2Eth(wei,18)
}
function getEth2Wei(eth) {
return globalUtils.eth2Wei(eth, 18)
}
function switchAccount(newIndex) {
if(Constants.isCppApp)
walletSectionAccounts.switchAccount(newIndex)
else
walletSection.switchAccount(newIndex)
}
function getEtherscanLink() {
return profileSectionModule.ensUsernamesModule.getEtherscanLink()
}
function hex2Eth(value) {
return globalUtils.hex2Eth(value)
}
}

View File

@ -38,10 +38,10 @@ QtObject {
return stickersModule.getCurrentCurrency()
}
function getFiatValue(balance, cryptoSymbo, fiatSymbol) {
function getFiatValue(balance, cryptoSymbol, fiatSymbol) {
if(!root.stickersModule)
return ""
return stickersModule.getFiatValue(balance, cryptoSymbo, fiatSymbol)
return stickersModule.getFiatValue(balance, cryptoSymbol, fiatSymbol)
}
function getGasEthValue(gweiValue, gasLimit) {
@ -62,10 +62,10 @@ QtObject {
return stickersModule.estimate(packId, selectedAccount, price, uuid)
}
function buy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) {
function authenticateAndBuy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, eip1559Enabled) {
if(!root.stickersModule)
return ""
return stickersModule.buy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled)
return stickersModule.authenticateAndBuy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, eip1559Enabled)
}
function getChainIdForStickers() {

View File

@ -365,12 +365,9 @@ Item {
id: cmpSendTransactionWithEns
SendModal {
id: sendTransactionWithEns
store: root.rootStore
contactsStore: root.contactsStore
onClosed: {
destroy()
}
launchedFromChat: true
preSelectedRecipient: {
parentModule.prepareChatContentModuleForChatId(activeChatId)
let chatContentModule = parentModule.getChatContentModule()

View File

@ -46,10 +46,10 @@ QtObject {
return ensUsernamesModule.setPubKeyGasEstimate(ensUsername, address)
}
function setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) {
function authenticateAndSetPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
return ensUsernamesModule.authenticateAndSetPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled)
}
function getEtherscanLink() {
@ -68,16 +68,10 @@ QtObject {
globalUtils.copyToClipboard(value)
}
function releaseEnsEstimate(ensUsername, address) {
function authenticateAndReleaseEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.releaseEnsEstimate(ensUsername, address)
}
function releaseEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.releaseEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
return ensUsernamesModule.authenticateAndReleaseEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, eip1559Enabled)
}
function ensConnectOwnedUsername(name, isStatus) {
@ -92,16 +86,10 @@ QtObject {
return ensUsernamesModule.getEnsRegisteredAddress()
}
function registerEnsGasEstimate(ensUsername, address) {
function authenticateAndRegisterEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, eip1559Enabled) {
if(!root.ensUsernamesModule)
return 0
return ensUsernamesModule.registerEnsGasEstimate(ensUsername, address)
}
function registerEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.registerEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled)
return
ensUsernamesModule.authenticateAndRegisterEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, eip1559Enabled)
}
function getEnsRegistry() {
@ -128,10 +116,10 @@ QtObject {
return ensUsernamesModule.getCurrentCurrency()
}
function getFiatValue(balance, cryptoSymbo, fiatSymbol) {
function getFiatValue(balance, cryptoSymbol, fiatSymbol) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getFiatValue(balance, cryptoSymbo, fiatSymbol)
return ensUsernamesModule.getFiatValue(balance, cryptoSymbol, fiatSymbol)
}
function getGasEthValue(gweiValue, gasLimit) {
@ -159,9 +147,5 @@ QtObject {
return ""
return ensUsernamesModule.getChainIdForEns()
}
function suggestedRoutes(account, amount, token) {
return JSON.parse(walletSectionTransactions.suggestedRoutes(account, amount, token)).networks
}
}

View File

@ -9,6 +9,7 @@ import StatusQ.Components 0.1
import utils 1.0
import shared.status 1.0
import shared.popups 1.0
Item {
id: root
@ -103,35 +104,73 @@ Item {
Component {
id: transactionDialogComponent
StatusETHTransactionModal {
ensUsernamesStore: root.ensUsernamesStore
contactsStore: root.contactsStore
ensUsername: root.username
chainId: root.ensUsernamesStore.getChainIdForEns()
title: qsTr("Release your username")
onClosed: {
destroy()
SendModal {
id: releaseEnsModal
modalHeader: qsTr("Release your username")
interactive: false
sendType: Constants.SendType.ENSRelease
preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(0)
preSelectedAsset: {
let assetsList = releaseEnsModal.store.currentAccount.assets
for(var i=0; i< assetsList.count;i++) {
if("ETH" === assetsList.rowData(i, "symbol"))
return {
name: assetsList.rowData(i, "name"),
symbol: assetsList.rowData(i, "symbol"),
totalBalance: assetsList.rowData(i, "totalBalance"),
totalCurrencyBalance: assetsList.rowData(i, "totalCurrencyBalance"),
balances: assetsList.rowData(i, "balances"),
decimals: assetsList.rowData(i, "decimals")
}
}
return {}
}
estimateGasFunction: function(selectedAccount) {
if (username === "" || !selectedAccount) return 100000;
return root.ensUsernamesStore.releaseEnsEstimate(username, selectedAccount.address)
sendTransaction: function() {
if(bestRoutes.length === 1) {
let path = bestRoutes[0]
let eip1559Enabled = path.gasFees.eip1559Enabled
let maxFeePerGas = (selectedPriority === 0) ? path.gasFees.maxFeePerGasL:
(selectedPriority === 1) ? path.gasFees.maxFeePerGasM:
path.gasFees.maxFeePerGasH
root.ensUsernamesStore.authenticateAndReleaseEns(
root.username,
selectedAccount.address,
path.gasAmount,
eip1559Enabled ? "" : path.gasFees.gasPrice,
eip1559Enabled ? path.gasFees.maxPriorityFeePerGas : "",
eip1559Enabled ? maxFeePerGas: path.gasFees.gasPrice,
eip1559Enabled,
)
}
}
onSendTransaction: function(userAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled){
return root.ensUsernamesStore.releaseEns(username,
userAddress,
gasLimit,
gasPrice,
tipLimit,
overallLimit,
password,
eip1559Enabled)
Connections {
target: root.ensUsernamesStore.ensUsernamesModule
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)) {
releaseEnsModal.setSendTxError()
return
}
releaseEnsModal.sendingError.text = response.result
return releaseEnsModal.sendingError.open()
}
usernameReleased(username);
let url = `${releaseEnsModal.store.getEtherscanLink()}/${response.result}`;
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
} catch (e) {
console.error('Error parsing the response', e)
}
releaseEnsModal.close()
}
}
onSuccess: function(){
usernameReleased(username);
}
width: 475
height: 500
}
}

View File

@ -11,6 +11,7 @@ import shared 1.0
import shared.panels 1.0
import shared.status 1.0
import shared.controls 1.0
import shared.popups 1.0
Item {
id: root
@ -57,35 +58,72 @@ Item {
Component {
id: transactionDialogComponent
StatusETHTransactionModal {
ensUsernamesStore: root.ensUsernamesStore
contactsStore: root.contactsStore
chainId: root.ensUsernamesStore.getChainIdForEns()
title: qsTr("Connect username with your pubkey")
onClosed: {
destroy()
SendModal {
id: releaseEnsModal
modalHeader: qsTr("Connect username with your pubkey")
interactive: false
sendType: Constants.SendType.ENSSetPubKey
preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(0)
preSelectedAsset: {
let assetsList = releaseEnsModal.store.currentAccount.assets
for(var i=0; i< assetsList.count;i++) {
if("ETH" === assetsList.rowData(i, "symbol"))
return {
name: assetsList.rowData(i, "name"),
symbol: assetsList.rowData(i, "symbol"),
totalBalance: assetsList.rowData(i, "totalBalance"),
totalCurrencyBalance: assetsList.rowData(i, "totalCurrencyBalance"),
balances: assetsList.rowData(i, "balances"),
decimals: assetsList.rowData(i, "decimals")
}
}
return {}
}
estimateGasFunction: function(selectedAccount) {
if (ensUsername.text === "" || !selectedAccount) return 80000;
return root.ensUsernamesStore.setPubKeyGasEstimate(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ), selectedAccount.address)
sendTransaction: function() {
if(bestRoutes.length === 1) {
let path = bestRoutes[0]
let eip1559Enabled = path.gasFees.eip1559Enabled
root.ensUsernamesStore.authenticateAndSetPubKey(
ensUsername.text + (isStatus ? ".stateofus.eth" : "" ),
selectedAccount.address,
path.gasAmount,
eip1559Enabled ? "" : path.gasFees.gasPrice,
"",
"",
eip1559Enabled,
)
}
}
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password, eip1559Enabled) {
return root.ensUsernamesStore.setPubKey(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ),
selectedAddress,
gasLimit,
gasPrice,
"",
"",
password,
eip1559Enabled)
Connections {
target: root.ensUsernamesStore.ensUsernamesModule
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)) {
releaseEnsModal.setSendTxError()
return
}
releaseEnsModal.sendingError.text = response.result
return releaseEnsModal.sendingError.open()
}
usernameUpdated(ensUsername.text);
let url = `${releaseEnsModal.store.getEtherscanLink()}/${response.result}`;
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
} catch (e) {
console.error('Error parsing the response', e)
}
releaseEnsModal.close()
}
}
onSuccess: function(){
usernameUpdated(ensUsername.text);
}
width: 475
height: 500
}
}
Item {

View File

@ -44,35 +44,71 @@ Item {
function closed() {
this.active = false // kill an opened instance
}
sourceComponent: StatusSNTTransactionModal {
store: root.ensUsernamesStore
contactsStore: root.contactsStore
stickersStore: root.stickersStore
asyncGasEstimateTarget: root.stickersStore.stickersModule
assetPrice: "10"
chainId: root.ensUsernamesStore.getChainIdForEns()
contractAddress: root.ensUsernamesStore.getEnsRegisteredAddress()
estimateGasFunction: function(selectedAccount, uuid) {
if (username === "" || !selectedAccount) return 380000;
return root.ensUsernamesStore.registerEnsGasEstimate(username, selectedAccount.address)
sourceComponent: SendModal {
id: buyEnsModal
interactive: false
sendType: Constants.SendType.ENSRegister
preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(10)
preSelectedAsset: {
let assetsList = buyEnsModal.store.currentAccount.assets
for(var i=0; i< assetsList.count;i++) {
let symbol = JSON.parse(root.stickersStore.getStatusToken()).symbol
if(symbol === assetsList.rowData(i, "symbol"))
return {
name: assetsList.rowData(i, "name"),
symbol: assetsList.rowData(i, "symbol"),
totalBalance: assetsList.rowData(i, "totalBalance"),
totalCurrencyBalance: assetsList.rowData(i, "totalCurrencyBalance"),
balances: assetsList.rowData(i, "balances"),
decimals: assetsList.rowData(i, "decimals")
}
}
return {}
}
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) {
return root.ensUsernamesStore.registerEns(
username,
selectedAddress,
gasLimit,
gasPrice,
tipLimit,
overallLimit,
password,
eip1559Enabled,
)
sendTransaction: function() {
if(bestRoutes.length === 1) {
let path = bestRoutes[0]
let eip1559Enabled = path.gasFees.eip1559Enabled
let maxFeePerGas = (selectedPriority === 0) ? path.gasFees.maxFeePerGasL:
(selectedPriority === 1) ? path.gasFees.maxFeePerGasM:
path.gasFees.maxFeePerGasH
root.ensUsernamesStore.authenticateAndRegisterEns(
username,
selectedAccount.address,
path.gasAmount,
eip1559Enabled ? "" : path.gasFees.gasPrice,
eip1559Enabled ? path.gasFees.maxPriorityFeePerGas : "",
eip1559Enabled ? maxFeePerGas: path.gasFees.gasPrice,
eip1559Enabled,
)
}
}
onSuccess: function(){
usernameRegistered(username);
}
onClosed: {
transactionDialog.closed()
Connections {
target: root.ensUsernamesStore.ensUsernamesModule
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)) {
buyEnsModal.setSendTxError()
return
}
buyEnsModal.sendingError.text = response.result
return buyEnsModal.sendingError.open()
}
usernameRegistered(username)
let url = `${buyEnsModal.store.getEtherscanLink()}/${response.result}`;
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
} catch (e) {
console.error('Error parsing the response', e)
}
}
}
}
}

View File

@ -322,15 +322,6 @@ Item {
Connections {
target: ensView.ensUsernamesStore.ensUsernamesModule
onTransactionWasSent: {
let url = `${ensView.ensUsernamesStore.getEtherscanLink()}/${txResult}`;
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url);
}
onTransactionCompleted: {
let title = ""
switch(trxType){

View File

@ -19,7 +19,7 @@ Item {
property var store
property var contactsStore
property var emojiPopup: null
property var sendModal
property var sendModalPopup
function showSigningPhrasePopup(){
if(!hideSignPhraseModal && !RootStore.hideSignPhraseModal){
@ -46,7 +46,7 @@ Item {
anchors.left: parent ? parent.left: undefined
anchors.right: parent ? parent.right: undefined
contactsStore: root.contactsStore
sendModal: root.sendModal
sendModal: root.sendModalPopup
}
}
@ -55,7 +55,7 @@ Item {
RightTabView {
store: root.store
contactsStore: root.contactsStore
sendModal: root.sendModal
sendModal: root.sendModalPopup
}
}

View File

@ -79,30 +79,6 @@ QtObject {
property var allNetworks: networksModule.all
property var disabledChainIds: []
function addRemoveDisabledChain(suggestedRoutes, chainID, isDisbaled) {
if(isDisbaled) {
disabledChainIds.push(chainID)
}
else {
for(var i = 0; i < disabledChainIds.length;i++) {
if(disabledChainIds[i] === chainID) {
disabledChainIds.splice(i, 1)
}
}
}
}
function checkIfDisabledByUser(chainID) {
for(var i = 0; i < disabledChainIds.length;i++) {
if(disabledChainIds[i] === chainID) {
return true
}
}
return false
}
function getEtherscanLink() {
return profileSectionModule.ensUsernamesModule.getEtherscanLink()
}
@ -135,21 +111,13 @@ QtObject {
function estimateGas(from_addr, to, assetSymbol, value, chainId, data) {
return walletSectionTransactions.estimateGas(from_addr, to, assetSymbol, value, chainId, data)
}
function getFiatValue(balance, cryptoSymbo, fiatSymbol) {
return profileSectionStore.ensUsernamesStore.getFiatValue(balance, cryptoSymbo, fiatSymbol)
function getFiatValue(balance, cryptoSymbol, fiatSymbol) {
return profileSectionStore.ensUsernamesStore.getFiatValue(balance, cryptoSymbol, fiatSymbol)
}
function getGasEthValue(gweiValue, gasLimit) {
return profileSectionStore.ensUsernamesStore.getGasEthValue(gweiValue, gasLimit)
}
function authenticateAndTransfer(from, to, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, chainId, uuid, eip1559Enabled) {
walletSectionTransactions.authenticateAndTransfer(
from, to, tokenSymbol, amount, gasLimit,
gasPrice, tipLimit, overallLimit, chainId, uuid,
eip1559Enabled
);
}
function suggestedFees(chainId) {
return JSON.parse(walletSectionTransactions.suggestedFees(chainId))
}
@ -166,10 +134,6 @@ QtObject {
return walletSectionTransactions.getChainIdForBrowser()
}
function suggestedRoutes(account, amount, token, disabledChainIds) {
return JSON.parse(walletSectionTransactions.suggestedRoutes(account, amount, token, disabledChainIds)).networks
}
function hex2Eth(value) {
return globalUtils.hex2Eth(value)
}

View File

@ -818,7 +818,7 @@ Item {
store: appMain.rootStore
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
emojiPopup: statusEmojiPopup
sendModal: sendModal
sendModalPopup: sendModal
}
}
@ -1045,8 +1045,6 @@ Item {
}
property var selectedAccount
sourceComponent: SendModal {
store: appMain.rootStore
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
onClosed: {
sendModal.closed()
}

View File

@ -15,8 +15,9 @@ ColumnLayout {
property bool transferPossible: false
property double amountToSend: 0
property bool isLoading: true
visible: !balancedExceededError.transferPossible && balancedExceededError.amountToSend > 0
visible: !balancedExceededError.transferPossible && balancedExceededError.amountToSend > 0 || isLoading
StatusIcon {
Layout.preferredHeight: 20
@ -24,6 +25,14 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
icon: "cancel"
color: Theme.palette.dangerColor1
visible: !isLoading
}
StatusLoadingIndicator {
Layout.preferredHeight: 24
Layout.preferredWidth: 24
Layout.alignment: Qt.AlignHCenter
color: Theme.palette.baseColor1
visible: isLoading
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
@ -31,7 +40,7 @@ ColumnLayout {
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: Theme.palette.dangerColor1
text: balancedExceededError.amountToSend > 0 ? qsTr("Balance exceeded"): qsTr("No networks available")
text: isLoading ? qsTr("Calculating fees"): balancedExceededError.amountToSend > 0 ? qsTr("Balance exceeded"): qsTr("No networks available")
wrapMode: Text.WordWrap
}
}

View File

@ -8,437 +8,195 @@ import shared.controls 1.0
import shared.controls.chat 1.0
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
Item {
id: root
width: parent.width
height: visible ? Style.current.smallPadding + prioritytext.height +
(advancedMode ? advancedModeItemGroup.height : selectorButtons.height) : 0
property var suggestedFees: ({
eip1559Enabled: true
})
property var getGasGweiValue: function () {}
property string selectedTokenSymbol
property string currentCurrency
property string currentCurrencySymbol
property bool advancedOrCustomMode: false
property var bestRoutes: []
property var estimatedGasFeesTime: []
property int selectedPriority: 1
property double selectedGasEthValue
property string selectedGasFiatValue
property string selectedTimeEstimate
property var getGasEthValue: function () {}
property var getFiatValue: function () {}
property var getEstimatedTime: function () {}
property string defaultCurrency: "USD"
property alias selectedGasPrice: inputGasPrice.text
property alias selectedGasLimit: inputGasLimit.text
property string defaultGasLimit: "0"
property string maxFiatFees: selectedGasFiatValue + root.defaultCurrency.toUpperCase()
property int estimatedTxTimeFlag: Constants.transactionEstimatedTime.unknown
property int chainId: 1
property alias selectedTipLimit: inputPerGasTipLimit.text
property alias selectedOverallLimit: inputGasPrice.text
property double selectedGasEthValue
property double selectedGasFiatValue
property string greaterThan0ErrorMessage: qsTr("Must be greater than 0")
property string invalidInputErrorMessage: qsTr("This needs to be a number")
property string noInputErrorMessage: qsTr("Please enter an amount")
property bool isValid: true
readonly property string uuid: Utils.uuid()
property bool advancedMode: false
// TODO: change these values false once EIP1559 suggestions are revised
property double perGasTipLimitFloor: 1 // Matches status-mobile minimum-priority-fee
property double perGasTipLimitAverage: formatDec(root.suggestedFees.maxPriorityFeePerGas, 2) // 1.5 // Matches status-mobile average-priority-fee
property bool showPriceLimitWarning : false
property bool showTipLimitWarning : false
function formatDec(num, dec){
return Math.round((num + Number.EPSILON) * Math.pow(10, dec)) / Math.pow(10, dec)
enum Priority {
SLOW, // 0
OPTIMAL, // 1
FAST // 2
}
function updateGasEthValue() {
// causes error on application load without this null check
if (!inputGasPrice || !inputGasLimit) {
return
}
Qt.callLater(function () {
let ethValue = root.getGasEthValue(inputGasPrice.text, inputGasLimit.text)
let fiatValue = root.getFiatValue(ethValue, "ETH", root.defaultCurrency)
selectedGasEthValue = ethValue
selectedGasFiatValue = fiatValue
root.estimatedTxTimeFlag = root.getEstimatedTime(root.chainId, inputGasPrice.text)
})
enum EstimatedTime {
Unknown = 0,
LessThanOneMin,
LessThanThreeMins,
LessThanFiveMins,
MoreThanFiveMins
}
function appendError(accum, error, nonBlocking = false) {
return accum + ` <span class="${nonBlocking ? "non-blocking" : ""}">${error}.</span>`
}
function checkLimits(){
if(!root.suggestedFees.eip1559Enabled) return;
let inputTipLimit = parseFloat(inputPerGasTipLimit.text || "0.00")
let inputOverallLimit = parseFloat(inputGasPrice.text || "0.00")
let gasLimit = parseInt(inputGasLimit.text, 10)
errorsText.text = "";
showPriceLimitWarning = false
showTipLimitWarning = false
let errorMsg = "";
if(gasLimit < 21000) {
errorMsg = appendError(errorMsg, qsTr("Min 21000 units"))
} else if (gasLimit < parseInt(defaultGasLimit)){
errorMsg = appendError(errorMsg, qsTr("Not enough gas").arg(perGasTipLimitAverage), true)
}
// Per-gas tip limit rules
if(inputTipLimit < perGasTipLimitFloor){
errorMsg = appendError(errorMsg, qsTr("Miners will currently not process transactions with a tip below %1 Gwei, the average is %2 Gwei").arg(perGasTipLimitFloor).arg(perGasTipLimitAverage))
showTipLimitWarning = true
} else if (inputTipLimit < perGasTipLimitAverage) {
errorMsg = appendError(errorMsg, qsTr("The average miner tip is %1 Gwei").arg(perGasTipLimitAverage), true)
}
errorsText.text = `<style type="text/css">span { color: "#ff0000" } span.non-blocking { color: "#FE8F59" }</style>${errorMsg}`
}
function checkOptimal() {
if (!optimalGasButton.checked) {
optimalGasButton.toggle()
QtObject {
id: d
function getLabelForEstimatedTxTime(estimatedFlag) {
switch(estimatedFlag) {
case GasSelector.EstimatedTime.Unknown:
return qsTr("~ Unknown")
case GasSelector.EstimatedTime.LessThanOneMin :
return qsTr("< 1 minute")
case GasSelector.EstimatedTime.LessThanThreeMins :
return qsTr("< 3 minutes")
case GasSelector.EstimatedTime.LessThanFiveMins:
return qsTr("< 5 minutes")
default:
return qsTr("> 5 minutes")
}
}
}
function validate() {
// causes error on application load without a null check
if (!inputGasLimit || !inputGasPrice || !inputPerGasTipLimit) {
return
}
width: parent.width
height: visible ? (!advancedOrCustomMode ? selectorButtons.height : advancedGasSelector.height) + Style.current.halfPadding : 0
inputGasLimit.validationError = ""
inputGasPrice.validationError = ""
inputPerGasTipLimit.validationError = ""
const noInputLimit = inputGasLimit.text === ""
const noInputPrice = inputGasPrice.text === ""
const noPerGasTip = inputPerGasTipLimit.text === ""
if (noInputLimit) {
inputGasLimit.validationError = root.noInputErrorMessage
}
if (noInputPrice) {
inputGasPrice.validationError = root.noInputErrorMessage
}
if (root.suggestedFees.eip1559Enabled && noPerGasTip) {
inputPerGasTipLimit.validationError = root.noInputErrorMessage
}
if (isNaN(inputGasLimit.text)) {
inputGasLimit.validationError = invalidInputErrorMessage
}
if (isNaN(inputGasPrice.text)) {
inputGasPrice.validationError = invalidInputErrorMessage
}
if (root.suggestedFees.eip1559Enabled && isNaN(inputPerGasTipLimit.text)) {
inputPerGasTipLimit.validationError = invalidInputErrorMessage
}
let inputLimit = parseFloat(inputGasLimit.text || "0.00")
let inputPrice = parseFloat(inputGasPrice.text || "0.00")
let inputTipLimit = parseFloat(inputPerGasTipLimit.text || "0.00")
if (inputLimit <= 0.00) {
inputGasLimit.validationError = root.greaterThan0ErrorMessage
}
if (inputPrice <= 0.00) {
inputGasPrice.validationError = root.greaterThan0ErrorMessage
}
if (root.suggestedFees.eip1559Enabled && inputTipLimit <= 0.00) {
inputPerGasTipLimit.validationError = root.greaterThan0ErrorMessage
}
return inputGasLimit.validationError === "" && inputGasPrice.validationError === "" && (!root.suggestedFees.eip1559Enabled || (root.suggestedFees.eip1559Enabled && inputPerGasTipLimit.validationError === ""))
}
StyledText {
id: prioritytext
anchors.top: parent.top
anchors.left: parent.left
text: root.suggestedFees.eip1559Enabled ? qsTr("Priority") : qsTr("Gas Price")
font.weight: Font.Medium
font.pixelSize: 13
color: Style.current.textColor
visible: root.suggestedFees.eip1559Enabled && advancedMode
}
StyledText {
id: baseFeeText
visible: root.suggestedFees.eip1559Enabled && advancedMode
anchors.top: parent.top
anchors.left: prioritytext.right
anchors.leftMargin: Style.current.smallPadding
text: qsTr("Current base fee: %1 %2").arg(root.suggestedFees.baseFee).arg("Gwei")
font.weight: Font.Medium
font.pixelSize: 13
color: Style.current.secondaryText
}
StatusButton {
anchors.verticalCenter: prioritytext.verticalCenter
anchors.right: parent.right
anchors.rightMargin: Style.current.bigPadding
height: 22
verticalPadding: 2
size: StatusBaseButton.Size.Tiny
visible: root.suggestedFees.eip1559Enabled
text: advancedMode ?
qsTr("Use suggestions") :
qsTr("Use custom")
font.pixelSize: 13
onClicked: advancedMode = !advancedMode
}
Row {
id: selectorButtons
visible: root.suggestedFees.eip1559Enabled && !advancedMode
anchors.top: prioritytext.bottom
visible: !root.advancedOrCustomMode
anchors.top: parent.top
anchors.topMargin: Style.current.halfPadding
spacing: 11
GasSelectorButton {
id: lowGasButton
objectName: "GasSelector_lowGasButton"
primaryText: qsTr("Low")
gasLimit: inputGasLimit ? inputGasLimit.text : ""
getGasEthValue: root.getGasEthValue
getFiatValue: root.getFiatValue
defaultCurrency: root.defaultCurrency
price: {
if (!root.suggestedFees.eip1559Enabled) return root.suggestedFees.gasPrice;
return formatDec(root.suggestedFees.maxFeePerGasL, 6)
}
onCheckedChanged: {
if(checked) {
if (root.suggestedFees.eip1559Enabled){
inputPerGasTipLimit.text = formatDec(root.suggestedFees.maxPriorityFeePerGas, 2);
inputGasPrice.text = formatDec(root.suggestedFees.maxFeePerGasL, 2);
} else {
inputGasPrice.text = price
}
root.updateGasEthValue()
root.checkLimits()
}
}
ButtonGroup {
buttons: gasPrioRepeater.children
}
GasSelectorButton {
id: optimalGasButton
objectName: "GasSelector_optimalGasButton"
primaryText: qsTr("Optimal")
price: {
if (!root.suggestedFees.eip1559Enabled) {
// Setting the gas price field here because the binding didn't work
inputGasPrice.text = root.suggestedFees.gasPrice
return root.suggestedFees.gasPrice
}
return formatDec(root.suggestedFees.maxFeePerGasM, 6)
}
gasLimit: inputGasLimit ? inputGasLimit.text : ""
getGasEthValue: root.getGasEthValue
getFiatValue: root.getFiatValue
defaultCurrency: root.defaultCurrency
onCheckedChanged: {
if(checked) {
if (root.suggestedFees.eip1559Enabled){
inputPerGasTipLimit.text = formatDec(root.suggestedFees.maxPriorityFeePerGas, 2);
inputGasPrice.text = formatDec(root.suggestedFees.maxFeePerGasM, 2);
} else {
inputGasPrice.text = root.suggestedFees.gasPrice
Repeater {
id: gasPrioRepeater
model: root.estimatedGasFeesTime
GasSelectorButton {
objectName: "GasSelector_slowGasButton"
property double totalFeesInFiat: parseFloat(root.getFiatValue(modelData.totalFeesInEth, "ETH", currentCurrency)) +
parseFloat(root.getFiatValue(modelData.totalTokenFees, root.selectedTokenSymbol, currentCurrency))
primaryText: index === 0 ? qsTr("Slow") : index === 1 ? qsTr("Optimal"): qsTr("Fast")
timeText: index === selectedPriority ? d.getLabelForEstimatedTxTime(modelData.totalTime): qsTr("~ Unknown")
totalGasEthValue: modelData.totalFeesInEth
totalGasFiatValue: index === selectedPriority ? "%1 %2".arg(LocaleUtils.numberToLocaleString(totalFeesInFiat)).arg(root.currentCurrency.toUpperCase()): qsTr("...")
checked: index === selectedPriority
onCheckedChanged: {
if(checked) {
root.selectedPriority = index
root.selectedGasEthValue = totalGasEthValue
root.selectedGasFiatValue = totalGasFiatValue
root.selectedTimeEstimate = timeText
}
root.updateGasEthValue()
root.checkLimits()
}
}
}
GasSelectorButton {
id: highGasButton
objectName: "GasSelector_highGasButton"
primaryText: qsTr("High")
price: {
if (!root.suggestedFees.eip1559Enabled) return root.suggestedFees.gasPrice;
return formatDec(root.suggestedFees.maxFeePerGasH,6);
}
gasLimit: inputGasLimit ? inputGasLimit.text : ""
getGasEthValue: root.getGasEthValue
getFiatValue: root.getFiatValue
defaultCurrency: root.defaultCurrency
onCheckedChanged: {
if(checked) {
if (root.suggestedFees.eip1559Enabled){
inputPerGasTipLimit.text = formatDec(root.suggestedFees.maxPriorityFeePerGas, 2);
inputGasPrice.text = formatDec(root.suggestedFees.maxFeePerGasH, 2);
} else {
inputGasPrice.text = price
}
root.updateGasEthValue()
root.checkLimits()
}
}
}
}
Item {
id: advancedModeItemGroup
anchors.top: prioritytext.bottom
anchors.topMargin: 14
visible: !root.suggestedFees.eip1559Enabled || root.advancedMode
Column {
id: advancedGasSelector
width: parent.width
height: childrenRect.height
anchors.top: parent.top
anchors.topMargin: Style.current.halfPadding
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 10
visible: root.advancedOrCustomMode
Input {
id: inputGasLimit
label: qsTr("Gas amount limit")
text: "21000"
inputLabel.color: Style.current.secondaryText
customHeight: 56
anchors.top: parent.top
anchors.left: parent.left
anchors.right: root.suggestedFees.eip1559Enabled ? inputPerGasTipLimit.left : inputGasPrice.left
anchors.rightMargin: Style.current.padding
placeholderText: "21000"
validator: IntValidator{
bottom: 1
}
validationErrorAlignment: TextEdit.AlignRight
validationErrorTopMargin: 8
onTextChanged: {
if (root.validate()) {
root.updateGasEthValue()
root.checkLimits()
}
}
}
spacing: Style.current.halfPadding
Input {
id: inputPerGasTipLimit
label: qsTr("Per-gas tip limit")
inputLabel.color: Style.current.secondaryText
anchors.top: parent.top
anchors.right: inputGasPrice.left
anchors.rightMargin: Style.current.padding
visible: root.suggestedFees.eip1559Enabled
width: 125
customHeight: 56
text: formatDec(root.suggestedFees.maxPriorityFeePerGas, 2);
placeholderText: "20"
onTextChanged: {
if (root.validate()) {
root.updateGasEthValue()
root.checkLimits()
}
}
}
StyledText {
color: Style.current.secondaryText
text: qsTr("Gwei")
visible: root.suggestedFees.eip1559Enabled
anchors.top: parent.top
anchors.topMargin: 42
anchors.right: inputPerGasTipLimit.right
anchors.rightMargin: Style.current.padding
font.pixelSize: 15
}
Input {
id: inputGasPrice
textField.objectName: "gasPriceSelectorInput"
label: qsTr("Per-gas overall limit")
inputLabel.color: Style.current.secondaryText
anchors.top: parent.top
anchors.right: parent.right
width: 125
customHeight: 56
placeholderText: "20"
onTextChanged: {
if (root.validate()) {
root.updateGasEthValue()
root.checkLimits()
}
}
}
StyledText {
color: Style.current.secondaryText
text: qsTr("Gwei")
anchors.top: parent.top
anchors.topMargin: 42
anchors.right: inputGasPrice.right
anchors.rightMargin: Style.current.padding
font.pixelSize: 15
}
StyledText {
id: errorsText
text: ""
width: parent.width - Style.current.padding
visible: text != ""
height: visible ? undefined : 0
anchors.top: inputGasLimit.bottom
anchors.topMargin: Style.current.smallPadding + 5
font.pixelSize: 13
textFormat: Text.RichText
color: Style.current.secondaryText
wrapMode: Text.WordWrap
}
StyledText {
id: maxPriorityFeeText
anchors.left: parent.left
visible: root.suggestedFees.eip1559Enabled
text: {
let v = selectedGasEthValue > 0.00009 ? selectedGasEthValue :
(selectedGasEthValue < 0.000001 ? "0.000000..." : selectedGasEthValue.toFixed(6))
return qsTr("Maximum priority fee: %1 ETH").arg(v)
}
anchors.top: errorsText.bottom
anchors.topMargin: Style.current.smallPadding + 5
font.pixelSize: 13
color: Style.current.textColor
}
StyledText {
id: maxPriorityFeeFiatText
text: root.maxFiatFees
visible: root.suggestedFees.eip1559Enabled
anchors.verticalCenter: maxPriorityFeeText.verticalCenter
anchors.left: maxPriorityFeeText.right
anchors.leftMargin: 6
color: Style.current.secondaryText
anchors.topMargin: 19
font.pixelSize: 13
}
StyledText {
id: maxPriorityFeeDetailsText
text: qsTr("Maximum overall price for the transaction. If the block base fee exceeds this, it will be included in a following block with a lower base fee.")
visible: root.suggestedFees.eip1559Enabled
StatusSwitchTabBar {
id: tabBar
width: parent.width
anchors.top: maxPriorityFeeText.bottom
anchors.topMargin: Style.current.smallPadding
font.pixelSize: 13
color: Style.current.secondaryText
wrapMode: Text.WordWrap
anchors.horizontalCenter: parent.horizontalCenter
visible: root.advancedOrCustomMode
StatusSwitchTabButton {
text: qsTr("Slow")
}
StatusSwitchTabButton {
text: qsTr("Optimal")
}
StatusSwitchTabButton {
text: qsTr("Fast")
}
currentIndex: GasSelector.Priority.OPTIMAL
onCurrentIndexChanged: {
root.selectedPriority = currentIndex
if(gasPrioRepeater.count === 3) {
root.selectedGasFiatValue = gasPrioRepeater.itemAt(currentIndex).totalGasFiatValue
root.selectedGasEthValue = gasPrioRepeater.itemAt(currentIndex).totalGasEthValue
root.selectedTimeEstimate = gasPrioRepeater.itemAt(currentIndex).timeText
}
}
}
// Normal transaction
Repeater {
model: root.bestRoutes
StatusListItem {
id: listItem
color: Theme.palette.statusListItem.backgroundColor
width: parent.width
asset.name: index == 0 ? "tiny/gas" : ""
title: qsTr("%1 transaction fee").arg(modelData.fromNetwork.chainName)
subTitle: "%1 eth".arg(LocaleUtils.numberToLocaleString(parseFloat(totalGasAmount)))
property string totalGasAmount : {
let maxFees = (tabBar.currentIndex === GasSelector.Priority.SLOW) ? modelData.gasFees.maxFeePerGasL :
(tabBar.currentIndex === GasSelector.Priority.OPTIMAL) ?
modelData.gasFees.maxFeePerGasM : modelData.gasFees.maxFeePerGasH
let gasPrice = modelData.gasFees.eip1559Enabled ? maxFees : modelData.gasFees.gasPrice
return root.getGasEthValue(gasPrice , modelData.gasAmount)
}
statusListItemSubTitle.width: listItem.width/2 - Style.current.smallPadding
statusListItemSubTitle.elide: Text.ElideMiddle
statusListItemSubTitle.wrapMode: Text.NoWrap
components: [
StatusBaseText {
Layout.alignment: Qt.AlignRight
text: "%1%2".arg(currentCurrencySymbol).arg(LocaleUtils.numberToLocaleString(parseFloat(root.getFiatValue(totalGasAmount, "ETH", root.currentCurrency))))
font.pixelSize: 15
color: Theme.palette.baseColor1
width: listItem.width/2 - Style.current.padding
elide: Text.ElideRight
}
]
}
}
// Bridge
Repeater {
model: root.bestRoutes
StatusListItem {
id: listItem2
color: Theme.palette.statusListItem.backgroundColor
width: parent.width
asset.name: index == 0 ? "tiny/bridge" : ""
title: qsTr("%1 -> %2 bridge").arg(modelData.fromNetwork.chainName).arg(modelData.toNetwork.chainName)
subTitle: "%1 %2".arg(LocaleUtils.numberToLocaleString(modelData.tokenFees)).arg(root.selectedTokenSymbol)
visible: modelData.bridgeName !== "Simple"
statusListItemSubTitle.width: 100//parent.width - Style.current.smallPadding
statusListItemSubTitle.elide: Text.ElideMiddle
components: [
StatusBaseText {
Layout.alignment: Qt.AlignRight
text: "%1%2".arg(currentCurrencySymbol).arg(LocaleUtils.numberToLocaleString(parseFloat(root.getFiatValue(modelData.tokenFees, root.selectedTokenSymbol, root.currentCurrency))))
font.pixelSize: 15
color: Theme.palette.baseColor1
width: listItem2.width/2 - Style.current.padding
elide: Text.ElideRight
}
]
}
}
}
}

View File

@ -4,62 +4,41 @@ import QtQuick.Layouts 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import utils 1.0
import "../status"
import "../"
import "../panels"
// TODO: use StatusQ components here
Column {
id: root
visible: !isValid
visible: !isValid || isLoading
spacing: 5
property alias errorMessage: txtValidationError.text
property var selectedAccount
property double selectedAmount
property var selectedAsset
property double selectedGasEthValue
property var selectedNetwork
property bool isValid: false
property bool isValid: true
property bool isLoading: false
onSelectedAccountChanged: validate()
onSelectedAmountChanged: validate()
onSelectedAssetChanged: validate()
onSelectedGasEthValueChanged: validate()
onSelectedNetworkChanged: validate()
function validate() {
let isValid = false
if (!(selectedAccount && selectedAccount.assets && selectedAsset && selectedGasEthValue > 0)) {
return root.isValid
}
let gasTotal = selectedGasEthValue
if (selectedAsset && selectedAsset.symbol && selectedAsset.symbol.toUpperCase() === "ETH") {
gasTotal += selectedAmount
}
const chainId = selectedNetwork && selectedNetwork.chainId
const currAcctGasAsset = Utils.findAssetByChainAndSymbol(chainId, selectedAccount.assets, "ETH")
if (currAcctGasAsset && currAcctGasAsset.totalBalance > gasTotal) {
isValid = true
}
root.isValid = isValid
return isValid
}
StatusIcon {
anchors.horizontalCenter: parent.horizontalCenter
height: 20
width: 20
icon: "cancel"
color: Theme.palette.dangerColor1
visible: !isValid && !isLoading
}
StatusLoadingIndicator {
anchors.horizontalCenter: parent.horizontalCenter
width: 24
height: 24
color: Theme.palette.baseColor1
visible: isLoading && isValid
}
StyledText {
id: txtValidationError
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Not enough ETH for gas")
text: isLoading? qsTr("Calculating fees"): qsTr("Balance exceeded")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 13

View File

@ -11,27 +11,10 @@ import utils 1.0
StatusRadioButton {
id: gasRectangle
property string primaryText: qsTr("Low")
property string gasLimit
property string defaultCurrency: "USD"
property double price: 1
property var getGasEthValue: function () {}
property var getFiatValue: function () {}
function formatDec(num, dec){
return Math.round((num + Number.EPSILON) * Math.pow(10, dec)) / Math.pow(10, dec)
}
QtObject {
id: d
property double fiatValue: getFiatValue(ethValue, "ETH", defaultCurrency)
property double ethValue: {
if (!gasLimit) {
return 0
}
return formatDec(parseFloat(getGasEthValue(gasRectangle.price, gasLimit)), 6)
}
}
property string primaryText
property string timeText
property string totalGasFiatValue
property double totalGasEthValue
width: contentItem.implicitWidth
@ -64,15 +47,17 @@ StatusRadioButton {
}
StatusBaseText {
id: secondaryLabel
Layout.maximumWidth: card.width - Style.current.smallPadding
font.pixelSize: 13
font.weight: Font.Medium
text: d.ethValue + " ETH"
text: gasRectangle.totalGasFiatValue
color: Theme.palette.primaryColor1
elide: Text.ElideRight
}
StatusBaseText {
id: tertiaryText
font.pixelSize: 10
text: d.fiatValue + " " + gasRectangle.defaultCurrency.toUpperCase()
text: gasRectangle.timeText
color: Theme.palette.directColor5
}
}

View File

@ -125,14 +125,14 @@ Item {
// TODO: move this out of StatusQ, this involves dependency on BE code
// WARNING: Wrong ComboBox value processing. Check `StatusAccountSelector` for more info.
root.userSelectedToken = symbol
root.selectedAsset = {name: name, symbol: symbol, totalBalance: totalBalance, totalCurrencyBalance: totalCurrencyBalance, balances: balances}
root.selectedAsset = {name: name, symbol: symbol, totalBalance: totalBalance, totalCurrencyBalance: totalCurrencyBalance, balances: balances, decimals: decimals}
}
// TODO: move this out of StatusQ, this involves dependency on BE code
// WARNING: Wrong ComboBox value processing. Check `StatusAccountSelector` for more info.
Component.onCompleted: {
if ((userSelectedToken === "" && index === 0) || symbol === userSelectedToken)
root.selectedAsset = { name: name, symbol: symbol, totalBalance: totalBalance, totalCurrencyBalance: totalCurrencyBalance, balances: balances}
root.selectedAsset = { name: name, symbol: symbol, totalBalance: totalBalance, totalCurrencyBalance: totalCurrencyBalance, balances: balances, decimals: decimals}
}
contentItem: RowLayout {

View File

@ -47,7 +47,8 @@ Popup {
text: qsTr("Send transaction")
icon.color: Style.current.purple
icon.name: "send"
onClicked: root.sendTransactionCommandButtonClicked()
// this flow doesnt work, commenting it out till it is worked on
// onClicked: root.sendTransactionCommandButtonClicked()
}
@ -56,7 +57,8 @@ Popup {
icon.color: Style.current.orange
icon.name: "send"
icon.rotation: 180
onClicked: root.receiveTransactionCommandButtonClicked()
// this flow doesnt work, commenting it out till it is worked on
// onClicked: root.receiveTransactionCommandButtonClicked()
}
}
}

View File

@ -22,13 +22,21 @@ import "../views"
StatusDialog {
id: popup
property alias addressText: recipientSelector.input.text
property string preSelectedRecipient
property string preDefinedAmountToSend
property var preSelectedAsset
property bool interactive: true
property var store
property var contactsStore
property alias modalHeader: modalHeader.text
property alias selectedPriority: gasSelector.selectedPriority
property var store: TransactionStore{}
property var contactsStore: store.contactStore
property var selectedAccount: store.currentAccount
property var preSelectedRecipient
property bool launchedFromChat: false
property var bestRoutes
property string addressText
property bool isLoading: false
property int sendType: Constants.SendType.Transfer
property MessageDialog sendingError: MessageDialog {
id: sendingError
title: qsTr("Error sending the transaction")
@ -36,63 +44,91 @@ StatusDialog {
standardButtons: StandardButton.Ok
}
function sendTransaction() {
property var sendTransaction: function() {
let recipientAddress = Utils.isValidAddress(popup.addressText) ? popup.addressText : d.resolvedENSAddress
d.isPending = true
d.isPendingTx = true
popup.store.authenticateAndTransfer(
popup.selectedAccount.address,
recipientAddress,
assetSelector.selectedAsset.symbol,
amountToSendInput.text,
gasSelector.selectedGasLimit,
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
networkSelector.selectedNetwork.chainId,
d.uuid,
gasSelector.suggestedFees.eip1559Enabled,
gasSelector.selectedPriority,
JSON.stringify(popup.bestRoutes)
)
}
property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function(disabledChainIds) {
if (disabledChainIds === undefined) disabledChainIds = []
networkSelector.suggestedRoutes = popup.store.suggestedRoutes(
popup.selectedAccount.address, amountToSendInput.text, assetSelector.selectedAsset.symbol, disabledChainIds
)
if (networkSelector.suggestedRoutes.length) {
networkSelector.selectedNetwork = networkSelector.suggestedRoutes[0]
gasSelector.suggestedFees = popup.store.suggestedFees(networkSelector.suggestedRoutes[0].chainId)
gasSelector.checkOptimal()
gasSelector.visible = true
} else {
networkSelector.selectedNetwork = ""
gasSelector.visible = false
property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() {
d.sendTxError = false
if(popup.selectedAccount && assetSelector.selectedAsset) {
popup.isLoading = true
let amount = parseFloat(amountToSendInput.text) * Math.pow(10, assetSelector.selectedAsset.decimals)
popup.store.suggestedRoutes(popup.selectedAccount.address, amount.toString(16), assetSelector.selectedAsset.symbol,
store.disabledChainIdsFromList, store.disabledChainIdsToList,
d.preferredChainIds, gasSelector.selectedPriority, popup.sendType)
}
})
enum StackGroup {
SendDetailsGroup = 0
function setSendTxError() {
d.sendTxError = true
d.sendTxErrorString = qsTr("Wrong password")
}
QtObject {
id: d
readonly property string maxFiatBalance: Utils.stripTrailingZeros(parseFloat(assetSelector.selectedAsset.totalBalance).toFixed(4))
readonly property double maxFiatBalance: {
console.error(assetSelector.selectedAsset.name," >>> recalaculayte maxFiatBalance = ", assetSelector.selectedAsset.totalBalance)
return assetSelector.selectedAsset ? assetSelector.selectedAsset.totalBalance: 0
}
readonly property bool isReady: amountToSendInput.valid && !amountToSendInput.pending && recipientReady
readonly property bool errorMode: networkSelector.suggestedRoutes && networkSelector.suggestedRoutes.length <= 0 || networkSelector.errorMode
readonly property bool errorMode: (networkSelector.bestRoutes && networkSelector.bestRoutes.length <= 0) || networkSelector.errorMode || isNaN(amountToSendInput.text)
readonly property bool recipientReady: (isAddressValid || isENSValid) && !recipientSelector.isPending
readonly property bool isAddressValid: Utils.isValidAddress(recipientSelector.input.text)
property bool isAddressValid: false
property bool isENSValid: false
readonly property var resolveENS: Backpressure.debounce(popup, 500, function (ensName) {
store.resolveENS(ensName)
})
property string resolvedENSAddress
onIsReadyChanged: {
if(!isReady && isLastGroup)
stack.currentIndex = SendModal.StackGroup.SendDetailsGroup
}
readonly property string uuid: Utils.uuid()
readonly property bool isLastGroup: stack.currentIndex === (stack.count - 1)
property bool isPending: false
property bool isPendingTx: false
property var preferredChainIds: []
property bool sendTxError: false
property string sendTxErrorString
property Timer waitTimer: Timer {
interval: 1000
onTriggered: {
d.isAddressValid = false
let splitWords = popup.store.plainText(recipientSelector.input.text).split(':')
let editedText = ""
for(var i=0; i<splitWords.length; i++) {
if(splitWords[i].startsWith("0x")) {
d.isAddressValid = Utils.isValidAddress(splitWords[i])
popup.addressText = splitWords[i]
editedText += splitWords[i]
} else {
let chainColor = popup.store.allNetworks.getNetworkColor(splitWords[i])
if(!!chainColor) {
d.addPreferredChain(popup.store.allNetworks.getNetworkChainId(splitWords[i]))
editedText += `<span style='color: %1'>%2</span>`.arg(chainColor).arg(splitWords[i])+':'
}
}
}
editedText +="</a></p>"
recipientSelector.input.text = editedText
popup.recalculateRoutesAndFees()
}
}
function addPreferredChain(chainID) {
if(!chainID)
return
if(preferredChainIds.includes(chainID))
return
preferredChainIds.push(chainID)
}
}
width: 556
@ -106,14 +142,22 @@ StatusDialog {
onSelectedAccountChanged: popup.recalculateRoutesAndFees()
onOpened: {
popup.store.disabledChainIdsFromList = []
popup.store.disabledChainIdsToList = []
amountToSendInput.input.edit.forceActiveFocus()
if(popup.launchedFromChat) {
recipientSelector.input.edit.readOnly = true
recipientSelector.input.text = popup.preSelectedRecipient.name
if(!!popup.preSelectedAsset) {
assetSelector.selectedAsset = popup.preSelectedAsset
}
popup.recalculateRoutesAndFees()
if(!!popup.preDefinedAmountToSend) {
amountToSendInput.text = popup.preDefinedAmountToSend
}
if(!!popup.preSelectedRecipient) {
recipientSelector.input.text = popup.preSelectedRecipient
d.waitTimer.restart()
}
}
header: SendModalHeader {
@ -152,7 +196,6 @@ StatusDialog {
color: Theme.palette.dropShadow
}
Column {
id: assetAndAmmountSelector
anchors.left: parent.left
@ -164,6 +207,7 @@ StatusDialog {
Row {
spacing: 16
StatusBaseText {
id: modalHeader
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Send")
font.pixelSize: 15
@ -172,11 +216,11 @@ StatusDialog {
StatusListItemTag {
height: 22
width: childrenRect.width
title: assetSelector.selectedAsset.totalBalance > 0 ? qsTr("Max: %1").arg(assetSelector.selectedAsset ? d.maxFiatBalance : "0.00") : qsTr("No balances active")
title: d.sendTxError ? d.sendTxErrorString : d.maxFiatBalance > 0 ? qsTr("Max: %1").arg(LocaleUtils.numberToLocaleString(d.maxFiatBalance)) : qsTr("No balances active")
closeButtonVisible: false
titleText.font.pixelSize: 12
color: d.errorMode ? Theme.palette.dangerColor2 : Theme.palette.primaryColor3
titleText.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
color: d.errorMode || d.sendTxError ? Theme.palette.dangerColor2 : Theme.palette.primaryColor3
titleText.color: d.errorMode || d.sendTxError ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
}
}
Item {
@ -188,13 +232,14 @@ StatusDialog {
anchors.left: parent.left
anchors.leftMargin: -Style.current.padding
width: parent.width - assetSelector.width
placeholderText: "0.00 %1".arg(assetSelector.selectedAsset.symbol)
placeholderText: assetSelector.selectedAsset ? "%1 %2".arg(LocaleUtils.numberToLocaleString(0, 2)).arg(assetSelector.selectedAsset.symbol) : ""
input.edit.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.directColor1
input.edit.readOnly: !popup.interactive
validators: [
StatusFloatValidator{
id: floatValidator
bottom: 0
top: d.maxFiatBalance
top: assetSelector.selectedAsset ? assetSelector.selectedAsset.totalBalance: 0
errorMessage: ""
}
]
@ -204,12 +249,6 @@ StatusDialog {
if (!Utils.containsOnlyDigits(amount) || isNaN(amount)) {
return
}
if (amount === "") {
txtFiatBalance.text = "0.00"
} else {
txtFiatBalance.text = popup.store.getFiatValue(amount, assetSelector.selectedAsset.symbol, popup.store.currentCurrency)
}
gasSelector.estimateGas()
popup.recalculateRoutesAndFees()
}
}
@ -217,7 +256,8 @@ StatusDialog {
id: assetSelector
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
assets: popup.selectedAccount.assets
enabled: popup.interactive
assets: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : []
defaultToken: Style.png("tokens/DEFAULT-TOKEN@3x")
getCurrencyBalanceString: function (currencyBalance) {
return "%1 %2".arg(Utils.toLocaleString(currencyBalance.toFixed(2), popup.store.locale, {"currency": true})).arg(popup.store.currentCurrency.toUpperCase())
@ -238,8 +278,6 @@ StatusDialog {
if (amountToSendInput.text === "" || isNaN(amountToSendInput.text)) {
return
}
txtFiatBalance.text = popup.store.getFiatValue(amountToSendInput.text, assetSelector.selectedAsset.symbol, popup.store.currentCurrency)
gasSelector.estimateGas()
popup.recalculateRoutesAndFees()
}
}
@ -261,8 +299,11 @@ StatusDialog {
input.background.color: "transparent"
input.background.border.width: 0
input.edit.color: txtFiatBalance.input.edit.activeFocus ? Theme.palette.directColor1 : Theme.palette.baseColor1
text: "0.00"
placeholderText: "0.00"
input.edit.readOnly: true
text: {
let fiatValue = popup.store.getFiatValue(amountToSendInput.text, assetSelector.selectedAsset.symbol, popup.store.currentCurrency)
return parseFloat(fiatValue) === 0 ? LocaleUtils.numberToLocaleString(parseFloat(fiatValue), 2) : LocaleUtils.numberToLocaleString(parseFloat(fiatValue))
}
input.implicitHeight: 15
implicitWidth: txtFiatBalance.input.edit.contentWidth + 50
input.rightComponent: StatusBaseText {
@ -313,8 +354,10 @@ StatusDialog {
input.background.color: Theme.palette.indirectColor1
input.background.border.width: 0
input.implicitHeight: 56
input.clearable: true
input.clearable: popup.interactive
input.edit.readOnly: !popup.interactive
multiline: false
input.edit.textFormat: TextEdit.RichText
input.rightComponent: RowLayout {
StatusButton {
visible: recipientSelector.text === ""
@ -333,10 +376,14 @@ StatusDialog {
icon.height: 16
icon.color: Theme.palette.baseColor1
backgroundHoverColor: "transparent"
onClicked: recipientSelector.input.edit.clear()
onClicked: {
recipientSelector.input.edit.clear()
d.waitTimer.restart()
}
}
}
Keys.onReleased: {
d.waitTimer.restart()
if(!d.isAddressValid) {
isPending = true
Qt.callLater(d.resolveENS, input.edit.text)
@ -365,6 +412,7 @@ StatusDialog {
store: popup.store
onContactSelected: {
recipientSelector.input.text = address
d.waitTimer.restart()
}
visible: !d.recipientReady
}
@ -377,17 +425,16 @@ StatusDialog {
anchors.leftMargin: Style.current.bigPadding
anchors.rightMargin: Style.current.bigPadding
store: popup.store
interactive: popup.interactive
selectedAccount: popup.selectedAccount
amountToSend: isNaN(parseFloat(amountToSendInput.text)) ? 0 : parseFloat(amountToSendInput.text)
requiredGasInEth: gasSelector.selectedGasEthValue
assets: popup.selectedAccount.assets
selectedAsset: assetSelector.selectedAsset
onNetworkChanged: function(chainId) {
gasSelector.suggestedFees = popup.store.suggestedFees(chainId)
gasSelector.updateGasEthValue()
}
onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees(disabledChainIds)
onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees()
visible: d.recipientReady
isLoading: popup.isLoading
bestRoutes: popup.bestRoutes
}
Rectangle {
@ -418,62 +465,46 @@ StatusDialog {
Column {
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.preferredWidth: fees.width - feesIcon.width - Style.current.xlPadding
StatusBaseText {
id: text
width: 410
font.pixelSize: 15
font.weight: Font.Medium
color: Theme.palette.directColor1
text: qsTr("Fees")
wrapMode: Text.WordWrap
Item {
width: parent.width
height: childrenRect.height
StatusBaseText {
id: text
anchors.left: parent.left
font.pixelSize: 15
font.weight: Font.Medium
color: Theme.palette.directColor1
text: qsTr("Fees")
wrapMode: Text.WordWrap
}
StatusBaseText {
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
id: totalFeesAdvanced
text: popup.isLoading ? "..." : gasSelector.selectedGasFiatValue
font.pixelSize: 15
color: Theme.palette.directColor1
visible: networkSelector.advancedOrCustomMode && popup.bestRoutes.length > 0
}
}
GasSelector {
id: gasSelector
width: parent.width
getGasEthValue: popup.store.getGasEthValue
getFiatValue: popup.store.getFiatValue
getEstimatedTime: popup.store.getEstimatedTime
defaultCurrency: popup.store.currentCurrency
chainId: networkSelector.selectedNetwork && networkSelector.selectedNetwork.chainId ? networkSelector.selectedNetwork.chainId : 1
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
if (!(popup.selectedAccount && popup.selectedAccount.address &&
popup.addressText && assetSelector.selectedAsset &&
assetSelector.selectedAsset.symbol && amountToSendInput.text)) {
selectedGasLimit = 250000
defaultGasLimit = selectedGasLimit
return
}
var chainID = networkSelector.selectedNetwork ? networkSelector.selectedNetwork.chainId: 1
var recipientAddress = popup.launchedFromChat ? popup.preSelectedRecipient.address : popup.addressText
let gasEstimate = JSON.parse(popup.store.estimateGas(
popup.selectedAccount.address,
recipientAddress,
assetSelector.selectedAsset.symbol,
amountToSendInput.text,
chainID,
""))
if (!gasEstimate.success) {
console.warn("error estimating gas: ", gasEstimate.error.message)
return
}
selectedGasLimit = gasEstimate.result
defaultGasLimit = selectedGasLimit
})
currentCurrency: popup.store.currencyStore.currentCurrency
currentCurrencySymbol: popup.store.currencyStore.currentCurrencySymbol
visible: gasValidator.isValid && !popup.isLoading
advancedOrCustomMode: networkSelector.advancedOrCustomMode
bestRoutes: popup.bestRoutes
selectedTokenSymbol: assetSelector.selectedAsset ? assetSelector.selectedAsset.symbol: ""
onSelectedPriorityChanged: popup.recalculateRoutesAndFees()
}
GasValidator {
id: gasValidator
width: parent.width
selectedAccount: popup.selectedAccount
selectedAmount: amountToSendInput.text === "" ? 0.0 :
parseFloat(amountToSendInput.text)
selectedAsset: assetSelector.selectedAsset
selectedGasEthValue: gasSelector.selectedGasEthValue
selectedNetwork: networkSelector.selectedNetwork ? networkSelector.selectedNetwork: null
isLoading: popup.isLoading
isValid: popup.bestRoutes ? popup.bestRoutes.length > 0 : true
}
}
}
@ -484,38 +515,11 @@ StatusDialog {
}
footer: SendModalFooter {
maxFiatFees: gasSelector.maxFiatFees
estimatedTxTimeFlag: gasSelector.estimatedTxTimeFlag
pending: d.isPending
isLastGroup: d.isLastGroup
visible: d.isReady && !isNaN(parseFloat(amountToSendInput.text)) && gasValidator.isValid
onNextButtonClicked: {
if(gasSelector.suggestedFees.eip1559Enabled && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.suggestedFees.baseFee,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor,
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.suggestedFees.baseFee + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
if (isLastGroup) {
return popup.sendTransaction()
}
}
})
return
}
}
if (isLastGroup) {
return popup.sendTransaction()
}
}
maxFiatFees: popup.isLoading ? "..." : gasSelector.selectedGasFiatValue
selectedTimeEstimate: popup.isLoading? "..." : gasSelector.selectedTimeEstimate
pending: d.isPendingTx
visible: d.isReady && !isNaN(amountToSendInput.text) && gasValidator.isValid && !d.errorMode
onNextButtonClicked: popup.sendTransaction()
}
Component {
@ -523,47 +527,53 @@ StatusDialog {
TransactionSettingsConfirmationPopup {}
}
Connections {
target: popup.store.walletSectionTransactionsInst
onSuggestedRoutesReady: {
let response = JSON.parse(suggestedRoutes)
if(!!response.error) {
popup.isLoading = false
return
}
popup.bestRoutes = response.suggestedRoutes.best
gasSelector.estimatedGasFeesTime = response.suggestedRoutes.gasTimeEstimates
popup.isLoading = false
}
}
Connections {
target: popup.store.walletSectionTransactionsInst
onTransactionSent: {
d.isPending = false
d.isPendingTx = false
try {
let response = JSON.parse(txResult)
if (response.uuid !== d.uuid) return
if (!response.success) {
sendingError.text = response.result
if (Utils.isInvalidPasswordMessage(response.error)) {
d.sendTxError = true
d.sendTxErrorString = qsTr("Wrong password")
return
}
sendingError.text = response.error
return sendingError.open()
}
for(var i=0; i<popup.bestRoutes.length; i++) {
let txHash = response.result[popup.bestRoutes[i].fromNetwork.chainId]
let url = `${popup.store.getEtherscanLink()}/${txHash}`
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
}
let url = `${popup.store.getEtherscanLink()}/${response.result}`
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
popup.close()
} catch (e) {
console.error('Error parsing the response', e)
}
}
// Not Refactored Yet
// onTransactionCompleted: {
// if (success) {
// //% "Transaction completed"
// Global.toastMessage.title = qsTr("Wrong password")
// Global.toastMessage.source = Style.svg("check-circle")
// Global.toastMessage.iconColor = Style.current.success
// } else {
// //% "Transaction failed"
// Global.toastMessage.title = qsTr("Wrong password")
// Global.toastMessage.source = Style.svg("block-icon")
// Global.toastMessage.iconColor = Style.current.danger
// }
// Global.toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
// Global.toastMessage.open()
// }
}
}

View File

@ -34,32 +34,32 @@ StatusModal {
property string trxData: ""
property int chainId
property alias transactionSigner: transactionSigner
// property alias transactionSigner: transactionSigner
property var sendTransaction: function() {
stack.currentGroup.isPending = true
let success = false
success = root.store.transfer(
selectFromAccount.selectedAccount.address,
selectRecipient.selectedRecipient.address,
root.selectedAsset.symbol,
root.selectedAmount,
gasSelector.selectedGasLimit,
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword,
root.chainId,
stack.uuid,
gasSelector.suggestedFees.eip1559Enabled,
)
// TODO remove this else once the thread and connection are back
// if(!success){
// //% "Invalid transaction parameters"
// sendingError.text = qsTr("Invalid transaction parameters")
// sendingError.open()
// }
}
// property var sendTransaction: function() {
// stack.currentGroup.isPending = true
// let success = false
// success = root.store.transfer(
// selectFromAccount.selectedAccount.address,
// selectRecipient.selectedRecipient.address,
// root.selectedAsset.symbol,
// root.selectedAmount,
// gasSelector.selectedGasLimit,
// gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
// gasSelector.selectedTipLimit,
// gasSelector.selectedOverallLimit,
// transactionSigner.enteredPassword,
// root.chainId,
// stack.uuid,
// gasSelector.suggestedFees.eip1559Enabled,
// )
// // TODO remove this else once the thread and connection are back
//// if(!success){
//// //% "Invalid transaction parameters"
//// sendingError.text = qsTr("Invalid transaction parameters")
//// sendingError.open()
//// }
// }
property MessageDialog sendingError: MessageDialog {
id: sendingError
@ -69,296 +69,296 @@ StatusModal {
}
signal openGasEstimateErrorPopup(string message)
onClosed: {
stack.pop(groupPreview, StackView.Immediate)
}
// onClosed: {
// stack.pop(groupPreview, StackView.Immediate)
// }
onOpened: {
gasSelector.suggestedFees = root.store.suggestedFees(root.chainId)
gasSelector.checkOptimal()
}
// onOpened: {
// gasSelector.suggestedFees = root.store.suggestedFees(root.chainId)
// gasSelector.checkOptimal()
// }
contentItem: Item {
width: root.width
height: childrenRect.height
TransactionStackView {
id: stack
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
initialItem: groupPreview
isLastGroup: stack.currentGroup === groupSignTx
onGroupActivated: {
root.header.title = group.headerText
btnNext.text = group.footerText
}
TransactionFormGroup {
id: groupSelectAcct
headerText: {
// Not Refactored Yet
// if(trxData.startsWith("0x095ea7b3")){
// const approveData = JSON.parse(root.store.walletModelInst.tokensView.decodeTokenApproval(selectedRecipient.address, trxData))
// if(approveData.symbol)
// //% "Authorize %1 %2"
// return qsTr("Error sending the transaction").arg(approveData.amount).arg(approveData.symbol)
// contentItem: Item {
// width: root.width
// height: childrenRect.height
// TransactionStackView {
// id: stack
// anchors.leftMargin: Style.current.padding
// anchors.rightMargin: Style.current.padding
// initialItem: groupPreview
// isLastGroup: stack.currentGroup === groupSignTx
// onGroupActivated: {
// root.header.title = group.headerText
// btnNext.text = group.footerText
// }
// TransactionFormGroup {
// id: groupSelectAcct
// headerText: {
// // Not Refactored Yet
//// if(trxData.startsWith("0x095ea7b3")){
//// const approveData = JSON.parse(root.store.walletModelInst.tokensView.decodeTokenApproval(selectedRecipient.address, trxData))
//// if(approveData.symbol)
//// //% "Authorize %1 %2"
//// return qsTr("Error sending the transaction").arg(approveData.amount).arg(approveData.symbol)
//// }
// return qsTr("Send");
// }
// footerText: qsTr("Continue")
// showNextBtn: false
// onBackClicked: function() {
// if(validate()) {
// stack.pop()
// }
return qsTr("Send");
}
footerText: qsTr("Continue")
showNextBtn: false
onBackClicked: function() {
if(validate()) {
stack.pop()
}
}
StatusAccountSelector {
id: selectFromAccount
accounts: root.store.accounts
currency: root.store.currentCurrency
width: stack.width
selectedAccount: root.selectedAccount
label: qsTr("Choose account")
showBalanceForAssetSymbol: root.selectedAsset.symbol
chainId: root.chainId
minRequiredAssetBalance: parseFloat(root.selectedAmount)
onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() }
}
RecipientSelector {
id: selectRecipient
visible: false
accounts: root.store.accounts
contactsStore: root.contactsStore
selectedRecipient: root.selectedRecipient
selectedType: root.selectedType
readOnly: true
}
}
TransactionFormGroup {
id: groupSelectGas
headerText: qsTr("Network fee")
footerText: qsTr("Continue")
showNextBtn: false
onBackClicked: function() {
stack.pop()
}
GasSelector {
id: gasSelector
anchors.topMargin: Style.current.padding
getGasEthValue: root.store.getGasEthValue
getFiatValue: root.store.getFiatValue
defaultCurrency: root.store.currentCurrency
width: stack.width
// }
// StatusAccountSelector {
// id: selectFromAccount
// accounts: root.store.accounts
// currency: root.store.currentCurrency
// width: stack.width
// selectedAccount: root.selectedAccount
// label: qsTr("Choose account")
// showBalanceForAssetSymbol: root.selectedAsset.symbol
// chainId: root.chainId
// minRequiredAssetBalance: parseFloat(root.selectedAmount)
// onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() }
// }
// RecipientSelector {
// id: selectRecipient
// visible: false
// accounts: root.store.accounts
// contactsStore: root.contactsStore
// selectedRecipient: root.selectedRecipient
// selectedType: root.selectedType
// readOnly: true
// }
// }
// TransactionFormGroup {
// id: groupSelectGas
// headerText: qsTr("Network fee")
// footerText: qsTr("Continue")
// showNextBtn: false
// onBackClicked: function() {
// stack.pop()
// }
// GasSelector {
// id: gasSelector
// anchors.topMargin: Style.current.padding
// getGasEthValue: root.store.getGasEthValue
// getFiatValue: root.store.getFiatValue
// currentCurrency: root.store.currentCurrency
// width: stack.width
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address &&
selectRecipient.selectedRecipient && selectRecipient.selectedRecipient.address &&
root.selectedAsset && root.selectedAsset.symbol &&
root.selectedAmount)) {
selectedGasLimit = 250000
defaultGasLimit = selectedGasLimit
return
}
// property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
// if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address &&
// selectRecipient.selectedRecipient && selectRecipient.selectedRecipient.address &&
// root.selectedAsset && root.selectedAsset.symbol &&
// root.selectedAmount)) {
// selectedGasLimit = 250000
// defaultGasLimit = selectedGasLimit
// return
// }
let gasEstimate = JSON.parse(root.store.estimateGas(
selectFromAccount.selectedAccount.address,
selectRecipient.selectedRecipient.address,
root.selectedAsset.symbol,
root.selectedAmount,
root.chainId,
trxData
))
// let gasEstimate = JSON.parse(root.store.estimateGas(
// selectFromAccount.selectedAccount.address,
// selectRecipient.selectedRecipient.address,
// root.selectedAsset.symbol,
// root.selectedAmount,
// root.chainId,
// trxData
// ))
if (!gasEstimate.success) {
let message = qsTr("Error estimating gas: %1").arg(gasEstimate.error.message)
root.openGasEstimateErrorPopup(message);
return
}
selectedGasLimit = gasEstimate.result
defaultGasLimit = selectedGasLimit
})
}
GasValidator {
id: gasValidator
anchors.top: gasSelector.bottom
anchors.horizontalCenter: parent.horizontalCenter
selectedAccount: selectFromAccount.selectedAccount
selectedAmount: parseFloat(root.selectedAmount)
selectedAsset: root.selectedAsset
selectedGasEthValue: gasSelector.selectedGasEthValue
}
}
// if (!gasEstimate.success) {
// let message = qsTr("Error estimating gas: %1").arg(gasEstimate.error.message)
// root.openGasEstimateErrorPopup(message);
// return
// }
// selectedGasLimit = gasEstimate.result
// defaultGasLimit = selectedGasLimit
// })
// }
// GasValidator {
// id: gasValidator
// anchors.top: gasSelector.bottom
// anchors.horizontalCenter: parent.horizontalCenter
//// selectedAccount: selectFromAccount.selectedAccount
//// selectedAmount: parseFloat(root.selectedAmount)
//// selectedAsset: root.selectedAsset
//// selectedGasEthValue: gasSelector.selectedGasEthValue
// }
// }
TransactionFormGroup {
id: groupPreview
headerText: qsTr("Transaction preview")
footerText: qsTr("Sign with password")
showBackBtn: false
onNextClicked: function() {
stack.push(groupSignTx, StackView.Immediate)
}
isValid: groupSelectAcct.isValid && groupSelectGas.isValid && pvwTransaction.isValid
// TransactionFormGroup {
// id: groupPreview
// headerText: qsTr("Transaction preview")
// footerText: qsTr("Sign with password")
// showBackBtn: false
// onNextClicked: function() {
// stack.push(groupSignTx, StackView.Immediate)
// }
// isValid: groupSelectAcct.isValid && groupSelectGas.isValid && pvwTransaction.isValid
TransactionPreview {
id: pvwTransaction
width: stack.width
fromAccount: selectFromAccount.selectedAccount
gas: {
"value": gasSelector.selectedGasEthValue,
"symbol": "ETH",
"fiatValue": gasSelector.selectedGasFiatValue
}
toAccount: selectRecipient.selectedRecipient
asset: root.selectedAsset
amount: { "value": root.selectedAmount, "fiatValue": root.selectedFiatAmount }
currency: root.store.currentCurrency
isFromEditable: false
trxData: root.trxData
isGasEditable: true
fromValid: balanceValidator.isValid
gasValid: gasValidator.isValid
onFromClicked: { stack.push(groupSelectAcct, StackView.Immediate) }
onGasClicked: { stack.push(groupSelectGas, StackView.Immediate) }
}
BalanceValidator {
id: balanceValidator
anchors.top: pvwTransaction.bottom
anchors.horizontalCenter: parent.horizontalCenter
account: selectFromAccount.selectedAccount
amount: !!root.selectedAmount ? parseFloat(root.selectedAmount) : 0.0
chainId: root.chainId
asset: root.selectedAsset
}
GasValidator {
id: gasValidator2
anchors.top: balanceValidator.visible ? balanceValidator.bottom : pvwTransaction.bottom
anchors.topMargin: balanceValidator.visible ? 5 : 0
anchors.horizontalCenter: parent.horizontalCenter
selectedAccount: selectFromAccount.selectedAccount
selectedAmount: parseFloat(root.selectedAmount)
selectedAsset: root.selectedAsset
selectedGasEthValue: gasSelector.selectedGasEthValue
}
}
TransactionFormGroup {
id: groupSignTx
headerText: qsTr("Sign with password")
footerText: qsTr("Send %1 %2").arg(root.selectedAmount).arg(!!root.selectedAsset ? root.selectedAsset.symbol : "")
onBackClicked: function() {
stack.pop()
}
// TransactionPreview {
// id: pvwTransaction
// width: stack.width
// fromAccount: selectFromAccount.selectedAccount
// gas: {
// "value": gasSelector.selectedGasEthValue,
// "symbol": "ETH",
// "fiatValue": gasSelector.selectedGasFiatValue
// }
// toAccount: selectRecipient.selectedRecipient
// asset: root.selectedAsset
// amount: { "value": root.selectedAmount, "fiatValue": root.selectedFiatAmount }
// currency: root.store.currentCurrency
// isFromEditable: false
// trxData: root.trxData
// isGasEditable: true
// fromValid: balanceValidator.isValid
// gasValid: gasValidator.isValid
// onFromClicked: { stack.push(groupSelectAcct, StackView.Immediate) }
// onGasClicked: { stack.push(groupSelectGas, StackView.Immediate) }
// }
// BalanceValidator {
// id: balanceValidator
// anchors.top: pvwTransaction.bottom
// anchors.horizontalCenter: parent.horizontalCenter
// account: selectFromAccount.selectedAccount
// amount: !!root.selectedAmount ? parseFloat(root.selectedAmount) : 0.0
// chainId: root.chainId
// asset: root.selectedAsset
// }
// GasValidator {
// id: gasValidator2
// anchors.top: balanceValidator.visible ? balanceValidator.bottom : pvwTransaction.bottom
// anchors.topMargin: balanceValidator.visible ? 5 : 0
// anchors.horizontalCenter: parent.horizontalCenter
//// selectedAccount: selectFromAccount.selectedAccount
//// selectedAmount: parseFloat(root.selectedAmount)
//// selectedAsset: root.selectedAsset
//// selectedGasEthValue: gasSelector.selectedGasEthValue
// }
// }
// TransactionFormGroup {
// id: groupSignTx
// headerText: qsTr("Sign with password")
// footerText: qsTr("Send %1 %2").arg(root.selectedAmount).arg(!!root.selectedAsset ? root.selectedAsset.symbol : "")
// onBackClicked: function() {
// stack.pop()
// }
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: root.store.signingPhrase
}
}
}
}
// TransactionSigner {
// id: transactionSigner
// width: stack.width
// signingPhrase: root.store.signingPhrase
// }
// }
// }
// }
leftButtons: [
StatusBackButton {
id: btnBack
visible: stack.currentGroup.showBackBtn
enabled: stack.currentGroup.isValid || stack.isLastGroup
onClicked: {
if (typeof stack.currentGroup.onBackClicked === "function") {
return stack.currentGroup.onBackClicked()
}
stack.back()
}
}
]
// leftButtons: [
// StatusBackButton {
// id: btnBack
// visible: stack.currentGroup.showBackBtn
// enabled: stack.currentGroup.isValid || stack.isLastGroup
// onClicked: {
// if (typeof stack.currentGroup.onBackClicked === "function") {
// return stack.currentGroup.onBackClicked()
// }
// stack.back()
// }
// }
// ]
rightButtons: [
StatusButton {
id: btnNext
text: qsTr("Next")
enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending
visible: stack.currentGroup.showNextBtn
onClicked: {
const validity = stack.currentGroup.validate()
if (validity.isValid && !validity.isPending) {
if (stack.isLastGroup) {
return root.sendTransaction(gasSelector.selectedGasLimit,
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword)
}
// rightButtons: [
// StatusButton {
// id: btnNext
// text: qsTr("Next")
// enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending
// visible: stack.currentGroup.showNextBtn
// onClicked: {
// const validity = stack.currentGroup.validate()
// if (validity.isValid && !validity.isPending) {
// if (stack.isLastGroup) {
// return root.sendTransaction(gasSelector.selectedGasLimit,
// gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
// gasSelector.selectedTipLimit,
// gasSelector.selectedOverallLimit,
// transactionSigner.enteredPassword)
// }
if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === groupSelectGas && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.suggestedFees.baseFee,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor, // TODO:
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.suggestedFees.baseFee + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.next();
}
})
return
}
}
// if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === groupSelectGas && gasSelector.advancedMode){
// if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
// Global.openPopup(transactionSettingsConfirmationPopupComponent, {
// currentBaseFee: gasSelector.suggestedFees.baseFee,
// currentMinimumTip: gasSelector.perGasTipLimitFloor,
// currentAverageTip: gasSelector.perGasTipLimitAverage,
// tipLimit: gasSelector.selectedTipLimit,
// suggestedTipLimit: gasSelector.perGasTipLimitFloor, // TODO:
// priceLimit: gasSelector.selectedOverallLimit,
// suggestedPriceLimit: gasSelector.suggestedFees.baseFee + gasSelector.perGasTipLimitFloor,
// showPriceLimitWarning: gasSelector.showPriceLimitWarning,
// showTipLimitWarning: gasSelector.showTipLimitWarning,
// onConfirm: function(){
// stack.next();
// }
// })
// return
// }
// }
if (typeof stack.currentGroup.onNextClicked === "function") {
return stack.currentGroup.onNextClicked()
}
stack.next()
}
}
}
]
// if (typeof stack.currentGroup.onNextClicked === "function") {
// return stack.currentGroup.onNextClicked()
// }
// stack.next()
// }
// }
// }
// ]
Component {
id: transactionSettingsConfirmationPopupComponent
TransactionSettingsConfirmationPopup { }
}
// Component {
// id: transactionSettingsConfirmationPopupComponent
// TransactionSettingsConfirmationPopup { }
// }
Connections {
target: root.store.walletSectionTransactionsInst
onTransactionSent: {
try {
let response = JSON.parse(txResult)
if (response.uuid !== stack.uuid)
return
// Connections {
// target: root.store.walletSectionTransactionsInst
// onTransactionSent: {
// try {
// let response = JSON.parse(txResult)
// if (response.uuid !== stack.uuid)
// return
stack.currentGroup.isPending = false
// stack.currentGroup.isPending = false
let transactionId = response.result
// let transactionId = response.result
if (!response.success) {
if (Utils.isInvalidPasswordMessage(transactionId)){
transactionSigner.validationError = qsTr("Wrong password")
return
}
sendingError.text = transactionId
return sendingError.open()
}
// if (!response.success) {
// if (Utils.isInvalidPasswordMessage(transactionId)){
// transactionSigner.validationError = qsTr("Wrong password")
// return
// }
// sendingError.text = transactionId
// return sendingError.open()
// }
if(isARequest)
root.store.acceptRequestTransaction(transactionId, msgId, root.store.getPubkey() + transactionId.substr(2))
// if(isARequest)
// root.store.acceptRequestTransaction(transactionId, msgId, root.store.getPubkey() + transactionId.substr(2))
// Refactor this
let url = "" //`${walletModel.utilsView.etherscanLink}/${response.result}`
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
// // Refactor this
// let url = "" //`${walletModel.utilsView.etherscanLink}/${response.result}`
// Global.displayToastMessage(qsTr("Transaction pending..."),
// qsTr("View on etherscan"),
// "",
// true,
// Constants.ephemeralNotificationType.normal,
// url)
root.close()
} catch (e) {
console.error('Error parsing the response', e)
}
}
}
// root.close()
// } catch (e) {
// console.error('Error parsing the response', e)
// }
// }
// }
}

View File

@ -1,242 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQuick.Dialogs 1.3
import utils 1.0
import StatusQ.Controls 0.1
import shared.views 1.0
import shared.popups 1.0
import shared.stores 1.0
import shared.controls 1.0
// TODO: replace with StatusModal
ModalPopup {
id: root
property var ensUsernamesStore
property var contactsStore
property string ensUsername
property int chainId
readonly property var asset: {"name": "Ethereum", "symbol": "ETH"}
title: qsTr("Contract interaction")
property var estimateGasFunction: (function(userAddress) { return 0; })
property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled){ return ""; })
property var onSuccess: (function(){})
height: 540
function sendTransaction() {
try {
let responseStr = onSendTransaction(
selectFromAccount.selectedAccount.address,
gasSelector.selectedGasLimit,
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword,
gasSelector.suggestedFees.eip1559Enabled);
let response = JSON.parse(responseStr)
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)){
transactionSigner.validationError = qsTr("Wrong password")
return
}
sendingError.text = response.result
return sendingError.open()
}
onSuccess();
root.close();
} catch (e) {
console.error('Error sending the transaction', e)
sendingError.text = qsTr("Error sending the transaction: %1").arg(e.message);
return sendingError.open()
}
}
onOpened: {
gasSelector.suggestedFees = root.ensUsernamesStore.suggestedFees(root.chainId)
gasSelector.checkOptimal()
}
property MessageDialog sendingError: MessageDialog {
id: sendingError
title: qsTr("Error sending the transaction")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
TransactionStackView {
id: stack
height: parent.height
anchors.fill: parent
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
onGroupActivated: {
root.title = group.headerText
btnNext.text = group.footerText
}
TransactionFormGroup {
id: group1
headerText: root.title
footerText: qsTr("Continue")
StatusAccountSelector {
id: selectFromAccount
accounts: walletSectionAccounts.model
selectedAccount: {
const currAcc = walletSectionCurrent
if (currAcc.walletType !== Constants.watchWalletType) {
return currAcc
}
return null
}
currency: root.ensUsernamesStore.getCurrentCurrency()
width: stack.width
chainId: root.chainId
label: qsTr("Choose account")
showBalanceForAssetSymbol: "ETH"
minRequiredAssetBalance: 0
onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() }
}
GasSelector {
id: gasSelector
visible: true
anchors.top: selectFromAccount.bottom
anchors.topMargin: Style.current.padding
getGasEthValue: root.ensUsernamesStore.getGasEthValue
getEstimatedTime: root.ensUsernamesStore.getEstimatedTime
getFiatValue: root.ensUsernamesStore.getFiatValue
defaultCurrency: root.ensUsernamesStore.getCurrentCurrency()
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount);
gasSelector.selectedGasLimit = estimatedGas
return estimatedGas;
})
}
GasValidator {
id: gasValidator
anchors.top: gasSelector.bottom
anchors.horizontalCenter: parent.horizontalCenter
selectedAccount: selectFromAccount.selectedAccount
selectedAsset: root.asset
selectedAmount: 0
selectedGasEthValue: gasSelector.selectedGasEthValue
selectedNetwork: {
return {chainId: root.chainId}
}
}
}
TransactionFormGroup {
id: group3
headerText: root.title
footerText: qsTr("Sign with password")
TransactionPreview {
id: pvwTransaction
width: stack.width
fromAccount: selectFromAccount.selectedAccount
gas: {
"value": gasSelector.selectedGasEthValue,
"symbol": "ETH",
"fiatValue": gasSelector.selectedGasFiatValue
}
toAccount: { "address": root.ensUsernamesStore.getEnsRegisteredAddress(), "type": RecipientSelector.Type.Address }
asset: root.asset
currency: root.ensUsernamesStore.getCurrentCurrency()
amount: {
const fiatValue = root.ensUsernamesStore.getFiatValue(0, root.asset.symbol, currency)
return { "value": 0, "fiatValue": fiatValue }
}
}
}
TransactionFormGroup {
id: group4
headerText: root.title
footerText: qsTr("Sign with password")
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: root.ensUsernamesStore.getSigningPhrase()
}
}
}
footer: Item {
width: parent.width
height: btnNext.height
StatusBackButton {
id: btnBack
anchors.left: parent.left
visible: stack.currentGroup.showBackBtn
enabled: stack.currentGroup.isValid || stack.isLastGroup
onClicked: {
if (typeof stack.currentGroup.onBackClicked === "function") {
return stack.currentGroup.onBackClicked()
}
stack.back()
}
}
Component {
id: transactionSettingsConfirmationPopupComponent
TransactionSettingsConfirmationPopup {
}
}
StatusButton {
id: btnNext
anchors.right: parent.right
text: qsTr("Next")
enabled: stack.currentGroup.isValid
onClicked: {
const validity = stack.currentGroup.validate()
if (validity.isValid && !validity.isPending) {
if (stack.isLastGroup) {
return root.sendTransaction()
}
if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === group3 && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.suggestedFees.baseFee,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor,
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.suggestedFees.baseFee + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.next();
}
})
return
}
}
stack.next()
}
}
}
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -1,272 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQuick.Dialogs 1.3
import utils 1.0
import StatusQ.Controls 0.1
import shared.views 1.0
import shared.popups 1.0
import shared.stores 1.0
import shared.controls 1.0
// TODO: replace with StatusModal
ModalPopup {
id: root
property var store
property var stickersStore
property var contactsStore
readonly property var asset: JSON.parse(root.stickersStore.getStatusToken())
property string assetPrice
property string contractAddress
property int chainId
property var estimateGasFunction: (function(userAddress, uuid) { return 0; })
property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled){ return ""; })
property var onSuccess: (function(){})
property var asyncGasEstimateTarget
Component.onCompleted: {
gasSelector.estimateGas();
}
height: 540
title: qsTr("Authorize %1 %2").arg(Utils.stripTrailingZeros(assetPrice)).arg(asset.symbol)
property MessageDialog sendingError: MessageDialog {
id: sendingError
title: qsTr("Error sending the transaction")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
function setAsyncGasLimitResult(uuid, value) {
if (uuid === gasSelector.uuid) {
gasSelector.selectedGasLimit = value
gasSelector.defaultGasLimit = value
gasSelector.updateGasEthValue();
}
}
function sendTransaction() {
let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address,
gasSelector.selectedGasLimit,
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword,
gasSelector.suggestedFees.eip1559Enabled);
let response = JSON.parse(responseStr)
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)){
transactionSigner.validationError = qsTr("Wrong password")
return
}
sendingError.text = response.result
return sendingError.open()
}
onSuccess();
root.close();
}
onOpened: {
gasSelector.suggestedFees = root.store.suggestedFees(root.chainId)
gasSelector.checkOptimal()
}
TransactionStackView {
id: stack
height: parent.height
anchors.fill: parent
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
initialItem: group1
isLastGroup: stack.currentGroup === group3
onGroupActivated: {
root.title = group.headerText
btnNext.text = group.footerText
}
TransactionFormGroup {
id: group1
headerText: qsTr("Authorize %1 %2").arg(Utils.stripTrailingZeros(root.assetPrice)).arg(root.asset.symbol)
footerText: qsTr("Continue")
showBackBtn: false
StatusAccountSelector {
id: selectFromAccount
accounts: walletSectionAccounts.model
selectedAccount: {
const currAcc = walletSectionCurrent
if (currAcc.walletType !== Constants.watchWalletType) {
return currAcc
}
return null
}
currency: walletSection.currentCurrency
width: stack.width
label: qsTr("Choose account")
showBalanceForAssetSymbol: root.asset.symbol
minRequiredAssetBalance: root.assetPrice
chainId: root.chainId
onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() }
}
RecipientSelector {
id: selectRecipient
visible: false
accounts: root.stickersStore.walletAccounts
contactsStore: root.contactsStore
selectedRecipient: { "address": contractAddress, "type": RecipientSelector.Type.Address }
readOnly: true
isValid: true
onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() }
}
Connections {
target: asyncGasEstimateTarget
onGasEstimateReturned: {
root.setAsyncGasLimitResult(uuid, estimate)
}
}
GasSelector {
id: gasSelector
anchors.top: selectFromAccount.bottom
anchors.topMargin: Style.current.padding
getGasEthValue: root.stickersStore.getGasEthValue
getFiatValue: root.stickersStore.getFiatValue
defaultCurrency: root.stickersStore.getCurrentCurrency()
width: stack.width
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount, uuid);
if (estimatedGas !== undefined) {
gasSelector.selectedGasLimit = estimatedGas
}
return estimatedGas;
})
}
GasValidator {
id: gasValidator
anchors.top: gasSelector.bottom
anchors.horizontalCenter: parent.horizontalCenter
selectedAccount: selectFromAccount.selectedAccount
selectedAsset: root.asset
selectedAmount: parseFloat(root.assetPrice)
selectedGasEthValue: gasSelector.selectedGasEthValue
selectedNetwork: {
return {chainId: root.chainId}
}
}
}
TransactionFormGroup {
id: group2
headerText: qsTr("Authorize %1 %2").arg(Utils.stripTrailingZeros(root.assetPrice)).arg(root.asset.symbol)
footerText: qsTr("Sign with password")
TransactionPreview {
id: pvwTransaction
width: stack.width
fromAccount: selectFromAccount.selectedAccount
gas: {
"value": gasSelector.selectedGasEthValue,
"symbol": "ETH",
"fiatValue": gasSelector.selectedGasFiatValue
}
toAccount: selectRecipient.selectedRecipient
asset: root.asset
amount: {
const fiatValue = root.stickersStore.getFiatValue(root.assetPrice || 0, root.asset.symbol, currency)
return { "value": root.assetPrice, "fiatValue": fiatValue }
}
currency: root.stickersStore.getCurrentCurrency()
}
}
TransactionFormGroup {
id: group3
headerText: qsTr("Send %1 %2").arg(Utils.stripTrailingZeros(root.assetPrice)).arg(root.asset.symbol)
footerText: qsTr("Sign with password")
TransactionSigner {
id: transactionSigner
width: stack.width
signingPhrase: root.stickersStore.getSigningPhrase()
}
}
}
footer: Item {
width: parent.width
height: btnNext.height
StatusBackButton {
id: btnBack
anchors.left: parent.left
visible: stack.currentGroup.showBackBtn
enabled: {
stack.currentGroup.isValid || stack.isLastGroup
}
onClicked: {
if (typeof stack.currentGroup.onBackClicked === "function") {
return stack.currentGroup.onBackClicked()
}
stack.back()
}
}
Component {
id: transactionSettingsConfirmationPopupComponent
TransactionSettingsConfirmationPopup {
}
}
StatusButton {
id: btnNext
anchors.right: parent.right
text: qsTr("Next")
objectName: "sendNextButton"
enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending
loading: stack.currentGroup.isPending
onClicked: {
const validity = stack.currentGroup.validate()
if (validity.isValid && !validity.isPending) {
if (stack.isLastGroup) {
return root.sendTransaction()
}
if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.suggestedFees.baseFee,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor, // TODO:
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.suggestedFees.baseFee + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.next();
}
})
return
}
}
stack.next()
}
}
}
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -149,33 +149,70 @@ Item {
}
Component {
id: stickerPackPurchaseModal
StatusSNTTransactionModal {
store: root.store
stickersStore: root.store.stickersStore
contractAddress: root.store.stickersStore.getStickersMarketAddress()
contactsStore: root.store.contactsStore
assetPrice: price
chainId: root.store.stickersStore.getChainIdForStickers()
estimateGasFunction: function(selectedAccount, uuid) {
if (packId < 0 || !selectedAccount || !price) return 325000
return root.store.stickersStore.estimate(packId, selectedAccount.address, price, uuid)
SendModal {
id: buyStickersModal
interactive: false
sendType: Constants.SendType.StickersBuy
preSelectedRecipient: root.store.stickersStore.getStickersMarketAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(parseFloat(price))
preSelectedAsset: {
let assetsList = buyStickersModal.store.currentAccount.assets
for(var i=0; i< assetsList.count;i++) {
let symbol = JSON.parse(root.store.stickersStore.getStatusToken()).symbol
if(symbol === assetsList.rowData(i, "symbol"))
return {
name: assetsList.rowData(i, "name"),
symbol: assetsList.rowData(i, "symbol"),
totalBalance: assetsList.rowData(i, "totalBalance"),
totalCurrencyBalance: assetsList.rowData(i, "totalCurrencyBalance"),
balances: assetsList.rowData(i, "balances"),
decimals: assetsList.rowData(i, "decimals")
}
}
return {}
}
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) {
return root.store.stickersStore.buy(packId,
selectedAddress,
gasLimit,
gasPrice,
tipLimit,
overallLimit,
password,
eip1559Enabled)
sendTransaction: function() {
if(bestRoutes.length === 1) {
let path = bestRoutes[0]
let eip1559Enabled = path.gasFees.eip1559Enabled
let maxFeePerGas = (selectedPriority === 0) ? path.gasFees.maxFeePerGasL:
(selectedPriority === 1) ? path.gasFees.maxFeePerGasM:
path.gasFees.maxFeePerGasH
root.store.stickersStore.authenticateAndBuy(packId,
selectedAccount.address,
path.gasAmount,
eip1559Enabled ? "" : path.gasFees.gasPrice,
eip1559Enabled ? path.gasFees.maxPriorityFeePerGas : "",
eip1559Enabled ? maxFeePerGas : path.gasFees.gasPrice,
eip1559Enabled)
}
}
onClosed: {
destroy()
Connections {
target: root.store.stickersStore.stickersModule
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)) {
buyStickersModal.setSendTxError()
return
}
buyStickersModal.sendingError.text = response.result
return buyStickersModal.sendingError.open()
}
let url = `${buyStickersModal.store.getEtherscanLink()}/${response.result}`;
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
buyStickersModal.close()
} catch (e) {
console.error('Error parsing the response', e)
}
}
}
asyncGasEstimateTarget: root.store.stickersStore.stickersModule
width: stickerPackDetailsPopup.width
height: stickerPackDetailsPopup.height
}
}

View File

@ -62,33 +62,70 @@ ModalPopup {
packId: stickerPackDetailsPopup.packId
Component {
id: stickerPackPurchaseModal
StatusSNTTransactionModal {
store: stickerPackDetailsPopup.store
stickersStore: stickerPackDetailsPopup.store.stickersStore
contactsStore: stickerPackDetailsPopup.store.contactsStore
contractAddress: root.store.stickersStore.getStickersMarketAddress()
assetPrice: price
chainId: root.store.stickersStore.getChainIdForStickers()
estimateGasFunction: function(selectedAccount, uuid) {
if (packId < 0 || !selectedAccount || !price) return 325000
return stickerPackDetailsPopup.store.stickersStore.estimate(packId, selectedAccount.address, price, uuid)
SendModal {
id: buyStickersPackModal
interactive: false
sendType: Constants.SendType.StickersBuy
preSelectedRecipient: stickerPackDetailsPopup.store.stickersStore.getStickersMarketAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(parseFloat(price))
preSelectedAsset: {
let assetsList = buyStickersPackModal.store.currentAccount.assets
for(var i=0; i< assetsList.count;i++) {
let symbol = JSON.parse(stickerPackDetailsPopup.store.stickersStore.getStatusToken()).symbol
if(symbol === assetsList.rowData(i, "symbol"))
return {
name: assetsList.rowData(i, "name"),
symbol: assetsList.rowData(i, "symbol"),
totalBalance: assetsList.rowData(i, "totalBalance"),
totalCurrencyBalance: assetsList.rowData(i, "totalCurrencyBalance"),
balances: assetsList.rowData(i, "balances"),
decimals: assetsList.rowData(i, "decimals")
}
}
return {}
}
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) {
return root.store.stickersStore.buy(packId,
selectedAddress,
gasLimit,
gasPrice,
tipLimit,
overallLimit,
password,
eip1559Enabled)
sendTransaction: function() {
if(bestRoutes.length === 1) {
let path = bestRoutes[0]
let eip1559Enabled = path.gasFees.eip1559Enabled
let maxFeePerGas = (selectedPriority === 0) ? path.gasFees.maxFeePerGasL:
(selectedPriority === 1) ? path.gasFees.maxFeePerGasM:
path.gasFees.maxFeePerGasH
stickerPackDetailsPopup.store.stickersStore.authenticateAndBuy(packId,
selectedAccount.address,
path.gasAmount,
eip1559Enabled ? "" : path.gasFees.gasPrice,
eip1559Enabled ? path.gasFees.maxPriorityFeePerGas : "",
eip1559Enabled ? maxFeePerGas : path.gasFees.gasPrice,
eip1559Enabled)
}
}
onClosed: {
destroy()
Connections {
target: stickerPackDetailsPopup.store.stickersStore.stickersModule
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)) {
buyStickersPackModal.setSendTxError()
return
}
buyStickersPackModal.sendingError.text = response.result
return buyStickersPackModal.sendingError.open()
}
let url = `${buyStickersPackModal.store.getEtherscanLink()}/${response.result}`;
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
"",
true,
Constants.ephemeralNotificationType.normal,
url)
buyStickersPackModal.close()
} catch (e) {
console.error('Error parsing the response', e)
}
}
}
asyncGasEstimateTarget: stickerPackDetailsPopup.store.stickersStore.stickersModule
width: stickerPackDetailsPopup.width
height: stickerPackDetailsPopup.height
}
}
}

View File

@ -12,7 +12,6 @@ StatusChatInputTextFormationAction 1.0 StatusChatInputTextFormationAction.qml
StatusEmojiPopup 1.0 StatusEmojiPopup.qml
StatusEmojiSection 1.0 StatusEmojiSection.qml
StatusEmojiSuggestionPopup 1.0 StatusEmojiSuggestionPopup.qml
StatusETHTransactionModal 1.0 StatusETHTransactionModal.qml
StatusExpandableAddress 1.0 StatusExpandableAddress.qml
StatusGifPopup 1.0 StatusGifPopup.qml
StatusImageModal 1.0 StatusImageModal.qml
@ -23,7 +22,6 @@ StatusSearchListPopup 1.0 StatusSearchListPopup.qml
StatusSectionDescItem 1.0 StatusSectionDescItem.qml
StatusSectionHeadline 1.0 StatusSectionHeadline.qml
StatusSettingsLineButton 1.0 StatusSettingsLineButton.qml
StatusSNTTransactionModal 1.0 StatusSNTTransactionModal.qml
StatusSticker 1.0 StatusSticker.qml
StatusStickerButton 1.0 StatusStickerButton.qml
StatusStickerList 1.0 StatusStickerList.qml

View File

@ -0,0 +1,128 @@
import QtQuick 2.13
import utils 1.0
import shared.stores 1.0
import "../../../app/AppLayouts/Profile/stores"
QtObject {
id: root
property CurrenciesStore currencyStore: CurrenciesStore { }
property ProfileSectionStore profileSectionStore: ProfileSectionStore {}
property var contactStore: profileSectionStore.contactsStore
property var mainModuleInst: mainModule
property var walletSectionTransactionsInst: walletSectionTransactions
property string locale: localAppSettings.language
property string currentCurrency: walletSection.currentCurrency
property var allNetworks: networksModule.all
property var accounts: walletSectionAccounts.model
property var currentAccount: walletSectionCurrent
property string signingPhrase: walletSection.signingPhrase
property var savedAddressesModel: walletSectionSavedAddresses.model
property var disabledChainIdsFromList: []
property var disabledChainIdsToList: []
function addRemoveDisabledFromChain(chainID, isDisabled) {
var tempList = disabledChainIdsFromList
if(isDisabled) {
tempList.push(chainID)
}
else {
for(var i = 0; i < tempList.length;i++) {
if(tempList[i] === chainID) {
tempList.splice(i, 1)
}
}
}
disabledChainIdsFromList = tempList
}
function addRemoveDisabledToChain(chainID, isDisabled) {
var tempList = disabledChainIdsToList
if(isDisabled) {
tempList.push(chainID)
}
else {
for(var i = 0; i < tempList.length;i++) {
if(tempList[i] === chainID) {
tempList.splice(i, 1)
}
}
}
disabledChainIdsToList = tempList
}
function getEtherscanLink() {
return profileSectionModule.ensUsernamesModule.getEtherscanLink()
}
function copyToClipboard(text) {
globalUtils.copyToClipboard(text)
}
function estimateGas(from_addr, to, assetSymbol, value, chainId, data) {
return walletSectionTransactions.estimateGas(from_addr, to, assetSymbol, value, chainId, data)
}
function getFiatValue(balance, cryptoSymbol, fiatSymbol) {
return profileSectionStore.ensUsernamesStore.getFiatValue(balance, cryptoSymbol, fiatSymbol)
}
function getGasEthValue(gweiValue, gasLimit) {
return profileSectionStore.ensUsernamesStore.getGasEthValue(gweiValue, gasLimit)
}
function authenticateAndTransfer(from, to, tokenSymbol, amount, uuid, priority, selectedRoutes) {
walletSectionTransactions.authenticateAndTransfer(from, to, tokenSymbol, amount, uuid, priority, selectedRoutes)
}
function suggestedFees(chainId) {
return JSON.parse(walletSectionTransactions.suggestedFees(chainId))
}
function getEstimatedTime(chainId, maxFeePerGas) {
return walletSectionTransactions.getEstimatedTime(chainId, maxFeePerGas)
}
function getChainIdForChat() {
return walletSectionTransactions.getChainIdForChat()
}
function getChainIdForBrowser() {
return walletSectionTransactions.getChainIdForBrowser()
}
function suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIds, priority, sendType) {
walletSectionTransactions.suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIds, priority, sendType)
}
function hex2Eth(value) {
return globalUtils.hex2Eth(value)
}
function switchAccount(newIndex) {
if(Constants.isCppApp)
walletSectionAccounts.switchAccount(newIndex)
else
walletSection.switchAccount(newIndex)
}
function resolveENS(value) {
mainModuleInst.resolveENS(value, "")
}
function getWei2Eth(wei) {
return globalUtils.wei2Eth(wei,18)
}
function getEth2Wei(eth) {
return globalUtils.eth2Wei(eth, 18)
}
function plainText(text) {
return globalUtils.plainText(text)
}
}

View File

@ -1,2 +1,4 @@
singleton RootStore 1.0 RootStore.qml
CurrenciesStore 1.0 CurrenciesStore.qml
TransactionStore 1.0 TransactionStore.qml
BIP39_en 1.0 BIP39_en.qml

View File

@ -11,54 +11,51 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
Item {
id: networkCardsComponent
id: root
property var assets
property var store
property string locale: ""
property var selectedAsset
property var suggestedRoutes
property bool customMode: false
property var selectedNetwork
property var bestRoutes
property var selectedAccount
property var selectedAsset
property var allNetworks
property bool customMode: false
property double amountToSend: 0
property double requiredGasInEth: 0
property bool errorMode: (d.customAmountToSend > amountToSend) ||
(d.customAmountToSend < amountToSend) ||
(d.customAmountToReceive > amountToSend) ||
(d.customAmountToReceive < amountToSend)
property bool errorMode: {
if(customMode) {
return (d.customAmountToSend > amountToSend) || (d.customAmountToSend < amountToSend) ||
(d.customAmountToReceive > amountToSend) || (d.customAmountToReceive < amountToSend)
}
else {
return !d.thereIsApossibleRoute
}
}
property bool interactive: true
property var weiToEth: function(wei) {}
signal reCalculateSuggestedRoute(var disabled)
signal reCalculateSuggestedRoute()
QtObject {
id: d
property var selectedFromNetwork
property var selectedToNetwork
property double customAmountToSend: 0
property double customAmountToReceive: 0
property bool thereIsApossibleRoute: false
function getBalance(chainID) {
for(var i=0; i< selectedAsset.balances.count; i++) {
if(selectedAsset.balances.rowData(i, "chainId") === chainID.toString()) {
return selectedAsset.balances.rowData(i, "balance")
}
function resetAllSetValues() {
for(var i = 0; i<fromNetworksRepeater.count; i++) {
fromNetworksRepeater.itemAt(i).amountToSend = 0
toNetworksRepeater.itemAt(i).amountToReceive = 0
}
}
onSelectedFromNetworkChanged: {
canvas.clear()
canvas.requestPaint()
}
onSelectedToNetworkChanged: {
canvas.clear()
canvas.requestPaint()
}
}
width: 410
height: networkCardsLayout.height
height: visible ? networkCardsLayout.height : 0
onBestRoutesChanged: {
canvas.clear()
canvas.requestPaint()
}
RowLayout {
id: networkCardsLayout
@ -73,44 +70,35 @@ Item {
text: qsTr("Your Balances").toUpperCase()
}
Repeater {
model: networkCardsComponent.allNetworks
id: fromNetworksRepeater
model: root.allNetworks
StatusCard {
id: fromNetwork
property var tokenBalanceOnChain: Utils.toLocaleString(parseFloat(d.getBalance(model.chainId)).toFixed(4), locale, {"currency": true})
property var hasGas: assets.hasGas(model.chainId, model.nativeCurrencySymbol, requiredGasInEth + parseFloat(amountToSend))
objectName: model.chainId
property double amountToSend: 0
property string tokenBalanceOnChain: selectedAccount && selectedAccount!== undefined && selectedAsset!== undefined ? selectedAccount.getTokenBalanceOnChain(model.chainId, selectedAsset.symbol) : ""
property var hasGas: selectedAccount.hasGas(model.chainId, model.nativeCurrencySymbol, requiredGasInEth)
primaryText: model.chainName
secondaryText: (parseFloat(tokenBalanceOnChain) === 0 && amountToSend !== 0) ?
qsTr("No Balance") : !hasGas ? qsTr("No Gas") :
(selectedNetwork && selectedNetwork.chainName === model.chainName) ?
amountToSend: 0
tertiaryText: qsTr("BALANCE: ") + tokenBalanceOnChain
state: tokenBalanceOnChain === 0 || !hasGas ? "unavailable" : networkCardsComponent.errorMode ? "error" : "default"
cardIcon.source: Style.png(model.iconUrl)
secondaryText: (parseFloat(tokenBalanceOnChain) === 0 && root.amountToSend !== 0) ?
qsTr("No Balance") : !hasGas ? qsTr("No Gas") : LocaleUtils.numberToLocaleString(fromNetwork.amountToSend)
tertiaryText: qsTr("BALANCE: ") + LocaleUtils.numberToLocaleString(parseFloat(tokenBalanceOnChain))
state: tokenBalanceOnChain === 0 || !hasGas ? "unavailable" : root.errorMode ? "error" : "default"
cardIcon.source: Style.svg(model.iconUrl)
disabledText: qsTr("Disabled")
advancedMode: networkCardsComponent.customMode
advancedInputText: (selectedNetwork && selectedNetwork.chainName === model.chainName) ? amountToSend: 0
Component.onCompleted: {
disabled = store.checkIfDisabledByUser(model.chainId)
if(selectedNetwork && selectedNetwork.chainName === model.chainName)
d.selectedFromNetwork = this
}
Connections {
target: networkCardsComponent
onSelectedNetworkChanged: {
if(selectedNetwork.chainName === model.chainName) {
d.selectedFromNetwork = fromNetwork
}
}
}
advancedMode: root.customMode
advancedInputText: LocaleUtils.numberToLocaleString(fromNetwork.amountToSend)
disabled: store.disabledChainIdsFromList.includes(model.chainId)
clickable: root.interactive
onClicked: {
store.addRemoveDisabledChain(suggestedRoutes, model.chainId, disabled)
reCalculateSuggestedRoute(store.disabledChainIds)
}
onAdvancedInputTextChanged: {
if(selectedNetwork && selectedNetwork.chainName === model.chainName) {
d.customAmountToSend = isNaN(parseFloat(advancedInputText)) ? 0 : parseFloat(advancedInputText)
}
store.addRemoveDisabledFromChain(model.chainId, disabled)
root.reCalculateSuggestedRoute()
}
// To-do needed for custom view
// onAdvancedInputTextChanged: {
// if(selectedNetwork && selectedNetwork.chainName === model.chainName) {
// d.customAmountToSend = isNaN(parseFloat(advancedInputText)) ? 0 : parseFloat(advancedInputText)
// }
// }
}
}
}
@ -127,39 +115,33 @@ Item {
elide: Text.ElideMiddle
}
Repeater {
model: networkCardsComponent.allNetworks
id: toNetworksRepeater
model: root.allNetworks
StatusCard {
id: toCard
objectName: model.chainId
property double amountToReceive: 0
primaryText: model.chainName
secondaryText: (selectedNetwork && selectedNetwork.chainName === model.chainName) ? amountToSend: 0
secondaryText: LocaleUtils.numberToLocaleString(amountToReceive)
tertiaryText: ""
// To-do preferred in not something that is supported yet
state: networkCardsComponent.errorMode ? "error" : "default"
state: root.errorMode ? "error" : "default"
// opacity: preferred ? 1 : 0
cardIcon.source: Style.png(model.iconUrl)
cardIcon.source: Style.svg(model.iconUrl)
disabledText: qsTr("Disabled")
advancedMode: networkCardsComponent.customMode
advancedInputText: (selectedNetwork && selectedNetwork.chainName === model.chainName) ? amountToSend: 0
Component.onCompleted: {
disabled = store.checkIfDisabledByUser(model.chainId)
if(selectedNetwork && selectedNetwork.chainName === model.chainName)
d.selectedToNetwork = this
}
Connections {
target: networkCardsComponent
onSelectedNetworkChanged: {
if(selectedNetwork && selectedNetwork.chainName === model.chainName)
d.selectedToNetwork = toCard
}
}
advancedMode: root.customMode
advancedInputText: LocaleUtils.numberToLocaleString(amountToReceive)
disabled: store.disabledChainIdsToList.includes(model.chainId)
clickable: root.interactive
onClicked: {
store.addRemoveDisabledChain(suggestedRoutes, model.chainId, disabled)
reCalculateSuggestedRoute(store.disabledChainIds)
}
onAdvancedInputTextChanged: {
if(selectedNetwork && selectedNetwork.chainName === model.chainName)
d.customAmountToReceive = isNaN(parseFloat(advancedInputText)) ? 0 : parseFloat(advancedInputText)
store.addRemoveDisabledToChain(model.chainId, disabled)
root.reCalculateSuggestedRoute()
}
// To-do needed for custom view
// onAdvancedInputTextChanged: {
// if(selectedNetwork && selectedNetwork.chainName === model.chainName)
// d.customAmountToReceive = isNaN(parseFloat(advancedInputText)) ? 0 : parseFloat(advancedInputText)
// }
}
}
}
@ -174,21 +156,47 @@ Item {
function clear() {
if(available) {
var ctx = getContext("2d");
if(ctx)
ctx.reset()
var ctx = getContext("2d");
if(ctx)
ctx.reset()
}
}
onPaint: {
if(d.selectedFromNetwork && d.selectedToNetwork) {
// Get the canvas context
var ctx = getContext("2d");
StatusQUtils.Utils.drawArrow(ctx, d.selectedFromNetwork.x + d.selectedFromNetwork.width,
d.selectedFromNetwork.y + d.selectedFromNetwork.height/2,
toNetworksLayout.x + d.selectedToNetwork.x,
d.selectedToNetwork.y + d.selectedToNetwork.height/2,
'#627EEA')
d.resetAllSetValues()
d.thereIsApossibleRoute = false
if(bestRoutes === undefined)
return
// Get the canvas context
var ctx = getContext("2d");
for(var i = 0; i< bestRoutes.length; i++) {
var fromN, toN = null
for(var j = 0; j<fromNetworksRepeater.count; j++) {
if(bestRoutes[i].fromNetwork.chainId === parseInt(fromNetworksRepeater.itemAt(j).objectName) &&
!store.disabledChainIdsFromList.includes(bestRoutes[i].fromNetwork.chainId)) {
fromN = fromNetworksRepeater.itemAt(j)
}
}
for(var k = 0; k<toNetworksRepeater.count; k++) {
if(bestRoutes[i].toNetwork.chainId === parseInt(toNetworksRepeater.itemAt(k).objectName) &&
!store.disabledChainIdsToList.includes(bestRoutes[i].toNetwork.chainId)) {
toN = toNetworksRepeater.itemAt(k)
}
}
if(toN !== null && fromN !== null) {
let amountToSend = weiToEth(bestRoutes[i].amountIn)
let amountToReceive = weiToEth(bestRoutes[i].amountOut)
fromN.amountToSend = amountToSend
toN.amountToReceive += amountToReceive
d.thereIsApossibleRoute = true
StatusQUtils.Utils.drawArrow(ctx, fromN.x + fromN.width,
fromN.y + fromN.height/2,
toNetworksLayout.x + toN.x,
toN.y + toN.height/2,
'#627EEA')
}
}
}
}

View File

@ -17,17 +17,20 @@ Item {
implicitHeight: visible ? tabBar.height + stackLayout.height + 2* Style.current.xlPadding : 0
property var store
property var suggestedRoutes
property var selectedNetwork
property var selectedAccount
property var selectedAsset
property var assets
property double amountToSend: 0
property double requiredGasInEth: 0
property bool errorMode: customNetworkRoutingPage.errorMode
property var bestRoutes
property bool isLoading: false
property bool advancedOrCustomMode: (tabBar.currentIndex === 1) || (tabBar.currentIndex === 2)
property bool errorMode: (tabBar.currentIndex === 1) ?
advancedNetworkRoutingPage.errorMode :
(tabBar.currentIndex === 2) ?
customNetworkRoutingPage.errorMode: false
property bool interactive: true
signal networkChanged(int chainId)
signal reCalculateSuggestedRoute(var disabledChainIds)
signal reCalculateSuggestedRoute()
QtObject {
id: d
@ -46,9 +49,10 @@ Item {
StatusSwitchTabButton {
text: qsTr("Advanced")
}
StatusSwitchTabButton {
text: qsTr("Custom")
}
// To-do Implementaion is not ready yet
// StatusSwitchTabButton {
// text: qsTr("Custom")
// }
}
StackLayout {
@ -71,12 +75,11 @@ Item {
anchors.left: parent.left
anchors.margins: Style.current.padding
width: stackLayout.width - Style.current.bigPadding
selectedNetwork: root.selectedNetwork
suggestedRoutes: root.suggestedRoutes
bestRoutes: root.bestRoutes
amountToSend: root.amountToSend
onNetworkChanged: {
root.selectedNetwork = network
root.networkChanged(network.chainId)
isLoading: root.isLoading
weiToEth: function(wei) {
return "%1 %2".arg(LocaleUtils.numberToLocaleString(parseFloat(store.getWei2Eth(wei)))).arg(selectedAsset.symbol)
}
}
}
@ -91,14 +94,17 @@ Item {
anchors.left: parent.left
anchors.margins: Style.current.padding
store: root.store
assets: root.assets
selectedNetwork: root.selectedNetwork
selectedAccount: root.selectedAccount
amountToSend: root.amountToSend
requiredGasInEth: root.requiredGasInEth
selectedAsset: root.selectedAsset
suggestedRoutes: root.suggestedRoutes
onReCalculateSuggestedRoute: root.reCalculateSuggestedRoute(disabledChainIds)
onReCalculateSuggestedRoute: root.reCalculateSuggestedRoute()
bestRoutes: root.bestRoutes
isLoading: root.isLoading
interactive: root.interactive
weiToEth: function(wei) {
return parseFloat(store.getWei2Eth(wei))
}
}
}
@ -113,14 +119,17 @@ Item {
anchors.margins: Style.current.padding
customMode: true
store: root.store
assets: root.assets
selectedNetwork: root.selectedNetwork
selectedAccount: root.selectedAccount
amountToSend: root.amountToSend
requiredGasInEth: root.requiredGasInEth
selectedAsset: root.selectedAsset
suggestedRoutes: root.suggestedRoutes
onReCalculateSuggestedRoute: root.reCalculateSuggestedRoute(disabledChainIds)
onReCalculateSuggestedRoute: root.reCalculateSuggestedRoute()
bestRoutes: root.bestRoutes
isLoading: root.isLoading
interactive: root.interactive
weiToEth: function(wei) {
return parseFloat(store.getWei2Eth(wei))
}
}
}
}

View File

@ -12,25 +12,21 @@ import StatusQ.Core.Theme 0.1
import "../controls"
ColumnLayout {
id: networksAdvancedCustomView
id: root
property var store
property var assets
property var selectedNetwork: ""
property var selectedAccount
property double amountToSend: 0
property double requiredGasInEth: 0
property bool customMode: false
property var selectedAsset
property var suggestedRoutes
property var bestRoutes
property bool isLoading: false
property bool errorMode: networksLoader.item ? networksLoader.item.errorMode : false
property var weiToEth: function(wei) {}
property bool interactive: true
signal reCalculateSuggestedRoute(var disabledChainIds)
onSelectedNetworkChanged: {
networksLoader.active = false
networksLoader.active = true
}
signal reCalculateSuggestedRoute()
RowLayout {
spacing: 10
@ -69,24 +65,32 @@ ColumnLayout {
text: qsTr("The networks where the receipient will receive tokens. Amounts calculated automatically for the lowest cost.")
wrapMode: Text.WordWrap
}
BalanceExceeded {
id: balanceExceeded
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Style.current.bigPadding
transferPossible: root.store.disabledChainIdsToList.length > 0 || root.store.disabledChainIdsFromList.length > 0 ? true : root.bestRoutes ? root.bestRoutes.length > 0 : false
amountToSend: root.amountToSend
isLoading: root.isLoading
}
Loader {
id: networksLoader
Layout.topMargin: Style.current.padding
active: false
active: !balanceExceeded.visible
visible: active
sourceComponent: NetworkCardsComponent {
store: networksAdvancedCustomView.store
selectedNetwork: networksAdvancedCustomView.selectedNetwork
selectedAccount: networksAdvancedCustomView.selectedAccount
allNetworks: networksAdvancedCustomView.store.allNetworks
amountToSend: networksAdvancedCustomView.amountToSend
customMode: networksAdvancedCustomView.customMode
requiredGasInEth: networksAdvancedCustomView.requiredGasInEth
assets: networksAdvancedCustomView.assets
selectedAsset: networksAdvancedCustomView.selectedAsset
locale: networksAdvancedCustomView.store.locale
suggestedRoutes: networksAdvancedCustomView.suggestedRoutes
onReCalculateSuggestedRoute: networksAdvancedCustomView.reCalculateSuggestedRoute(disabled)
store: root.store
selectedAccount: root.selectedAccount
allNetworks: root.store.allNetworks
amountToSend: root.amountToSend
customMode: root.customMode
requiredGasInEth: root.requiredGasInEth
selectedAsset: root.selectedAsset
onReCalculateSuggestedRoute: root.reCalculateSuggestedRoute()
bestRoutes: root.bestRoutes
weiToEth: root.weiToEth
interactive: root.interactive
}
}
}

View File

@ -13,13 +13,12 @@ import StatusQ.Core.Theme 0.1
import "../controls"
RowLayout {
id: networksSimpleRoutingView
id: root
property var selectedNetwork
property var suggestedRoutes
property var bestRoutes
property double amountToSend: 0
signal networkChanged(var network)
property bool isLoading: false
property var weiToEth: function(wei) {}
spacing: 10
@ -30,7 +29,7 @@ RowLayout {
}
ColumnLayout {
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: networksSimpleRoutingView.width
Layout.preferredWidth: root.width
StatusBaseText {
Layout.maximumWidth: 410
font.pixelSize: 15
@ -43,16 +42,16 @@ RowLayout {
Layout.maximumWidth: 410
font.pixelSize: 15
color: Theme.palette.baseColor1
text: qsTr("Choose a network to use for the transaction")
text: qsTr("The networks where the receipient will receive tokens. Amounts calculated automatically for the lowest cost.")
wrapMode: Text.WordWrap
}
BalanceExceeded {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Style.current.bigPadding
visible: !transferPossible
transferPossible: networksSimpleRoutingView.suggestedRoutes ? networksSimpleRoutingView.suggestedRoutes.length > 0 : false
amountToSend: networksSimpleRoutingView.amountToSend
transferPossible: root.bestRoutes !== undefined ? root.bestRoutes.length > 0 : true
amountToSend: root.amountToSend
isLoading: root.isLoading
}
ScrollView {
Layout.fillWidth: true
@ -63,30 +62,28 @@ RowLayout {
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: ScrollBar.AsNeeded
clip: true
visible: networksSimpleRoutingView.suggestedRoutes ? networksSimpleRoutingView.suggestedRoutes.length > 0 : false
visible: !root.isLoading ? root.bestRoutes !== undefined ? root.bestRoutes.length > 0 : true : false
Row {
id: row
spacing: Style.current.padding
Repeater {
id: repeater
objectName: "networksList"
model: networksSimpleRoutingView.suggestedRoutes
model: root.bestRoutes
StatusListItem {
id: item
objectName: modelData.chainName
objectName: modelData.toNetwork.chainName
leftPadding: 5
rightPadding: 5
implicitWidth: 126
title: modelData.chainName
subTitle: ""
implicitWidth: 150
title: modelData.toNetwork.chainName
subTitle: root.weiToEth(modelData.amountIn)
statusListItemSubTitle.color: Theme.palette.primaryColor1
asset.width: 32
asset.height: 32
asset.name: Style.png("networks/" + modelData.chainName.toLowerCase())
asset.name: Style.svg("tiny/" + modelData.toNetwork.iconUrl)
asset.isImage: true
color: "transparent"
border.color: Style.current.primary
border.width: networksSimpleRoutingView.selectedNetwork !== undefined ? networksSimpleRoutingView.selectedNetwork.chainId === modelData.chainId ? 1 : 0 : 0
onClicked: networksSimpleRoutingView.networkChanged(modelData)
}
}
}

View File

@ -13,17 +13,12 @@ import StatusQ.Core.Theme 0.1
Rectangle {
id: footer
property string maxFiatFees: ""
property int estimatedTxTimeFlag: Constants.transactionEstimatedTime.unknown
property string maxFiatFees: "..."
property alias selectedTimeEstimate: estimatedTime.text
property bool pending: true
property bool isLastGroup: false
signal nextButtonClicked()
onEstimatedTxTimeFlagChanged: {
estimatedTime.text = Utils.getLabelForEstimatedTxTime(estimatedTxTimeFlag)
}
width: parent.width
height: 82
radius: 8
@ -85,7 +80,6 @@ Rectangle {
}
StatusFlatButton {
icon.name: isLastGroup ? "" : "password"
text: qsTr("Send")
objectName: "sendModalFooterSendButton"
size: StatusBaseButton.Size.Large

View File

@ -153,7 +153,7 @@ Item {
objectName: model.name
height: visible ? 64 : 0
title: !!model.name ? model.name : ""
subTitle: Utils.toLocaleString(model.currencyBalance.toFixed(2), store.locale, {"model.currency": true}) + " " + store.currentCurrency.toUpperCase()
subTitle: "%1 %2".arg(LocaleUtils.numberToLocaleString(model.currencyBalance)).arg(store.currentCurrency.toUpperCase())
asset.emoji: !!model.emoji ? model.emoji: ""
asset.color: model.color
asset.name: !model.emoji ? "filled-account": ""

View File

@ -430,14 +430,6 @@ QtObject {
readonly property int success: 1
}
readonly property QtObject transactionEstimatedTime: QtObject {
readonly property int unknown: 0
readonly property int lessThanOneMin: 1
readonly property int lessThanThreeMins: 2
readonly property int lessThanFiveMins: 3
readonly property int moreThanFiveMins: 4
}
readonly property QtObject translationsState: QtObject {
readonly property int alpha: 0
readonly property int beta: 1
@ -669,4 +661,12 @@ QtObject {
Failure = 0,
Success = 1
}
enum SendType {
Transfer,
ENSRegister,
ENSRelease,
ENSSetPubKey,
StickersBuy
}
}

View File

@ -527,24 +527,6 @@ QtObject {
/* Validation section end */
function getLabelForEstimatedTxTime(estimatedFlag) {
if (estimatedFlag === Constants.transactionEstimatedTime.unknown) {
return qsTr("Unknown")
}
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanOneMin) {
return qsTr("< 1 min")
}
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanThreeMins) {
return qsTr("< 3 mins")
}
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanFiveMins) {
return qsTr("< 5 mins")
}
return qsTr("> 5 mins")
}
function getContactDetailsAsJson(publicKey, getVerificationRequest=true) {
let jsonObj = mainModuleInst.getContactDetailsAsJson(publicKey, getVerificationRequest)
try {

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 7eb66d09e7ff1ad3bac24d7049681f7fbb6b7905
Subproject commit 993c236c04517deb636e6551dfd553f427ffcce7