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(
ctx context.Context,
sendType SendType,
account common.Address,
addrFrom common.Address,
addrTo common.Address,
amountIn *hexutil.Big,
tokenID string,
disabledFromChainIDs,
@ -461,7 +462,8 @@ func (api *API) GetSuggestedRoutes(
fromLockedAmount map[uint64]*hexutil.Big,
) (*SuggestedRoutes, error) {
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)

View File

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

View File

@ -1,14 +1,18 @@
package bridge
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math/big"
"net/http"
"strings"
"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/common"
"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
}
func (s *CBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: replace by estimate function
if token.IsNative() {
return 22000, nil // default gas limit for eth transaction
func (s *CBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
var input []byte
value := new(big.Int)
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 {

View File

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

View File

@ -2,10 +2,14 @@ package bridge
import (
"context"
"errors"
"math"
"math/big"
"strings"
"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/common"
"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/contracts"
"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/params"
"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
}
func (h *HopBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: find why this doesn't work
// ethClient, err := s.contractMaker.RPCClient.EthClient(from.ChainID)
// if err != nil {
// return 0, err
// }
// zero := common.HexToAddress("0x0")
// zeroInt := big.NewInt(0)
// var data []byte
// if from.Layer == 1 {
// bridgeABI, err := abi.JSON(strings.NewReader(hopBridge.HopBridgeABI))
// if err != nil {
// return 0, err
// }
// data, err = bridgeABI.Pack("sendToL2", big.NewInt(int64(to.ChainID)), zero, amountIn, zeroInt, zeroInt, zero, zeroInt)
// if err != nil {
// return 0, err
// }
// } else {
// wrapperABI, err := abi.JSON(strings.NewReader(hopWrapper.HopWrapperABI))
// if err != nil {
// return 0, err
// }
// data, err = wrapperABI.Pack("swapAndSend", big.NewInt(int64(to.ChainID)), zero, amountIn, zeroInt, zeroInt, zeroInt, zeroInt, zeroInt)
// if err != nil {
// return 0, err
// }
// }
// estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
// From: zero,
// To: &token.Address,
// Value: big.NewInt(0),
// Data: data,
// })
return 500000 + 1000, nil
func (h *HopBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
var input []byte
value := new(big.Int)
now := time.Now()
deadline := big.NewInt(now.Unix() + 604800)
if token.IsNative() {
value = amountIn
}
contractAddress := h.GetContractAddress(fromNetwork, token)
if contractAddress == nil {
return 0, errors.New("contract not found")
}
ctx := context.Background()
if fromNetwork.Layer == 1 {
ABI, err := abi.JSON(strings.NewReader(hopBridge.HopBridgeABI))
if err != nil {
return 0, err
}
input, err = ABI.Pack("sendToL2",
big.NewInt(int64(toNetwork.ChainID)),
to,
amountIn,
big.NewInt(0),
deadline,
common.HexToAddress("0x0"),
big.NewInt(0))
if err != 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 {

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
}
func (s *TransferBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: replace by estimate function
if token.IsNative() {
return 22000, nil // default gas limit for eth transaction
func (s *TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Network, from common.Address, to common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
estimation, err := s.transactor.EstimateGas(fromNetwork, from, to, amountIn, []byte("eth_sendRawTransaction"))
if err != nil {
return 0, err
}
return 200000, nil //default gas limit for erc20 transaction
increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor
return uint64(increasedEstimation), nil
}
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(
ctx context.Context,
sendType SendType,
account common.Address,
addrFrom common.Address,
addrTo common.Address,
amountIn *big.Int,
tokenID string,
disabledFromChainIDs,
@ -553,7 +554,7 @@ func (r *Router) suggestedRoutes(
continue
}
token := sendType.FindToken(r.s, account, network, tokenID)
token := sendType.FindToken(r.s, addrFrom, network, tokenID)
if token == nil {
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
balance := big.NewInt(1)
if sendType != ERC721Transfer {
balance, err = r.getBalance(ctx, network, token, account)
balance, err = r.getBalance(ctx, network, token, addrFrom)
if err != nil {
return err
}
@ -586,7 +587,7 @@ func (r *Router) suggestedRoutes(
maxAmountIn = amount
}
nativeBalance, err := r.getBalance(ctx, network, nativeToken, account)
nativeBalance, err := r.getBalance(ctx, network, nativeToken, addrFrom)
if err != nil {
return err
}
@ -640,12 +641,12 @@ func (r *Router) suggestedRoutes(
}
gasLimit := uint64(0)
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 {
continue
}
} 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)))
// 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 {
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 {
continue
}

View File

@ -17,6 +17,7 @@ import (
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
)
@ -80,6 +81,21 @@ func (t *Transactor) NextNonce(rpcClient *rpc.Client, chainID uint64, from types
wrapper := newRPCWrapper(rpcClient, chainID)
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.
func (t *Transactor) SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {