From ee6a927e1cc7303c9d7cab4112db2c654f70b7ba Mon Sep 17 00:00:00 2001 From: Christian Lohr Date: Thu, 7 Mar 2024 14:43:21 +0100 Subject: [PATCH] feat: add gas limit multiplication (#358) * feat: add gas limit multiplication * fix: rotator test --- chain/evm/client.go | 49 ++++++++++++++++++++++++++++++------ chain/evm/client_test.go | 2 +- util/rotator/rotator_test.go | 2 +- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/chain/evm/client.go b/chain/evm/client.go index 33a81e15..ea49fd9b 100644 --- a/chain/evm/client.go +++ b/chain/evm/client.go @@ -141,7 +141,7 @@ type mevClient interface { //go:generate mockery --name=ethClientConn --inpackage --testonly type ethClientConn interface { - bind.ContractBackend + ethClienter TransactionByHash(ctx context.Context, hash common.Hash) (tx *etherumtypes.Transaction, isPending bool, err error) HeaderByNumber(ctx context.Context, number *big.Int) (*etherumtypes.Header, error) BlockByHash(ctx context.Context, hash common.Hash) (*etherumtypes.Block, error) @@ -170,6 +170,11 @@ type CompassBindingFilterer interface { WatchValsetUpdated(opts *bind.WatchOpts, sink chan<- *compassABI.CompassValsetUpdated) (event.Subscription, error) } +//go:generate mockery --name=ethClienter --inpackage --testonly +type ethClienter interface { + bind.ContractBackend +} + //go:generate mockery --name=CompassBinding type CompassBinding interface { CompassBindingCaller @@ -217,11 +222,6 @@ func (c *Client) newCompass(addr common.Address) (CompassBinding, error) { return compassABI.NewCompass(addr, c.conn) } -//go:generate mockery --name=ethClienter --inpackage --testonly -type ethClienter interface { - bind.ContractBackend -} - type executeSmartContractIn struct { ethClient ethClienter mevClient mevClient @@ -333,7 +333,14 @@ func callSmartContract( txOpts.From = args.signingAddr // https://github.com/VolumeFi/paloma/issues/1048 - txOpts.GasLimit = uint64(float64(txOpts.GasLimit) * 1.1) + value := new(big.Int) + gasFeeCap := new(big.Int) + if args.txType == 2 { + gasFeeCap = gasPrice + } + gasLimit, err := estimateGasLimit(ctx, args.ethClient, txOpts, &args.contract, packedBytes, gasPrice, gasTipCap, gasFeeCap, value) + whoops.Assert(err) + txOpts.GasLimit = uint64(float64(gasLimit) * 1.2) if args.txType == 2 { txOpts.GasFeeCap = gasPrice @@ -406,6 +413,34 @@ func callSmartContract( }) } +// Copied from https://github.com/ethereum/go-ethereum/blob/e5d5e09faae48dac3723634e2b1813e4f2e89535/accounts/abi/bind/base.go#L357 +// since the original implementation is not exported, but we need access to the gas limit to multiply it +// before we send the TX. +// More detail: https://github.com/VolumeFi/paloma/issues/1048 +// And: https://github.com/VolumeFi/paloma/issues/1158#issuecomment-1896488916 +func estimateGasLimit(ctx context.Context, c ethClienter, opts *bind.TransactOpts, contract *ethcommon.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) { + if contract != nil { + // Gas estimation cannot succeed without code for method invocations. + if code, err := c.PendingCodeAt(ctx, *contract); err != nil { + return 0, err + } else if len(code) == 0 { + return 0, bind.ErrNoCode + } + } + + msg := ethereum.CallMsg{ + From: opts.From, + To: contract, + GasPrice: gasPrice, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Value: value, + Data: input, + } + + return c.EstimateGas(ctx, msg) +} + func (c *Client) sign(ctx context.Context, bytes []byte) ([]byte, error) { return c.keystore.SignHash( accounts.Account{Address: c.addr}, diff --git a/chain/evm/client_test.go b/chain/evm/client_test.go index 88e1fb20..e4a38633 100644 --- a/chain/evm/client_test.go +++ b/chain/evm/client_test.go @@ -135,7 +135,7 @@ func TestExecutingSmartContract(t *testing.T) { ethMock.On("EstimateGas", mock.Anything, mock.Anything).Return(uint64(222), nil) mevMock := newMockMevClient(t) - mevMock.On("Relay", mock.Anything, mock.Anything, mock.Anything).Return(common.HexToHash("0xb19cc052f5066cb445ea09461d99df913f6938fc825676e645f40d5d24abddda"), nil) + mevMock.On("Relay", mock.Anything, mock.Anything, mock.Anything).Return(common.HexToHash("0xde13fda4e25fc73f1d7f3b3e7652a56a0d0a8a6a361dcaec4c8c77f3720a05d1"), nil) args.ethClient = ethMock args.mevClient = mevMock diff --git a/util/rotator/rotator_test.go b/util/rotator/rotator_test.go index 0ef5f9e4..844c978b 100644 --- a/util/rotator/rotator_test.go +++ b/util/rotator/rotator_test.go @@ -23,8 +23,8 @@ func Test_Rotator_RotateKeys(t *testing.T) { wg := &sync.WaitGroup{} // Confirm rotator is concurrency safe + wg.Add(3 * 99) for i := 0; i < 3; i++ { - wg.Add(1) go func() { for i := 1; i < 100; i++ { r.RotateKeys(context.Background())