chore: router v2 integration

Closes: #15204
This commit is contained in:
Sale Djenic 2024-06-26 01:03:19 +02:00 committed by saledjenic
parent d3d0080b2d
commit 7e9f680e0b
19 changed files with 436 additions and 189 deletions

View File

@ -3,7 +3,10 @@
type SignalType* {.pure.} = enum
Message = "messages.new"
MessageDelivered = "message.delivered"
## Wallet Signals
Wallet = "wallet"
WalletSignTransactions = "wallet.sign.transactions"
WalletSuggestedRoutes = "wallet.suggested.routes"
NodeReady = "node.ready"
NodeCrashed = "node.crashed"
NodeStarted = "node.started"

View File

@ -1,8 +1,10 @@
import json, options
import json, options, sequtils, sugar, chronicles
import base
import signal_type
import app_service/service/transaction/dtoV2
const SignTransactionsEventType* = "sing-transactions"
type WalletSignal* = ref object of Signal
@ -17,20 +19,20 @@ type WalletSignal* = ref object of Signal
message*: string
requestId*: Option[int]
txHashes*: seq[string]
uuid*: string
bestRoute*: seq[TransactionPathDtoV2]
error*: string
errorCode*: string
proc fromEvent*(T: type WalletSignal, jsonSignal: JsonNode): WalletSignal =
proc fromEvent*(T: type WalletSignal, signalType: SignalType, jsonSignal: JsonNode): WalletSignal =
result = WalletSignal()
result.signalType = SignalType.Wallet
result.content = $jsonSignal
let event = jsonSignal["event"]
if event.kind != JNull:
if event.kind == JNull:
return
if signalType == SignalType.Wallet:
result.content = $jsonSignal
result.eventType = event["type"].getStr
if result.eventType == SignTransactionsEventType:
if event["transactions"].kind != JArray:
return
for tx in event["transactions"]:
result.txHashes.add(tx.getStr)
return
result.blockNumber = event{"blockNumber"}.getInt
result.erc20 = event{"erc20"}.getBool
result.accounts = @[]
@ -43,3 +45,23 @@ proc fromEvent*(T: type WalletSignal, jsonSignal: JsonNode): WalletSignal =
const requestIdName = "requestId"
if event.contains(requestIdName):
result.requestId = some(event[requestIdName].getInt())
return
if signalType == SignalType.WalletSignTransactions:
if event.kind != JArray:
return
for tx in event:
result.txHashes.add(tx.getStr)
return
if signalType == SignalType.WalletSuggestedRoutes:
try:
if event.contains("Uuid"):
result.uuid = event["Uuid"].getStr()
if event.contains("Best"):
result.bestRoute = event["Best"].getElems().map(x => x.toTransactionPathDtoV2())
if event.contains("details"):
result.error = event["details"].getStr
if event.contains("code"):
result.errorCode = event["code"].getStr
except:
error "Error parsing best route"
return

View File

@ -26,7 +26,7 @@ QtObject:
result.setup()
result.events = events
# This method might be called with `ChroniclesLogs` event from `nim_status_client`.
# This method might be called with `ChroniclesLogs` event from `nim_status_client`.
# In such case we must not log anything to prevent recursive calls.
# I tried to make a better solution, but ended up with such workaround, check out PR for more details.
proc processSignal(self: SignalsManager, statusSignal: string, allowLogging: bool) =
@ -79,7 +79,10 @@ QtObject:
of SignalType.EnvelopeSent: EnvelopeSentSignal.fromEvent(jsonSignal)
of SignalType.EnvelopeExpired: EnvelopeExpiredSignal.fromEvent(jsonSignal)
of SignalType.WhisperFilterAdded: WhisperFilterSignal.fromEvent(jsonSignal)
of SignalType.Wallet: WalletSignal.fromEvent(jsonSignal)
of SignalType.Wallet,
SignalType.WalletSignTransactions,
SignalType.WalletSuggestedRoutes:
WalletSignal.fromEvent(signalType, jsonSignal)
of SignalType.NodeReady,
SignalType.NodeCrashed,
SignalType.NodeStarted,

View File

@ -1,4 +1,4 @@
import sugar, sequtils, stint
import Tables, sugar, sequtils
import uuids, chronicles, options
import io_interface
import app_service/service/wallet_account/service as wallet_account_service
@ -71,10 +71,8 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_SUGGESTED_ROUTES_READY) do(e:Args):
self.delegate.suggestedRoutesReady(SuggestedRoutesArgs(e).suggestedRoutes)
self.events.on(SignalType.Wallet.event) do(e:Args):
self.events.on(SignalType.WalletSignTransactions.event) do(e:Args):
var data = WalletSignal(e)
if data.eventType != SignTransactionsEventType:
return
self.delegate.prepareSignaturesForTransactions(data.txHashes)
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
@ -109,10 +107,20 @@ proc authenticate*(self: Controller, keyUid = "") =
keyUid: keyUid)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
proc suggestedRoutes*(self: Controller, accountFrom: string, accountTo: string, amount: Uint256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string) =
self.transactionService.suggestedRoutes(accountFrom, accountTo, amount, token, toToken, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts)
proc suggestedRoutes*(self: Controller,
sendType: SendType,
accountFrom: string,
accountTo: string,
token: string,
amountIn: string,
toToken: string = "",
amountOut: string = "",
disabledFromChainIDs: seq[int] = @[],
disabledToChainIDs: seq[int] = @[],
lockedInAmounts: Table[string, string] = initTable[string, string](),
extraParamsTable: Table[string, string] = initTable[string, string]()) =
self.transactionService.suggestedRoutes(sendType, accountFrom, accountTo, token, amountIn, toToken, amountOut,
disabledFromChainIDs, disabledToChainIDs, lockedInAmounts, extraParamsTable)
proc transfer*(self: Controller, from_addr: string, to_addr: string, assetKey: string, toAssetKey: string,
uuid: string, selectedRoutes: seq[TransactionPathDto], password: string, sendType: SendType,

View File

@ -1,4 +1,4 @@
import stint, options
import Tables, options
import app/modules/shared_models/currency_amount
import app_service/service/transaction/dto
import app/modules/shared_models/collectibles_model as collectibles
@ -24,8 +24,18 @@ method refreshWalletAccounts*(self: AccessInterface) {.base.} =
method getTokenBalance*(self: AccessInterface, address: string, chainId: int, tokensKey: string): CurrencyAmount {.base.} =
raise newException(ValueError, "No implementation available")
method suggestedRoutes*(self: AccessInterface, accountFrom: string, accountTo: string, amount: UInt256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string) {.base.} =
method suggestedRoutes*(self: AccessInterface,
sendType: SendType,
accountFrom: string,
accountTo: string,
token: string,
amountIn: string,
toToken: string = "",
amountOut: string = "",
disabledFromChainIDs: seq[int] = @[],
disabledToChainIDs: seq[int] = @[],
lockedInAmounts: Table[string, string] = initTable[string, string](),
extraParamsTable: Table[string, string] = initTable[string, string]()) {.base.} =
raise newException(ValueError, "No implementation available")
method suggestedRoutesReady*(self: AccessInterface, suggestedRoutes: SuggestedRoutesDto) {.base.} =

View File

@ -339,11 +339,6 @@ method transactionWasSent*(self: Module, chainId: int, txHash, uuid, error: stri
return
self.view.sendTransactionSentSignal(chainId, txHash, uuid, error)
method suggestedRoutes*(self: Module, accountFrom: string, accountTo: string, amount: UInt256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string) =
self.controller.suggestedRoutes(accountFrom, accountTo, amount, token, toToken, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts)
method suggestedRoutesReady*(self: Module, suggestedRoutes: SuggestedRoutesDto) =
self.tmpSendTransactionDetails.paths = suggestedRoutes.best
self.tmpSendTransactionDetails.slippagePercentage = none(float)
@ -363,6 +358,32 @@ method suggestedRoutesReady*(self: Module, suggestedRoutes: SuggestedRoutesDto)
rawPaths = suggestedRoutes.rawBest)
self.view.setTransactionRoute(transactionRoutes)
method suggestedRoutes*(self: Module,
sendType: SendType,
accountFrom: string,
accountTo: string,
token: string,
amountIn: string,
toToken: string = "",
amountOut: string = "",
disabledFromChainIDs: seq[int] = @[],
disabledToChainIDs: seq[int] = @[],
lockedInAmounts: Table[string, string] = initTable[string, string](),
extraParamsTable: Table[string, string] = initTable[string, string]()) =
self.controller.suggestedRoutes(
sendType,
accountFrom,
accountTo,
token,
amountIn,
toToken,
amountOut,
disabledFromChainIDs,
disabledToChainIDs,
lockedInAmounts,
extraParamsTable
)
method filterChanged*(self: Module, addresses: seq[string], chainIds: seq[int]) =
if addresses.len == 0:
return

View File

@ -205,12 +205,12 @@ QtObject:
disbaledChains.add(item.getChainId())
return disbaledChains
proc getRouteLockedChainIds*(self: NetworkModel): string =
var jsonObject = newJObject()
proc getRouteLockedChainIds*(self: NetworkModel): Table[string, string] =
var lockedChains: Table[string, string]
for item in self.items:
if item.getLocked():
jsonObject[$item.getChainId()] = %* ("0x" & item.getLockedAmount())
return $jsonObject
lockedChains[$item.getChainId()] = "0x" & item.getLockedAmount()
return lockedChains
proc updateRoutePreferredChains*(self: NetworkModel, chainIds: string) =
try:
@ -232,12 +232,6 @@ QtObject:
except:
discard
proc getRoutePreferredNetworkChainIds*(self: NetworkModel): seq[int] =
var preferredChains: seq[int] = @[]
for item in self.items:
if item.getIsPreferred():
preferredChains.add(item.getChainId())
return preferredChains
proc disableRouteUnpreferredChains*(self: NetworkModel) =
for i in 0 ..< self.items.len:

View File

@ -1,4 +1,4 @@
import NimQml, sequtils, strutils, stint, sugar, options
import NimQml, Tables, json, sequtils, strutils, stint, sugar, options, chronicles
import ./io_interface, ./accounts_model, ./account_item, ./network_model, ./network_item, ./suggested_route_item, ./transaction_routes
import app/modules/shared_models/collectibles_model as collectibles
@ -218,14 +218,6 @@ QtObject:
proc sendTransactionSentSignal*(self: View, chainId: int, txHash: string, uuid: string, error: string) =
self.transactionSent(chainId, txHash, uuid, error)
proc parseAmount(amount: string): Uint256 =
var parsedAmount = stint.u256(0)
try:
parsedAmount = amount.parse(Uint256)
except Exception as e:
discard
return parsedAmount
proc parseChainIds(chainIds: string): seq[int] =
var parsedChainIds: seq[int] = @[]
for chainId in chainIds.split(':'):
@ -241,11 +233,28 @@ QtObject:
self.transactionRoutes = routes
self.suggestedRoutesReady(newQVariant(self.transactionRoutes))
proc suggestedRoutes*(self: View, amount: string) {.slot.} =
self.delegate.suggestedRoutes(self.selectedSenderAccount.address(), self.selectedRecipient,
parseAmount(amount), self.selectedAssetKey, self.selectedToAssetKey, self.fromNetworksModel.getRouteDisabledNetworkChainIds(),
self.toNetworksModel.getRouteDisabledNetworkChainIds(), self.toNetworksModel.getRoutePreferredNetworkChainIds(),
self.sendType, self.fromNetworksModel.getRouteLockedChainIds())
proc suggestedRoutes*(self: View, amountIn: string, amountOut: string, extraParamsJson: string) {.slot.} =
var extraParamsTable: Table[string, string]
try:
if extraParamsJson.len > 0:
for key, value in parseJson(extraParamsJson):
extraParamsTable[key] = value.getStr()
except Exception as e:
error "Error parsing extraParamsJson: ", msg=e.msg
self.delegate.suggestedRoutes(
self.sendType,
self.selectedSenderAccount.address(),
self.selectedRecipient,
self.selectedAssetKey,
amountIn,
self.selectedToAssetKey,
amountOut,
self.fromNetworksModel.getRouteDisabledNetworkChainIds(),
self.toNetworksModel.getRouteDisabledNetworkChainIds(),
self.fromNetworksModel.getRouteLockedChainIds(),
extraParamsTable
)
proc switchSenderAccountByAddress*(self: View, address: string) {.slot.} =
let (account, index) = self.senderAccounts.getItemByAddress(address)
@ -332,13 +341,37 @@ QtObject:
return self.fromNetworksModel.getIconUrl(chainId)
# "Stateless" methods
proc fetchSuggestedRoutesWithParameters*(self: View, accountFrom: string, accountTo: string, amount: string, token: string, toToken: string,
disabledFromChainIDs: string, disabledToChainIDs: string, preferredChainIDs: string, sendType: int, lockedInAmounts: string) {.slot.} =
self.delegate.suggestedRoutes(accountFrom, accountTo,
parseAmount(amount), token, toToken,
parseChainIds(disabledFromChainIDs), parseChainIds(disabledToChainIDs), parseChainIds(preferredChainIDs),
SendType(sendType), lockedInAmounts)
proc fetchSuggestedRoutesWithParameters*(self: View,
accountFrom: string,
accountTo: string,
amountIn: string,
amountOut: string,
token: string,
toToken: string,
disabledFromChainIDs: string,
disabledToChainIDs: string,
sendType: int,
lockedInAmounts: string) {.slot.} =
# Prepare lockedInAmountsTable
var lockedInAmountsTable = Table[string, string] : initTable[string, string]()
try:
for chainId, lockedAmount in parseJson(lockedInAmounts):
lockedInAmountsTable[chainId] = lockedAmount.getStr
except:
discard
# Resolve the best route
self.delegate.suggestedRoutes(
SendType(sendType),
accountFrom,
accountTo,
token,
amountIn,
toToken,
amountOut,
parseChainIds(disabledFromChainIDs),
parseChainIds(disabledToChainIDs),
lockedInAmountsTable)
proc authenticateAndTransferWithParameters*(self: View, uuid: string, accountFrom: string, accountTo: string, token: string, toToken: string,
sendTypeInt: int, tokenName: string, tokenIsOwnerToken: bool, rawPaths: string, slippagePercentageString: string) {.slot.} =

View File

@ -1,4 +1,5 @@
import json, times, strutils, sugar, os, re, chronicles
import json, times, stint, strutils, sugar, os, re, chronicles
import nimcrypto
import account_constants
@ -7,12 +8,12 @@ import ../../constants as main_constants
const STATUS_DOMAIN* = ".stateofus.eth"
const ETH_DOMAIN* = ".eth"
proc arrayContains*[T](arr: seq[T], value: T): bool =
proc arrayContains*[T](arr: seq[T], value: T): bool =
return arr.any(x => x == value)
proc hashPassword*(password: string, lower: bool = true): string =
let hashed = "0x" & $keccak_256.digest(password)
if lower:
return hashed.toLowerAscii()
@ -71,7 +72,7 @@ proc validateLink*(link: string): bool =
proc isPathOutOfTheDefaultStatusDerivationTree*(path: string): bool =
if not path.startsWith(account_constants.PATH_WALLET_ROOT&"/") or
path.count("'") != 3 or
path.count("/") != 5:
path.count("/") != 5:
return true
return false
@ -82,3 +83,11 @@ proc intersectSeqs*[T](seq1, seq2: seq[T]): seq[T] =
for item in seq1:
if item in seq2:
result.add(item)
proc stringToUint256*(value: string): Uint256 =
var parsedValue = stint.u256(0)
try:
parsedValue = value.parse(Uint256)
except Exception as e:
discard
return parsedValue

View File

@ -41,6 +41,7 @@ proc getFeesTotal*(paths: seq[TransactionPathDto]): FeesDto =
fees.totalFeesInEth += getGasEthValue(optimalPrice, path.gasAmount)
fees.totalFeesInEth += parseFloat(service_conversion.wei2Eth(service_conversion.gwei2Wei(path.gasFees.l1GasFee)))
fees.totalFeesInEth += path.approvalGasFees
fees.totalTokenFees += path.tokenFees
fees.totalTime += path.estimatedTime
return fees
@ -77,47 +78,6 @@ proc addFirstSimpleBridgeTxFlag(paths: seq[TransactionPathDto]) : seq[Transactio
return txPaths
proc getSuggestedRoutesTask*(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[GetSuggestedRoutesTaskArg](argEncoded)
try:
let amountAsHex = "0x" & eth_utils.stripLeadingZeros(arg.amount.toHex)
var lockedInAmounts = Table[string, string] : initTable[string, string]()
try:
for chainId, lockedAmount in parseJson(arg.lockedInAmounts):
lockedInAmounts[chainId] = lockedAmount.getStr
except:
discard
let response = eth.suggestedRoutes(arg.accountFrom, arg.accountTo, amountAsHex, arg.token, arg.toToken, arg.disabledFromChainIDs,
arg.disabledToChainIDs, arg.preferredChainIDs, ord(arg.sendType), lockedInAmounts).result
var bestPaths = response["Best"].getElems().map(x => x.toTransactionPathDto())
# retry along with unpreferred chains incase no route is possible with preferred chains
if arg.sendType != SendType.Swap and bestPaths.len == 0 and arg.preferredChainIDs.len > 0:
let response = eth.suggestedRoutes(arg.accountFrom, arg.accountTo, amountAsHex, arg.token, arg.toToken, arg.disabledFromChainIDs,
arg.disabledToChainIDs, @[], ord(arg.sendType), lockedInAmounts).result
bestPaths = response["Best"].getElems().map(x => x.toTransactionPathDto())
bestPaths.sort(sortAsc[TransactionPathDto])
let output = %*{
"suggestedRoutes": SuggestedRoutesDto(
best: addFirstSimpleBridgeTxFlag(bestPaths),
gasTimeEstimate: getFeesTotal(bestPaths),
amountToReceive: getTotalAmountToReceive(bestPaths),
toNetworks: getToNetworksList(bestPaths)),
"error": ""
}
arg.finish(output)
except Exception as e:
let output = %* {
"suggestedRoutes": SuggestedRoutesDto(best: @[], gasTimeEstimate: FeesDto(), amountToReceive: stint.u256(0), toNetworks: @[]),
"error": fmt"Error getting suggested routes: {e.msg}"
}
arg.finish(output)
type

View File

@ -1,4 +1,4 @@
import json, strutils, stint, json_serialization, stew/shims/strformat, sugar, sequtils
import json, strutils, stint, json_serialization, stew/shims/strformat
import
web3/ethtypes
@ -334,7 +334,7 @@ proc convertToTransactionPathsDto*(jsonObj: JsonNode): seq[TransactionPathDto] =
proc convertToTransactionPathsDto*(paths: string): seq[TransactionPathDto] =
return paths.parseJson.convertToTransactionPathsDto()
type
FeesDto* = ref object
totalFeesInEth*: float
@ -384,19 +384,3 @@ type
amountToReceive*: UInt256
toNetworks*: seq[SendToNetwork]
proc `$`*(self: SuggestedRoutesDto): string =
return fmt"""SuggestedRoutesDto(
best:{self.best},
rawBest:{self.rawBest},
gasTimeEstimate:{self.gasTimeEstimate},
amountToReceive:{self.amountToReceive},
toNetworks:{self.toNetworks},
)"""
proc convertToSuggestedRoutesDto*(jsonObj: JsonNode): SuggestedRoutesDto =
result = SuggestedRoutesDto()
result.rawBest = $jsonObj["suggestedRoutes"]["best"]
result.best = result.rawBest.convertToTransactionPathsDto()
result.gasTimeEstimate = jsonObj["suggestedRoutes"]["gasTimeEstimate"].convertToFeesDto()
result.amountToReceive = stint.u256(jsonObj["suggestedRoutes"]["amountToReceive"].getStr)
result.toNetworks = jsonObj["suggestedRoutes"]["toNetworks"].getElems().map(x => x.convertSendToNetwork())

View File

@ -0,0 +1,79 @@
# import json, json_serialization, stint
# import ../network/dto, ../token/dto
# include app_service/common/json_utils
import json, strutils, stint, json_serialization
import
web3/ethtypes
include ../../common/json_utils
import ../network/dto, ../token/dto
type
SuggestedLevelsForMaxFeesPerGasDto* = ref object
low*: UInt256
medium*: UInt256
high*: UInt256
type
TransactionPathDtoV2* = ref object
processorName*: string
fromChain*: NetworkDto
toChain*: NetworkDto
fromToken*: TokenDto
amountIn*: UInt256
amountInLocked*: bool
amountOut*: UInt256
suggestedLevelsForMaxFeesPerGas*: SuggestedLevelsForMaxFeesPerGasDto
txBaseFee*: UInt256
txPriorityFee*: UInt256
txGasAmount*: uint64
txBonderFees*: UInt256
txTokenFees*: UInt256
txL1Fee*: UInt256
approvalRequired*: bool
approvalAmountRequired*: UInt256
approvalContractAddress*: string
approvalBaseFee*: UInt256
approvalPriorityFee*: UInt256
approvalGasAmount*: uint64
approvalL1Fee*: UInt256
estimatedTime*: int
proc toSuggestedLevelsForMaxFeesPerGasDto*(jsonObj: JsonNode): SuggestedLevelsForMaxFeesPerGasDto =
result = SuggestedLevelsForMaxFeesPerGasDto()
var value: string
if jsonObj.getProp("low", value):
result.low = stint.fromHex(UInt256, $value)
if jsonObj.getProp("medium", value):
result.medium = stint.fromHex(UInt256, $value)
if jsonObj.getProp("high", value):
result.high = stint.fromHex(UInt256, $value)
proc toTransactionPathDtoV2*(jsonObj: JsonNode): TransactionPathDtoV2 =
result = TransactionPathDtoV2()
discard jsonObj.getProp("ProcessorName", result.processorName)
result.fromChain = Json.decode($jsonObj["FromChain"], NetworkDto, allowUnknownFields = true)
result.toChain = Json.decode($jsonObj["ToChain"], NetworkDto, allowUnknownFields = true)
result.fromToken = Json.decode($jsonObj["FromToken"], TokenDto, allowUnknownFields = true)
result.amountIn = stint.fromHex(UInt256, jsonObj{"AmountIn"}.getStr)
discard jsonObj.getProp("AmountInLocked", result.amountInLocked)
result.amountOut = stint.fromHex(UInt256, jsonObj{"AmountOut"}.getStr)
result.suggestedLevelsForMaxFeesPerGas = jsonObj["SuggestedLevelsForMaxFeesPerGas"].toSuggestedLevelsForMaxFeesPerGasDto()
result.txBaseFee = stint.fromHex(UInt256, jsonObj{"TxBaseFee"}.getStr)
result.txPriorityFee = stint.fromHex(UInt256, jsonObj{"TxPriorityFee"}.getStr)
discard jsonObj.getProp("TxGasAmount", result.txGasAmount)
result.txBonderFees = stint.fromHex(UInt256, jsonObj{"TxBonderFees"}.getStr)
result.txTokenFees = stint.fromHex(UInt256, jsonObj{"TxTokenFees"}.getStr)
result.txL1Fee = stint.fromHex(UInt256, jsonObj{"TxL1Fee"}.getStr)
discard jsonObj.getProp("ApprovalRequired", result.approvalRequired)
result.approvalAmountRequired = stint.fromHex(UInt256, jsonObj{"ApprovalAmountRequired"}.getStr)
discard jsonObj.getProp("ApprovalContractAddress", result.approvalContractAddress)
result.approvalBaseFee = stint.fromHex(UInt256, jsonObj{"ApprovalBaseFee"}.getStr)
result.approvalPriorityFee = stint.fromHex(UInt256, jsonObj{"ApprovalPriorityFee"}.getStr)
discard jsonObj.getProp("ApprovalGasAmount", result.approvalGasAmount)
result.approvalL1Fee = stint.fromHex(UInt256, jsonObj{"ApprovalL1Fee"}.getStr)
result.estimatedTime = jsonObj{"EstimatedTime"}.getInt

View File

@ -1,4 +1,4 @@
import Tables, NimQml, chronicles, sequtils, sugar, stint, strutils, json, stew/shims/strformat, algorithm
import Tables, NimQml, chronicles, sequtils, sugar, stint, strutils, json, algorithm, uuids, stew/shims/strformat
import backend/collectibles as collectibles
import backend/transactions as transactions
@ -23,6 +23,7 @@ import app_service/service/settings/service as settings_service
import app_service/service/eth/dto/transaction as transaction_data_dto
import app_service/service/eth/dto/[coder, method_dto]
import ./dto as transaction_dto
import ./dtoV2
import ./cryptoRampDto
import app_service/service/eth/utils as eth_utils
import app_service/common/conversion
@ -128,6 +129,9 @@ QtObject:
settingsService: settings_service.Service
tokenService: token_service.Service
## Forward declarations
proc suggestedRoutesV2Ready(self: Service, uuid: string, route: seq[TransactionPathDtoV2], error: string, errCode: string)
proc delete*(self: Service) =
self.QObject.delete
@ -155,6 +159,10 @@ QtObject:
of transactions.EventFetchingHistoryError:
self.events.emit(SIGNAL_HISTORY_ERROR, Args())
self.events.on(SignalType.WalletSuggestedRoutes.event) do(e:Args):
var data = WalletSignal(e)
self.suggestedRoutesV2Ready(data.uuid, data.bestRoute, data.error, data.errorCode)
self.events.on(PendingTransactionTypeDto.WalletTransfer.event) do(e: Args):
try:
var receivedData = TransactionMinedArgs(e)
@ -561,46 +569,106 @@ QtObject:
except Exception as e:
error "Error getting suggested fees", msg = e.msg
proc suggestedRoutesReady*(self: Service, suggestedRoutes: string) {.slot.} =
var suggestedRoutesDto: SuggestedRoutesDto = SuggestedRoutesDto()
try:
let responseObj = suggestedRoutes.parseJson
suggestedRoutesDto = responseObj.convertToSuggestedRoutesDto()
except Exception as e:
error "error handling suggestedRoutesReady response", errDesription=e.msg
self.events.emit(SIGNAL_SUGGESTED_ROUTES_READY, SuggestedRoutesArgs(suggestedRoutes: suggestedRoutesDto))
proc convertToOldRoute(route: seq[TransactionPathDtoV2]): seq[TransactionPathDto] =
const
gweiDecimals = 9
ethDecimals = 18
for p in route:
var
fees = SuggestedFeesDto()
trPath = TransactionPathDto()
proc suggestedRoutes*(self: Service, accountFrom: string, accountTo: string, amount: Uint256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string) =
var
tokenId: string
toTokenId: string
try:
# prepare fees
fees.gasPrice = 0
var value = conversion.wei2Eth(input = p.txBaseFee, decimals = gweiDecimals)
fees.baseFee = parseFloat(value)
value = conversion.wei2Eth(input = p.txPriorityFee, decimals = gweiDecimals)
fees.maxPriorityFeePerGas = parseFloat(value)
value = conversion.wei2Eth(input = p.suggestedLevelsForMaxFeesPerGas.low, decimals = gweiDecimals)
fees.maxFeePerGasL = parseFloat(value)
value = conversion.wei2Eth(input = p.suggestedLevelsForMaxFeesPerGas.medium, decimals = gweiDecimals)
fees.maxFeePerGasM = parseFloat(value)
value = conversion.wei2Eth(input = p.suggestedLevelsForMaxFeesPerGas.high, decimals = gweiDecimals)
fees.maxFeePerGasH = parseFloat(value)
value = conversion.wei2Eth(input = p.txL1Fee, decimals = gweiDecimals)
fees.l1GasFee = parseFloat(value)
fees.eip1559Enabled = true
if self.isCollectiblesTransfer(sendType):
tokenId = token
else:
let token = self.tokenService.getTokenBySymbolByTokensKey(token)
if token != nil:
tokenId = token.symbol
let toToken = self.tokenService.getTokenBySymbolByTokensKey(toToken)
if toToken != nil:
toTokenId = toToken.symbol
let arg = GetSuggestedRoutesTaskArg(
tptr: getSuggestedRoutesTask,
vptr: cast[ByteAddress](self.vptr),
slot: "suggestedRoutesReady",
accountFrom: accountFrom,
accountTo: accountTo,
amount: amount,
token: tokenId,
toToken: toTokenId,
disabledFromChainIDs: disabledFromChainIDs,
disabledToChainIDs: disabledToChainIDs,
preferredChainIDs: preferredChainIDs,
sendType: sendType,
lockedInAmounts: lockedInAmounts
# prepare tx path
trPath.bridgeName = p.processorName
trPath.fromNetwork = p.fromChain
trPath.toNetwork = p.toChain
trPath.gasFees = fees
# trPath.cost = not in use for old approach in the desktop app
value = conversion.wei2Eth(input = p.txTokenFees, decimals = p.fromToken.decimals)
trPath.tokenFees = parseFloat(value)
value = conversion.wei2Eth(input = p.txBonderFees, decimals = p.fromToken.decimals)
trPath.bonderFees = value
trPath.tokenFees += parseFloat(value) # we add bonder fees to the token fees cause in the UI, atm, we show only token fees
trPath.maxAmountIn = stint.fromHex(UInt256, "0x0")
trPath.amountIn = p.amountIn
trPath.amountOut = p.amountOut
trPath.approvalRequired = p.approvalRequired
trPath.approvalAmountRequired = p.approvalAmountRequired
trPath.approvalContractAddress = p.approvalContractAddress
trPath.amountInLocked = p.amountInLocked
trPath.estimatedTime = p.estimatedTime
trPath.gasAmount = p.txGasAmount
value = conversion.wei2Eth(p.suggestedLevelsForMaxFeesPerGas.medium, decimals = ethDecimals)
trPath.approvalGasFees = parseFloat(value) * float64(p.approvalGasAmount)
value = conversion.wei2Eth(p.approvalL1Fee, decimals = ethDecimals)
trPath.approvalGasFees += parseFloat(value)
trPath.isFirstSimpleTx = false
trPath.isFirstBridgeTx = false
except Exception as e:
error "Error converting to old path", msg = e.msg
# add tx path to the list
result.add(trPath)
result.sort(sortAsc[TransactionPathDto])
proc suggestedRoutesV2Ready(self: Service, uuid: string, route: seq[TransactionPathDtoV2], error: string, errCode: string) =
# TODO: refactor sending modal part of the app, but for now since we're integrating the router v2 just map params to the old dto
var oldRoute = convertToOldRoute(route)
let suggestedDto = SuggestedRoutesDto(
best: addFirstSimpleBridgeTxFlag(oldRoute),
gasTimeEstimate: getFeesTotal(oldRoute),
amountToReceive: getTotalAmountToReceive(oldRoute),
toNetworks: getToNetworksList(oldRoute),
)
self.threadpool.start(arg)
self.events.emit(SIGNAL_SUGGESTED_ROUTES_READY, SuggestedRoutesArgs(suggestedRoutes: suggestedDto))
proc suggestedRoutes*(self: Service,
sendType: SendType,
accountFrom: string,
accountTo: string,
token: string,
amountIn: string,
toToken: string = "",
amountOut: string = "",
disabledFromChainIDs: seq[int] = @[],
disabledToChainIDs: seq[int] = @[],
lockedInAmounts: Table[string, string] = initTable[string, string](),
extraParamsTable: Table[string, string] = initTable[string, string]()) =
let
bigAmountIn = common_utils.stringToUint256(amountIn)
bigAmountOut = common_utils.stringToUint256(amountOut)
amountInHex = "0x" & eth_utils.stripLeadingZeros(bigAmountIn.toHex)
amountOutHex = "0x" & eth_utils.stripLeadingZeros(bigAmountOut.toHex)
try:
let uuid = $genUUID()
let res = eth.suggestedRoutesV2Async(uuid, ord(sendType), accountFrom, accountTo, amountInHex, amountOutHex, token,
toToken, disabledFromChainIDs, disabledToChainIDs, lockedInAmounts, extraParamsTable)
except CatchableError as e:
error "suggestedRoutes", exception=e.msg
proc onFetchCryptoServices*(self: Service, response: string) {.slot.} =
let cryptoServices = parseJson(response){"result"}.getElems().map(x => x.toCryptoRampDto())

View File

@ -4,6 +4,18 @@ from ./gen import rpc
export response_type
const
GasFeeLow* = 0
GasFeeMedium* = 1
GasFeeHigh* = 2
const
ExtraKeyUsername* = "username"
ExtraKeyPublicKey* = "publicKey"
ExtraKeyPackId* = "packID"
ExtraKeys = @[ExtraKeyUsername, ExtraKeyPublicKey, ExtraKeyPackId]
proc getAccounts*(): RpcResponse[JsonNode] =
return core.callPrivateRPC("eth_accounts")
@ -26,11 +38,56 @@ proc suggestedFees*(chainId: int): RpcResponse[JsonNode] =
let payload = %* [chainId]
return core.callPrivateRPC("wallet_getSuggestedFees", payload)
proc suggestedRoutes*(accountFrom: string, accountTo: string, amount: string, token: string, toToken: string, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs: seq[int], sendType: int, lockedInAmounts: var Table[string, string]): RpcResponse[JsonNode] =
let payload = %* [sendType, accountFrom, accountTo, amount, token, toToken, disabledFromChainIDs, disabledToChainIDs,
preferredChainIDs, 1, lockedInAmounts]
return core.callPrivateRPC("wallet_getSuggestedRoutes", payload)
proc prepareDataForSuggestedRoutesV2(uuid: string, sendType: int, accountFrom: string, accountTo: string, amountIn: string, amountOut: string,
token: string, toToken: string, disabledFromChainIDs, disabledToChainIDs: seq[int], lockedInAmounts: Table[string, string],
extraParamsTable: Table[string, string]): JsonNode =
let data = %* {
"uuid": uuid,
"sendType": sendType,
"addrFrom": accountFrom,
"addrTo": accountTo,
"amountIn": amountIn,
"amountOut": amountOut,
"tokenID": token,
"toTokenID": toToken,
"disabledFromChainIDs": disabledFromChainIDs,
"disabledToChainIDs": disabledToChainIDs,
"gasFeeMode": GasFeeMedium,
"fromLockedAmount": lockedInAmounts
}
# `extraParamsTable` is used for send types like EnsRegister, EnsRelease, EnsSetPubKey, StickersBuy
# keys that can be used in `extraParamsTable` are:
# "username", "publicKey", "packID"
for key, value in extraParamsTable:
if key in ExtraKeys:
data[key] = %* value
else:
return nil
return %* [data]
proc suggestedRoutesV2*(sendType: int, accountFrom: string, accountTo: string, amountIn: string, amountOut: string, token: string,
toToken: string, disabledFromChainIDs, disabledToChainIDs: seq[int], lockedInAmounts: Table[string, string],
extraParamsTable: Table[string, string]): RpcResponse[JsonNode] {.raises: [RpcException].} =
let payload = prepareDataForSuggestedRoutesV2(uuid = "", sendType, accountFrom, accountTo, amountIn, amountOut, token, toToken, disabledFromChainIDs,
disabledToChainIDs, lockedInAmounts, extraParamsTable)
if payload.isNil:
raise newException(RpcException, "Invalid key in extraParamsTable")
return core.callPrivateRPC("wallet_getSuggestedRoutesV2", payload)
proc suggestedRoutesV2Async*(uuid: string, sendType: int, accountFrom: string, accountTo: string, amountIn: string, amountOut: string, token: string,
toToken: string, disabledFromChainIDs, disabledToChainIDs: seq[int], lockedInAmounts: Table[string, string],
extraParamsTable: Table[string, string]): RpcResponse[JsonNode] {.raises: [RpcException].} =
let payload = prepareDataForSuggestedRoutesV2(uuid, sendType, accountFrom, accountTo, amountIn, amountOut, token, toToken, disabledFromChainIDs,
disabledToChainIDs, lockedInAmounts, extraParamsTable)
if payload.isNil:
raise newException(RpcException, "Invalid key in extraParamsTable")
return core.callPrivateRPC("wallet_getSuggestedRoutesV2Async", payload)
proc stopSuggestedRoutesV2AsyncCalcualtion*() : RpcResponse[JsonNode] =
return core.callPrivateRPC("wallet_stopSuggestedRoutesV2AsyncCalcualtion")
rpc(getEstimatedLatestBlockNumber, "wallet"):
chainId: int

View File

@ -188,10 +188,6 @@ QObject {
return root.currencyStore.formatCurrencyAmountFromBigInt(balance, symbol, decimals, options)
}
function getAllChainIds() {
return ModelUtils.joinModelEntries(root.filteredFlatNetworksModel, "chainId", ":")
}
function getDisabledChainIds(enabledChainId) {
let disabledChainIds = []
let chainIds = ModelUtils.modelToFlatArray(root.filteredFlatNetworksModel, "chainId")
@ -203,8 +199,8 @@ QObject {
return disabledChainIds.join(":")
}
function fetchSuggestedRoutes(cryptoValueRaw) {
if (root.swapFormData.isFormFilledCorrectly() && !!cryptoValueRaw) {
function fetchSuggestedRoutes(cryptoValueInRaw) {
if (root.swapFormData.isFormFilledCorrectly() && !!cryptoValueInRaw) {
root.swapProposalLoading = true
root.swapOutputData.reset()
@ -214,12 +210,10 @@ QObject {
let account = selectedAccountEntry.item
let accountAddress = account.address
let disabledChainIds = getDisabledChainIds(root.swapFormData.selectedNetworkChainId)
let preferedChainIds = getAllChainIds()
root.swapStore.fetchSuggestedRoutes(accountAddress, accountAddress,
cryptoValueRaw, root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
disabledChainIds, disabledChainIds, preferedChainIds,
Constants.SendType.Swap, "")
cryptoValueInRaw, "0", root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
disabledChainIds, disabledChainIds, Constants.SendType.Swap, "")
} else {
root.swapProposalLoading = false
}
@ -230,7 +224,7 @@ QObject {
let accountAddress = account.address
root.swapStore.authenticateAndTransfer(d.uuid, accountAddress, accountAddress,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
Constants.SendType.Approve, "", false, root.swapOutputData.rawPaths, "")
}
@ -239,7 +233,7 @@ QObject {
let accountAddress = account.address
root.swapStore.authenticateAndTransfer(d.uuid, accountAddress, accountAddress,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
Constants.SendType.Swap, "", false, root.swapOutputData.rawPaths, root.swapFormData.selectedSlippage)
}
}

View File

@ -25,11 +25,12 @@ QtObject {
}
}
function fetchSuggestedRoutes(accountFrom, accountTo, amount, tokenFrom, tokenTo,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts) {
const value = AmountsArithmetic.fromNumber(amount)
root.walletSectionSendInst.fetchSuggestedRoutesWithParameters(accountFrom, accountTo, value.toFixed(),
tokenFrom, tokenTo, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts)
function fetchSuggestedRoutes(accountFrom, accountTo, amountIn, amountOut, tokenFrom, tokenTo,
disabledFromChainIDs, disabledToChainIDs, sendType, lockedInAmounts) {
const valueIn = AmountsArithmetic.fromNumber(amountIn)
const valueOut = AmountsArithmetic.fromNumber(amountOut)
root.walletSectionSendInst.fetchSuggestedRoutesWithParameters(accountFrom, accountTo, valueIn.toFixed(), valueOut.toFixed(),
tokenFrom, tokenTo, disabledFromChainIDs, disabledToChainIDs, sendType, lockedInAmounts)
}
function authenticateAndTransfer(uuid, accountFrom, accountTo,

View File

@ -57,9 +57,9 @@ Item {
return fee
}
property double totalGasAmountL1Eth: {
let maxFees = modelData.gasFees.maxFeePerGasM
let gasPrice = modelData.gasFees.eip1559Enabled? maxFees : modelData.gasFees.gasPrice
return root.getGasEthValue(gasPrice , modelData.gasFees.l1GasFee)
const l1FeeInGWei = modelData.gasFees.l1GasFee
const l1FeeInEth = globalUtils.wei2Eth(l1FeeInGWei, 9)
return l1FeeInEth
}
property double totalGasAmountEth: {

View File

@ -66,9 +66,10 @@ QtObject {
walletSectionSendInst.authenticateAndTransfer(uuid)
}
function suggestedRoutes(amount) {
const value = AmountsArithmetic.fromNumber(amount)
walletSectionSendInst.suggestedRoutes(value.toFixed())
function suggestedRoutes(amountIn, amountOut = "0", extraParamsJson = "") {
const valueIn = AmountsArithmetic.fromNumber(amountIn)
const valueOut = AmountsArithmetic.fromNumber(amountOut)
walletSectionSendInst.suggestedRoutes(valueIn.toFixed(), valueOut.toFixed(), extraParamsJson)
}
function resolveENS(value) {

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit e6bf7e7df9939efb05b0349a87c163540fe08dd9
Subproject commit 4d7c2683f5536c2b5a10a3b7d2230ec9c8860f0f