From 11f83780d1deabdd4aa00e6a3054db73ef6f08c8 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Wed, 5 Jun 2024 09:56:02 +0200 Subject: [PATCH] chore_: ens register improvements --- services/wallet/bridge/bridge.go | 73 +++++++- services/wallet/bridge/cbridge.go | 91 +++++----- services/wallet/bridge/ens_register.go | 165 +++++++++++++++++++ services/wallet/bridge/erc1155_transfer.go | 58 ++++--- services/wallet/bridge/erc721_transfer.go | 56 +++---- services/wallet/bridge/hop.go | 97 ++++++----- services/wallet/bridge/mock_bridge/bridge.go | 58 ++++--- services/wallet/bridge/swap_paraswap.go | 61 ++++--- services/wallet/bridge/transfer.go | 58 ++++--- services/wallet/router/filter.go | 3 +- services/wallet/router/filter_test.go | 11 +- services/wallet/router/router.go | 62 ++++--- services/wallet/router/router_send_type.go | 20 ++- services/wallet/router/router_v2.go | 119 ++++++++++--- 14 files changed, 630 insertions(+), 302 deletions(-) create mode 100644 services/wallet/bridge/ens_register.go diff --git a/services/wallet/bridge/bridge.go b/services/wallet/bridge/bridge.go index 55b65c487..5f680a121 100644 --- a/services/wallet/bridge/bridge.go +++ b/services/wallet/bridge/bridge.go @@ -1,6 +1,7 @@ package bridge import ( + "encoding/hex" "math/big" ethTypes "github.com/ethereum/go-ethereum/core/types" @@ -8,15 +9,33 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/status-im/status-go/account" + "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/params" "github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/transactions" ) -var ZeroAddress = common.Address{} +var ( + ZeroAddress = common.Address{} + ZeroBigIntValue = big.NewInt(0) +) -const IncreaseEstimatedGasFactor = 1.1 +const ( + IncreaseEstimatedGasFactor = 1.1 + + EthSymbol = "ETH" + SntSymbol = "SNT" + SttSymbol = "STT" + + TransferName = "Transfer" + HopName = "Hop" + CBridgeName = "CBridge" + SwapParaswapName = "Paraswap" + ERC721TransferName = "ERC721Transfer" + ERC1155TransferName = "ERC1155Transfer" + ENSRegisterName = "ENSRegister" +) func getSigner(chainID uint64, from types.Address, verifiedAccount *account.SelectedExtKey) bind.SignerFn { return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) { @@ -100,19 +119,55 @@ func (t *TransactionBridge) Data() types.HexBytes { return types.HexBytes("") } +type BridgeParams struct { + FromChain *params.Network + ToChain *params.Network + FromAddr common.Address + ToAddr common.Address + FromToken *token.Token + ToToken *token.Token + AmountIn *big.Int + + // extra params + BonderFee *big.Int + Username string + PublicKey string +} + type Bridge interface { // returns the name of the bridge Name() string // checks if the bridge is available for the given networks/tokens - AvailableFor(from *params.Network, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) + AvailableFor(params BridgeParams) (bool, error) // calculates the fees for the bridge and returns the amount BonderFee and TokenFee (used for bridges) - CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) + CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) // Pack the method for sending tx and method call's data - PackTxInputData(contractType string, fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) - EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, toToken *token.Token, amountIn *big.Int) (uint64, error) - CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) + PackTxInputData(params BridgeParams, contractType string) ([]byte, error) + EstimateGas(params BridgeParams) (uint64, error) + CalculateAmountOut(params BridgeParams) (*big.Int, error) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) - GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) + GetContractAddress(params BridgeParams) (common.Address, error) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) - BuildTx(fromNetwork, toNetwork *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error) + BuildTx(params BridgeParams) (*ethTypes.Transaction, error) +} + +func extractCoordinates(pubkey string) ([32]byte, [32]byte) { + x, _ := hex.DecodeString(pubkey[4:68]) + y, _ := hex.DecodeString(pubkey[68:132]) + + var xByte [32]byte + copy(xByte[:], x) + + var yByte [32]byte + copy(yByte[:], y) + + return xByte, yByte +} + +func usernameToLabel(username string) [32]byte { + usernameHashed := crypto.Keccak256([]byte(username)) + var label [32]byte + copy(label[:], usernameHashed) + + return label } diff --git a/services/wallet/bridge/cbridge.go b/services/wallet/bridge/cbridge.go index db12115d3..55f43a5f4 100644 --- a/services/wallet/bridge/cbridge.go +++ b/services/wallet/bridge/cbridge.go @@ -34,7 +34,6 @@ const ( testBaseURL = "https://cbridge-v2-test.celer.network" maxSlippage = uint32(1000) - ethSymbol = "ETH" ) type CBridgeTxArgs struct { @@ -64,7 +63,7 @@ func NewCbridge(rpcClient *rpc.Client, transactor transactions.TransactorIface, } func (s *CBridge) Name() string { - return "CBridge" + return CBridgeName } func (s *CBridge) estimateAmt(from, to *params.Network, amountIn *big.Int, symbol string) (*cbridge.EstimateAmtResponse, error) { @@ -127,12 +126,12 @@ func (s *CBridge) getTransferConfig(isTest bool) (*cbridge.GetTransferConfigsRes return &res, nil } -func (s *CBridge) AvailableFor(from, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) { - if from.ChainID == to.ChainID || toToken != nil { +func (s *CBridge) AvailableFor(params BridgeParams) (bool, error) { + if params.FromChain.ChainID == params.ToChain.ChainID || params.ToToken != nil { return false, nil } - transferConfig, err := s.getTransferConfig(from.IsTest) + transferConfig, err := s.getTransferConfig(params.FromChain.IsTest) if err != nil { return false, err } @@ -143,11 +142,11 @@ func (s *CBridge) AvailableFor(from, to *params.Network, token *token.Token, toT var fromAvailable *cbridge.Chain var toAvailable *cbridge.Chain for _, chain := range transferConfig.Chains { - if uint64(chain.GetId()) == from.ChainID && chain.GasTokenSymbol == ethSymbol { + if uint64(chain.GetId()) == params.FromChain.ChainID && chain.GasTokenSymbol == EthSymbol { fromAvailable = chain } - if uint64(chain.GetId()) == to.ChainID && chain.GasTokenSymbol == ethSymbol { + if uint64(chain.GetId()) == params.ToChain.ChainID && chain.GasTokenSymbol == EthSymbol { toAvailable = chain } } @@ -162,7 +161,7 @@ func (s *CBridge) AvailableFor(from, to *params.Network, token *token.Token, toT } for _, tokenInfo := range transferConfig.ChainToken[fromAvailable.GetId()].Token { - if tokenInfo.Token.Symbol == token.Symbol { + if tokenInfo.Token.Symbol == params.FromToken.Symbol { found = true break } @@ -173,19 +172,21 @@ func (s *CBridge) AvailableFor(from, to *params.Network, token *token.Token, toT found = false for _, tokenInfo := range transferConfig.ChainToken[toAvailable.GetId()].Token { - if tokenInfo.Token.Symbol == token.Symbol { + if tokenInfo.Token.Symbol == params.FromToken.Symbol { found = true break } } + if !found { return false, nil } + return true, nil } -func (s *CBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) { - amt, err := s.estimateAmt(from, to, amountIn, token.Symbol) +func (s *CBridge) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { + amt, err := s.estimateAmt(params.FromChain, params.ToChain, params.AmountIn, params.FromToken.Symbol) if err != nil { return nil, nil, err } @@ -201,46 +202,46 @@ func (s *CBridge) CalculateFees(from, to *params.Network, token *token.Token, am return big.NewInt(0), new(big.Int).Add(baseFee, percFee), nil } -func (c *CBridge) PackTxInputData(contractType string, fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) { +func (c *CBridge) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { abi, err := abi.JSON(strings.NewReader(celer.CelerABI)) if err != nil { return []byte{}, err } - if token.IsNative() { + if params.FromToken.IsNative() { return abi.Pack("sendNative", - to, - amountIn, - toNetwork.ChainID, + params.ToAddr, + params.AmountIn, + params.ToChain.ChainID, uint64(time.Now().UnixMilli()), maxSlippage, ) } else { return abi.Pack("send", - to, - token.Address, - amountIn, - toNetwork.ChainID, + params.ToAddr, + params.FromToken.Address, + params.AmountIn, + params.ToChain.ChainID, uint64(time.Now().UnixMilli()), maxSlippage, ) } } -func (s *CBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, toToken *token.Token, amountIn *big.Int) (uint64, error) { +func (s *CBridge) EstimateGas(params BridgeParams) (uint64, error) { value := new(big.Int) - input, err := s.PackTxInputData("", fromNetwork, toNetwork, from, to, token, amountIn) + input, err := s.PackTxInputData(params, "") if err != nil { return 0, err } - contractAddress, err := s.GetContractAddress(fromNetwork, nil) + contractAddress, err := s.GetContractAddress(params) if err != nil { return 0, err } - ethClient, err := s.rpcClient.EthClient(fromNetwork.ChainID) + ethClient, err := s.rpcClient.EthClient(params.FromChain.ChainID) if err != nil { return 0, err } @@ -248,7 +249,7 @@ func (s *CBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Net ctx := context.Background() msg := ethereum.CallMsg{ - From: from, + From: params.FromAddr, To: &contractAddress, Value: value, Data: input, @@ -256,7 +257,7 @@ func (s *CBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Net estimation, err := ethClient.EstimateGas(ctx, msg) if err != nil { - if !token.IsNative() { + if !params.FromToken.IsNative() { // TODO: this is a temporary solution until we find a better way to estimate the gas // hardcoding the estimation for other than ETH, cause we cannot get a proper estimation without having an approval placed first // this is an error we're facing otherwise: `execution reverted: ERC20: transfer amount exceeds allowance` @@ -269,29 +270,29 @@ func (s *CBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Net return uint64(increasedEstimation), nil } -func (s *CBridge) BuildTx(fromNetwork, toNetwork *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error) { - toAddr := types.Address(toAddress) +func (s *CBridge) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + toAddr := types.Address(params.ToAddr) sendArgs := &TransactionBridge{ CbridgeTx: &CBridgeTxArgs{ SendTxArgs: transactions.SendTxArgs{ - From: types.Address(fromAddress), + From: types.Address(params.FromAddr), To: &toAddr, - Value: (*hexutil.Big)(amountIn), + Value: (*hexutil.Big)(params.AmountIn), Data: types.HexBytes("0x0"), }, - ChainID: toNetwork.ChainID, - Symbol: token.Symbol, - Recipient: toAddress, - Amount: (*hexutil.Big)(amountIn), + ChainID: params.ToChain.ChainID, + Symbol: params.FromToken.Symbol, + Recipient: params.ToAddr, + Amount: (*hexutil.Big)(params.AmountIn), }, - ChainID: fromNetwork.ChainID, + ChainID: params.FromChain.ChainID, } return s.BuildTransaction(sendArgs) } -func (s *CBridge) GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) { - transferConfig, err := s.getTransferConfig(network.IsTest) +func (s *CBridge) GetContractAddress(params BridgeParams) (common.Address, error) { + transferConfig, err := s.getTransferConfig(params.FromChain.IsTest) if err != nil { return common.Address{}, err } @@ -300,7 +301,7 @@ func (s *CBridge) GetContractAddress(network *params.Network, token *token.Token } for _, chain := range transferConfig.Chains { - if uint64(chain.Id) == network.ChainID { + if uint64(chain.Id) == params.FromChain.ChainID { return common.HexToAddress(chain.ContractAddr), nil } } @@ -309,15 +310,17 @@ func (s *CBridge) GetContractAddress(network *params.Network, token *token.Token } func (s *CBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (*ethTypes.Transaction, error) { - fromNetwork := s.rpcClient.NetworkManager.Find(sendArgs.ChainID) - if fromNetwork == nil { + fromChain := s.rpcClient.NetworkManager.Find(sendArgs.ChainID) + if fromChain == nil { return nil, errors.New("network not found") } - token := s.tokenManager.FindToken(fromNetwork, sendArgs.CbridgeTx.Symbol) + token := s.tokenManager.FindToken(fromChain, sendArgs.CbridgeTx.Symbol) if token == nil { return nil, errors.New("token not found") } - addrs, err := s.GetContractAddress(fromNetwork, nil) + addrs, err := s.GetContractAddress(BridgeParams{ + FromChain: fromChain, + }) if err != nil { return nil, err } @@ -367,8 +370,8 @@ func (s *CBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Trans return s.sendOrBuild(sendArgs, nil) } -func (s *CBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) { - amt, err := s.estimateAmt(from, to, amountIn, symbol) +func (s *CBridge) CalculateAmountOut(params BridgeParams) (*big.Int, error) { + amt, err := s.estimateAmt(params.FromChain, params.ToChain, params.AmountIn, params.FromToken.Symbol) if err != nil { return nil, err } diff --git a/services/wallet/bridge/ens_register.go b/services/wallet/bridge/ens_register.go new file mode 100644 index 000000000..c7042d2a3 --- /dev/null +++ b/services/wallet/bridge/ens_register.go @@ -0,0 +1,165 @@ +package bridge + +import ( + "context" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + "github.com/status-im/status-go/contracts" + "github.com/status-im/status-go/contracts/registrar" + "github.com/status-im/status-go/contracts/snt" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/rpc" + "github.com/status-im/status-go/services/ens" + walletCommon "github.com/status-im/status-go/services/wallet/common" + "github.com/status-im/status-go/transactions" +) + +type ENSRegisterBridge struct { + contractMaker *contracts.ContractMaker + transactor transactions.TransactorIface + ensService *ens.Service +} + +func NewENSRegisterBridge(rpcClient *rpc.Client, transactor transactions.TransactorIface, ensService *ens.Service) *ENSRegisterBridge { + return &ENSRegisterBridge{ + contractMaker: &contracts.ContractMaker{ + RPCClient: rpcClient, + }, + // rpcClient: rpcClient, + transactor: transactor, + ensService: ensService, + } +} + +func (s *ENSRegisterBridge) Name() string { + return ENSRegisterName +} + +func (s *ENSRegisterBridge) GetPriceForRegisteringEnsName(chainID uint64) (*big.Int, error) { + registryAddr, err := s.ensService.API().GetRegistrarAddress(context.Background(), chainID) + if err != nil { + return nil, err + } + registrar, err := s.contractMaker.NewUsernameRegistrar(chainID, registryAddr) + if err != nil { + return nil, err + } + + callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} + return registrar.GetPrice(callOpts) +} + +func (s *ENSRegisterBridge) AvailableFor(params BridgeParams) (bool, error) { + return params.FromChain.ChainID == walletCommon.EthereumMainnet || params.FromChain.ChainID == walletCommon.EthereumSepolia, nil +} + +func (s *ENSRegisterBridge) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { + return big.NewInt(0), big.NewInt(0), nil +} + +func (s *ENSRegisterBridge) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { + price, err := s.GetPriceForRegisteringEnsName(params.FromChain.ChainID) + if err != nil { + return []byte{}, err + } + + registrarABI, err := abi.JSON(strings.NewReader(registrar.UsernameRegistrarABI)) + if err != nil { + return []byte{}, err + } + + x, y := extractCoordinates(params.PublicKey) + extraData, err := registrarABI.Pack("register", usernameToLabel(params.Username), params.FromAddr, x, y) + if err != nil { + return []byte{}, err + } + + sntABI, err := abi.JSON(strings.NewReader(snt.SNTABI)) + if err != nil { + return []byte{}, err + } + + registryAddr, err := s.ensService.API().GetRegistrarAddress(context.Background(), params.FromChain.ChainID) + if err != nil { + return []byte{}, err + } + + return sntABI.Pack("approveAndCall", registryAddr, price, extraData) +} + +func (s *ENSRegisterBridge) EstimateGas(params BridgeParams) (uint64, error) { + contractAddress, err := s.GetContractAddress(params) + if err != nil { + return 0, err + } + + input, err := s.PackTxInputData(params, "") + if err != nil { + return 0, err + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, err + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: &contractAddress, + Value: big.NewInt(0), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, err + } + + increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor + + return uint64(increasedEstimation), nil +} + +func (s *ENSRegisterBridge) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + toAddr := types.Address(params.ToAddr) + inputData, err := s.PackTxInputData(params, "") + if err != nil { + return nil, err + } + + sendArgs := &TransactionBridge{ + TransferTx: &transactions.SendTxArgs{ + From: types.Address(params.FromAddr), + To: &toAddr, + Value: (*hexutil.Big)(ZeroBigIntValue), + Data: inputData, + }, + ChainID: params.FromChain.ChainID, + } + + return s.BuildTransaction(sendArgs) +} + +func (s *ENSRegisterBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount) +} + +func (s *ENSRegisterBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx) +} + +func (s *ENSRegisterBridge) CalculateAmountOut(params BridgeParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *ENSRegisterBridge) GetContractAddress(params BridgeParams) (common.Address, error) { + return snt.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/bridge/erc1155_transfer.go b/services/wallet/bridge/erc1155_transfer.go index 0ba603274..3c530c4dd 100644 --- a/services/wallet/bridge/erc1155_transfer.go +++ b/services/wallet/bridge/erc1155_transfer.go @@ -15,9 +15,7 @@ import ( "github.com/status-im/status-go/account" "github.com/status-im/status-go/contracts/ierc1155" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" "github.com/status-im/status-go/rpc" - "github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/transactions" ) @@ -38,53 +36,53 @@ func NewERC1155TransferBridge(rpcClient *rpc.Client, transactor transactions.Tra } func (s *ERC1155TransferBridge) Name() string { - return "ERC1155Transfer" + return ERC1155TransferName } -func (s *ERC1155TransferBridge) AvailableFor(from, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) { - return from.ChainID == to.ChainID && toToken == nil, nil +func (s *ERC1155TransferBridge) AvailableFor(params BridgeParams) (bool, error) { + return params.FromChain.ChainID == params.ToChain.ChainID && params.ToToken == nil, nil } -func (s *ERC1155TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) { +func (s *ERC1155TransferBridge) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { return big.NewInt(0), big.NewInt(0), nil } -func (s *ERC1155TransferBridge) PackTxInputData(contractType string, fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) { +func (s *ERC1155TransferBridge) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { abi, err := abi.JSON(strings.NewReader(ierc1155.Ierc1155ABI)) if err != nil { return []byte{}, err } - id, success := big.NewInt(0).SetString(token.Symbol, 0) + id, success := big.NewInt(0).SetString(params.FromToken.Symbol, 0) if !success { - return []byte{}, fmt.Errorf("failed to convert %s to big.Int", token.Symbol) + return []byte{}, fmt.Errorf("failed to convert %s to big.Int", params.FromToken.Symbol) } return abi.Pack("safeTransferFrom", - from, - to, + params.FromAddr, + params.ToAddr, id, - amountIn, + params.AmountIn, []byte{}, ) } -func (s *ERC1155TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, toToken *token.Token, amountIn *big.Int) (uint64, error) { - ethClient, err := s.rpcClient.EthClient(fromNetwork.ChainID) +func (s *ERC1155TransferBridge) EstimateGas(params BridgeParams) (uint64, error) { + ethClient, err := s.rpcClient.EthClient(params.FromChain.ChainID) if err != nil { return 0, err } value := new(big.Int) - input, err := s.PackTxInputData("", fromNetwork, toNetwork, from, to, token, amountIn) + input, err := s.PackTxInputData(params, "") if err != nil { return 0, err } msg := ethereum.CallMsg{ - From: from, - To: &token.Address, + From: params.FromAddr, + To: ¶ms.FromToken.Address, Value: value, Data: input, } @@ -97,28 +95,28 @@ func (s *ERC1155TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwo return uint64(increasedEstimation), nil } -func (s *ERC1155TransferBridge) BuildTx(network, _ *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, _ *big.Int) (*ethTypes.Transaction, error) { - contractAddress := types.Address(token.Address) +func (s *ERC1155TransferBridge) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + contractAddress := types.Address(params.FromToken.Address) // We store ERC1155 Token ID using big.Int.String() in token.Symbol - tokenID, success := new(big.Int).SetString(token.Symbol, 10) + tokenID, success := new(big.Int).SetString(params.FromToken.Symbol, 10) if !success { - return nil, fmt.Errorf("failed to convert ERC1155's Symbol %s to big.Int", token.Symbol) + return nil, fmt.Errorf("failed to convert ERC1155's Symbol %s to big.Int", params.FromToken.Symbol) } sendArgs := &TransactionBridge{ ERC1155TransferTx: &ERC1155TransferTxArgs{ SendTxArgs: transactions.SendTxArgs{ - From: types.Address(fromAddress), + From: types.Address(params.FromAddr), To: &contractAddress, - Value: (*hexutil.Big)(amountIn), + Value: (*hexutil.Big)(params.AmountIn), Data: types.HexBytes("0x0"), }, TokenID: (*hexutil.Big)(tokenID), - Recipient: toAddress, - Amount: (*hexutil.Big)(amountIn), + Recipient: params.ToAddr, + Amount: (*hexutil.Big)(params.AmountIn), }, - ChainID: network.ChainID, + ChainID: params.FromChain.ChainID, } return s.BuildTransaction(sendArgs) @@ -165,10 +163,10 @@ func (s *ERC1155TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (* return s.sendOrBuild(sendArgs, nil) } -func (s *ERC1155TransferBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) { - return amountIn, nil +func (s *ERC1155TransferBridge) CalculateAmountOut(params BridgeParams) (*big.Int, error) { + return params.AmountIn, nil } -func (s *ERC1155TransferBridge) GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) { - return token.Address, nil +func (s *ERC1155TransferBridge) GetContractAddress(params BridgeParams) (common.Address, error) { + return params.FromToken.Address, nil } diff --git a/services/wallet/bridge/erc721_transfer.go b/services/wallet/bridge/erc721_transfer.go index f89d3358c..fca811e72 100644 --- a/services/wallet/bridge/erc721_transfer.go +++ b/services/wallet/bridge/erc721_transfer.go @@ -15,9 +15,7 @@ import ( "github.com/status-im/status-go/account" "github.com/status-im/status-go/contracts/community-tokens/collectibles" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" "github.com/status-im/status-go/rpc" - "github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/transactions" ) @@ -37,51 +35,51 @@ func NewERC721TransferBridge(rpcClient *rpc.Client, transactor transactions.Tran } func (s *ERC721TransferBridge) Name() string { - return "ERC721Transfer" + return ERC721TransferName } -func (s *ERC721TransferBridge) AvailableFor(from, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) { - return from.ChainID == to.ChainID && toToken == nil, nil +func (s *ERC721TransferBridge) AvailableFor(params BridgeParams) (bool, error) { + return params.FromChain.ChainID == params.ToChain.ChainID && params.ToToken == nil, nil } -func (s *ERC721TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) { +func (s *ERC721TransferBridge) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { return big.NewInt(0), big.NewInt(0), nil } -func (s *ERC721TransferBridge) PackTxInputData(contractType string, fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) { +func (s *ERC721TransferBridge) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { abi, err := abi.JSON(strings.NewReader(collectibles.CollectiblesMetaData.ABI)) if err != nil { return []byte{}, err } - id, success := big.NewInt(0).SetString(token.Symbol, 0) + id, success := big.NewInt(0).SetString(params.FromToken.Symbol, 0) if !success { - return []byte{}, fmt.Errorf("failed to convert %s to big.Int", token.Symbol) + return []byte{}, fmt.Errorf("failed to convert %s to big.Int", params.FromToken.Symbol) } return abi.Pack("safeTransferFrom", - from, - to, + params.FromAddr, + params.ToAddr, id, ) } -func (s *ERC721TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, toToken *token.Token, amountIn *big.Int) (uint64, error) { - ethClient, err := s.rpcClient.EthClient(fromNetwork.ChainID) +func (s *ERC721TransferBridge) EstimateGas(params BridgeParams) (uint64, error) { + ethClient, err := s.rpcClient.EthClient(params.FromChain.ChainID) if err != nil { return 0, err } value := new(big.Int) - input, err := s.PackTxInputData("", fromNetwork, toNetwork, from, to, token, amountIn) + input, err := s.PackTxInputData(params, "") if err != nil { return 0, err } msg := ethereum.CallMsg{ - From: from, - To: &token.Address, + From: params.FromAddr, + To: ¶ms.FromToken.Address, Value: value, Data: input, } @@ -90,31 +88,32 @@ func (s *ERC721TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwor if err != nil { return 0, err } + increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor return uint64(increasedEstimation), nil } -func (s *ERC721TransferBridge) BuildTx(network, _ *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, _ *big.Int) (*ethTypes.Transaction, error) { - contractAddress := types.Address(token.Address) +func (s *ERC721TransferBridge) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + contractAddress := types.Address(params.FromToken.Address) // We store ERC721 Token ID using big.Int.String() in token.Symbol - tokenID, success := new(big.Int).SetString(token.Symbol, 10) + tokenID, success := new(big.Int).SetString(params.FromToken.Symbol, 10) if !success { - return nil, fmt.Errorf("failed to convert ERC721's Symbol %s to big.Int", token.Symbol) + return nil, fmt.Errorf("failed to convert ERC721's Symbol %s to big.Int", params.FromToken.Symbol) } sendArgs := &TransactionBridge{ ERC721TransferTx: &ERC721TransferTxArgs{ SendTxArgs: transactions.SendTxArgs{ - From: types.Address(fromAddress), + From: types.Address(params.FromAddr), To: &contractAddress, - Value: (*hexutil.Big)(amountIn), + Value: (*hexutil.Big)(params.AmountIn), Data: types.HexBytes("0x0"), }, TokenID: (*hexutil.Big)(tokenID), - Recipient: toAddress, + Recipient: params.ToAddr, }, - ChainID: network.ChainID, + ChainID: params.FromChain.ChainID, } return s.BuildTransaction(sendArgs) @@ -139,6 +138,7 @@ func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn argNonce := hexutil.Uint64(nonce) sendArgs.ERC721TransferTx.Nonce = &argNonce txOpts := sendArgs.ERC721TransferTx.ToTransactOpts(signerFn) + tx, err = contract.SafeTransferFrom(txOpts, common.Address(sendArgs.ERC721TransferTx.From), sendArgs.ERC721TransferTx.Recipient, sendArgs.ERC721TransferTx.TokenID.ToInt()) @@ -157,10 +157,10 @@ func (s *ERC721TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*e return s.sendOrBuild(sendArgs, nil) } -func (s *ERC721TransferBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) { - return amountIn, nil +func (s *ERC721TransferBridge) CalculateAmountOut(params BridgeParams) (*big.Int, error) { + return params.AmountIn, nil } -func (s *ERC721TransferBridge) GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) { - return token.Address, nil +func (s *ERC721TransferBridge) GetContractAddress(params BridgeParams) (common.Address, error) { + return params.FromToken.Address, nil } diff --git a/services/wallet/bridge/hop.go b/services/wallet/bridge/hop.go index 184e2a62d..3881e80e7 100644 --- a/services/wallet/bridge/hop.go +++ b/services/wallet/bridge/hop.go @@ -27,7 +27,6 @@ import ( hopL2CctpImplementation "github.com/status-im/status-go/contracts/hop/l2Contracts/l2CctpImplementation" hopL2OptimismBridge "github.com/status-im/status-go/contracts/hop/l2Contracts/l2OptimismBridge" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc/chain" "github.com/status-im/status-go/services/wallet/bigint" @@ -124,14 +123,14 @@ func NewHopBridge(rpcClient *rpc.Client, transactor transactions.TransactorIface } func (h *HopBridge) Name() string { - return "Hop" + return HopName } -func (h *HopBridge) AvailableFor(from, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) { +func (h *HopBridge) AvailableFor(params BridgeParams) (bool, error) { // We chcek if the contract is available on the network for the token - _, err := h.GetContractAddress(from, token) + _, err := h.GetContractAddress(params) // toToken is not nil only if the send type is Swap - return err == nil && toToken == nil, nil + return err == nil && params.ToToken == nil, nil } func (c *HopBridge) getAppropriateABI(contractType string, chainID uint64, token *token.Token) (abi.ABI, error) { @@ -164,51 +163,51 @@ func (c *HopBridge) getAppropriateABI(contractType string, chainID uint64, token return abi.ABI{}, errors.New("not available for contract type") } -func (h *HopBridge) PackTxInputData(contractType string, fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) { - abi, err := h.getAppropriateABI(contractType, fromNetwork.ChainID, token) +func (h *HopBridge) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { + abi, err := h.getAppropriateABI(contractType, params.FromChain.ChainID, params.FromToken) if err != nil { return []byte{}, err } switch contractType { case hop.CctpL1Bridge: - return h.packCctpL1BridgeTx(abi, toNetwork.ChainID, to) + return h.packCctpL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr) case hop.L1Bridge: - return h.packL1BridgeTx(abi, toNetwork.ChainID, to) + return h.packL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr) case hop.L2AmmWrapper: - return h.packL2AmmWrapperTx(abi, toNetwork.ChainID, to) + return h.packL2AmmWrapperTx(abi, params.ToChain.ChainID, params.ToAddr) case hop.CctpL2Bridge: - return h.packCctpL2BridgeTx(abi, toNetwork.ChainID, to) + return h.packCctpL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr) case hop.L2Bridge: - return h.packL2BridgeTx(abi, toNetwork.ChainID, to) + return h.packL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr) } return []byte{}, errors.New("contract type not supported yet") } -func (h *HopBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, toToken *token.Token, amountIn *big.Int) (uint64, error) { +func (h *HopBridge) EstimateGas(params BridgeParams) (uint64, error) { value := big.NewInt(0) - if token.IsNative() { - value = amountIn + if params.FromToken.IsNative() { + value = params.AmountIn } - contractAddress, contractType, err := hop.GetContractAddress(fromNetwork.ChainID, token.Symbol) + contractAddress, contractType, err := hop.GetContractAddress(params.FromChain.ChainID, params.FromToken.Symbol) if err != nil { return 0, err } - input, err := h.PackTxInputData(contractType, fromNetwork, toNetwork, from, to, token, amountIn) + input, err := h.PackTxInputData(params, contractType) if err != nil { return 0, err } - ethClient, err := h.contractMaker.RPCClient.EthClient(fromNetwork.ChainID) + ethClient, err := h.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) if err != nil { return 0, err } msg := ethereum.CallMsg{ - From: from, + From: params.FromAddr, To: &contractAddress, Value: value, Data: input, @@ -216,7 +215,7 @@ func (h *HopBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.N estimation, err := ethClient.EstimateGas(context.Background(), msg) if err != nil { - if !token.IsNative() { + if !params.FromToken.IsNative() { // TODO: this is a temporary solution until we find a better way to estimate the gas // hardcoding the estimation for other than ETH, cause we cannot get a proper estimation without having an approval placed first // this is an error we're facing otherwise: `execution reverted: ERC20: transfer amount exceeds allowance` @@ -230,42 +229,42 @@ func (h *HopBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.N return uint64(increasedEstimation), nil } -func (h *HopBridge) BuildTx(fromNetwork, toNetwork *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error) { - toAddr := types.Address(toAddress) +func (h *HopBridge) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + toAddr := types.Address(params.ToAddr) sendArgs := &TransactionBridge{ HopTx: &HopTxArgs{ SendTxArgs: transactions.SendTxArgs{ - From: types.Address(fromAddress), + From: types.Address(params.FromAddr), To: &toAddr, - Value: (*hexutil.Big)(amountIn), + Value: (*hexutil.Big)(params.AmountIn), Data: types.HexBytes("0x0"), }, - Symbol: token.Symbol, - Recipient: toAddress, - Amount: (*hexutil.Big)(amountIn), - BonderFee: (*hexutil.Big)(bonderFee), - ChainID: toNetwork.ChainID, + Symbol: params.FromToken.Symbol, + Recipient: params.ToAddr, + Amount: (*hexutil.Big)(params.AmountIn), + BonderFee: (*hexutil.Big)(params.BonderFee), + ChainID: params.ToChain.ChainID, }, - ChainID: fromNetwork.ChainID, + ChainID: params.FromChain.ChainID, } return h.BuildTransaction(sendArgs) } -func (h *HopBridge) GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) { - address, _, err := hop.GetContractAddress(network.ChainID, token.Symbol) +func (h *HopBridge) GetContractAddress(params BridgeParams) (common.Address, error) { + address, _, err := hop.GetContractAddress(params.FromChain.ChainID, params.FromToken.Symbol) return address, err } func (h *HopBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) { - fromNetwork := h.contractMaker.RPCClient.NetworkManager.Find(sendArgs.ChainID) - if fromNetwork == nil { + fromChain := h.contractMaker.RPCClient.NetworkManager.Find(sendArgs.ChainID) + if fromChain == nil { return tx, fmt.Errorf("ChainID not supported %d", sendArgs.ChainID) } - token := h.tokenManager.FindToken(fromNetwork, sendArgs.HopTx.Symbol) + token := h.tokenManager.FindToken(fromChain, sendArgs.HopTx.Symbol) - nonce, err := h.transactor.NextNonce(h.contractMaker.RPCClient, fromNetwork.ChainID, sendArgs.HopTx.From) + nonce, err := h.transactor.NextNonce(h.contractMaker.RPCClient, fromChain.ChainID, sendArgs.HopTx.From) if err != nil { return tx, err } @@ -278,12 +277,12 @@ func (h *HopBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.Signe txOpts.Value = (*big.Int)(sendArgs.HopTx.Amount) } - ethClient, err := h.contractMaker.RPCClient.EthClient(fromNetwork.ChainID) + ethClient, err := h.contractMaker.RPCClient.EthClient(fromChain.ChainID) if err != nil { return tx, err } - contractAddress, contractType, err := hop.GetContractAddress(fromNetwork.ChainID, sendArgs.HopTx.Symbol) + contractAddress, contractType, err := hop.GetContractAddress(fromChain.ChainID, sendArgs.HopTx.Symbol) if err != nil { return tx, err } @@ -316,32 +315,32 @@ func (h *HopBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Tra return h.sendOrBuild(sendArgs, nil) } -func (h *HopBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) { +func (h *HopBridge) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { hopChainsMap := map[uint64]string{ walletCommon.EthereumMainnet: "ethereum", walletCommon.OptimismMainnet: "optimism", walletCommon.ArbitrumMainnet: "arbitrum", } - fromChainName, ok := hopChainsMap[from.ChainID] + fromChainName, ok := hopChainsMap[params.FromChain.ChainID] if !ok { return nil, nil, errors.New("from chain not supported") } - toChainName, ok := hopChainsMap[to.ChainID] + toChainName, ok := hopChainsMap[params.ToChain.ChainID] if !ok { return nil, nil, errors.New("to chain not supported") } - params := netUrl.Values{} - params.Add("amount", amountIn.String()) - params.Add("token", token.Symbol) - params.Add("fromChain", fromChainName) - params.Add("toChain", toChainName) - params.Add("slippage", "0.5") // menas 0.5% + reqParams := netUrl.Values{} + reqParams.Add("amount", params.AmountIn.String()) + reqParams.Add("token", params.FromToken.Symbol) + reqParams.Add("fromChain", fromChainName) + reqParams.Add("toChain", toChainName) + reqParams.Add("slippage", "0.5") // menas 0.5% url := "https://api.hop.exchange/v1/quote" - response, err := h.httpClient.DoGetRequest(context.Background(), url, params) + response, err := h.httpClient.DoGetRequest(context.Background(), url, reqParams) if err != nil { return nil, nil, err } @@ -360,7 +359,7 @@ func (h *HopBridge) CalculateFees(from, to *params.Network, token *token.Token, return h.bonderFee.BonderFee.Int, tokenFee, nil } -func (h *HopBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) { +func (h *HopBridge) CalculateAmountOut(params BridgeParams) (*big.Int, error) { return h.bonderFee.EstimatedRecieved.Int, nil } diff --git a/services/wallet/bridge/mock_bridge/bridge.go b/services/wallet/bridge/mock_bridge/bridge.go index 833a91fcf..10e363203 100644 --- a/services/wallet/bridge/mock_bridge/bridge.go +++ b/services/wallet/bridge/mock_bridge/bridge.go @@ -13,9 +13,7 @@ import ( gomock "github.com/golang/mock/gomock" account "github.com/status-im/status-go/account" types0 "github.com/status-im/status-go/eth-node/types" - params "github.com/status-im/status-go/params" bridge "github.com/status-im/status-go/services/wallet/bridge" - token "github.com/status-im/status-go/services/wallet/token" ) // MockBridge is a mock of Bridge interface. @@ -42,18 +40,18 @@ func (m *MockBridge) EXPECT() *MockBridgeMockRecorder { } // AvailableFor mocks base method. -func (m *MockBridge) AvailableFor(from, to *params.Network, token, toToken *token.Token) (bool, error) { +func (m *MockBridge) AvailableFor(params bridge.BridgeParams) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AvailableFor", from, to, token, toToken) + ret := m.ctrl.Call(m, "AvailableFor", params) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // AvailableFor indicates an expected call of AvailableFor. -func (mr *MockBridgeMockRecorder) AvailableFor(from, to, token, toToken interface{}) *gomock.Call { +func (mr *MockBridgeMockRecorder) AvailableFor(params interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AvailableFor", reflect.TypeOf((*MockBridge)(nil).AvailableFor), from, to, token, toToken) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AvailableFor", reflect.TypeOf((*MockBridge)(nil).AvailableFor), params) } // BuildTransaction mocks base method. @@ -72,39 +70,39 @@ func (mr *MockBridgeMockRecorder) BuildTransaction(sendArgs interface{}) *gomock } // BuildTx mocks base method. -func (m *MockBridge) BuildTx(fromNetwork, toNetwork *params.Network, fromAddress, toAddress common.Address, token *token.Token, amountIn, bonderFee *big.Int) (*types.Transaction, error) { +func (m *MockBridge) BuildTx(params bridge.BridgeParams) (*types.Transaction, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BuildTx", fromNetwork, toNetwork, fromAddress, toAddress, token, amountIn, bonderFee) + ret := m.ctrl.Call(m, "BuildTx", params) ret0, _ := ret[0].(*types.Transaction) ret1, _ := ret[1].(error) return ret0, ret1 } // BuildTx indicates an expected call of BuildTx. -func (mr *MockBridgeMockRecorder) BuildTx(fromNetwork, toNetwork, fromAddress, toAddress, token, amountIn, bonderFee interface{}) *gomock.Call { +func (mr *MockBridgeMockRecorder) BuildTx(params interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTx", reflect.TypeOf((*MockBridge)(nil).BuildTx), fromNetwork, toNetwork, fromAddress, toAddress, token, amountIn, bonderFee) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTx", reflect.TypeOf((*MockBridge)(nil).BuildTx), params) } // CalculateAmountOut mocks base method. -func (m *MockBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) { +func (m *MockBridge) CalculateAmountOut(params bridge.BridgeParams) (*big.Int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CalculateAmountOut", from, to, amountIn, symbol) + ret := m.ctrl.Call(m, "CalculateAmountOut", params) ret0, _ := ret[0].(*big.Int) ret1, _ := ret[1].(error) return ret0, ret1 } // CalculateAmountOut indicates an expected call of CalculateAmountOut. -func (mr *MockBridgeMockRecorder) CalculateAmountOut(from, to, amountIn, symbol interface{}) *gomock.Call { +func (mr *MockBridgeMockRecorder) CalculateAmountOut(params interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateAmountOut", reflect.TypeOf((*MockBridge)(nil).CalculateAmountOut), from, to, amountIn, symbol) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateAmountOut", reflect.TypeOf((*MockBridge)(nil).CalculateAmountOut), params) } // CalculateFees mocks base method. -func (m *MockBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) { +func (m *MockBridge) CalculateFees(params bridge.BridgeParams) (*big.Int, *big.Int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CalculateFees", from, to, token, amountIn) + ret := m.ctrl.Call(m, "CalculateFees", params) ret0, _ := ret[0].(*big.Int) ret1, _ := ret[1].(*big.Int) ret2, _ := ret[2].(error) @@ -112,39 +110,39 @@ func (m *MockBridge) CalculateFees(from, to *params.Network, token *token.Token, } // CalculateFees indicates an expected call of CalculateFees. -func (mr *MockBridgeMockRecorder) CalculateFees(from, to, token, amountIn interface{}) *gomock.Call { +func (mr *MockBridgeMockRecorder) CalculateFees(params interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateFees", reflect.TypeOf((*MockBridge)(nil).CalculateFees), from, to, token, amountIn) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateFees", reflect.TypeOf((*MockBridge)(nil).CalculateFees), params) } // EstimateGas mocks base method. -func (m *MockBridge) EstimateGas(fromNetwork, toNetwork *params.Network, from, to common.Address, token, toToken *token.Token, amountIn *big.Int) (uint64, error) { +func (m *MockBridge) EstimateGas(params bridge.BridgeParams) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EstimateGas", fromNetwork, toNetwork, from, to, token, toToken, amountIn) + ret := m.ctrl.Call(m, "EstimateGas", params) ret0, _ := ret[0].(uint64) ret1, _ := ret[1].(error) return ret0, ret1 } // EstimateGas indicates an expected call of EstimateGas. -func (mr *MockBridgeMockRecorder) EstimateGas(fromNetwork, toNetwork, from, to, token, toToken, amountIn interface{}) *gomock.Call { +func (mr *MockBridgeMockRecorder) EstimateGas(params interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimateGas", reflect.TypeOf((*MockBridge)(nil).EstimateGas), fromNetwork, toNetwork, from, to, token, toToken, amountIn) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimateGas", reflect.TypeOf((*MockBridge)(nil).EstimateGas), params) } // GetContractAddress mocks base method. -func (m *MockBridge) GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) { +func (m *MockBridge) GetContractAddress(params bridge.BridgeParams) (common.Address, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetContractAddress", network, token) + ret := m.ctrl.Call(m, "GetContractAddress", params) ret0, _ := ret[0].(common.Address) ret1, _ := ret[1].(error) return ret0, ret1 } // GetContractAddress indicates an expected call of GetContractAddress. -func (mr *MockBridgeMockRecorder) GetContractAddress(network, token interface{}) *gomock.Call { +func (mr *MockBridgeMockRecorder) GetContractAddress(params interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContractAddress", reflect.TypeOf((*MockBridge)(nil).GetContractAddress), network, token) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContractAddress", reflect.TypeOf((*MockBridge)(nil).GetContractAddress), params) } // Name mocks base method. @@ -162,18 +160,18 @@ func (mr *MockBridgeMockRecorder) Name() *gomock.Call { } // PackTxInputData mocks base method. -func (m *MockBridge) PackTxInputData(contractType string, fromNetwork, toNetwork *params.Network, from, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) { +func (m *MockBridge) PackTxInputData(params bridge.BridgeParams, contractType string) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PackTxInputData", contractType, fromNetwork, toNetwork, from, to, token, amountIn) + ret := m.ctrl.Call(m, "PackTxInputData", params, contractType) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // PackTxInputData indicates an expected call of PackTxInputData. -func (mr *MockBridgeMockRecorder) PackTxInputData(contractType, fromNetwork, toNetwork, from, to, token, amountIn interface{}) *gomock.Call { +func (mr *MockBridgeMockRecorder) PackTxInputData(params, contractType interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackTxInputData", reflect.TypeOf((*MockBridge)(nil).PackTxInputData), contractType, fromNetwork, toNetwork, from, to, token, amountIn) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackTxInputData", reflect.TypeOf((*MockBridge)(nil).PackTxInputData), params, contractType) } // Send mocks base method. diff --git a/services/wallet/bridge/swap_paraswap.go b/services/wallet/bridge/swap_paraswap.go index ca5832247..efe4e8d49 100644 --- a/services/wallet/bridge/swap_paraswap.go +++ b/services/wallet/bridge/swap_paraswap.go @@ -11,11 +11,9 @@ import ( ethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/status-im/status-go/account" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" "github.com/status-im/status-go/rpc" walletCommon "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/thirdparty/paraswap" - "github.com/status-im/status-go/services/wallet/token" walletToken "github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/transactions" ) @@ -39,22 +37,22 @@ func NewSwapParaswap(rpcClient *rpc.Client, transactor transactions.TransactorIf } func (s *SwapParaswap) Name() string { - return "Paraswap" + return SwapParaswapName } -func (s *SwapParaswap) AvailableFor(from, to *params.Network, token *walletToken.Token, toToken *walletToken.Token) (bool, error) { - if token == nil || toToken == nil { +func (s *SwapParaswap) AvailableFor(params BridgeParams) (bool, error) { + if params.FromToken == nil || params.ToToken == nil { return false, errors.New("token and toToken cannot be nil") } - if from.ChainID != to.ChainID { + if params.FromChain.ChainID != params.ToChain.ChainID { return false, nil } - s.paraswapClient.SetChainID(from.ChainID) + s.paraswapClient.SetChainID(params.FromChain.ChainID) - searchForToken := token.Address == ZeroAddress - searchForToToken := toToken.Address == ZeroAddress + searchForToken := params.FromToken.Address == ZeroAddress + searchForToToken := params.ToToken.Address == ZeroAddress if searchForToToken || searchForToken { tokensList, err := s.paraswapClient.FetchTokensList(context.Background()) if err != nil { @@ -62,17 +60,17 @@ func (s *SwapParaswap) AvailableFor(from, to *params.Network, token *walletToken } for _, t := range tokensList { - if searchForToken && t.Symbol == token.Symbol { - token.Address = common.HexToAddress(t.Address) - token.Decimals = t.Decimals + if searchForToken && t.Symbol == params.FromToken.Symbol { + params.FromToken.Address = common.HexToAddress(t.Address) + params.FromToken.Decimals = t.Decimals if !searchForToToken { break } } - if searchForToToken && t.Symbol == toToken.Symbol { - toToken.Address = common.HexToAddress(t.Address) - toToken.Decimals = t.Decimals + if searchForToToken && t.Symbol == params.ToToken.Symbol { + params.ToToken.Address = common.HexToAddress(t.Address) + params.ToToken.Decimals = t.Decimals if !searchForToken { break } @@ -80,24 +78,25 @@ func (s *SwapParaswap) AvailableFor(from, to *params.Network, token *walletToken } } - if token.Address == ZeroAddress || toToken.Address == ZeroAddress { + if params.FromToken.Address == ZeroAddress || params.ToToken.Address == ZeroAddress { return false, errors.New("cannot resolve token/s") } return true, nil } -func (s *SwapParaswap) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) { +func (s *SwapParaswap) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { return big.NewInt(0), big.NewInt(0), nil } -func (s *SwapParaswap) PackTxInputData(contractType string, fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) { +func (s *SwapParaswap) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { // not sure what we can do here since we're using the api to build the transaction return []byte{}, nil } -func (s *SwapParaswap) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, toToken *token.Token, amountIn *big.Int) (uint64, error) { - priceRoute, err := s.paraswapClient.FetchPriceRoute(context.Background(), token.Address, token.Decimals, toToken.Address, toToken.Decimals, amountIn, from, to) +func (s *SwapParaswap) EstimateGas(params BridgeParams) (uint64, error) { + priceRoute, err := s.paraswapClient.FetchPriceRoute(context.Background(), params.FromToken.Address, params.FromToken.Decimals, + params.ToToken.Address, params.ToToken.Decimals, params.AmountIn, params.FromAddr, params.ToAddr) if err != nil { return 0, err } @@ -107,12 +106,12 @@ func (s *SwapParaswap) EstimateGas(fromNetwork *params.Network, toNetwork *param return priceRoute.GasCost.Uint64(), nil } -func (s *SwapParaswap) GetContractAddress(network *params.Network, token *token.Token) (address common.Address, err error) { - if network.ChainID == walletCommon.EthereumMainnet { +func (s *SwapParaswap) GetContractAddress(params BridgeParams) (address common.Address, err error) { + if params.FromChain.ChainID == walletCommon.EthereumMainnet { address = common.HexToAddress("0x216b4b4ba9f3e719726886d34a177484278bfcae") - } else if network.ChainID == walletCommon.ArbitrumMainnet { + } else if params.FromChain.ChainID == walletCommon.ArbitrumMainnet { address = common.HexToAddress("0x216b4b4ba9f3e719726886d34a177484278bfcae") - } else if network.ChainID == walletCommon.OptimismMainnet { + } else if params.FromChain.ChainID == walletCommon.OptimismMainnet { address = common.HexToAddress("0x216b4b4ba9f3e719726886d34a177484278bfcae") } else { err = errors.New("unsupported network") @@ -120,18 +119,18 @@ func (s *SwapParaswap) GetContractAddress(network *params.Network, token *token. return } -func (s *SwapParaswap) BuildTx(network, _ *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, _ *big.Int) (*ethTypes.Transaction, error) { - toAddr := types.Address(toAddress) +func (s *SwapParaswap) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + toAddr := types.Address(params.ToAddr) sendArgs := &TransactionBridge{ SwapTx: &SwapTxArgs{ SendTxArgs: transactions.SendTxArgs{ - From: types.Address(fromAddress), + From: types.Address(params.FromAddr), To: &toAddr, - Value: (*hexutil.Big)(amountIn), + Value: (*hexutil.Big)(params.AmountIn), Data: types.HexBytes("0x0"), - Symbol: token.Symbol, + Symbol: params.FromToken.Symbol, }, - ChainID: network.ChainID, + ChainID: params.FromChain.ChainID, }, } @@ -202,6 +201,6 @@ func (s *SwapParaswap) Send(sendArgs *TransactionBridge, verifiedAccount *accoun return s.transactor.SendTransactionWithChainID(txBridgeArgs.ChainID, txBridgeArgs.SwapTx.SendTxArgs, verifiedAccount) } -func (s *SwapParaswap) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) { +func (s *SwapParaswap) CalculateAmountOut(params BridgeParams) (*big.Int, error) { return s.priceRoute.DestAmount.Int, nil } diff --git a/services/wallet/bridge/transfer.go b/services/wallet/bridge/transfer.go index a29a5da4d..452035e98 100644 --- a/services/wallet/bridge/transfer.go +++ b/services/wallet/bridge/transfer.go @@ -13,9 +13,7 @@ import ( "github.com/status-im/status-go/account" "github.com/status-im/status-go/contracts/ierc20" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" "github.com/status-im/status-go/rpc" - "github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/transactions" ) @@ -29,19 +27,19 @@ func NewTransferBridge(rpcClient *rpc.Client, transactor transactions.Transactor } func (s *TransferBridge) Name() string { - return "Transfer" + return TransferName } -func (s *TransferBridge) AvailableFor(from, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) { - return from.ChainID == to.ChainID && token != nil && toToken == nil, nil +func (s *TransferBridge) AvailableFor(params BridgeParams) (bool, error) { + return params.FromChain.ChainID == params.ToChain.ChainID && params.FromToken != nil && params.ToToken == nil, nil } -func (s *TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) { +func (s *TransferBridge) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { return big.NewInt(0), big.NewInt(0), nil } -func (s *TransferBridge) PackTxInputData(contractType string, fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) { - if token.IsNative() { +func (s *TransferBridge) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { + if params.FromToken.IsNative() { return []byte("eth_sendRawTransaction"), nil } else { abi, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI)) @@ -49,28 +47,28 @@ func (s *TransferBridge) PackTxInputData(contractType string, fromNetwork *param return []byte{}, err } return abi.Pack("transfer", - to, - amountIn, + params.ToAddr, + params.AmountIn, ) } } -func (s *TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, toToken *token.Token, amountIn *big.Int) (uint64, error) { +func (s *TransferBridge) EstimateGas(params BridgeParams) (uint64, error) { estimation := uint64(0) var err error - input, err := s.PackTxInputData("", fromNetwork, toNetwork, from, to, token, amountIn) + input, err := s.PackTxInputData(params, "") if err != nil { return 0, err } - if token.IsNative() { - estimation, err = s.transactor.EstimateGas(fromNetwork, from, to, amountIn, input) + if params.FromToken.IsNative() { + estimation, err = s.transactor.EstimateGas(params.FromChain, params.FromAddr, params.ToAddr, params.AmountIn, input) if err != nil { return 0, err } } else { - ethClient, err := s.rpcClient.EthClient(fromNetwork.ChainID) + ethClient, err := s.rpcClient.EthClient(params.FromChain.ChainID) if err != nil { return 0, err } @@ -78,8 +76,8 @@ func (s *TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *par ctx := context.Background() msg := ethereum.CallMsg{ - From: from, - To: &token.Address, + From: params.FromAddr, + To: ¶ms.FromToken.Address, Data: input, } @@ -94,17 +92,17 @@ func (s *TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *par return uint64(increasedEstimation), nil } -func (s *TransferBridge) BuildTx(network, _ *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error) { - toAddr := types.Address(toAddress) - if token.IsNative() { +func (s *TransferBridge) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + toAddr := types.Address(params.ToAddr) + if params.FromToken.IsNative() { sendArgs := &TransactionBridge{ TransferTx: &transactions.SendTxArgs{ - From: types.Address(fromAddress), + From: types.Address(params.FromAddr), To: &toAddr, - Value: (*hexutil.Big)(amountIn), + Value: (*hexutil.Big)(params.AmountIn), Data: types.HexBytes("0x0"), }, - ChainID: network.ChainID, + ChainID: params.FromChain.ChainID, } return s.BuildTransaction(sendArgs) @@ -114,20 +112,20 @@ func (s *TransferBridge) BuildTx(network, _ *params.Network, fromAddress common. return nil, err } input, err := abi.Pack("transfer", - toAddress, - amountIn, + params.ToAddr, + params.AmountIn, ) if err != nil { return nil, err } sendArgs := &TransactionBridge{ TransferTx: &transactions.SendTxArgs{ - From: types.Address(fromAddress), + From: types.Address(params.FromAddr), To: &toAddr, Value: (*hexutil.Big)(big.NewInt(0)), Data: input, }, - ChainID: network.ChainID, + ChainID: params.FromChain.ChainID, } return s.BuildTransaction(sendArgs) @@ -141,10 +139,10 @@ func (s *TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethType return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx) } -func (s *TransferBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) { - return amountIn, nil +func (s *TransferBridge) CalculateAmountOut(params BridgeParams) (*big.Int, error) { + return params.AmountIn, nil } -func (s *TransferBridge) GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) { +func (s *TransferBridge) GetContractAddress(params BridgeParams) (common.Address, error) { return common.Address{}, nil } diff --git a/services/wallet/router/filter.go b/services/wallet/router/filter.go index a352fbb1b..35bb5b551 100644 --- a/services/wallet/router/filter.go +++ b/services/wallet/router/filter.go @@ -4,6 +4,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/status-im/status-go/services/wallet/bridge" "go.uber.org/zap" ) @@ -88,7 +89,7 @@ func setupRouteValidationMapsV2(fromLockedAmount map[uint64]*hexutil.Big) (map[u fromExcluded := make(map[uint64]bool) for chainID, amount := range fromLockedAmount { - if amount.ToInt().Cmp(zero) <= 0 { + if amount.ToInt().Cmp(bridge.ZeroBigIntValue) <= 0 { fromExcluded[chainID] = false } else { fromIncluded[chainID] = false diff --git a/services/wallet/router/filter_test.go b/services/wallet/router/filter_test.go index 12f2a7eb7..5e83ba5e5 100644 --- a/services/wallet/router/filter_test.go +++ b/services/wallet/router/filter_test.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/status-im/status-go/params" + "github.com/status-im/status-go/services/wallet/bridge" "github.com/stretchr/testify/assert" ) @@ -83,9 +84,9 @@ func TestSetupRouteValidationMapsV2(t *testing.T) { { name: "Mixed zero and non-zero amounts", fromLockedAmount: map[uint64]*hexutil.Big{ - 1: (*hexutil.Big)(zero), + 1: (*hexutil.Big)(bridge.ZeroBigIntValue), 2: (*hexutil.Big)(big.NewInt(200)), - 3: (*hexutil.Big)(zero), + 3: (*hexutil.Big)(bridge.ZeroBigIntValue), 4: (*hexutil.Big)(big.NewInt(400)), }, expectedIncluded: map[uint64]bool{ @@ -112,8 +113,8 @@ func TestSetupRouteValidationMapsV2(t *testing.T) { { name: "All zero amounts", fromLockedAmount: map[uint64]*hexutil.Big{ - 1: (*hexutil.Big)(zero), - 2: (*hexutil.Big)(zero), + 1: (*hexutil.Big)(bridge.ZeroBigIntValue), + 2: (*hexutil.Big)(bridge.ZeroBigIntValue), }, expectedIncluded: map[uint64]bool{}, expectedExcluded: map[uint64]bool{ @@ -134,7 +135,7 @@ func TestSetupRouteValidationMapsV2(t *testing.T) { { name: "Single zero amount", fromLockedAmount: map[uint64]*hexutil.Big{ - 1: (*hexutil.Big)(zero), + 1: (*hexutil.Big)(bridge.ZeroBigIntValue), }, expectedIncluded: map[uint64]bool{}, expectedExcluded: map[uint64]bool{ diff --git a/services/wallet/router/router.go b/services/wallet/router/router.go index f0928650a..485bd7caa 100644 --- a/services/wallet/router/router.go +++ b/services/wallet/router/router.go @@ -37,12 +37,9 @@ import ( // rename and make `router_v2.go` file the main and only file // ////////////////////////////////////////////////////////////////////////////// +// TODO: remove the following two consts once we fully move to routerV2 const EstimateUsername = "RandomUsername" const EstimatePubKey = "0x04bb2024ce5d72e45d4a4f8589ae657ef9745855006996115a23a1af88d536cf02c0524a585fce7bfa79d6a9669af735eda6205d6c7e5b3cdc2b8ff7b2fa1f0b56" -const ERC721TransferString = "ERC721Transfer" -const ERC1155TransferString = "ERC1155Transfer" - -var zero = big.NewInt(0) type Path struct { BridgeName string @@ -151,7 +148,7 @@ func filterRoutes(routes [][]*Path, amountIn *big.Int, fromLockedAmount map[uint fromIncluded := make(map[uint64]bool) fromExcluded := make(map[uint64]bool) for chainID, amount := range fromLockedAmount { - if amount.ToInt().Cmp(zero) == 0 { + if amount.ToInt().Cmp(bridge.ZeroBigIntValue) == 0 { fromExcluded[chainID] = false } else { fromIncluded[chainID] = false @@ -262,7 +259,7 @@ func newSuggestedRoutes( rest := new(big.Int).Set(amountIn) for _, path := range best { diff := new(big.Int).Sub(rest, path.MaxAmountIn.ToInt()) - if diff.Cmp(zero) >= 0 { + if diff.Cmp(bridge.ZeroBigIntValue) >= 0 { path.AmountIn = (*hexutil.Big)(path.MaxAmountIn.ToInt()) } else { path.AmountIn = (*hexutil.Big)(new(big.Int).Set(rest)) @@ -286,12 +283,15 @@ func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, token cbridge := bridge.NewCbridge(rpcClient, transactor, tokenManager) hop := bridge.NewHopBridge(rpcClient, transactor, tokenManager) paraswap := bridge.NewSwapParaswap(rpcClient, transactor, tokenManager) + ensRegister := bridge.NewENSRegisterBridge(rpcClient, transactor, ensService) + bridges[transfer.Name()] = transfer bridges[erc721Transfer.Name()] = erc721Transfer bridges[hop.Name()] = hop bridges[cbridge.Name()] = cbridge bridges[erc1155Transfer.Name()] = erc1155Transfer bridges[paraswap.Name()] = paraswap + bridges[ensRegister.Name()] = ensRegister return &Router{ rpcClient: rpcClient, @@ -338,7 +338,7 @@ type Router struct { func (r *Router) requireApproval(ctx context.Context, sendType SendType, approvalContractAddress *common.Address, account common.Address, network *params.Network, token *token.Token, amountIn *big.Int) ( bool, *big.Int, uint64, uint64, error) { - if sendType.IsCollectiblesTransfer() { + if sendType.IsCollectiblesTransfer() || sendType.IsEnsTransfer() { return false, nil, 0, 0, nil } @@ -542,8 +542,13 @@ func (r *Router) SuggestedRoutes( maxFees := gasFees.feeFor(gasFeeMode) estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, maxFees) - for _, bridge := range r.bridges { - if !sendType.canUseBridge(bridge) { + for _, brdg := range r.bridges { + // Skip bridges that are added because of the Router V2, to not break the current functionality + if brdg.Name() == bridge.ENSRegisterName { + continue + } + + if !sendType.canUseBridge(brdg) { continue } @@ -567,7 +572,17 @@ func (r *Router) SuggestedRoutes( continue } - can, err := bridge.AvailableFor(network, dest, token, toToken) + bridgeParams := bridge.BridgeParams{ + FromChain: network, + ToChain: dest, + FromToken: token, + ToToken: toToken, + ToAddr: addrTo, + FromAddr: addrFrom, + AmountIn: amountIn, + } + + can, err := brdg.AvailableFor(bridgeParams) if err != nil || !can { continue } @@ -575,11 +590,11 @@ func (r *Router) SuggestedRoutes( continue } - bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountIn) + bonderFees, tokenFees, err := brdg.CalculateFees(bridgeParams) if err != nil { continue } - if bonderFees.Cmp(zero) != 0 { + if bonderFees.Cmp(bridge.ZeroBigIntValue) != 0 { if maxAmountIn.ToInt().Cmp(amountIn) >= 0 { if bonderFees.Cmp(amountIn) >= 0 { continue @@ -592,7 +607,7 @@ func (r *Router) SuggestedRoutes( } gasLimit := uint64(0) if sendType.isTransfer(false) { - gasLimit, err = bridge.EstimateGas(network, dest, addrFrom, addrTo, token, toToken, amountIn) + gasLimit, err = brdg.EstimateGas(bridgeParams) if err != nil { continue } @@ -600,7 +615,7 @@ func (r *Router) SuggestedRoutes( gasLimit = sendType.EstimateGas(r.ensService, r.stickersService, network, addrFrom, tokenID) } - approvalContractAddress, err := bridge.GetContractAddress(network, token) + approvalContractAddress, err := brdg.GetContractAddress(bridgeParams) if err != nil { continue } @@ -611,7 +626,7 @@ func (r *Router) SuggestedRoutes( var l1GasFeeWei uint64 if sendType.needL1Fee() { - txInputData, err := bridge.PackTxInputData("", network, dest, addrFrom, addrTo, token, amountIn) + txInputData, err := brdg.PackTxInputData(bridgeParams, "") if err != nil { continue } @@ -661,12 +676,12 @@ func (r *Router) SuggestedRoutes( cost.Add(cost, l1GasCost) mu.Lock() candidates = append(candidates, &Path{ - BridgeName: bridge.Name(), + BridgeName: brdg.Name(), From: network, To: dest, MaxAmountIn: maxAmountIn, - AmountIn: (*hexutil.Big)(zero), - AmountOut: (*hexutil.Big)(zero), + AmountIn: (*hexutil.Big)(bridge.ZeroBigIntValue), + AmountOut: (*hexutil.Big)(bridge.ZeroBigIntValue), GasAmount: gasLimit, GasFees: gasFees, BonderFees: (*hexutil.Big)(bonderFees), @@ -691,7 +706,16 @@ func (r *Router) SuggestedRoutes( suggestedRoutes.TokenPrice = prices[tokenID] suggestedRoutes.NativeChainTokenPrice = prices["ETH"] for _, path := range suggestedRoutes.Best { - amountOut, err := r.bridges[path.BridgeName].CalculateAmountOut(path.From, path.To, (*big.Int)(path.AmountIn), tokenID) + bridgeParams := bridge.BridgeParams{ + FromChain: path.From, + ToChain: path.To, + AmountIn: path.AmountIn.ToInt(), + FromToken: &token.Token{ + Symbol: tokenID, + }, + } + + amountOut, err := r.bridges[path.BridgeName].CalculateAmountOut(bridgeParams) if err != nil { continue } diff --git a/services/wallet/router/router_send_type.go b/services/wallet/router/router_send_type.go index 047f830db..fb84f5d78 100644 --- a/services/wallet/router/router_send_type.go +++ b/services/wallet/router/router_send_type.go @@ -39,6 +39,10 @@ func (s SendType) IsCollectiblesTransfer() bool { return s == ERC721Transfer || s == ERC1155Transfer } +func (s SendType) IsEnsTransfer() bool { + return s == ENSRegister || s == ENSRelease || s == ENSSetPubKey +} + func (s SendType) FetchPrices(marketManager *market.Manager, tokenID string) (map[string]float64, error) { symbols := []string{tokenID, "ETH"} if s.IsCollectiblesTransfer() { @@ -83,6 +87,7 @@ func (s SendType) FindToken(tokenManager *token.Manager, collectibles *collectib } } +// TODO: remove this function once we fully move to routerV2 func (s SendType) isTransfer(routerV2Logic bool) bool { return s == Transfer || s == Bridge && routerV2Logic || @@ -98,16 +103,18 @@ func (s SendType) canUseBridge(b bridge.Bridge) bool { bridgeName := b.Name() switch s { case ERC721Transfer: - return bridgeName == ERC721TransferString + return bridgeName == bridge.ERC721TransferName case ERC1155Transfer: - return bridgeName == ERC1155TransferString + return bridgeName == bridge.ERC1155TransferName + case ENSRegister: + return bridgeName == bridge.ENSRegisterName default: return true } } func (s SendType) isAvailableBetween(from, to *params.Network) bool { - if s.IsCollectiblesTransfer() { + if s.IsCollectiblesTransfer() || s.IsEnsTransfer() { return from.ChainID == to.ChainID } @@ -142,6 +149,10 @@ func (s SendType) isAvailableFor(network *params.Network) bool { return swapAllowedNetworks[network.ChainID] } + if s.IsEnsTransfer() { + return network.ChainID == walletCommon.EthereumMainnet || network.ChainID == walletCommon.EthereumSepolia + } + // Check for any SendType available for all networks if s == Transfer || s == Bridge || s.IsCollectiblesTransfer() || allAllowedNetworks[network.ChainID] { return true @@ -150,10 +161,11 @@ func (s SendType) isAvailableFor(network *params.Network) bool { return false } +// TODO: remove this function once we fully move to routerV2 func (s SendType) EstimateGas(ensService *ens.Service, stickersService *stickers.Service, network *params.Network, from common.Address, tokenID string) uint64 { tx := transactions.SendTxArgs{ From: (types.Address)(from), - Value: (*hexutil.Big)(zero), + Value: (*hexutil.Big)(bridge.ZeroBigIntValue), } switch s { case ENSRegister: diff --git a/services/wallet/router/router_v2.go b/services/wallet/router/router_v2.go index 0ace3ed15..fed918fe9 100644 --- a/services/wallet/router/router_v2.go +++ b/services/wallet/router/router_v2.go @@ -12,9 +12,25 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/status-im/status-go/params" "github.com/status-im/status-go/services/wallet/async" + "github.com/status-im/status-go/services/wallet/bridge" + walletCommon "github.com/status-im/status-go/services/wallet/common" walletToken "github.com/status-im/status-go/services/wallet/token" ) +var ( + supportedNetworks = map[uint64]bool{ + walletCommon.EthereumMainnet: true, + walletCommon.OptimismMainnet: true, + walletCommon.ArbitrumMainnet: true, + } + + supportedTestNetworks = map[uint64]bool{ + walletCommon.EthereumSepolia: true, + walletCommon.OptimismSepolia: true, + walletCommon.ArbitrumSepolia: true, + } +) + type RouteInputParams struct { SendType SendType `json:"sendType" validate:"required"` AddrFrom common.Address `json:"addrFrom" validate:"required"` @@ -28,6 +44,10 @@ type RouteInputParams struct { GasFeeMode GasFeeMode `json:"gasFeeMode" validate:"required"` FromLockedAmount map[uint64]*hexutil.Big `json:"fromLockedAmount"` TestnetMode bool `json:"testnetMode"` + + // For send types like EnsRegister, EnsRelease, EnsSetPubKey, StickersBuy + Username string `json:"username"` + PublicKey string `json:"publicKey"` } type PathV2 struct { @@ -112,7 +132,7 @@ func newSuggestedRoutesV2( rest := new(big.Int).Set(amountIn) for _, path := range best { diff := new(big.Int).Sub(rest, path.AmountIn.ToInt()) - if diff.Cmp(zero) >= 0 { + if diff.Cmp(bridge.ZeroBigIntValue) >= 0 { path.AmountIn = (*hexutil.Big)(path.AmountIn.ToInt()) } else { path.AmountIn = (*hexutil.Big)(new(big.Int).Set(rest)) @@ -214,7 +234,7 @@ func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice fl pathCost = new(big.Float).Mul(txFeeInEth, nativeTokenPrice) } - if path.TxBonderFees != nil && path.TxBonderFees.ToInt().Cmp(zero) > 0 { + if path.TxBonderFees != nil && path.TxBonderFees.ToInt().Cmp(bridge.ZeroBigIntValue) > 0 { path.requiredTokenBalance.Add(path.requiredTokenBalance, path.TxBonderFees.ToInt()) pathCost.Add(pathCost, new(big.Float).Mul( new(big.Float).Quo(new(big.Float).SetInt(path.TxBonderFees.ToInt()), tokenDenominator), @@ -222,7 +242,7 @@ func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice fl } - if path.TxL1Fee != nil && path.TxL1Fee.ToInt().Cmp(zero) > 0 { + if path.TxL1Fee != nil && path.TxL1Fee.ToInt().Cmp(bridge.ZeroBigIntValue) > 0 { l1FeeInWei := path.TxL1Fee.ToInt() l1FeeInEth := gweiToEth(weiToGwei(l1FeeInWei)) @@ -230,7 +250,7 @@ func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice fl pathCost.Add(pathCost, new(big.Float).Mul(l1FeeInEth, nativeTokenPrice)) } - if path.TxTokenFees != nil && path.TxTokenFees.ToInt().Cmp(zero) > 0 && path.FromToken != nil { + if path.TxTokenFees != nil && path.TxTokenFees.ToInt().Cmp(bridge.ZeroBigIntValue) > 0 && path.FromToken != nil { path.requiredTokenBalance.Add(path.requiredTokenBalance, path.TxTokenFees.ToInt()) pathCost.Add(pathCost, new(big.Float).Mul( new(big.Float).Quo(new(big.Float).SetInt(path.TxTokenFees.ToInt()), tokenDenominator), @@ -268,7 +288,50 @@ func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice fl return best } +func validateInputData(input *RouteInputParams) error { + if input.SendType == ENSRegister { + if input.Username == "" || input.PublicKey == "" { + return errors.New("username and public key are required for ENSRegister") + } + if input.TestnetMode { + if input.TokenID != bridge.SttSymbol { + return errors.New("only STT is supported for ENSRegister on testnet") + } + } else { + if input.TokenID != bridge.SntSymbol { + return errors.New("only SNT is supported for ENSRegister") + } + } + return nil + } + + if input.FromLockedAmount != nil && len(input.FromLockedAmount) > 0 { + for chainID, amount := range input.FromLockedAmount { + if input.TestnetMode { + if !supportedTestNetworks[chainID] { + return errors.New("locked amount is not supported for the selected network") + } + } else { + if !supportedNetworks[chainID] { + return errors.New("locked amount is not supported for the selected network") + } + } + + if amount == nil || amount.ToInt().Sign() < 0 { + return errors.New("locked amount must be positive") + } + } + } + + return nil +} + func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) (*SuggestedRoutesV2, error) { + err := validateInputData(input) + if err != nil { + return nil, err + } + networks, err := r.rpcClient.NetworkManager.Get(false) if err != nil { return nil, err @@ -294,12 +357,16 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) continue } - token := input.SendType.FindToken(r.tokenManager, r.collectiblesService, input.AddrFrom, network, input.TokenID) + var ( + token *walletToken.Token + toToken *walletToken.Token + ) + + token = input.SendType.FindToken(r.tokenManager, r.collectiblesService, input.AddrFrom, network, input.TokenID) if token == nil { continue } - var toToken *walletToken.Token if input.SendType == Swap { toToken = input.SendType.FindToken(r.tokenManager, r.collectiblesService, common.Address{}, network, input.ToTokenID) } @@ -325,8 +392,8 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) return err } - for _, bridge := range r.bridges { - if !input.SendType.canUseBridge(bridge) { + for _, brdg := range r.bridges { + if !input.SendType.canUseBridge(brdg) { continue } @@ -351,27 +418,35 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) continue } - can, err := bridge.AvailableFor(network, dest, token, toToken) + bridgeParams := bridge.BridgeParams{ + FromChain: network, + ToChain: dest, + FromToken: token, + ToToken: toToken, + ToAddr: input.AddrTo, + FromAddr: input.AddrFrom, + AmountIn: amountToSend, + + Username: input.Username, + PublicKey: input.PublicKey, + } + + can, err := brdg.AvailableFor(bridgeParams) if err != nil || !can { continue } - bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountToSend) + bonderFees, tokenFees, err := brdg.CalculateFees(bridgeParams) if err != nil { continue } - gasLimit := uint64(0) - if input.SendType.isTransfer(true) { - gasLimit, err = bridge.EstimateGas(network, dest, input.AddrFrom, input.AddrTo, token, toToken, amountToSend) - if err != nil { - continue - } - } else { - gasLimit = input.SendType.EstimateGas(r.ensService, r.stickersService, network, input.AddrFrom, input.TokenID) + gasLimit, err := brdg.EstimateGas(bridgeParams) + if err != nil { + continue } - approvalContractAddress, err := bridge.GetContractAddress(network, token) + approvalContractAddress, err := brdg.GetContractAddress(bridgeParams) if err != nil { continue } @@ -383,7 +458,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) var l1FeeWei uint64 if input.SendType.needL1Fee() { - txInputData, err := bridge.PackTxInputData("", network, dest, input.AddrFrom, input.AddrTo, token, amountToSend) + txInputData, err := brdg.PackTxInputData(bridgeParams, "") if err != nil { continue } @@ -407,7 +482,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) selctedPriorityFee = priorityFees.Low } - amountOut, err := bridge.CalculateAmountOut(network, dest, amountToSend, token.Symbol) + amountOut, err := brdg.CalculateAmountOut(bridgeParams) if err != nil { continue } @@ -421,7 +496,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) mu.Lock() candidates = append(candidates, &PathV2{ - BridgeName: bridge.Name(), + BridgeName: brdg.Name(), FromChain: network, ToChain: network, FromToken: token,