From aed67d7c3e51c2ea341b1165e40eff177e0bf5b3 Mon Sep 17 00:00:00 2001 From: Stefan Liu Date: Thu, 2 Jun 2022 18:21:25 +0800 Subject: [PATCH] Support estimate with gas limit as max (#28) * main_O3V2_new_checkfee * Add fee checking with max limit * Reformat with indention Co-authored-by: rain-zxn <206131521@qq.com> --- chains/bridge/fee.go | 18 +++++++--- wallet/wallet.go | 82 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/chains/bridge/fee.go b/chains/bridge/fee.go index 6198069..2dfc480 100644 --- a/chains/bridge/fee.go +++ b/chains/bridge/fee.go @@ -17,15 +17,18 @@ package bridge -import "github.com/polynetwork/bridge-common/tools" +import ( + "github.com/polynetwork/bridge-common/tools" +) type CheckFeeStatus int const ( - SKIP CheckFeeStatus = -2 // Skip since not our tx - NOT_PAID CheckFeeStatus = -1 // Not paid or paid too low - MISSING CheckFeeStatus = 0 // Tx not received yet - PAID CheckFeeStatus = 1 // Paid and enough pass + SKIP CheckFeeStatus = -2 // Skip since not our tx + NOT_PAID CheckFeeStatus = -1 // Not paid or paid too low + MISSING CheckFeeStatus = 0 // Tx not received yet + PAID CheckFeeStatus = 1 // Paid and enough pass + PAID_LIMIT CheckFeeStatus = 2 // Paid but need EstimateGas ) type CheckFeeRequest struct { @@ -34,6 +37,7 @@ type CheckFeeRequest struct { PolyHash string Paid float64 Min float64 + PaidGas float64 Status CheckFeeStatus } @@ -49,6 +53,10 @@ func (r *CheckFeeRequest) Missing() bool { return r == nil || r.Status == MISSING } +func (r *CheckFeeRequest) PaidLimit() bool { + return r != nil && r.Status == PAID_LIMIT +} + func (c *Client) CheckFee(req map[string]*CheckFeeRequest) (err error) { return tools.PostJsonFor(c.address+"/newcheckfee", req, &req) } diff --git a/wallet/wallet.go b/wallet/wallet.go index 665cc66..7c07c13 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -55,7 +55,10 @@ type IWallet interface { SendWithAccount(account accounts.Account, addr common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, gasPriceX *big.Float, data []byte) (hash string, err error) EstimateWithAccount(account accounts.Account, addr common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, gasPriceX *big.Float, data []byte) (hash string, err error) Accounts() []accounts.Account + Select() (accounts.Account, Provider, NonceProvider) GetBalance(common.Address) (*big.Int, error) + EstimateGasWithAccount(account accounts.Account, addr common.Address, amount *big.Int, data []byte) (gasPrice *big.Int, gasLimit uint64, err error) + SendWithMaxLimit(account accounts.Account, addr common.Address, amount *big.Int, maxLimit *big.Int, gasPrice *big.Int, gasPriceX *big.Float, data []byte) (hash string, err error) } type Wallet struct { @@ -260,3 +263,82 @@ func (w *Wallet) Select() (accounts.Account, Provider, NonceProvider) { func (w *Wallet) Upgrade() *EthWallet { return w } + +func (w *Wallet) EstimateGasWithAccount(account accounts.Account, addr common.Address, amount *big.Int, data []byte) (gasPrice *big.Int, gasLimit uint64, err error) { + gasPrice, err = w.GasPrice() + if err != nil { + err = fmt.Errorf("Get gas price error %v", err) + return + } + msg := ethereum.CallMsg{From: account.Address, To: &addr, GasPrice: gasPrice, Value: amount, Data: data} + gasLimit, err = w.sdk.Node().EstimateGas(context.Background(), msg) + if err != nil { + err = fmt.Errorf("Estimate gas limit error %v, account %s", err, account.Address) + return + } + return +} + + +func (w *Wallet) SendWithMaxLimit(account accounts.Account, addr common.Address, amount *big.Int, maxLimit *big.Int, gasPrice *big.Int, gasPriceX *big.Float, data []byte) (hash string, err error) { + if maxLimit == nil || maxLimit.Sign() <= 0 { + err = fmt.Errorf("max limit is zero or missing") + return + } + if gasPrice == nil || gasPrice.Sign() <= 0 { + gasPrice, err = w.GasPrice() + if err != nil { + err = fmt.Errorf("Get gas price error %v", err) + return + } + if gasPriceX != nil { + gasPrice, _ = new(big.Float).Mul(new(big.Float).SetInt(gasPrice), gasPriceX).Int(nil) + } + } + + provider, nonces := w.GetAccount(account) + nonce, err := nonces.Acquire() + if err != nil { + return + } + + var gasLimit uint64 + msg := ethereum.CallMsg{From: account.Address, To: &addr, GasPrice: gasPrice, Value: amount, Data: data} + gasLimit, err = w.sdk.Node().EstimateGas(context.Background(), msg) + if err != nil { + nonces.Update(false) + if strings.Contains(err.Error(), "has been executed") { + log.Info("Transaction already executed") + return "", nil + } + + err = fmt.Errorf("Estimate gas limit error %v, account %s", err, account.Address) + return + } + + limit := GetChainGasLimit(w.chainId, gasLimit) + if limit < gasLimit { + nonces.Update(false) + err = fmt.Errorf("Send tx estimated gas limit(%v) higher than chain max limit %v", gasLimit, limit) + return + } + + if maxLimit.Cmp(new(big.Int).Mul(big.NewInt(int64(limit)), gasPrice)) == -1 { + nonces.Update(false) + err = fmt.Errorf("Send tx estimated gas (limit %v, price %s) higher than max limit %s", limit, gasPrice, maxLimit) + return + } + + tx := types.NewTransaction(nonce, addr, amount, limit, gasPrice, data) + tx, err = provider.SignTx(account, tx, big.NewInt(int64(w.chainId))) + if err != nil { + nonces.Update(false) + err = fmt.Errorf("Sign tx error %v", err) + return + } + log.Info("Compose dst chain tx", "hash", tx.Hash(), "account", account.Address) + err = w.sdk.Node().SendTransaction(context.Background(), tx) + //TODO: Check err here before update nonces + nonces.Update(true) + return tx.Hash().String(), err +}