Skip to content

Commit

Permalink
Expose eth_maxPriorityFeePerGas JSON RPC function (#251)
Browse files Browse the repository at this point in the history
* Expose eth_maxPriorityFeePerGas function

* Prevent marshaling gas price in case of dynamic fee transactions

* Minor fix

* Address comment

* Fix unit tests

* Add assertions for gas price (it should be 0 for dynamic fee tx)

* Rename parameters in FeeHistory funciton and add rewardPercentiles parameter to the function
  • Loading branch information
Stefan-Ethernal committed Aug 8, 2023
1 parent 3fac12f commit b501468
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 31 deletions.
2 changes: 1 addition & 1 deletion contract/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ 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.GasPrice)
assert.NotZero(t, txnObj.MaxFeePerGas)
assert.NotZero(t, txnObj.MaxPriorityFeePerGas)
}
15 changes: 13 additions & 2 deletions jsonrpc/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,21 @@ 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
}

// 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
}
44 changes: 40 additions & 4 deletions jsonrpc/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,10 +394,46 @@ 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)
}

func TestEthMaxPriorityFeePerGas(t *testing.T) {
s := testutil.NewTestServer(t)
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 := &ethgo.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(1, ethgo.BlockNumber(latestBlock), nil)
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()
t.Log(initialMaxPriorityFee)
t.Log(newMaxPriorityFee)
require.NoError(t, err)
require.True(t, initialMaxPriorityFee.Cmp(newMaxPriorityFee) <= 0)
}
20 changes: 11 additions & 9 deletions structs_marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -298,4 +300,4 @@ func (s StateOverride) MarshalJSON() ([]byte, error) {
defaultArena.Put(a)

return res, nil
}
}
22 changes: 11 additions & 11 deletions structs_unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions testsuite/transaction-eip1159.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 9 additions & 2 deletions testutil/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.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
}
}
if txn.Gas == 0 {
txn.Gas = DefaultGasLimit
Expand Down

0 comments on commit b501468

Please sign in to comment.