chore_: the router implementation simplification

This commit is contained in:
Sale Djenic 2024-05-14 21:11:16 +02:00 committed by saledjenic
parent 437ad499c8
commit 08fef4afde
17 changed files with 801 additions and 283 deletions

View File

@ -445,6 +445,25 @@ func (api *API) GetSuggestedRoutes(
disabledToChaindIDs, preferedChainIDs, gasFeeMode, fromLockedAmount)
}
func (api *API) GetSuggestedRoutesV2(
ctx context.Context,
sendType SendType,
addrFrom common.Address,
addrTo common.Address,
amountIn *hexutil.Big,
tokenID string,
toTokenID string,
disabledFromChainIDs,
disabledToChaindIDs,
preferedChainIDs []uint64,
gasFeeMode GasFeeMode,
fromLockedAmount map[uint64]*hexutil.Big,
) (*SuggestedRoutesV2, error) {
log.Debug("call to GetSuggestedRoutesV2")
return api.router.suggestedRoutesV2(ctx, sendType, addrFrom, addrTo, amountIn.ToInt(), tokenID, toTokenID, disabledFromChainIDs,
disabledToChaindIDs, preferedChainIDs, gasFeeMode, fromLockedAmount)
}
// Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)
func (api *API) GetDerivedAddresses(ctx context.Context, password string, derivedFrom string, paths []string) ([]*DerivedAddress, error) {
info, err := api.s.gethManager.AccountsGenerator().LoadAccount(derivedFrom, password)

View File

@ -101,9 +101,12 @@ func (t *TransactionBridge) Data() types.HexBytes {
}
type Bridge interface {
// returns the name of the bridge
Name() string
Can(from *params.Network, to *params.Network, token *token.Token, toToken *token.Token, balance *big.Int) (bool, error)
CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error)
// 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)
// 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)
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)
Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error)

View File

@ -5,9 +5,9 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math/big"
"net/http"
netUrl "net/url"
"strconv"
"strings"
"time"
@ -24,6 +24,7 @@ import (
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/bridge/cbridge"
"github.com/status-im/status-go/services/wallet/thirdparty"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
@ -41,6 +42,7 @@ type CBridgeTxArgs struct {
type CBridge struct {
rpcClient *rpc.Client
httpClient *thirdparty.HTTPClient
transactor *transactions.Transactor
tokenManager *token.Manager
prodTransferConfig *cbridge.GetTransferConfigsResponse
@ -50,6 +52,7 @@ type CBridge struct {
func NewCbridge(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager) *CBridge {
return &CBridge{
rpcClient: rpcClient,
httpClient: thirdparty.NewHTTPClient(),
transactor: transactor,
tokenManager: tokenManager,
}
@ -60,43 +63,27 @@ func (s *CBridge) Name() string {
}
func (s *CBridge) estimateAmt(from, to *params.Network, amountIn *big.Int, symbol string) (*cbridge.EstimateAmtResponse, error) {
client := &http.Client{
Timeout: time.Second * 5,
}
base := baseURL
if from.IsTest {
base = testBaseURL
}
url := fmt.Sprintf(
"%s/v2/estimateAmt?src_chain_id=%d&dst_chain_id=%d&token_symbol=%s&amt=%s&usr_addr=0xaa47c83316edc05cf9ff7136296b026c5de7eccd&slippage_tolerance=500",
base,
from.ChainID,
to.ChainID,
symbol,
amountIn.String(),
)
req, err := http.NewRequest(http.MethodGet, url, nil)
params := netUrl.Values{}
params.Add("src_chain_id", strconv.Itoa(int(from.ChainID)))
params.Add("dst_chain_id", strconv.Itoa(int(to.ChainID)))
params.Add("token_symbol", symbol)
params.Add("amt", amountIn.String())
params.Add("usr_addr", "0xaa47c83316edc05cf9ff7136296b026c5de7eccd")
params.Add("slippage_tolerance", "500")
url := fmt.Sprintf("%s/v2/estimateAmt", base)
response, err := s.httpClient.DoGetRequest(context.Background(), url, params)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
fmt.Println("failed to close cbridge request body", "err", err)
}
}()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var res cbridge.EstimateAmtResponse
err = json.Unmarshal(body, &res)
err = json.Unmarshal(response, &res)
if err != nil {
return nil, err
}
@ -112,36 +99,18 @@ func (s *CBridge) getTransferConfig(isTest bool) (*cbridge.GetTransferConfigsRes
return s.testTransferConfig, nil
}
client := &http.Client{
Timeout: time.Second * 5,
}
base := baseURL
if isTest {
base = testBaseURL
}
url := fmt.Sprintf("%s/v2/getTransferConfigs", base)
req, err := http.NewRequest(http.MethodGet, url, nil)
response, err := s.httpClient.DoGetRequest(context.Background(), url, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
fmt.Println("failed to close cbridge request body", "err", err)
}
}()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var res cbridge.GetTransferConfigsResponse
err = json.Unmarshal(body, &res)
err = json.Unmarshal(response, &res)
if err != nil {
return nil, err
}
@ -153,7 +122,7 @@ func (s *CBridge) getTransferConfig(isTest bool) (*cbridge.GetTransferConfigsRes
return &res, nil
}
func (s *CBridge) Can(from, to *params.Network, token *token.Token, toToken *token.Token, balance *big.Int) (bool, error) {
func (s *CBridge) AvailableFor(from, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) {
if from.ChainID == to.ChainID || toToken != nil {
return false, nil
}
@ -210,7 +179,7 @@ func (s *CBridge) Can(from, to *params.Network, token *token.Token, toToken *tok
return true, nil
}
func (s *CBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
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)
if err != nil {
return nil, nil, err

View File

@ -41,11 +41,11 @@ func (s *ERC1155TransferBridge) Name() string {
return "ERC1155Transfer"
}
func (s *ERC1155TransferBridge) Can(from, to *params.Network, token *token.Token, toToken *token.Token, balance *big.Int) (bool, error) {
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) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
func (s *ERC1155TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) {
return big.NewInt(0), big.NewInt(0), nil
}

View File

@ -40,11 +40,11 @@ func (s *ERC721TransferBridge) Name() string {
return "ERC721Transfer"
}
func (s *ERC721TransferBridge) Can(from, to *params.Network, token *token.Token, toToken *token.Token, balance *big.Int) (bool, error) {
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) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
func (s *ERC721TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) {
return big.NewInt(0), big.NewInt(0), nil
}

View File

@ -2,10 +2,11 @@ package bridge
import (
"context"
"encoding/json"
"errors"
"fmt"
"math"
"math/big"
netUrl "net/url"
"strings"
"time"
@ -23,65 +24,12 @@ import (
"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"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
const HopLpFeeBps = 4
const HopCanonicalTokenIndex = 0
const HophTokenIndex = 1
const HopMinBonderFeeUsd = 0.25
var HopBondTransferGasLimit = map[uint64]int64{
1: 165000,
5: 165000,
10: 100000000,
42161: 2500000,
420: 100000000,
421613: 2500000,
}
var HopSettlementGasLimitPerTx = map[uint64]int64{
1: 5141,
5: 5141,
10: 8545,
42161: 19843,
420: 8545,
421613: 19843,
}
var HopBonderFeeBps = map[string]map[uint64]int64{
"USDC": {
1: 14,
5: 14,
10: 14,
42161: 14,
420: 14,
421613: 14,
},
"USDT": {
1: 26,
10: 26,
421613: 26,
},
"DAI": {
1: 26,
10: 26,
42161: 26,
},
"ETH": {
1: 5,
5: 5,
10: 5,
42161: 5,
420: 5,
421613: 5,
},
"WBTC": {
1: 23,
10: 23,
42161: 23,
},
}
type HopTxArgs struct {
transactions.SendTxArgs
ChainID uint64 `json:"chainId"`
@ -91,15 +39,71 @@ type HopTxArgs struct {
BonderFee *hexutil.Big `json:"bonderFee"`
}
type BonderFee struct {
AmountIn *big.Int `json:"amountIn"`
Slippage float32 `json:"slippage"`
AmountOutMin *big.Int `json:"amountOutMin"`
DestinationAmountOutMin *big.Int `json:"destinationAmountOutMin"`
BonderFee *big.Int `json:"bonderFee"`
EstimatedRecieved *big.Int `json:"estimatedRecieved"`
Deadline int64 `json:"deadline"`
DestinationDeadline int64 `json:"destinationDeadline"`
}
func (bf *BonderFee) UnmarshalJSON(data []byte) error {
type Alias BonderFee
aux := &struct {
AmountIn string `json:"amountIn"`
Slippage float32 `json:"slippage"`
AmountOutMin string `json:"amountOutMin"`
DestinationAmountOutMin string `json:"destinationAmountOutMin"`
BonderFee string `json:"bonderFee"`
EstimatedRecieved string `json:"estimatedRecieved"`
Deadline int64 `json:"deadline"`
DestinationDeadline *int64 `json:"destinationDeadline"`
*Alias
}{
Alias: (*Alias)(bf),
}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
bf.AmountIn = new(big.Int)
bf.AmountIn.SetString(aux.AmountIn, 10)
bf.AmountOutMin = new(big.Int)
bf.AmountOutMin.SetString(aux.AmountOutMin, 10)
bf.DestinationAmountOutMin = new(big.Int)
bf.DestinationAmountOutMin.SetString(aux.DestinationAmountOutMin, 10)
bf.BonderFee = new(big.Int)
bf.BonderFee.SetString(aux.BonderFee, 10)
bf.EstimatedRecieved = new(big.Int)
bf.EstimatedRecieved.SetString(aux.EstimatedRecieved, 10)
if aux.DestinationDeadline != nil {
bf.DestinationDeadline = *aux.DestinationDeadline
}
return nil
}
type HopBridge struct {
transactor *transactions.Transactor
httpClient *thirdparty.HTTPClient
tokenManager *token.Manager
contractMaker *contracts.ContractMaker
bonderFee *BonderFee
}
func NewHopBridge(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager) *HopBridge {
return &HopBridge{
contractMaker: &contracts.ContractMaker{RPCClient: rpcClient},
httpClient: thirdparty.NewHTTPClient(),
transactor: transactor,
tokenManager: tokenManager,
}
@ -109,27 +113,16 @@ func (h *HopBridge) Name() string {
return "Hop"
}
func (h *HopBridge) Can(from, to *params.Network, token *token.Token, toToken *token.Token, balance *big.Int) (bool, error) {
if balance.Cmp(big.NewInt(0)) == 0 {
return false, nil
}
func (h *HopBridge) AvailableFor(from, to *params.Network, token *token.Token, toToken *token.Token) (bool, error) {
if from.ChainID == to.ChainID || toToken != nil {
return false, nil
}
fees, ok := HopBonderFeeBps[token.Symbol]
if !ok {
// currently Hop bridge is not available for testnets
if from.IsTest || to.IsTest {
return false, nil
}
if _, ok := fees[from.ChainID]; !ok {
return false, nil
}
if _, ok := fees[to.ChainID]; !ok {
return false, nil
}
return true, nil
}
@ -349,142 +342,50 @@ func (h *HopBridge) swapAndSend(chainID uint64, hopArgs *HopTxArgs, signerFn bin
return tx, err
}
// CalculateBonderFees logics come from: https://docs.hop.exchange/fee-calculation
func (h *HopBridge) CalculateBonderFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, error) {
amount := new(big.Float).SetInt(amountIn)
totalFee := big.NewFloat(0)
destinationTxFee, err := h.getDestinationTxFee(from, to, nativeTokenPrice, tokenPrice, gasPrice)
if err != nil {
return nil, err
func (h *HopBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) {
const (
HopMainnetChainName = "ethereum"
HopOptimismChainName = "optimism"
HopArbitrumChainName = "arbitrum"
)
fromChainName := HopMainnetChainName
if from.ChainID == walletCommon.OptimismMainnet {
fromChainName = HopOptimismChainName
} else if from.ChainID == walletCommon.ArbitrumMainnet {
fromChainName = HopArbitrumChainName
}
bonderFeeRelative, err := h.getBonderFeeRelative(from, to, amount, token)
if err != nil {
return nil, err
toChainName := HopMainnetChainName
if from.ChainID == walletCommon.OptimismMainnet {
toChainName = HopOptimismChainName
} else if from.ChainID == walletCommon.ArbitrumMainnet {
toChainName = HopArbitrumChainName
}
if from.Layer != 1 {
adjustedBonderFee, err := h.calcFromHTokenAmount(to, bonderFeeRelative, token.Symbol)
if err != nil {
return nil, err
}
adjustedDestinationTxFee, err := h.calcToHTokenAmount(to, destinationTxFee, token.Symbol)
if err != nil {
return nil, err
}
bonderFeeAbsolute := h.getBonderFeeAbsolute(tokenPrice)
if adjustedBonderFee.Cmp(bonderFeeAbsolute) == -1 {
adjustedBonderFee = bonderFeeAbsolute
}
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%
totalFee.Add(adjustedBonderFee, adjustedDestinationTxFee)
}
res, _ := new(big.Float).Mul(totalFee, big.NewFloat(math.Pow(10, float64(token.Decimals)))).Int(nil)
return res, nil
}
func (h *HopBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
bonderFees, err := h.CalculateBonderFees(from, to, token, amountIn, nativeTokenPrice, tokenPrice, gasPrice)
url := "https://api.hop.exchange/v1/quote"
response, err := h.httpClient.DoGetRequest(context.Background(), url, params)
if err != nil {
return nil, nil, err
}
amountOut, err := h.amountOut(from, to, new(big.Float).SetInt(amountIn), token.Symbol)
err = json.Unmarshal(response, h.bonderFee)
if err != nil {
return nil, nil, err
}
amountOutInt, _ := amountOut.Int(nil)
return bonderFees, new(big.Int).Add(
bonderFees,
new(big.Int).Sub(amountIn, amountOutInt),
), nil
}
tokenFee := new(big.Int).Sub(h.bonderFee.AmountIn, h.bonderFee.EstimatedRecieved)
func (h *HopBridge) calcToHTokenAmount(network *params.Network, amount *big.Float, symbol string) (*big.Float, error) {
if network.Layer == 1 || amount.Cmp(big.NewFloat(0)) == 0 {
return amount, nil
}
contract, err := h.contractMaker.NewHopL2SaddlSwap(network.ChainID, symbol)
if err != nil {
return nil, err
}
amountInt, _ := amount.Int(nil)
res, err := contract.CalculateSwap(&bind.CallOpts{Context: context.Background()}, HopCanonicalTokenIndex, HophTokenIndex, amountInt)
if err != nil {
return nil, err
}
return new(big.Float).SetInt(res), nil
}
func (h *HopBridge) calcFromHTokenAmount(network *params.Network, amount *big.Float, symbol string) (*big.Float, error) {
if network.Layer == 1 || amount.Cmp(big.NewFloat(0)) == 0 {
return amount, nil
}
contract, err := h.contractMaker.NewHopL2SaddlSwap(network.ChainID, symbol)
if err != nil {
return nil, err
}
amountInt, _ := amount.Int(nil)
res, err := contract.CalculateSwap(&bind.CallOpts{Context: context.Background()}, HophTokenIndex, HopCanonicalTokenIndex, amountInt)
if err != nil {
return nil, err
}
return new(big.Float).SetInt(res), nil
return h.bonderFee.BonderFee, tokenFee, nil
}
func (h *HopBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
amountOut, err := h.amountOut(from, to, new(big.Float).SetInt(amountIn), symbol)
if err != nil {
return nil, err
}
amountOutInt, _ := amountOut.Int(nil)
return amountOutInt, nil
}
func (h *HopBridge) amountOut(from, to *params.Network, amountIn *big.Float, symbol string) (*big.Float, error) {
hTokenAmount, err := h.calcToHTokenAmount(from, amountIn, symbol)
if err != nil {
return nil, err
}
return h.calcFromHTokenAmount(to, hTokenAmount, symbol)
}
func (h *HopBridge) getBonderFeeRelative(from, to *params.Network, amount *big.Float, token *token.Token) (*big.Float, error) {
if from.Layer != 1 {
return big.NewFloat(0), nil
}
hTokenAmount, err := h.calcToHTokenAmount(from, amount, token.Symbol)
if err != nil {
return nil, err
}
feeBps := HopBonderFeeBps[token.Symbol][to.ChainID]
factor := new(big.Float).Mul(hTokenAmount, big.NewFloat(float64(feeBps)))
return new(big.Float).Quo(
factor,
big.NewFloat(10000),
), nil
}
func (h *HopBridge) getBonderFeeAbsolute(tokenPrice float64) *big.Float {
return new(big.Float).Quo(big.NewFloat(HopMinBonderFeeUsd), big.NewFloat(tokenPrice))
}
func (h *HopBridge) getDestinationTxFee(from, to *params.Network, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Float, error) {
if from.Layer != 1 {
return big.NewFloat(0), nil
}
bondTransferGasLimit := HopBondTransferGasLimit[to.ChainID]
settlementGasLimit := HopSettlementGasLimitPerTx[to.ChainID]
totalGasLimit := new(big.Int).Add(big.NewInt(bondTransferGasLimit), big.NewInt(settlementGasLimit))
rate := new(big.Float).Quo(big.NewFloat(nativeTokenPrice), big.NewFloat(tokenPrice))
txFeeEth := new(big.Float).Mul(gasPrice, new(big.Float).SetInt(totalGasLimit))
return new(big.Float).Mul(txFeeEth, rate), nil
return h.bonderFee.EstimatedRecieved, nil
}

View File

@ -42,7 +42,7 @@ func (s *SwapParaswap) Name() string {
return "Paraswap"
}
func (s *SwapParaswap) Can(from, to *params.Network, token *walletToken.Token, toToken *walletToken.Token, balance *big.Int) (bool, error) {
func (s *SwapParaswap) AvailableFor(from, to *params.Network, token *walletToken.Token, toToken *walletToken.Token) (bool, error) {
if token == nil || toToken == nil {
return false, errors.New("token and toToken cannot be nil")
}
@ -87,7 +87,7 @@ func (s *SwapParaswap) Can(from, to *params.Network, token *walletToken.Token, t
return true, nil
}
func (s *SwapParaswap) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
func (s *SwapParaswap) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) {
return big.NewInt(0), big.NewInt(0), nil
}

View File

@ -32,11 +32,11 @@ func (s *TransferBridge) Name() string {
return "Transfer"
}
func (s *TransferBridge) Can(from, to *params.Network, token *token.Token, toToken *token.Token, balance *big.Int) (bool, error) {
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) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
func (s *TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) {
return big.NewInt(0), big.NewInt(0), nil
}

View File

@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/params"
gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/rpc/chain"
)
type GasFeeMode int
@ -23,6 +24,9 @@ const (
GasFeeHigh
)
// //////////////////////////////////////////////////////////////////////////////
// TODO: remove `SuggestedFees` struct once new router is in place
// //////////////////////////////////////////////////////////////////////////////
type SuggestedFees struct {
GasPrice *big.Float `json:"gasPrice"`
BaseFee *big.Float `json:"baseFee"`
@ -34,6 +38,12 @@ type SuggestedFees struct {
EIP1559Enabled bool `json:"eip1559Enabled"`
}
type PriorityFees struct {
Low *big.Int `json:"low"`
Medium *big.Int `json:"medium"`
High *big.Int `json:"high"`
}
func (s *SuggestedFees) feeFor(mode GasFeeMode) *big.Float {
if !s.EIP1559Enabled {
return s.GasPrice
@ -89,6 +99,9 @@ func gweiToWei(val *big.Float) *big.Int {
return res
}
// //////////////////////////////////////////////////////////////////////////////
// TODO: remove `suggestedFees` function once new router is in place
// //////////////////////////////////////////////////////////////////////////////
func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*SuggestedFees, error) {
backend, err := f.RPCClient.EthClient(chainID)
if err != nil {
@ -163,6 +176,57 @@ func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*Sugges
}, nil
}
func (f *FeeManager) getBaseFee(ctx context.Context, client chain.ClientInterface, testnetMode bool) (*big.Int, error) {
header, err := client.HeaderByNumber(ctx, nil)
if err != nil {
return nil, err
}
config := params.MainnetChainConfig
if testnetMode {
config = params.SepoliaChainConfig
}
baseFee := misc.CalcBaseFee(config, header)
return baseFee, nil
}
func (f *FeeManager) getPriorityFees(ctx context.Context, client chain.ClientInterface, baseFee *big.Int) (PriorityFees, error) {
var priorityFee PriorityFees
fees, err := f.getFeeHistorySorted(client.NetworkID())
if err != nil {
return priorityFee, err
}
suggestedPriorityFee, err := client.SuggestGasTipCap(ctx)
if err != nil {
return priorityFee, err
}
// Calculate Low priority fee
priorityFee.Low = fees[int64(0.1*float64(len(fees)))-1]
// Calculate Medium priority fee
priorityFee.Medium = fees[int64(0.2*float64(len(fees)))-1]
if baseFee.Cmp(priorityFee.Medium) > 0 {
priorityFee.Medium = baseFee
}
if suggestedPriorityFee.Cmp(priorityFee.Medium) > 0 {
priorityFee.Medium = suggestedPriorityFee
}
// Calculate High priority fee
priorityFee.High = new(big.Int).Mul(suggestedPriorityFee, big.NewInt(2))
twoTimesBaseFee := new(big.Int).Mul(baseFee, big.NewInt(2))
if twoTimesBaseFee.Cmp(priorityFee.High) > 0 {
priorityFee.High = twoTimesBaseFee
}
return priorityFee, nil
}
func (f *FeeManager) transactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) TransactionEstimation {
fees, err := f.getFeeHistorySorted(chainID)
if err != nil {

View File

@ -27,6 +27,11 @@ import (
walletToken "github.com/status-im/status-go/services/wallet/token"
)
// //////////////////////////////////////////////////////////////////////////////
// TODO: once new router is in place, remove this `router.go` file,
// rename and make `router_v2.go` file the main and only file
// //////////////////////////////////////////////////////////////////////////////
const EstimateUsername = "RandomUsername"
const EstimatePubKey = "0x04bb2024ce5d72e45d4a4f8589ae657ef9745855006996115a23a1af88d536cf02c0524a585fce7bfa79d6a9669af735eda6205d6c7e5b3cdc2b8ff7b2fa1f0b56"
const ERC721TransferString = "ERC721Transfer"
@ -524,10 +529,6 @@ func (r *Router) suggestedRoutes(
continue
}
if !sendType.isAvailableBetween(network, dest) {
continue
}
if len(preferedChainIDs) > 0 && !containsNetworkChainID(dest, preferedChainIDs) {
continue
}
@ -535,11 +536,15 @@ func (r *Router) suggestedRoutes(
continue
}
can, err := bridge.Can(network, dest, token, toToken, maxAmountIn.ToInt())
can, err := bridge.AvailableFor(network, dest, token, toToken)
if err != nil || !can {
continue
}
bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountIn, prices["ETH"], prices[tokenID], gasFees.GasPrice)
if maxAmountIn.ToInt().Cmp(big.NewInt(0)) == 0 {
continue
}
bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountIn)
if err != nil {
continue
}

View File

@ -87,22 +87,6 @@ func (s SendType) needL1Fee() bool {
return s != ENSRegister && s != ENSRelease && s != ENSSetPubKey && s != StickersBuy
}
func (s SendType) isAvailableBetween(from, to *params.Network) bool {
if s.IsCollectiblesTransfer() {
return from.ChainID == to.ChainID
}
if s == Bridge {
return from.ChainID != to.ChainID
}
if s == Swap {
return from.ChainID == to.ChainID
}
return true
}
func (s SendType) canUseBridge(b bridge.Bridge) bool {
if s == ERC721Transfer && b.Name() != ERC721TransferString {
return false

View File

@ -0,0 +1,571 @@
package wallet
import (
"context"
"errors"
"math"
"math/big"
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/async"
walletToken "github.com/status-im/status-go/services/wallet/token"
)
type PathV2 struct {
BridgeName string
From *params.Network // Source chain
To *params.Network // Destination chain
FromToken *walletToken.Token // Token on the source chain
AmountIn *hexutil.Big // Amount that will be sent from the source chain
AmountInLocked bool // Is the amount locked
AmountOut *hexutil.Big // Amount that will be received on the destination chain
SuggestedPriorityFees *PriorityFees // Suggested priority fees for the transaction
TxBaseFee *hexutil.Big // Base fee for the transaction
TxPriorityFee *hexutil.Big // Priority fee for the transaction, by default we're using the Medium priority fee
TxGasAmount uint64 // Gas used for the transaction
TxBonderFees *hexutil.Big // Bonder fees for the transaction - used for Hop bridge
TxTokenFees *hexutil.Big // Token fees for the transaction - used for bridges (represent the difference between the amount in and the amount out)
TxL1Fee *hexutil.Big // L1 fee for the transaction - used for for transactions placed on L2 chains
ApprovalRequired bool // Is approval required for the transaction
ApprovalAmountRequired *hexutil.Big // Amount required for the approval transaction
ApprovalContractAddress *common.Address // Address of the contract that needs to be approved
ApprovalBaseFee *hexutil.Big // Base fee for the approval transaction
ApprovalPriorityFee *hexutil.Big // Priority fee for the approval transaction
ApprovalGasAmount uint64 // Gas used for the approval transaction
ApprovalL1Fee *hexutil.Big // L1 fee for the approval transaction - used for for transactions placed on L2 chains
EstimatedTime TransactionEstimation
requiredTokenBalance *big.Int
requiredNativeBalance *big.Int
}
func (p *PathV2) Equal(o *PathV2) bool {
return p.From.ChainID == o.From.ChainID && p.To.ChainID == o.To.ChainID
}
type SuggestedRoutesV2 struct {
Best []*PathV2
Candidates []*PathV2
TokenPrice float64
NativeChainTokenPrice float64
}
type GraphV2 = []*NodeV2
type NodeV2 struct {
Path *PathV2
Children GraphV2
}
func newSuggestedRoutesV2(
amountIn *big.Int,
candidates []*PathV2,
fromLockedAmount map[uint64]*hexutil.Big,
tokenPrice float64,
nativeChainTokenPrice float64,
) *SuggestedRoutesV2 {
suggestedRoutes := &SuggestedRoutesV2{
Candidates: candidates,
Best: candidates,
TokenPrice: tokenPrice,
NativeChainTokenPrice: nativeChainTokenPrice,
}
if len(candidates) == 0 {
return suggestedRoutes
}
node := &NodeV2{
Path: nil,
Children: buildGraphV2(amountIn, candidates, 0, []uint64{}),
}
routes := node.buildAllRoutesV2()
routes = filterRoutesV2(routes, amountIn, fromLockedAmount)
best := findBestV2(routes, tokenPrice, nativeChainTokenPrice)
if len(best) > 0 {
sort.Slice(best, func(i, j int) bool {
return best[i].AmountInLocked
})
rest := new(big.Int).Set(amountIn)
for _, path := range best {
diff := new(big.Int).Sub(rest, path.AmountIn.ToInt())
if diff.Cmp(zero) >= 0 {
path.AmountIn = (*hexutil.Big)(path.AmountIn.ToInt())
} else {
path.AmountIn = (*hexutil.Big)(new(big.Int).Set(rest))
}
rest.Sub(rest, path.AmountIn.ToInt())
}
}
suggestedRoutes.Best = best
return suggestedRoutes
}
func newNodeV2(path *PathV2) *NodeV2 {
return &NodeV2{Path: path, Children: make(GraphV2, 0)}
}
func buildGraphV2(AmountIn *big.Int, routes []*PathV2, level int, sourceChainIDs []uint64) GraphV2 {
graph := make(GraphV2, 0)
for _, route := range routes {
found := false
for _, chainID := range sourceChainIDs {
if chainID == route.From.ChainID {
found = true
break
}
}
if found {
continue
}
node := newNodeV2(route)
newRoutes := make([]*PathV2, 0)
for _, r := range routes {
if route.Equal(r) {
continue
}
newRoutes = append(newRoutes, r)
}
newAmountIn := new(big.Int).Sub(AmountIn, route.AmountIn.ToInt())
if newAmountIn.Sign() > 0 {
newSourceChainIDs := make([]uint64, len(sourceChainIDs))
copy(newSourceChainIDs, sourceChainIDs)
newSourceChainIDs = append(newSourceChainIDs, route.From.ChainID)
node.Children = buildGraphV2(newAmountIn, newRoutes, level+1, newSourceChainIDs)
if len(node.Children) == 0 {
continue
}
}
graph = append(graph, node)
}
return graph
}
func (n NodeV2) buildAllRoutesV2() [][]*PathV2 {
res := make([][]*PathV2, 0)
if len(n.Children) == 0 && n.Path != nil {
res = append(res, []*PathV2{n.Path})
}
for _, node := range n.Children {
for _, route := range node.buildAllRoutesV2() {
extendedRoute := route
if n.Path != nil {
extendedRoute = append([]*PathV2{n.Path}, route...)
}
res = append(res, extendedRoute)
}
}
return res
}
func filterRoutesV2(routes [][]*PathV2, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) [][]*PathV2 {
if len(fromLockedAmount) == 0 {
return routes
}
filteredRoutesLevel1 := make([][]*PathV2, 0)
for _, route := range routes {
routeOk := true
fromIncluded := make(map[uint64]bool)
fromExcluded := make(map[uint64]bool)
for chainID, amount := range fromLockedAmount {
if amount.ToInt().Cmp(zero) == 0 {
fromExcluded[chainID] = false
} else {
fromIncluded[chainID] = false
}
}
for _, path := range route {
if _, ok := fromExcluded[path.From.ChainID]; ok {
routeOk = false
break
}
if _, ok := fromIncluded[path.From.ChainID]; ok {
fromIncluded[path.From.ChainID] = true
}
}
for _, value := range fromIncluded {
if !value {
routeOk = false
break
}
}
if routeOk {
filteredRoutesLevel1 = append(filteredRoutesLevel1, route)
}
}
filteredRoutesLevel2 := make([][]*PathV2, 0)
for _, route := range filteredRoutesLevel1 {
routeOk := true
for _, path := range route {
if amount, ok := fromLockedAmount[path.From.ChainID]; ok {
requiredAmountIn := new(big.Int).Sub(amountIn, amount.ToInt())
restAmountIn := big.NewInt(0)
for _, otherPath := range route {
if path.Equal(otherPath) {
continue
}
restAmountIn = new(big.Int).Add(otherPath.AmountIn.ToInt(), restAmountIn)
}
if restAmountIn.Cmp(requiredAmountIn) >= 0 {
path.AmountIn = amount
path.AmountInLocked = true
} else {
routeOk = false
break
}
}
}
if routeOk {
filteredRoutesLevel2 = append(filteredRoutesLevel2, route)
}
}
return filteredRoutesLevel2
}
func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice float64) []*PathV2 {
var best []*PathV2
bestCost := big.NewFloat(math.Inf(1))
for _, route := range routes {
currentCost := big.NewFloat(0)
for _, path := range route {
if path.FromToken.IsNative() {
path.requiredNativeBalance = new(big.Int).Set(path.AmountIn.ToInt())
} else {
path.requiredTokenBalance = new(big.Int).Set(path.AmountIn.ToInt())
path.requiredNativeBalance = big.NewInt(0)
}
// ecaluate the cost of the path
pathCost := big.NewFloat(0)
nativeTokenPrice := new(big.Float).SetFloat64(nativeChainTokenPrice)
if path.TxBaseFee != nil && path.TxPriorityFee != nil {
feePerGas := new(big.Int).Add(path.TxBaseFee.ToInt(), path.TxPriorityFee.ToInt())
txFeeInWei := new(big.Int).Mul(feePerGas, big.NewInt(int64(path.TxGasAmount)))
txFeeInEth := gweiToEth(weiToGwei(txFeeInWei))
path.requiredNativeBalance.Add(path.requiredNativeBalance, txFeeInWei)
pathCost = new(big.Float).Mul(txFeeInEth, nativeTokenPrice)
}
if path.TxBonderFees != nil && path.TxBonderFees.ToInt().Cmp(zero) > 0 {
bonderFeeInWei := path.TxBonderFees.ToInt()
bonderFeeInEth := gweiToEth(weiToGwei(bonderFeeInWei))
path.requiredNativeBalance.Add(path.requiredNativeBalance, bonderFeeInWei)
pathCost.Add(pathCost, new(big.Float).Mul(bonderFeeInEth, nativeTokenPrice))
}
if path.TxL1Fee != nil && path.TxL1Fee.ToInt().Cmp(zero) > 0 {
l1FeeInWei := path.TxL1Fee.ToInt()
l1FeeInEth := gweiToEth(weiToGwei(l1FeeInWei))
path.requiredNativeBalance.Add(path.requiredNativeBalance, l1FeeInWei)
pathCost.Add(pathCost, new(big.Float).Mul(l1FeeInEth, nativeTokenPrice))
}
if path.TxTokenFees != nil && path.TxTokenFees.ToInt().Cmp(zero) > 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()), big.NewFloat(math.Pow(10, float64(path.FromToken.Decimals)))),
new(big.Float).SetFloat64(tokenPrice)))
}
if path.ApprovalRequired {
if path.ApprovalBaseFee != nil && path.ApprovalPriorityFee != nil {
feePerGas := new(big.Int).Add(path.ApprovalBaseFee.ToInt(), path.ApprovalPriorityFee.ToInt())
txFeeInWei := new(big.Int).Mul(feePerGas, big.NewInt(int64(path.ApprovalGasAmount)))
txFeeInEth := gweiToEth(weiToGwei(txFeeInWei))
path.requiredNativeBalance.Add(path.requiredNativeBalance, txFeeInWei)
pathCost.Add(pathCost, new(big.Float).Mul(txFeeInEth, nativeTokenPrice))
}
if path.ApprovalL1Fee != nil {
l1FeeInWei := path.ApprovalL1Fee.ToInt()
l1FeeInEth := gweiToEth(weiToGwei(l1FeeInWei))
path.requiredNativeBalance.Add(path.requiredNativeBalance, l1FeeInWei)
pathCost.Add(pathCost, new(big.Float).Mul(l1FeeInEth, nativeTokenPrice))
}
}
currentCost = new(big.Float).Add(currentCost, pathCost)
}
if currentCost.Cmp(bestCost) == -1 {
best = route
bestCost = currentCost
}
}
return best
}
func (r *Router) suggestedRoutesV2(
ctx context.Context,
sendType SendType,
addrFrom common.Address,
addrTo common.Address,
amountIn *big.Int,
tokenID string,
toTokenID string,
disabledFromChainIDs,
disabledToChaindIDs,
preferedChainIDs []uint64,
gasFeeMode GasFeeMode,
fromLockedAmount map[uint64]*hexutil.Big,
) (*SuggestedRoutesV2, error) {
areTestNetworksEnabled, err := r.s.accountsDB.GetTestNetworksEnabled()
if err != nil {
return nil, err
}
networks, err := r.s.rpcClient.NetworkManager.Get(false)
if err != nil {
return nil, err
}
var (
group = async.NewAtomicGroup(ctx)
mu sync.Mutex
candidates = make([]*PathV2, 0)
)
for networkIdx := range networks {
network := networks[networkIdx]
if network.IsTest != areTestNetworksEnabled {
continue
}
if containsNetworkChainID(network, disabledFromChainIDs) {
continue
}
if !sendType.isAvailableFor(network) {
continue
}
token := sendType.FindToken(r.s, addrFrom, network, tokenID)
if token == nil {
continue
}
var toToken *walletToken.Token
if sendType == Swap {
toToken = sendType.FindToken(r.s, common.Address{}, network, toTokenID)
}
amountLocked := false
amountToSend := amountIn
if lockedAmount, ok := fromLockedAmount[network.ChainID]; ok {
amountToSend = lockedAmount.ToInt()
amountLocked = true
}
if len(fromLockedAmount) > 0 {
for chainID, lockedAmount := range fromLockedAmount {
if chainID == network.ChainID {
continue
}
amountToSend = new(big.Int).Sub(amountToSend, lockedAmount.ToInt())
}
}
group.Add(func(c context.Context) error {
client, err := r.s.rpcClient.EthClient(network.ChainID)
if err != nil {
return err
}
for _, bridge := range r.bridges {
if !sendType.canUseBridge(bridge) {
continue
}
for _, dest := range networks {
if dest.IsTest != areTestNetworksEnabled {
continue
}
if len(preferedChainIDs) > 0 && !containsNetworkChainID(dest, preferedChainIDs) {
continue
}
if containsNetworkChainID(dest, disabledToChaindIDs) {
continue
}
can, err := bridge.AvailableFor(network, dest, token, toToken)
if err != nil || !can {
continue
}
bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountToSend)
if err != nil {
continue
}
gasLimit := uint64(0)
if sendType.isTransfer() {
gasLimit, err = bridge.EstimateGas(network, dest, addrFrom, addrTo, token, toToken, amountToSend)
if err != nil {
continue
}
} else {
gasLimit = sendType.EstimateGas(r.s, network, addrFrom, tokenID)
}
approvalContractAddress := bridge.GetContractAddress(network, token)
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, sendType, approvalContractAddress, addrFrom, network, token, amountToSend)
if err != nil {
continue
}
var l1FeeWei uint64
if sendType.needL1Fee() {
tx, err := bridge.BuildTx(network, dest, addrFrom, addrTo, token, amountToSend, bonderFees)
if err != nil {
continue
}
l1FeeWei, _ = r.s.feesManager.GetL1Fee(ctx, network.ChainID, tx)
}
baseFee, err := r.s.feesManager.getBaseFee(ctx, client, areTestNetworksEnabled)
if err != nil {
continue
}
priorityFees, err := r.s.feesManager.getPriorityFees(ctx, client, baseFee)
if err != nil {
continue
}
selctedPriorityFee := priorityFees.Medium
if gasFeeMode == GasFeeHigh {
selctedPriorityFee = priorityFees.High
} else if gasFeeMode == GasFeeLow {
selctedPriorityFee = priorityFees.Low
}
amountOut, err := bridge.CalculateAmountOut(network, dest, amountToSend, token.Symbol)
if err != nil {
continue
}
maxFeesPerGas := new(big.Float)
maxFeesPerGas.Add(new(big.Float).SetInt(baseFee), new(big.Float).SetInt(selctedPriorityFee))
estimatedTime := r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, maxFeesPerGas)
if approvalRequired && estimatedTime < MoreThanFiveMinutes {
estimatedTime += 1
}
mu.Lock()
candidates = append(candidates, &PathV2{
BridgeName: bridge.Name(),
From: network,
To: network,
FromToken: token,
AmountIn: (*hexutil.Big)(amountToSend),
AmountInLocked: amountLocked,
AmountOut: (*hexutil.Big)(amountOut),
SuggestedPriorityFees: &priorityFees,
TxBaseFee: (*hexutil.Big)(baseFee),
TxPriorityFee: (*hexutil.Big)(selctedPriorityFee),
TxGasAmount: gasLimit,
TxBonderFees: (*hexutil.Big)(bonderFees),
TxTokenFees: (*hexutil.Big)(tokenFees),
TxL1Fee: (*hexutil.Big)(big.NewInt(int64(l1FeeWei))),
ApprovalRequired: approvalRequired,
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
ApprovalContractAddress: approvalContractAddress,
ApprovalBaseFee: (*hexutil.Big)(baseFee),
ApprovalPriorityFee: (*hexutil.Big)(selctedPriorityFee),
ApprovalGasAmount: approvalGasLimit,
ApprovalL1Fee: (*hexutil.Big)(big.NewInt(int64(l1ApprovalFee))),
EstimatedTime: estimatedTime,
},
)
mu.Unlock()
}
}
return nil
})
}
group.Wait()
prices, err := sendType.FetchPrices(r.s, tokenID)
if err != nil {
return nil, err
}
suggestedRoutes := newSuggestedRoutesV2(amountIn, candidates, fromLockedAmount, prices[tokenID], prices["ETH"])
// check the best route for the required balances
for _, path := range suggestedRoutes.Best {
if path.requiredTokenBalance != nil && path.requiredTokenBalance.Cmp(big.NewInt(0)) > 0 {
tokenBalance := big.NewInt(1)
if sendType == ERC1155Transfer {
tokenBalance, err = r.getERC1155Balance(ctx, path.From, path.FromToken, addrFrom)
if err != nil {
return nil, err
}
} else if sendType != ERC721Transfer {
tokenBalance, err = r.getBalance(ctx, path.From, path.FromToken, addrFrom)
if err != nil {
return nil, err
}
}
if tokenBalance.Cmp(path.requiredTokenBalance) == -1 {
return suggestedRoutes, errors.New("not enough token balance")
}
}
nativeToken := r.s.tokenManager.FindToken(path.From, path.From.NativeCurrencySymbol)
if nativeToken == nil {
return nil, errors.New("native token not found")
}
nativeBalance, err := r.getBalance(ctx, path.From, nativeToken, addrFrom)
if err != nil {
return nil, err
}
if nativeBalance.Cmp(path.requiredNativeBalance) == -1 {
return suggestedRoutes, errors.New("not enough native balance")
}
}
return suggestedRoutes, nil
}

View File

@ -1,4 +1,4 @@
package paraswap
package thirdparty
import (
"bytes"
@ -24,7 +24,7 @@ func NewHTTPClient() *HTTPClient {
}
}
func (c *HTTPClient) doGetRequest(ctx context.Context, url string, params netUrl.Values) ([]byte, error) {
func (c *HTTPClient) DoGetRequest(ctx context.Context, url string, params netUrl.Values) ([]byte, error) {
if len(params) > 0 {
url = url + "?" + params.Encode()
}
@ -48,7 +48,7 @@ func (c *HTTPClient) doGetRequest(ctx context.Context, url string, params netUrl
return body, nil
}
func (c *HTTPClient) doPostRequest(ctx context.Context, url string, params map[string]interface{}) ([]byte, error) {
func (c *HTTPClient) DoPostRequest(ctx context.Context, url string, params map[string]interface{}) ([]byte, error) {
jsonData, err := json.Marshal(params)
if err != nil {
return nil, err

View File

@ -1,13 +1,15 @@
package paraswap
import "github.com/status-im/status-go/services/wallet/thirdparty"
type ClientV5 struct {
httpClient *HTTPClient
httpClient *thirdparty.HTTPClient
chainID uint64
}
func NewClientV5(chainID uint64) *ClientV5 {
return &ClientV5{
httpClient: NewHTTPClient(),
httpClient: thirdparty.NewHTTPClient(),
chainID: chainID,
}
}

View File

@ -40,7 +40,7 @@ func (c *ClientV5) BuildTransaction(ctx context.Context, srcTokenAddress common.
params["priceRoute"] = priceRoute
url := fmt.Sprintf(transactionsURL, c.chainID)
response, err := c.httpClient.doPostRequest(ctx, url, params)
response, err := c.httpClient.DoPostRequest(ctx, url, params)
if err != nil {
return Transaction{}, err
}

View File

@ -47,7 +47,7 @@ func (c *ClientV5) FetchPriceRoute(ctx context.Context, srcTokenAddress common.A
params.Add("side", "SELL")
url := pricesURL
response, err := c.httpClient.doGetRequest(ctx, url, params)
response, err := c.httpClient.DoGetRequest(ctx, url, params)
if err != nil {
return Route{}, err
}

View File

@ -24,7 +24,7 @@ type TokensResponse struct {
func (c *ClientV5) FetchTokensList(ctx context.Context) ([]Token, error) {
url := fmt.Sprintf(tokensURL, c.chainID)
response, err := c.httpClient.doGetRequest(ctx, url, nil)
response, err := c.httpClient.DoGetRequest(ctx, url, nil)
if err != nil {
return nil, err
}