feat: swap via paraswap backend implementation

Closes #14241
This commit is contained in:
Sale Djenic 2024-04-05 21:40:49 +02:00 committed by saledjenic
parent 62c48a0072
commit 5bf8f5c456
10 changed files with 125 additions and 57 deletions

View File

@ -109,16 +109,16 @@ 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, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): string =
let suggestedRoutes = self.transactionService.suggestedRoutes(accountFrom, accountTo, amount, token, disabledFromChainIDs,
proc suggestedRoutes*(self: Controller, accountFrom: string, accountTo: string, amount: Uint256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): string =
let suggestedRoutes = self.transactionService.suggestedRoutes(accountFrom, accountTo, amount, token, toToken, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts)
return suggestedRoutes.toJson()
proc transfer*(self: Controller, from_addr: string, to_addr: string, assetKey: string,
proc transfer*(self: Controller, from_addr: string, to_addr: string, assetKey: string, toAssetKey: string,
uuid: string, selectedRoutes: seq[TransactionPathDto], password: string, sendType: SendType,
usePassword: bool, doHashing: bool, tokenName: string, isOwnerToken: bool) =
self.transactionService.transfer(from_addr, to_addr, assetKey, uuid, selectedRoutes, password, sendType,
self.transactionService.transfer(from_addr, to_addr, assetKey, toAssetKey, uuid, selectedRoutes, password, sendType,
usePassword, doHashing, tokenName, isOwnerToken)
proc proceedWithTransactionsSignatures*(self: Controller, fromAddr: string, toAddr: string, uuid: string,

View File

@ -24,15 +24,15 @@ 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, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): string {.base.} =
method suggestedRoutes*(self: AccessInterface, accountFrom: string, accountTo: string, amount: UInt256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method suggestedRoutesReady*(self: AccessInterface, suggestedRoutes: SuggestedRoutesDto) {.base.} =
raise newException(ValueError, "No implementation available")
method authenticateAndTransfer*(self: AccessInterface, from_addr: string, to_addr: string,
assetKey: string, uuid: string, sendType: SendType, selectedTokenName: string, selectedTokenIsOwnerToken: bool) {.base.} =
method authenticateAndTransfer*(self: AccessInterface, from_addr: string, to_addr: string, assetKey: string,
toAssetKey: string, uuid: string, sendType: SendType, selectedTokenName: string, selectedTokenIsOwnerToken: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} =

View File

@ -34,6 +34,7 @@ type TmpSendTransactionDetails = object
fromAddrPath: string
toAddr: string
assetKey: string
toAssetKey: string
paths: seq[TransactionPathDto]
uuid: string
sendType: SendType
@ -256,11 +257,12 @@ method viewDidLoad*(self: Module) =
method getTokenBalance*(self: Module, address: string, chainId: int, tokensKey: string): CurrencyAmount =
return self.controller.getTokenBalance(address, chainId, tokensKey)
method authenticateAndTransfer*(self: Module, fromAddr: string, toAddr: string, assetKey: string, uuid: string,
method authenticateAndTransfer*(self: Module, fromAddr: string, toAddr: string, assetKey: string, toAssetKey: string, uuid: string,
sendType: SendType, selectedTokenName: string, selectedTokenIsOwnerToken: bool) =
self.tmpSendTransactionDetails.fromAddr = fromAddr
self.tmpSendTransactionDetails.toAddr = toAddr
self.tmpSendTransactionDetails.assetKey = assetKey
self.tmpSendTransactionDetails.toAssetKey = toAssetKey
self.tmpSendTransactionDetails.uuid = uuid
self.tmpSendTransactionDetails.sendType = sendType
self.tmpSendTransactionDetails.fromAddrPath = ""
@ -288,7 +290,7 @@ method onUserAuthenticated*(self: Module, password: string, pin: string) =
let usePassword = self.tmpSendTransactionDetails.fromAddrPath.len == 0
self.controller.transfer(
self.tmpSendTransactionDetails.fromAddr, self.tmpSendTransactionDetails.toAddr,
self.tmpSendTransactionDetails.assetKey, self.tmpSendTransactionDetails.uuid,
self.tmpSendTransactionDetails.assetKey, self.tmpSendTransactionDetails.toAssetKey, self.tmpSendTransactionDetails.uuid,
self.tmpSendTransactionDetails.paths, password, self.tmpSendTransactionDetails.sendType, usePassword, doHashing,
self.tmpSendTransactionDetails.tokenName, self.tmpSendTransactionDetails.isOwnerToken
)
@ -330,9 +332,10 @@ 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, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): string =
return self.controller.suggestedRoutes(accountFrom, accountTo, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts)
method suggestedRoutes*(self: Module, accountFrom: string, accountTo: string, amount: UInt256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): string =
return self.controller.suggestedRoutes(accountFrom, accountTo, amount, token, toToken, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts)
method suggestedRoutesReady*(self: Module, suggestedRoutes: SuggestedRoutesDto) =
self.tmpSendTransactionDetails.paths = suggestedRoutes.best

View File

@ -21,6 +21,7 @@ QtObject:
toNetworksModel: NetworkModel
transactionRoutes: TransactionRoutes
selectedAssetKey: string
selectedToAssetKey: string
showUnPreferredChains: bool
sendType: transaction_dto.SendType
selectedTokenIsOwnerToken: bool
@ -138,6 +139,18 @@ QtObject:
read = getSelectedAssetKey
notify = selectedAssetKeyChanged
proc selectedToAssetKeyChanged*(self: View) {.signal.}
proc getSelectedToAssetKey*(self: View): string {.slot.} =
return self.selectedToAssetKey
proc setSelectedToAssetKey(self: View, assetKey: string) {.slot.} =
self.selectedToAssetKey = assetKey
self.updateNetworksTokenBalance()
self.selectedToAssetKeyChanged()
QtProperty[string] selectedToAssetKey:
write = setSelectedToAssetKey
read = getSelectedToAssetKey
notify = selectedToAssetKeyChanged
proc showUnPreferredChainsChanged*(self: View) {.signal.}
proc getShowUnPreferredChains(self: View): bool {.slot.} =
return self.showUnPreferredChains
@ -207,7 +220,7 @@ QtObject:
proc authenticateAndTransfer*(self: View, uuid: string) {.slot.} =
self.delegate.authenticateAndTransfer(self.selectedSenderAccount.address(), self.selectedRecipient, self.selectedAssetKey,
uuid, self.sendType, self.selectedTokenName, self.selectedTokenIsOwnerToken)
self.selectedToAssetKey, uuid, self.sendType, self.selectedTokenName, self.selectedTokenIsOwnerToken)
proc suggestedRoutesReady*(self: View, suggestedRoutes: QVariant) {.signal.}
proc setTransactionRoute*(self: View, routes: TransactionRoutes) =
@ -222,7 +235,7 @@ QtObject:
discard
return self.delegate.suggestedRoutes(self.selectedSenderAccount.address(), self.selectedRecipient,
parsedAmount, self.selectedAssetKey, self.fromNetworksModel.getRouteDisabledNetworkChainIds(),
parsedAmount, self.selectedAssetKey, self.selectedToAssetKey, self.fromNetworksModel.getRouteDisabledNetworkChainIds(),
self.toNetworksModel.getRouteDisabledNetworkChainIds(), self.toNetworksModel.getRoutePreferredNetworkChainIds(),
self.sendType, self.fromNetworksModel.getRouteLockedChainIds())

View File

@ -68,6 +68,7 @@ type TransactionBridgeDto* = object
cbridgeTx*: TransactionDataDto
eRC721TransferTx*: TransactionDataDto
eRC1155TransferTx*: TransactionDataDto
swapTx*: TransactionDataDto
proc `%`*(x: TransactionBridgeDto): JsonNode =
result = newJobject()
@ -78,3 +79,4 @@ proc `%`*(x: TransactionBridgeDto): JsonNode =
result["cbridgeTx"] = %x.cbridgeTx
result["eRC721TransferTx"] = %x.eRC721TransferTx
result["eRC1155TransferTx"] = %x.eRC1155TransferTx
result["swapTx"] = %x.swapTx

View File

@ -17,6 +17,7 @@ type
accountTo: string
amount: Uint256
token: string
toToken: string # used for swap only
disabledFromChainIDs: seq[int]
disabledToChainIDs: seq[int]
preferredChainIDs: seq[int]
@ -89,17 +90,18 @@ const getSuggestedRoutesTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall
except:
discard
let response = eth.suggestedRoutes(arg.accountFrom, arg.accountTo, amountAsHex, arg.token, arg.disabledFromChainIDs,
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(bestPaths.len == 0 and arg.preferredChainIDs.len > 0):
let response = eth.suggestedRoutes(arg.accountFrom, arg.accountTo, amountAsHex, arg.token, arg.disabledFromChainIDs,
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),

View File

@ -19,6 +19,7 @@ type
Bridge
ERC721Transfer
ERC1155Transfer
Swap
type
PendingTransactionTypeDto* {.pure.} = enum
@ -202,14 +203,21 @@ proc decodeSuggestedFeesDto*(jsonObj: JsonNode): SuggestedFeesDto =
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)
if jsonObj.hasKey("l1GasFee"):
result.l1GasFee = parseFloat(jsonObj{"l1GasFee"}.getStr)
var stringValue: string
if jsonObj.getProp("gasPrice", stringValue) and stringValue.len > 0:
result.gasPrice = parseFloat(stringValue)
if jsonObj.getProp("baseFee", stringValue) and stringValue.len > 0:
result.baseFee = parseFloat(stringValue)
if jsonObj.getProp("maxPriorityFeePerGas", stringValue) and stringValue.len > 0:
result.maxPriorityFeePerGas = parseFloat(stringValue)
if jsonObj.getProp("maxFeePerGasLow", stringValue) and stringValue.len > 0:
result.maxFeePerGasL = parseFloat(stringValue)
if jsonObj.getProp("maxFeePerGasMedium", stringValue) and stringValue.len > 0:
result.maxFeePerGasM = parseFloat(stringValue)
if jsonObj.getProp("maxFeePerGasHigh", stringValue) and stringValue.len > 0:
result.maxFeePerGasH = parseFloat(stringValue)
if jsonObj.getProp("l1GasFee", stringValue) and stringValue.len > 0:
result.l1GasFee = parseFloat(stringValue)
result.eip1559Enabled = jsonObj{"eip1559Enabled"}.getbool
proc `$`*(self: SuggestedFeesDto): string =
@ -275,8 +283,11 @@ proc toTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =
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)
var stringValue: string
if jsonObj.getProp("Cost", stringValue) and stringValue.len > 0:
result.cost = parseFloat(stringValue)
if jsonObj.getProp("TokenFees", stringValue) and stringValue.len > 0:
result.tokenFees = parseFloat(stringValue)
result.bonderFees = jsonObj{"BonderFees"}.getStr
result.maxAmountIn = stint.fromHex(UInt256, jsonObj{"MaxAmountIn"}.getStr)
result.amountIn = stint.fromHex(UInt256, jsonObj{"AmountIn"}.getStr)
@ -288,7 +299,8 @@ proc toTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =
result.isFirstBridgeTx = false
discard jsonObj.getProp("ApprovalRequired", result.approvalRequired)
result.approvalAmountRequired = stint.fromHex(UInt256, jsonObj{"ApprovalAmountRequired"}.getStr)
result.approvalGasFees = parseFloat(jsonObj{"ApprovalGasFees"}.getStr)
if jsonObj.getProp("ApprovalGasFees", stringValue) and stringValue.len > 0:
result.approvalGasFees = parseFloat(stringValue)
discard jsonObj.getProp("ApprovalContractAddress", result.approvalContractAddress)
proc convertToTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =

View File

@ -50,6 +50,7 @@ const SIMPLE_TX_BRIDGE_NAME = "Transfer"
const HOP_TX_BRIDGE_NAME = "Hop"
const ERC721_TRANSFER_NAME = "ERC721Transfer"
const ERC1155_TRANSFER_NAME = "ERC1155Transfer"
const SWAP_PARASWAP_NAME = "Paraswap"
type TokenTransferMetadata* = object
tokenName*: string
@ -263,6 +264,7 @@ QtObject:
var cbridgeTx = TransactionDataDto()
var eRC721TransferTx = TransactionDataDto()
var eRC1155TransferTx = TransactionDataDto()
var swapTx = TransactionDataDto()
if(route.bridgeName == SIMPLE_TX_BRIDGE_NAME):
path.transferTx = txData
@ -287,6 +289,10 @@ QtObject:
eRC1155TransferTx.tokenID = stint.u256(tokenSymbol).some
eRC1155TransferTx.amount = route.amountIn.some
path.eRC1155TransferTx = eRC1155TransferTx
elif(route.bridgeName == SWAP_PARASWAP_NAME):
swapTx = txData
swapTx.chainID = route.toNetwork.chainId.some
path.swapTx = swapTx
else:
cbridgeTx = txData
cbridgeTx.chainID = route.toNetwork.chainId.some
@ -324,9 +330,11 @@ QtObject:
from_addr: string,
to_addr: string,
tokenSymbol: string,
toTokenSymbol: string,
uuid: string,
routes: seq[TransactionPathDto],
password: string
password: string,
sendType: SendType
) =
try:
var paths: seq[TransactionBridgeDto] = @[]
@ -350,15 +358,20 @@ QtObject:
paths.add(self.createPath(route, txData, tokenSymbol, to_addr))
let response = transactions.createMultiTransaction(
MultiTransactionCommandDto(
var mtCommand = MultiTransactionCommandDto(
fromAddress: from_addr,
toAddress: to_addr,
fromAsset: tokenSymbol,
toAsset: tokenSymbol,
toAsset: toTokenSymbol,
fromAmount: "0x" & totalAmountToSend.toHex,
multiTxType: transactions.MultiTransactionType.MultiTransactionSend,
),
)
if sendType == Swap:
mtCommand.multiTxType = transactions.MultiTransactionType.MultiTransactionSwap
let response = transactions.createMultiTransaction(
mtCommand,
paths,
password,
)
@ -376,6 +389,8 @@ QtObject:
to_addr: string,
assetKey: string,
asset: TokenBySymbolItem,
toAssetKey: string,
toAsset: TokenBySymbolItem,
uuid: string,
routes: seq[TransactionPathDto],
password: string,
@ -391,7 +406,7 @@ QtObject:
fromAddress: from_addr,
toAddress: to_addr,
fromAsset: if not asset.isNil: asset.symbol else: assetKey,
toAsset: if not asset.isNil: asset.symbol else: assetKey,
toAsset: if not toAsset.isNil: toAsset.symbol else: toAssetKey,
multiTxType: transactions.MultiTransactionType.MultiTransactionSend,
)
@ -406,6 +421,9 @@ QtObject:
error "Invalid assetKey for collectibles transfer", assetKey=assetKey
return
if sendType == Swap:
mtCommand.multiTxType = transactions.MultiTransactionType.MultiTransactionSwap
try:
for route in routes:
var txData = TransactionDataDto()
@ -466,6 +484,7 @@ QtObject:
fromAddr: string,
toAddr: string,
assetKey: string,
toAssetKey: string,
uuid: string,
selectedRoutes: seq[TransactionPathDto],
password: string,
@ -487,20 +506,29 @@ QtObject:
chainID = selectedRoutes[0].fromNetwork.chainID
# asset == nil means transferToken is executed for a collectibles transfer
var asset: TokenBySymbolItem
var
asset: TokenBySymbolItem
toAsset: TokenBySymbolItem
if not self.isCollectiblesTransfer(sendType):
asset = self.tokenService.getTokenBySymbolByTokensKey(assetKey)
if not asset.isNil:
let network = self.networkService.getNetworkByChainId(chainID)
if not network.isNil and network.nativeCurrencySymbol == asset.symbol:
self.transferEth(fromAddr, toAddr, asset.symbol, uuid, selectedRoutes, finalPassword)
return
# else continue with asset transfer
else:
if asset.isNil:
error "Asset not found for", assetKey=assetKey
return
self.transferToken(fromAddr, toAddr, assetKey, asset, uuid, selectedRoutes, finalPassword, sendType, tokenName, isOwnerToken)
toAsset = asset
if sendType == Swap:
toAsset = self.tokenService.getTokenBySymbolByTokensKey(toAssetKey)
if toAsset.isNil:
error "Asset not found for", assetKey=assetKey
return
let network = self.networkService.getNetworkByChainId(chainID)
if not network.isNil and network.nativeCurrencySymbol == asset.symbol:
self.transferEth(fromAddr, toAddr, asset.symbol, toAsset.symbol, uuid, selectedRoutes, finalPassword, sendType)
return
self.transferToken(fromAddr, toAddr, assetKey, asset, toAssetKey, toAsset, uuid, selectedRoutes, finalPassword,
sendType, tokenName, isOwnerToken)
except Exception as e:
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(chainId: 0, txHash: "", uuid: uuid, error: fmt"Error sending token transfer transaction: {e.msg}"))
@ -529,15 +557,21 @@ QtObject:
error "error handling suggestedRoutesReady response", errDesription=e.msg
self.events.emit(SIGNAL_SUGGESTED_ROUTES_READY, SuggestedRoutesArgs(suggestedRoutes: suggestedRoutesDto))
proc suggestedRoutes*(self: Service, accountFrom: string, accountTo: string, amount: Uint256, token: string, disabledFromChainIDs,
disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): SuggestedRoutesDto =
var tokenId: string = ""
proc suggestedRoutes*(self: Service, accountFrom: string, accountTo: string, amount: Uint256, token: string, toToken: string,
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs: seq[int], sendType: SendType, lockedInAmounts: string): SuggestedRoutesDto =
var
tokenId: string
toTokenId: string
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: cast[ByteAddress](getSuggestedRoutesTask),
vptr: cast[ByteAddress](self.vptr),
@ -546,6 +580,7 @@ QtObject:
accountTo: accountTo,
amount: amount,
token: tokenId,
toToken: toTokenId,
disabledFromChainIDs: disabledFromChainIDs,
disabledToChainIDs: disabledToChainIDs,
preferredChainIDs: preferredChainIDs,

View File

@ -26,9 +26,10 @@ 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, disabledFromChainIDs,
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, disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, 1, lockedInAmounts]
let payload = %* [sendType, accountFrom, accountTo, amount, token, toToken, disabledFromChainIDs, disabledToChainIDs,
preferredChainIDs, 1, lockedInAmounts]
return core.callPrivateRPC("wallet_getSuggestedRoutes", payload)
rpc(getEstimatedLatestBlockNumber, "wallet"):

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit c921079761cf15c835901eeca254233e2ed79f5f
Subproject commit 7b09ee073d12315e16be93ae498ee4fc47f94be2