fix: estimate gas function resoved that it uses real network estimation instead of hardcoded value

This commit is contained in:
Sale Djenic 2023-11-02 12:35:28 +01:00 committed by saledjenic
parent 4fddcb54ff
commit 05baec8bec
8 changed files with 239 additions and 85 deletions

View File

@ -451,7 +451,8 @@ func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64,
func (api *API) GetSuggestedRoutes( func (api *API) GetSuggestedRoutes(
ctx context.Context, ctx context.Context,
sendType SendType, sendType SendType,
account common.Address, addrFrom common.Address,
addrTo common.Address,
amountIn *hexutil.Big, amountIn *hexutil.Big,
tokenID string, tokenID string,
disabledFromChainIDs, disabledFromChainIDs,
@ -461,7 +462,8 @@ func (api *API) GetSuggestedRoutes(
fromLockedAmount map[uint64]*hexutil.Big, fromLockedAmount map[uint64]*hexutil.Big,
) (*SuggestedRoutes, error) { ) (*SuggestedRoutes, error) {
log.Debug("call to GetSuggestedRoutes") log.Debug("call to GetSuggestedRoutes")
return api.router.suggestedRoutes(ctx, sendType, account, amountIn.ToInt(), tokenID, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs, gasFeeMode, fromLockedAmount) return api.router.suggestedRoutes(ctx, sendType, addrFrom, addrTo, amountIn.ToInt(), tokenID, disabledFromChainIDs,
disabledToChaindIDs, preferedChainIDs, gasFeeMode, fromLockedAmount)
} }
// Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function) // Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)

View File

@ -14,6 +14,8 @@ import (
"github.com/status-im/status-go/transactions" "github.com/status-im/status-go/transactions"
) )
const IncreaseEstimatedGasFactor = 1.1
func getSigner(chainID uint64, from types.Address, verifiedAccount *account.SelectedExtKey) bind.SignerFn { func getSigner(chainID uint64, from types.Address, verifiedAccount *account.SelectedExtKey) bind.SignerFn {
return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) { return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) {
s := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID)) s := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
@ -90,7 +92,7 @@ type Bridge interface {
Name() string Name() string
Can(from *params.Network, to *params.Network, token *token.Token, balance *big.Int) (bool, error) Can(from *params.Network, to *params.Network, token *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) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error)
EstimateGas(from *params.Network, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error)
CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error)
Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
GetContractAddress(network *params.Network, token *token.Token) *common.Address GetContractAddress(network *params.Network, token *token.Token) *common.Address

View File

@ -1,14 +1,18 @@
package bridge package bridge
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
@ -217,13 +221,71 @@ func (s *CBridge) CalculateFees(from, to *params.Network, token *token.Token, am
return big.NewInt(0), new(big.Int).Add(baseFee, percFee), nil return big.NewInt(0), new(big.Int).Add(baseFee, percFee), nil
} }
func (s *CBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) { func (s *CBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: replace by estimate function var input []byte
if token.IsNative() { value := new(big.Int)
return 22000, nil // default gas limit for eth transaction
abi, err := abi.JSON(strings.NewReader(celer.CelerABI))
if err != nil {
return 0, err
} }
return 200000, nil //default gas limit for erc20 transaction if token.IsNative() {
input, err = abi.Pack("sendNative",
to,
amountIn,
toNetwork.ChainID,
uint64(time.Now().UnixMilli()),
500,
)
if err != nil {
return 0, err
}
} else {
input, err = abi.Pack("send",
to,
token.Address,
amountIn,
toNetwork.ChainID,
uint64(time.Now().UnixMilli()),
500,
)
if err != nil {
return 0, err
}
}
contractAddress := s.GetContractAddress(fromNetwork, nil)
if contractAddress == nil {
return 0, errors.New("contract not found")
}
ethClient, err := s.rpcClient.EthClient(fromNetwork.ChainID)
if err != nil {
return 0, err
}
ctx := context.Background()
if code, err := ethClient.PendingCodeAt(ctx, *contractAddress); err != nil {
return 0, err
} else if len(code) == 0 {
return 0, bind.ErrNoCode
}
msg := ethereum.CallMsg{
From: from,
To: contractAddress,
Value: value,
Data: input,
}
estimation, err := ethClient.EstimateGas(ctx, msg)
if err != nil {
return 0, err
}
increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor
return uint64(increasedEstimation), nil
} }
func (s *CBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address { func (s *CBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address {

View File

@ -1,8 +1,12 @@
package bridge package bridge
import ( import (
"context"
"math/big" "math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
@ -43,37 +47,52 @@ func (s *ERC721TransferBridge) CalculateFees(from, to *params.Network, token *to
return big.NewInt(0), big.NewInt(0), nil return big.NewInt(0), big.NewInt(0), nil
} }
func (s *ERC721TransferBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) { func (s *ERC721TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// ethClient, err := s.rpcClient.EthClient(from.ChainID) ethClient, err := s.rpcClient.EthClient(fromNetwork.ChainID)
// if err != nil { if err != nil {
// return 0, err return 0, err
// } }
// collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI))
// if err != nil {
// return 0, err
// }
// toAddress := common.HexToAddress("0x0") var input []byte
// tokenID, success := new(big.Int).SetString(token.Symbol, 10) value := new(big.Int)
// if !success {
// return 0, err
// }
// data, err := collectiblesABI.Pack("safeTransferFrom", account, toAddress, tokenID) contractAddress := to
// if err != nil {
// return 0, err abi, err := abi.JSON(strings.NewReader(collectibles.CollectiblesMetaData.ABI))
// } if err != nil {
// estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{ return 0, err
// From: account, }
// To: &toAddress,
// Value: big.NewInt(0), input, err = abi.Pack("safeTransferFrom",
// Data: data, from,
// }) to,
// if err != nil { new(big.Int))
// return 0, err
// } if err != nil {
// return estimate + 1000, nil return 0, err
return 80000, nil }
ctx := context.Background()
if code, err := ethClient.PendingCodeAt(ctx, contractAddress); err != nil {
return 0, err
} else if len(code) == 0 {
return 0, bind.ErrNoCode
}
msg := ethereum.CallMsg{
From: from,
To: &contractAddress,
Value: value,
Data: input,
}
estimation, err := ethClient.EstimateGas(ctx, msg)
if err != nil {
return 0, err
}
increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor
return uint64(increasedEstimation), nil
} }
func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) { func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {

View File

@ -2,10 +2,14 @@ package bridge
import ( import (
"context" "context"
"errors"
"math" "math"
"math/big" "math/big"
"strings"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
@ -13,6 +17,8 @@ import (
"github.com/status-im/status-go/account" "github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts" "github.com/status-im/status-go/contracts"
"github.com/status-im/status-go/contracts/hop" "github.com/status-im/status-go/contracts/hop"
hopBridge "github.com/status-im/status-go/contracts/hop/bridge"
hopWrapper "github.com/status-im/status-go/contracts/hop/wrapper"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc"
@ -126,41 +132,87 @@ func (h *HopBridge) Can(from, to *params.Network, token *token.Token, balance *b
return true, nil return true, nil
} }
func (h *HopBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) { func (h *HopBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: find why this doesn't work var input []byte
// ethClient, err := s.contractMaker.RPCClient.EthClient(from.ChainID) value := new(big.Int)
// if err != nil {
// return 0, err now := time.Now()
// } deadline := big.NewInt(now.Unix() + 604800)
// zero := common.HexToAddress("0x0")
// zeroInt := big.NewInt(0) if token.IsNative() {
// var data []byte value = amountIn
// if from.Layer == 1 { }
// bridgeABI, err := abi.JSON(strings.NewReader(hopBridge.HopBridgeABI))
// if err != nil { contractAddress := h.GetContractAddress(fromNetwork, token)
// return 0, err if contractAddress == nil {
// } return 0, errors.New("contract not found")
// data, err = bridgeABI.Pack("sendToL2", big.NewInt(int64(to.ChainID)), zero, amountIn, zeroInt, zeroInt, zero, zeroInt) }
// if err != nil {
// return 0, err ctx := context.Background()
// }
// } else { if fromNetwork.Layer == 1 {
// wrapperABI, err := abi.JSON(strings.NewReader(hopWrapper.HopWrapperABI)) ABI, err := abi.JSON(strings.NewReader(hopBridge.HopBridgeABI))
// if err != nil { if err != nil {
// return 0, err return 0, err
// } }
// data, err = wrapperABI.Pack("swapAndSend", big.NewInt(int64(to.ChainID)), zero, amountIn, zeroInt, zeroInt, zeroInt, zeroInt, zeroInt)
// if err != nil { input, err = ABI.Pack("sendToL2",
// return 0, err big.NewInt(int64(toNetwork.ChainID)),
// } to,
// } amountIn,
// estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{ big.NewInt(0),
// From: zero, deadline,
// To: &token.Address, common.HexToAddress("0x0"),
// Value: big.NewInt(0), big.NewInt(0))
// Data: data,
// }) if err != nil {
return 500000 + 1000, nil return 0, err
}
} else {
ABI, err := abi.JSON(strings.NewReader(hopWrapper.HopWrapperABI))
if err != nil {
return 0, err
}
input, err = ABI.Pack("swapAndSend",
big.NewInt(int64(toNetwork.ChainID)),
to,
amountIn,
big.NewInt(0),
big.NewInt(0),
deadline,
big.NewInt(0),
deadline)
if err != nil {
return 0, err
}
}
ethClient, err := h.contractMaker.RPCClient.EthClient(fromNetwork.ChainID)
if err != nil {
return 0, err
}
if code, err := ethClient.PendingCodeAt(ctx, *contractAddress); err != nil {
return 0, err
} else if len(code) == 0 {
return 0, bind.ErrNoCode
}
msg := ethereum.CallMsg{
From: from,
To: contractAddress,
Value: value,
Data: input,
}
estimation, err := ethClient.EstimateGas(ctx, msg)
if err != nil {
return 0, err
}
increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor
return uint64(increasedEstimation), nil
} }
func (h *HopBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address { func (h *HopBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address {

View File

@ -32,13 +32,13 @@ func (s *TransferBridge) CalculateFees(from, to *params.Network, token *token.To
return big.NewInt(0), big.NewInt(0), nil return big.NewInt(0), big.NewInt(0), nil
} }
func (s *TransferBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) { func (s *TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: replace by estimate function estimation, err := s.transactor.EstimateGas(fromNetwork, from, to, amountIn, []byte("eth_sendRawTransaction"))
if token.IsNative() { if err != nil {
return 22000, nil // default gas limit for eth transaction return 0, err
} }
increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor
return 200000, nil //default gas limit for erc20 transaction return uint64(increasedEstimation), nil
} }
func (s *TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) { func (s *TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {

View File

@ -510,7 +510,8 @@ func (r *Router) getBalance(ctx context.Context, network *params.Network, token
func (r *Router) suggestedRoutes( func (r *Router) suggestedRoutes(
ctx context.Context, ctx context.Context,
sendType SendType, sendType SendType,
account common.Address, addrFrom common.Address,
addrTo common.Address,
amountIn *big.Int, amountIn *big.Int,
tokenID string, tokenID string,
disabledFromChainIDs, disabledFromChainIDs,
@ -553,7 +554,7 @@ func (r *Router) suggestedRoutes(
continue continue
} }
token := sendType.FindToken(r.s, account, network, tokenID) token := sendType.FindToken(r.s, addrFrom, network, tokenID)
if token == nil { if token == nil {
continue continue
} }
@ -572,7 +573,7 @@ func (r *Router) suggestedRoutes(
// Default value is 1 as in case of erc721 as we built the token we are sure the account owns it // Default value is 1 as in case of erc721 as we built the token we are sure the account owns it
balance := big.NewInt(1) balance := big.NewInt(1)
if sendType != ERC721Transfer { if sendType != ERC721Transfer {
balance, err = r.getBalance(ctx, network, token, account) balance, err = r.getBalance(ctx, network, token, addrFrom)
if err != nil { if err != nil {
return err return err
} }
@ -586,7 +587,7 @@ func (r *Router) suggestedRoutes(
maxAmountIn = amount maxAmountIn = amount
} }
nativeBalance, err := r.getBalance(ctx, network, nativeToken, account) nativeBalance, err := r.getBalance(ctx, network, nativeToken, addrFrom)
if err != nil { if err != nil {
return err return err
} }
@ -640,12 +641,12 @@ func (r *Router) suggestedRoutes(
} }
gasLimit := uint64(0) gasLimit := uint64(0)
if sendType.isTransfer() { if sendType.isTransfer() {
gasLimit, err = bridge.EstimateGas(network, dest, account, token, amountIn) gasLimit, err = bridge.EstimateGas(network, dest, addrFrom, addrTo, token, amountIn)
if err != nil { if err != nil {
continue continue
} }
} else { } else {
gasLimit = sendType.EstimateGas(r.s, network, account, tokenID) gasLimit = sendType.EstimateGas(r.s, network, addrFrom, tokenID)
} }
requiredNativeBalance := new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(gasLimit))) requiredNativeBalance := new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(gasLimit)))
// Removed the required fees from maxAMount in case of native token tx // Removed the required fees from maxAMount in case of native token tx
@ -655,7 +656,7 @@ func (r *Router) suggestedRoutes(
if nativeBalance.Cmp(requiredNativeBalance) <= 0 { if nativeBalance.Cmp(requiredNativeBalance) <= 0 {
continue continue
} }
approvalRequired, approvalAmountRequired, approvalGasLimit, approvalContractAddress, err := r.requireApproval(ctx, sendType, bridge, account, network, token, amountIn) approvalRequired, approvalAmountRequired, approvalGasLimit, approvalContractAddress, err := r.requireApproval(ctx, sendType, bridge, addrFrom, network, token, amountIn)
if err != nil { if err != nil {
continue continue
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/status-im/status-go/account" "github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc"
) )
@ -80,6 +81,21 @@ func (t *Transactor) NextNonce(rpcClient *rpc.Client, chainID uint64, from types
wrapper := newRPCWrapper(rpcClient, chainID) wrapper := newRPCWrapper(rpcClient, chainID)
return t.nonce.Next(wrapper, from) return t.nonce.Next(wrapper, from)
} }
func (t *Transactor) EstimateGas(network *params.Network, from common.Address, to common.Address, value *big.Int, input []byte) (uint64, error) {
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, network.ChainID)
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
defer cancel()
msg := ethereum.CallMsg{
From: from,
To: &to,
Value: value,
Data: input,
}
return rpcWrapper.EstimateGas(ctx, msg)
}
// SendTransaction is an implementation of eth_sendTransaction. It queues the tx to the sign queue. // SendTransaction is an implementation of eth_sendTransaction. It queues the tx to the sign queue.
func (t *Transactor) SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) { func (t *Transactor) SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {