chore_: the router implementation simplification
This commit is contained in:
parent
437ad499c8
commit
08fef4afde
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue