From ec7ffb39358a1a05a128f30af5eb58d128fe04b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Mon, 7 Aug 2023 15:18:02 +0200 Subject: [PATCH 1/7] Expose eth_maxPriorityFeePerGas function --- jsonrpc/eth.go | 11 +++++++++++ jsonrpc/eth_test.go | 37 +++++++++++++++++++++++++++++++++++++ testutil/server.go | 11 +++++++++-- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/jsonrpc/eth.go b/jsonrpc/eth.go index d1053462..82afde3d 100644 --- a/jsonrpc/eth.go +++ b/jsonrpc/eth.go @@ -273,3 +273,14 @@ func (e *Eth) FeeHistory(from, to ethgo.BlockNumber) (*FeeHistory, error) { } return out, nil } + +// MaxPriorityFeePerGas returns a fee per gas that is an estimate of how much you can pay as a priority fee, or 'tip', +// to get a transaction included in the current block (EIP-1559). +func (e *Eth) MaxPriorityFeePerGas() (*big.Int, error) { + var out string + if err := e.c.Call("eth_maxPriorityFeePerGas", &out); err != nil { + return big.NewInt(0), err + } + + return parseBigInt(out), nil +} diff --git a/jsonrpc/eth_test.go b/jsonrpc/eth_test.go index bf8f6737..a56f4949 100644 --- a/jsonrpc/eth_test.go +++ b/jsonrpc/eth_test.go @@ -401,3 +401,40 @@ func TestEthFeeHistory(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, fee) } + +func TestEthMaxPriorityFeePerGas(t *testing.T) { + s := testutil.DeployTestServer(t, nil) + c, err := NewClient(s.HTTPAddr()) + require.NoError(t, err) + + initialMaxPriorityFee, err := c.Eth().MaxPriorityFeePerGas() + require.NoError(t, err) + + // wait for 2 blocks + require.NoError(t, s.ProcessBlock()) + require.NoError(t, s.ProcessBlock()) + + txn := ðgo.Transaction{ + To: &testutil.DummyAddr, + Value: ethgo.Gwei(1), + Type: ethgo.TransactionDynamicFee, + MaxPriorityFeePerGas: ethgo.Gwei(1), + } + + latestBlock, err := c.Eth().BlockNumber() + require.NoError(t, err) + + feeHistory, err := c.Eth().FeeHistory(ethgo.BlockNumber(latestBlock-1), ethgo.BlockNumber(latestBlock)) + require.NoError(t, err) + + latestBaseFee := feeHistory.BaseFee[len(feeHistory.BaseFee)-1] + txn.MaxFeePerGas = new(big.Int).Add(latestBaseFee, txn.MaxPriorityFeePerGas) + + receipt, err := s.SendTxn(txn) + require.NoError(t, err) + require.Equal(t, uint64(1), receipt.Status) + + newMaxPriorityFee, err := c.Eth().MaxPriorityFeePerGas() + require.NoError(t, err) + require.True(t, initialMaxPriorityFee.Cmp(newMaxPriorityFee) < 0) +} diff --git a/testutil/server.go b/testutil/server.go index 2de865e3..d41d4778 100644 --- a/testutil/server.go +++ b/testutil/server.go @@ -247,8 +247,15 @@ func (t *TestServer) SendTxn(txn *ethgo.Transaction) (*ethgo.Receipt, error) { if isEmptyAddr(txn.From) { txn.From = t.Account(0) } - if txn.GasPrice == 0 { - txn.GasPrice = DefaultGasPrice + + if txn.Type == ethgo.TransactionLegacy { + if txn.GasPrice == 0 { + txn.GasPrice = DefaultGasPrice + } + } else if txn.Type == ethgo.TransactionDynamicFee { + if txn.MaxPriorityFeePerGas.Cmp(big.NewInt(0)) == 0 { + txn.MaxPriorityFeePerGas = new(big.Int).SetUint64(DefaultGasPrice) + } } if txn.Gas == 0 { txn.Gas = DefaultGasLimit From ff3c370fa67e42039cc71f0c98d5b04eaec62a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Mon, 7 Aug 2023 15:18:28 +0200 Subject: [PATCH 2/7] Prevent marshaling gas price in case of dynamic fee transactions --- structs_marshal.go | 20 +++++++++++--------- structs_unmarshal.go | 22 +++++++++++----------- testsuite/transaction-eip1159.json | 3 +-- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/structs_marshal.go b/structs_marshal.go index c058c682..b88e079e 100644 --- a/structs_marshal.go +++ b/structs_marshal.go @@ -117,18 +117,20 @@ func (t *Transaction) marshalJSON(a *fastjson.Arena) *fastjson.Value { if t.Value != nil { o.Set("value", a.NewString(fmt.Sprintf("0x%x", t.Value))) } - o.Set("gasPrice", a.NewString(fmt.Sprintf("0x%x", t.GasPrice))) - + if t.Type == TransactionDynamicFee { + if t.MaxPriorityFeePerGas != nil { + o.Set("maxPriorityFeePerGas", a.NewString(fmt.Sprintf("0x%x", t.MaxPriorityFeePerGas))) + } + if t.MaxFeePerGas != nil { + o.Set("maxFeePerGas", a.NewString(fmt.Sprintf("0x%x", t.MaxFeePerGas))) + } + } else { + o.Set("gasPrice", a.NewString(fmt.Sprintf("0x%x", t.GasPrice))) + } // gas limit fields if t.Gas != 0 { o.Set("gas", a.NewString(fmt.Sprintf("0x%x", t.Gas))) } - if t.MaxPriorityFeePerGas != nil { - o.Set("maxPriorityFeePerGas", a.NewString(fmt.Sprintf("0x%x", t.MaxPriorityFeePerGas))) - } - if t.MaxFeePerGas != nil { - o.Set("maxFeePerGas", a.NewString(fmt.Sprintf("0x%x", t.MaxFeePerGas))) - } if t.Nonce != 0 { // we can remove this once we include support for custom nonces @@ -298,4 +300,4 @@ func (s StateOverride) MarshalJSON() ([]byte, error) { defaultArena.Put(a) return res, nil -} +} diff --git a/structs_unmarshal.go b/structs_unmarshal.go index 4b7ef4eb..a3f28c0b 100644 --- a/structs_unmarshal.go +++ b/structs_unmarshal.go @@ -156,8 +156,17 @@ func (t *Transaction) unmarshalJSON(v *fastjson.Value) error { if err = decodeAddr(&t.From, v, "from"); err != nil { return err } - if t.GasPrice, err = decodeUint(v, "gasPrice"); err != nil { - return err + if typ == TransactionDynamicFee { + if t.MaxPriorityFeePerGas, err = decodeBigInt(t.MaxPriorityFeePerGas, v, "maxPriorityFeePerGas"); err != nil { + return err + } + if t.MaxFeePerGas, err = decodeBigInt(t.MaxFeePerGas, v, "maxFeePerGas"); err != nil { + return err + } + } else { + if t.GasPrice, err = decodeUint(v, "gasPrice"); err != nil { + return err + } } if t.Input, err = decodeBytes(t.Input[:0], v, "input"); err != nil { return err @@ -207,15 +216,6 @@ func (t *Transaction) unmarshalJSON(v *fastjson.Value) error { return err } - if typ == TransactionDynamicFee { - if t.MaxPriorityFeePerGas, err = decodeBigInt(t.MaxPriorityFeePerGas, v, "maxPriorityFeePerGas"); err != nil { - return err - } - if t.MaxFeePerGas, err = decodeBigInt(t.MaxFeePerGas, v, "maxFeePerGas"); err != nil { - return err - } - } - // Check if the block hash field is set // If it's not -> the transaction is a pending txn, so these fields should be omitted // If it is -> the transaction is a sealed txn, so these fields should be included diff --git a/testsuite/transaction-eip1159.json b/testsuite/transaction-eip1159.json index 534ebe36..bad0eb94 100644 --- a/testsuite/transaction-eip1159.json +++ b/testsuite/transaction-eip1159.json @@ -3,10 +3,9 @@ "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", - "gasPrice": "0x0", - "gas": "0x10", "maxPriorityFeePerGas": "0x10", "maxFeePerGas": "0x10", + "gas": "0x10", "nonce": "0x10", "to": null, "v":"0x25", From 06d892d16c37909735319ab5f4c2854ff0f08bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Tue, 8 Aug 2023 07:47:25 +0200 Subject: [PATCH 3/7] Minor fix --- testutil/server.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/testutil/server.go b/testutil/server.go index d41d4778..e70cdfc8 100644 --- a/testutil/server.go +++ b/testutil/server.go @@ -248,14 +248,14 @@ func (t *TestServer) SendTxn(txn *ethgo.Transaction) (*ethgo.Receipt, error) { txn.From = t.Account(0) } - if txn.Type == ethgo.TransactionLegacy { + if txn.Type == ethgo.TransactionDynamicFee { + if txn.MaxPriorityFeePerGas == nil || txn.MaxPriorityFeePerGas.Cmp(big.NewInt(0)) == 0 { + txn.MaxPriorityFeePerGas = new(big.Int).SetUint64(DefaultGasPrice) + } + } else { if txn.GasPrice == 0 { txn.GasPrice = DefaultGasPrice } - } else if txn.Type == ethgo.TransactionDynamicFee { - if txn.MaxPriorityFeePerGas.Cmp(big.NewInt(0)) == 0 { - txn.MaxPriorityFeePerGas = new(big.Int).SetUint64(DefaultGasPrice) - } } if txn.Gas == 0 { txn.Gas = DefaultGasLimit From 2be27397e624f23ce1a56c6c3104630adfe6f2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Tue, 8 Aug 2023 13:42:56 +0200 Subject: [PATCH 4/7] Address comment --- jsonrpc/eth_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/eth_test.go b/jsonrpc/eth_test.go index a56f4949..ffb49065 100644 --- a/jsonrpc/eth_test.go +++ b/jsonrpc/eth_test.go @@ -403,7 +403,7 @@ func TestEthFeeHistory(t *testing.T) { } func TestEthMaxPriorityFeePerGas(t *testing.T) { - s := testutil.DeployTestServer(t, nil) + s := testutil.NewTestServer(t) c, err := NewClient(s.HTTPAddr()) require.NoError(t, err) From fc48177cc7d434df566f99b80d9e7016bd97c1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Tue, 8 Aug 2023 13:47:28 +0200 Subject: [PATCH 5/7] Fix unit tests --- contract/contract_test.go | 1 - jsonrpc/eth_test.go | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contract/contract_test.go b/contract/contract_test.go index 7774a046..f0f1d90d 100644 --- a/contract/contract_test.go +++ b/contract/contract_test.go @@ -295,7 +295,6 @@ func TestContract_EIP1559(t *testing.T) { assert.NoError(t, err) assert.NotZero(t, txnObj.Gas) - assert.NotZero(t, txnObj.GasPrice) assert.NotZero(t, txnObj.MaxFeePerGas) assert.NotZero(t, txnObj.MaxPriorityFeePerGas) } diff --git a/jsonrpc/eth_test.go b/jsonrpc/eth_test.go index ffb49065..4dfee806 100644 --- a/jsonrpc/eth_test.go +++ b/jsonrpc/eth_test.go @@ -435,6 +435,8 @@ func TestEthMaxPriorityFeePerGas(t *testing.T) { require.Equal(t, uint64(1), receipt.Status) newMaxPriorityFee, err := c.Eth().MaxPriorityFeePerGas() + t.Log(initialMaxPriorityFee) + t.Log(newMaxPriorityFee) require.NoError(t, err) - require.True(t, initialMaxPriorityFee.Cmp(newMaxPriorityFee) < 0) + require.True(t, initialMaxPriorityFee.Cmp(newMaxPriorityFee) <= 0) } From 5f4cedb337544b991569e7d78882f5a0592e3c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Tue, 8 Aug 2023 13:50:15 +0200 Subject: [PATCH 6/7] Add assertions for gas price (it should be 0 for dynamic fee tx) --- contract/contract_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/contract/contract_test.go b/contract/contract_test.go index f0f1d90d..ae631d07 100644 --- a/contract/contract_test.go +++ b/contract/contract_test.go @@ -294,6 +294,7 @@ func TestContract_EIP1559(t *testing.T) { txnObj, err := client.Eth().GetTransactionByHash(txn.Hash()) assert.NoError(t, err) + assert.Zero(t, txnObj.GasPrice) assert.NotZero(t, txnObj.Gas) assert.NotZero(t, txnObj.MaxFeePerGas) assert.NotZero(t, txnObj.MaxPriorityFeePerGas) From 647d932eafd102ba647bf389ffe3099990c72476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Tue, 8 Aug 2023 14:38:50 +0200 Subject: [PATCH 7/7] Rename parameters in FeeHistory funciton and add rewardPercentiles parameter to the function --- jsonrpc/eth.go | 4 ++-- jsonrpc/eth_test.go | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/jsonrpc/eth.go b/jsonrpc/eth.go index 82afde3d..f3096064 100644 --- a/jsonrpc/eth.go +++ b/jsonrpc/eth.go @@ -266,9 +266,9 @@ func (f *FeeHistory) UnmarshalJSON(data []byte) error { } // FeeHistory returns base fee per gas and transaction effective priority fee -func (e *Eth) FeeHistory(from, to ethgo.BlockNumber) (*FeeHistory, error) { +func (e *Eth) FeeHistory(blockCount uint64, newestBlock ethgo.BlockNumber, rewardPercentiles []float64) (*FeeHistory, error) { var out *FeeHistory - if err := e.c.Call("eth_feeHistory", &out, from.String(), to.String(), nil); err != nil { + if err := e.c.Call("eth_feeHistory", &out, blockCount, newestBlock.String(), rewardPercentiles); err != nil { return nil, err } return out, nil diff --git a/jsonrpc/eth_test.go b/jsonrpc/eth_test.go index 4dfee806..916b633c 100644 --- a/jsonrpc/eth_test.go +++ b/jsonrpc/eth_test.go @@ -394,10 +394,7 @@ func TestEthFeeHistory(t *testing.T) { lastBlock, err := c.Eth().BlockNumber() assert.NoError(t, err) - from := ethgo.BlockNumber(lastBlock - 2) - to := ethgo.BlockNumber(lastBlock) - - fee, err := c.Eth().FeeHistory(from, to) + fee, err := c.Eth().FeeHistory(1, ethgo.BlockNumber(lastBlock), []float64{25, 75}) assert.NoError(t, err) assert.NotNil(t, fee) } @@ -424,7 +421,7 @@ func TestEthMaxPriorityFeePerGas(t *testing.T) { latestBlock, err := c.Eth().BlockNumber() require.NoError(t, err) - feeHistory, err := c.Eth().FeeHistory(ethgo.BlockNumber(latestBlock-1), ethgo.BlockNumber(latestBlock)) + feeHistory, err := c.Eth().FeeHistory(1, ethgo.BlockNumber(latestBlock), nil) require.NoError(t, err) latestBaseFee := feeHistory.BaseFee[len(feeHistory.BaseFee)-1]