From 664561b6f1619b71e73cd3fa7a19b0382b021405 Mon Sep 17 00:00:00 2001 From: Yusuf Kelo Date: Thu, 10 Dec 2020 19:35:31 +0400 Subject: [PATCH] Add gas multiplier (#564) - Added GasMultipler To Config - Added GasMultiplier to Connection object --- Makefile | 2 +- README.md | 1 + chains/ethereum/chain.go | 2 +- chains/ethereum/config.go | 15 ++++++++ chains/ethereum/config_test.go | 17 ++++++--- chains/ethereum/test_utils_test.go | 3 +- connections/ethereum/connection.go | 49 +++++++++++++++++-------- connections/ethereum/connection_test.go | 10 +++-- shared/ethereum/client.go | 1 + 9 files changed, 72 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 8211e5916..032691603 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ get: go mod tidy && go mod download get-lint: - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s latest + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.31.0 .PHONY: lint lint: diff --git a/README.md b/README.md index 1c2fb6075..74363e91d 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Ethereum chains support the following additional options: "genericHandler": "0x1234...", // Address of generic handler (required) "maxGasPrice": "0x1234", // Gas price for transactions (default: 20000000000) "gasLimit": "0x1234", // Gas limit for transactions (default: 6721975) + "gasMultiplier": "1.25", // Multiplies the gas price by the supplied value (default: 1) "http": "true", // Whether the chain connection is ws or http (default: false) "startBlock": "1234", // The block to start processing events from (default: 0) "blockConfirmations": "10" // Number of blocks to wait before processing a block diff --git a/chains/ethereum/chain.go b/chains/ethereum/chain.go index 7cb4638c3..c0a043338 100644 --- a/chains/ethereum/chain.go +++ b/chains/ethereum/chain.go @@ -107,7 +107,7 @@ func InitializeChain(chainCfg *core.ChainConfig, logger log15.Logger, sysErr cha } stop := make(chan int) - conn := connection.NewConnection(cfg.endpoint, cfg.http, kp, logger, cfg.gasLimit, cfg.maxGasPrice) + conn := connection.NewConnection(cfg.endpoint, cfg.http, kp, logger, cfg.gasLimit, cfg.maxGasPrice, cfg.gasMultiplier) err = conn.Connect() if err != nil { return nil, err diff --git a/chains/ethereum/config.go b/chains/ethereum/config.go index d83ae681b..b90420f16 100644 --- a/chains/ethereum/config.go +++ b/chains/ethereum/config.go @@ -17,6 +17,7 @@ import ( const DefaultGasLimit = 6721975 const DefaultGasPrice = 20000000000 const DefaultBlockConfirmations = 10 +const DefaultGasMultiplier = 1 // Chain specific options var ( @@ -26,6 +27,7 @@ var ( GenericHandlerOpt = "genericHandler" MaxGasPriceOpt = "maxGasPrice" GasLimitOpt = "gasLimit" + GasMultiplier = "gasMultiplier" HttpOpt = "http" StartBlockOpt = "startBlock" BlockConfirmationsOpt = "blockConfirmations" @@ -46,6 +48,7 @@ type Config struct { genericHandlerContract common.Address gasLimit *big.Int maxGasPrice *big.Int + gasMultiplier *big.Float http bool // Config for type of connection startBlock *big.Int blockConfirmations *big.Int @@ -68,6 +71,7 @@ func parseChainConfig(chainCfg *core.ChainConfig) (*Config, error) { genericHandlerContract: utils.ZeroAddress, gasLimit: big.NewInt(DefaultGasLimit), maxGasPrice: big.NewInt(DefaultGasPrice), + gasMultiplier: big.NewFloat(DefaultGasMultiplier), http: false, startBlock: big.NewInt(0), blockConfirmations: big.NewInt(0), @@ -111,6 +115,17 @@ func parseChainConfig(chainCfg *core.ChainConfig) (*Config, error) { } } + if gasMultiplier, ok := chainCfg.Opts[GasMultiplier]; ok { + multilier := big.NewFloat(1) + _, pass := multilier.SetString(gasMultiplier) + if pass { + config.gasMultiplier = multilier + delete(chainCfg.Opts, GasMultiplier) + } else { + return nil, errors.New("unable to parse gasMultiplier to float") + } + } + if HTTP, ok := chainCfg.Opts[HttpOpt]; ok && HTTP == "true" { config.http = true delete(chainCfg.Opts, HttpOpt) diff --git a/chains/ethereum/config_test.go b/chains/ethereum/config_test.go index 0c236b690..c5a410ff3 100644 --- a/chains/ethereum/config_test.go +++ b/chains/ethereum/config_test.go @@ -28,6 +28,7 @@ func TestParseChainConfig(t *testing.T) { "erc721Handler": "0x1234", "genericHandler": "0x1234", "gasLimit": "10", + "gasMultiplier": "1", "maxGasPrice": "20", "http": "true", "startBlock": "10", @@ -53,6 +54,7 @@ func TestParseChainConfig(t *testing.T) { genericHandlerContract: common.HexToAddress("0x1234"), gasLimit: big.NewInt(10), maxGasPrice: big.NewInt(20), + gasMultiplier: big.NewFloat(1), http: true, startBlock: big.NewInt(10), blockConfirmations: big.NewInt(50), @@ -74,12 +76,13 @@ func TestChainConfigOneContract(t *testing.T) { KeystorePath: "./keys", Insecure: false, Opts: map[string]string{ - "bridge": "0x1234", - "erc20Handler": "0x1234", - "gasLimit": "10", - "maxGasPrice": "20", - "http": "true", - "startBlock": "10", + "bridge": "0x1234", + "erc20Handler": "0x1234", + "gasLimit": "10", + "maxGasPrice": "20", + "gasMultiplier": "1", + "http": "true", + "startBlock": "10", }, } @@ -99,6 +102,7 @@ func TestChainConfigOneContract(t *testing.T) { erc20HandlerContract: common.HexToAddress("0x1234"), gasLimit: big.NewInt(10), maxGasPrice: big.NewInt(20), + gasMultiplier: big.NewFloat(1), http: true, startBlock: big.NewInt(10), blockConfirmations: big.NewInt(0), @@ -156,6 +160,7 @@ func TestExtraOpts(t *testing.T) { "bridge": "0x1234", "gasLimit": "10", "maxGasPrice": "20", + "gasMultiplier": "1", "http": "true", "incorrect_opt": "error", }, diff --git a/chains/ethereum/test_utils_test.go b/chains/ethereum/test_utils_test.go index cc409640e..d97d54194 100644 --- a/chains/ethereum/test_utils_test.go +++ b/chains/ethereum/test_utils_test.go @@ -46,6 +46,7 @@ func createConfig(name string, startBlock *big.Int, contracts *utils.DeployedCon genericHandlerContract: common.Address{}, gasLimit: big.NewInt(DefaultGasLimit), maxGasPrice: big.NewInt(DefaultGasPrice), + gasMultiplier: big.NewFloat(DefaultGasMultiplier), http: false, startBlock: startBlock, blockConfirmations: big.NewInt(0), @@ -69,7 +70,7 @@ func newTestLogger(name string) log15.Logger { func newLocalConnection(t *testing.T, cfg *Config) *connection.Connection { kp := keystore.TestKeyRing.EthereumKeys[cfg.from] - conn := connection.NewConnection(TestEndpoint, false, kp, TestLogger, big.NewInt(DefaultGasLimit), big.NewInt(DefaultGasPrice)) + conn := connection.NewConnection(TestEndpoint, false, kp, TestLogger, big.NewInt(DefaultGasLimit), big.NewInt(DefaultGasPrice), big.NewFloat(DefaultGasMultiplier)) err := conn.Connect() if err != nil { t.Fatal(err) diff --git a/connections/ethereum/connection.go b/connections/ethereum/connection.go index 0e65ba4d8..080608b19 100644 --- a/connections/ethereum/connection.go +++ b/connections/ethereum/connection.go @@ -23,12 +23,13 @@ import ( var BlockRetryInterval = time.Second * 5 type Connection struct { - endpoint string - http bool - kp *secp256k1.Keypair - gasLimit *big.Int - maxGasPrice *big.Int - conn *ethclient.Client + endpoint string + http bool + kp *secp256k1.Keypair + gasLimit *big.Int + maxGasPrice *big.Int + gasMultiplier *big.Float + conn *ethclient.Client // signer ethtypes.Signer opts *bind.TransactOpts callOpts *bind.CallOpts @@ -39,15 +40,16 @@ type Connection struct { } // NewConnection returns an uninitialized connection, must call Connection.Connect() before using. -func NewConnection(endpoint string, http bool, kp *secp256k1.Keypair, log log15.Logger, gasLimit, gasPrice *big.Int) *Connection { +func NewConnection(endpoint string, http bool, kp *secp256k1.Keypair, log log15.Logger, gasLimit, gasPrice *big.Int, gasMultiplier *big.Float) *Connection { return &Connection{ - endpoint: endpoint, - http: http, - kp: kp, - gasLimit: gasLimit, - maxGasPrice: gasPrice, - log: log, - stop: make(chan int), + endpoint: endpoint, + http: http, + kp: kp, + gasLimit: gasLimit, + maxGasPrice: gasPrice, + gasMultiplier: gasMultiplier, + log: log, + stop: make(chan int), } } @@ -115,11 +117,15 @@ func (c *Connection) CallOpts() *bind.CallOpts { } func (c *Connection) SafeEstimateGas(ctx context.Context) (*big.Int, error) { - gasPrice, err := c.conn.SuggestGasPrice(context.TODO()) + + suggestedGasPrice, err := c.conn.SuggestGasPrice(context.TODO()) + if err != nil { return nil, err } + gasPrice := multiplyGasPrice(suggestedGasPrice, c.gasMultiplier) + // Check we aren't exceeding our limit if gasPrice.Cmp(c.maxGasPrice) == 1 { return c.maxGasPrice, nil @@ -128,6 +134,19 @@ func (c *Connection) SafeEstimateGas(ctx context.Context) (*big.Int, error) { } } +func multiplyGasPrice(gasEstimate *big.Int, gasMultiplier *big.Float) *big.Int { + + gasEstimateFloat := new(big.Float).SetInt(gasEstimate) + + result := gasEstimateFloat.Mul(gasEstimateFloat, gasMultiplier) + + gasPrice := new(big.Int) + + result.Int(gasPrice) + + return gasPrice +} + // LockAndUpdateOpts acquires a lock on the opts before updating the nonce // and gas price. func (c *Connection) LockAndUpdateOpts() error { diff --git a/connections/ethereum/connection_test.go b/connections/ethereum/connection_test.go index 3ba0a4849..4c3c7100b 100644 --- a/connections/ethereum/connection_test.go +++ b/connections/ethereum/connection_test.go @@ -20,8 +20,10 @@ var AliceKp = keystore.TestKeyRing.EthereumKeys[keystore.AliceKey] var GasLimit = big.NewInt(ethutils.DefaultGasLimit) var MaxGasPrice = big.NewInt(ethutils.DefaultMaxGasPrice) +var GasMultipler = big.NewFloat(ethutils.DefaultGasMultiplier) + func TestConnect(t *testing.T) { - conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, MaxGasPrice) + conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, MaxGasPrice, GasMultipler) err := conn.Connect() if err != nil { t.Fatal(err) @@ -38,7 +40,7 @@ func TestContractCode(t *testing.T) { t.Fatal(err) } - conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, MaxGasPrice) + conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, MaxGasPrice, GasMultipler) err = conn.Connect() if err != nil { t.Fatal(err) @@ -60,7 +62,7 @@ func TestContractCode(t *testing.T) { func TestConnection_SafeEstimateGas(t *testing.T) { // MaxGasPrice is the constant price on the dev network, so we increase it here by 1 to ensure it adjusts - conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, MaxGasPrice.Add(MaxGasPrice, big.NewInt(1))) + conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, MaxGasPrice.Add(MaxGasPrice, big.NewInt(1)), GasMultipler) err := conn.Connect() if err != nil { t.Fatal(err) @@ -79,7 +81,7 @@ func TestConnection_SafeEstimateGas(t *testing.T) { func TestConnection_SafeEstimateGasMax(t *testing.T) { maxPrice := big.NewInt(1) - conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, maxPrice) + conn := NewConnection(TestEndpoint, false, AliceKp, log15.Root(), GasLimit, maxPrice, GasMultipler) err := conn.Connect() if err != nil { t.Fatal(err) diff --git a/shared/ethereum/client.go b/shared/ethereum/client.go index a1dc7593f..522fbc15f 100644 --- a/shared/ethereum/client.go +++ b/shared/ethereum/client.go @@ -19,6 +19,7 @@ import ( const DefaultGasLimit = 6721975 const DefaultMaxGasPrice = 20000000000 +const DefaultGasMultiplier = 1 var ExpectedBlockTime = time.Second