Skip to content

Commit

Permalink
Added txpool Method Bindings (#60)
Browse files Browse the repository at this point in the history
* module/txpool: added `Status()`

* module/txpool: added `Content()` and `ContentFrom(...)`

* rpctest: compare `types.Transaction.inner`

* added docs

* docs: fix link

---------

Co-authored-by: lmittmann <[email protected]>
  • Loading branch information
lmittmann and lmittmann authored Jul 19, 2023
1 parent a2acd31 commit 8698052
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 1 deletion.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ List of supported RPC methods.
| `debug_traceCall` | `debug.TraceCall(msg *w3types.Message, blockNumber *big.Int, config *debug.TraceConfig).Returns(trace *debug.Trace)`<br>`debug.CallTraceCall(msg *w3types.Message, blockNumber *big.Int, overrides w3types.State).Returns(trace *debug.CallTrace)`
| `debug_traceTransaction` | `debug.TraceTx(txHash common.Hash, config *debug.TraceConfig).Returns(trace *debug.Trace)`<br>`debug.CallTraceTx(txHash common.Hash, overrides w3types.State).Returns(trace *debug.CallTrace)`

### [`txpool`](https://pkg.go.dev/github.com/lmittmann/w3/module/txpool)

| Method | Go Code
| :--------------------| :-------
| `txpool_content` | `txpool.Content().Returns(resp *txpool.ContentResponse)`
| `txpool_contentFrom` | `txpool.ContentFrom(addr common.Address).Returns(resp *txpool.ContentFromResponse)`
| `txpool_status` | `txpool.Status().Returns(resp *txpool.StatusResponse)`

### [`web3`](https://pkg.go.dev/github.com/lmittmann/w3/module/web3)

| Method | Go Code
Expand Down
8 changes: 8 additions & 0 deletions docs/pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,14 @@ List of supported RPC methods.
| `debug_traceCall` | `debug.TraceCall(msg *w3types.Message, blockNumber *big.Int, config *debug.TraceConfig).Returns(trace *debug.Trace)`<br />`debug.CallTraceCall(msg *w3types.Message, blockNumber *big.Int, overrides w3types.State).Returns(trace *debug.CallTrace)`
| `debug_traceTransaction` | `debug.TraceTx(txHash common.Hash, config *debug.TraceConfig).Returns(trace *debug.Trace)`<br />`debug.CallTraceTx(txHash common.Hash, overrides w3types.State).Returns(trace *debug.CallTrace)`

### [`txpool`](https://pkg.go.dev/github.com/lmittmann/w3/module/txpool)

| Method | Go Code
| :--------------------| :-------
| `txpool_content` | `txpool.Content().Returns(resp *txpool.ContentResponse)`
| `txpool_contentFrom` | `txpool.ContentFrom(addr common.Address).Returns(resp *txpool.ContentFromResponse)`
| `txpool_status` | `txpool.Status().Returns(resp *txpool.StatusResponse)`

### [`web3`](https://pkg.go.dev/github.com/lmittmann/w3/module/web3)

| Method | Go Code
Expand Down
2 changes: 2 additions & 0 deletions docs/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-agent: *
Allow: /
100 changes: 100 additions & 0 deletions module/txpool/content.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package txpool

import (
"encoding/json"
"sort"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/lmittmann/w3/internal/module"
"github.com/lmittmann/w3/w3types"
)

// Content requests the pending and queued transactions in the transaction pool.
func Content() w3types.CallerFactory[ContentResponse] {
return module.NewFactory[ContentResponse](
"txpool_content",
nil,
)
}

// ContentFrom requests the pending and queued transactions in the transaction pool
// from the given address.
func ContentFrom(addr common.Address) w3types.CallerFactory[ContentFromResponse] {
return module.NewFactory[ContentFromResponse](
"txpool_contentFrom",
[]any{addr},
)
}

type ContentResponse struct {
Pending map[common.Address][]*types.Transaction
Queued map[common.Address][]*types.Transaction
}

func (c *ContentResponse) UnmarshalJSON(data []byte) error {
type contentResponse struct {
Pending map[common.Address]map[uint64]*types.Transaction `json:"pending"`
Queued map[common.Address]map[uint64]*types.Transaction `json:"queued"`
}

var dec contentResponse
if err := json.Unmarshal(data, &dec); err != nil {
return err
}

c.Pending = make(map[common.Address][]*types.Transaction, len(dec.Pending))
for addr, nonceTx := range dec.Pending {
txs := make(types.TxByNonce, 0, len(nonceTx))
for _, tx := range nonceTx {
txs = append(txs, tx)
}
sort.Sort(txs)
c.Pending[addr] = txs
}

c.Queued = make(map[common.Address][]*types.Transaction, len(dec.Queued))
for addr, nonceTx := range dec.Queued {
txs := make(types.TxByNonce, 0, len(nonceTx))
for _, tx := range nonceTx {
txs = append(txs, tx)
}
sort.Sort(txs)
c.Queued[addr] = txs
}

return nil
}

type ContentFromResponse struct {
Pending []*types.Transaction
Queued []*types.Transaction
}

func (cf *ContentFromResponse) UnmarshalJSON(data []byte) error {
type contentFromResponse struct {
Pending map[uint64]*types.Transaction `json:"pending"`
Queued map[uint64]*types.Transaction `json:"queued"`
}

var dec contentFromResponse
if err := json.Unmarshal(data, &dec); err != nil {
return err
}

txs := make(types.TxByNonce, 0, len(dec.Pending))
for _, tx := range dec.Pending {
txs = append(txs, tx)
}
sort.Sort(txs)
cf.Pending = txs

txs = make(types.TxByNonce, 0, len(dec.Queued))
for _, tx := range dec.Queued {
txs = append(txs, tx)
}
sort.Sort(txs)
cf.Queued = txs

return nil
}
85 changes: 85 additions & 0 deletions module/txpool/content_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package txpool_test

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/lmittmann/w3/module/txpool"
"github.com/lmittmann/w3/rpctest"
)

func TestContent(t *testing.T) {
tests := []rpctest.TestCase[txpool.ContentResponse]{
{
Golden: "content",
Call: txpool.Content(),
WantRet: txpool.ContentResponse{
Pending: map[common.Address][]*types.Transaction{
common.HexToAddress("0x000454307bB96E303044046a6eB2736D2aD560B6"): {
types.NewTx(&types.DynamicFeeTx{
ChainID: big.NewInt(1),
Nonce: 4652,
GasTipCap: big.NewInt(31407912032),
GasFeeCap: big.NewInt(202871575924),
Gas: 1100000,
To: ptr(common.HexToAddress("0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B")),
Value: big.NewInt(81000000000000000),
Data: common.FromHex("0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000001896e196d5300000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000011fc51222ce800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000004657febe8d8000000000000000000000000000000000000000000000000000011fc51222ce800000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000008d538a82c84d7003aa0e7f1098bd9dc5ea1777be000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"),
R: new(big.Int).SetBytes(common.FromHex("0xf91729846ac8bb780a7239b7f0157d53330bba310a0811f4eec2eae25172c252")),
S: new(big.Int).SetBytes(common.FromHex("0x1bb9a1b9aea8b128e0e8dc42b17664be50c7b4073973c730b6c4cf2a3b3503cb")),
}),
},
},
Queued: map[common.Address][]*types.Transaction{
common.HexToAddress("0x1BA4Ca9ac6ff4CF192C11E8C1624563f302cAA87"): {
types.NewTx(&types.DynamicFeeTx{
ChainID: big.NewInt(1),
Nonce: 183,
GasTipCap: big.NewInt(110000000),
GasFeeCap: big.NewInt(20027736270),
Gas: 99226,
To: ptr(common.HexToAddress("0x1BA4Ca9ac6ff4CF192C11E8C1624563f302cAA87")),
Value: big.NewInt(0),
Data: []byte{},
R: new(big.Int).SetBytes(common.FromHex("0xea35c7c0643b79664b0bbb7f42d64edd371508ae4c33c1f817a80a2655465935")),
S: new(big.Int).SetBytes(common.FromHex("0x76d39f794e9e1ee359d66b7d3b19b90aaf2051b2159c68f3bb8c954558989da8")),
}),
},
},
},
},
}

rpctest.RunTestCases(t, tests)
}

func TestContentFrom(t *testing.T) {
tests := []rpctest.TestCase[txpool.ContentFromResponse]{
{
Golden: "contentFrom",
Call: txpool.ContentFrom(common.HexToAddress("0x1BA4Ca9ac6ff4CF192C11E8C1624563f302cAA87")),
WantRet: txpool.ContentFromResponse{
Queued: []*types.Transaction{
types.NewTx(&types.DynamicFeeTx{
ChainID: big.NewInt(1),
Nonce: 183,
GasTipCap: big.NewInt(110000000),
GasFeeCap: big.NewInt(20027736270),
Gas: 99226,
To: ptr(common.HexToAddress("0x1BA4Ca9ac6ff4CF192C11E8C1624563f302cAA87")),
Value: big.NewInt(0),
Data: []byte{},
R: new(big.Int).SetBytes(common.FromHex("0xea35c7c0643b79664b0bbb7f42d64edd371508ae4c33c1f817a80a2655465935")),
S: new(big.Int).SetBytes(common.FromHex("0x76d39f794e9e1ee359d66b7d3b19b90aaf2051b2159c68f3bb8c954558989da8")),
}),
},
},
},
}

rpctest.RunTestCases(t, tests)
}

func ptr[T any](x T) *T { return &x }
4 changes: 4 additions & 0 deletions module/txpool/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*
Package txpool implements RPC API bindings for methods in the "txpool" namespace.
*/
package txpool
37 changes: 37 additions & 0 deletions module/txpool/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package txpool

import (
"encoding/json"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/lmittmann/w3/internal/module"
"github.com/lmittmann/w3/w3types"
)

// Status requests the number of pending and queued transactions in the transaction pool.
func Status() w3types.CallerFactory[StatusResponse] {
return module.NewFactory[StatusResponse](
"txpool_status",
nil,
)
}

type StatusResponse struct {
Pending uint
Queued uint
}

func (s *StatusResponse) UnmarshalJSON(data []byte) error {
type statusResponse struct {
Pending hexutil.Uint `json:"pending"`
Queued hexutil.Uint `json:"queued"`
}

var dec statusResponse
if err := json.Unmarshal(data, &dec); err != nil {
return err
}
s.Pending = uint(dec.Pending)
s.Queued = uint(dec.Queued)
return nil
}
20 changes: 20 additions & 0 deletions module/txpool/status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package txpool_test

import (
"testing"

"github.com/lmittmann/w3/module/txpool"
"github.com/lmittmann/w3/rpctest"
)

func TestStatus(t *testing.T) {
tests := []rpctest.TestCase[txpool.StatusResponse]{
{
Golden: "status",
Call: txpool.Status(),
WantRet: txpool.StatusResponse{Pending: 10, Queued: 7},
},
}

rpctest.RunTestCases(t, tests)
}
2 changes: 2 additions & 0 deletions module/txpool/testdata/content.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> {"jsonrpc":"2.0","id":1,"method":"txpool_content"}
< {"jsonrpc":"2.0","id":1,"result":{"pending":{"0x000454307bB96E303044046a6eB2736D2aD560B6":{"4652":{"blockHash":null,"blockNumber":null,"from":"0x000454307bb96e303044046a6eb2736d2ad560b6","gas":"0x10c8e0","gasPrice":"0x2f3c169574","maxFeePerGas":"0x2f3c169574","maxPriorityFeePerGas":"0x7500eb460","hash":"0xb4f36425ff7007c1fc6d29148f062f5ed3232ad99bc185f5ad26f071851409c7","input":"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000001896e196d5300000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000011fc51222ce800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000004657febe8d8000000000000000000000000000000000000000000000000000011fc51222ce800000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000008d538a82c84d7003aa0e7f1098bd9dc5ea1777be000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000","nonce":"0x122c","to":"0xef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","transactionIndex":null,"value":"0x11fc51222ce8000","type":"0x2","accessList":[],"chainId":"0x1","v":"0x0","r":"0xf91729846ac8bb780a7239b7f0157d53330bba310a0811f4eec2eae25172c252","s":"0x1bb9a1b9aea8b128e0e8dc42b17664be50c7b4073973c730b6c4cf2a3b3503cb"}}},"queued":{"0x1BA4Ca9ac6ff4CF192C11E8C1624563f302cAA87":{"183":{"blockHash":null,"blockNumber":null,"from":"0x1ba4ca9ac6ff4cf192c11e8c1624563f302caa87","gas":"0x1839a","gasPrice":"0x4a9bf00ce","maxFeePerGas":"0x4a9bf00ce","maxPriorityFeePerGas":"0x68e7780","hash":"0x222c11451c3bf3607a4ef18fa87f26575a0dca97e10ceb81b8ebebdd45d5bbfb","input":"0x","nonce":"0xb7","to":"0x1ba4ca9ac6ff4cf192c11e8c1624563f302caa87","transactionIndex":null,"value":"0x0","type":"0x2","accessList":[],"chainId":"0x1","v":"0x0","r":"0xea35c7c0643b79664b0bbb7f42d64edd371508ae4c33c1f817a80a2655465935","s":"0x76d39f794e9e1ee359d66b7d3b19b90aaf2051b2159c68f3bb8c954558989da8"}}}}}
2 changes: 2 additions & 0 deletions module/txpool/testdata/contentFrom.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> {"jsonrpc":"2.0","id":1,"method":"txpool_contentFrom","params":["0x1ba4ca9ac6ff4cf192c11e8c1624563f302caa87"]}
< {"jsonrpc":"2.0","id":1,"result":{"queued":{"183":{"blockHash":null,"blockNumber":null,"from":"0x1ba4ca9ac6ff4cf192c11e8c1624563f302caa87","gas":"0x1839a","gasPrice":"0x4a9bf00ce","maxFeePerGas":"0x4a9bf00ce","maxPriorityFeePerGas":"0x68e7780","hash":"0x222c11451c3bf3607a4ef18fa87f26575a0dca97e10ceb81b8ebebdd45d5bbfb","input":"0x","nonce":"0xb7","to":"0x1ba4ca9ac6ff4cf192c11e8c1624563f302caa87","transactionIndex":null,"value":"0x0","type":"0x2","accessList":[],"chainId":"0x1","v":"0x0","r":"0xea35c7c0643b79664b0bbb7f42d64edd371508ae4c33c1f817a80a2655465935","s":"0x76d39f794e9e1ee359d66b7d3b19b90aaf2051b2159c68f3bb8c954558989da8"}}}}
2 changes: 2 additions & 0 deletions module/txpool/testdata/status.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> {"jsonrpc":"2.0","id":1,"method":"txpool_status"}
< {"jsonrpc":"2.0","id":1,"result":{"pending":"0xa","queued":"0x7"}}
4 changes: 3 additions & 1 deletion rpctest/test_case.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/core/types"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/lmittmann/w3"
Expand Down Expand Up @@ -51,7 +52,8 @@ func comp[T any](t *testing.T, wantVal, gotVal T, wantErr, gotErr error, opts ..

// compare values
opts = append(opts,
cmp.AllowUnexported(big.Int{}),
cmp.AllowUnexported(big.Int{}, types.Transaction{}),
cmpopts.IgnoreFields(types.Transaction{}, "time", "hash", "size", "from"),
cmpopts.EquateEmpty(),
)
if diff := cmp.Diff(wantVal, gotVal, opts...); diff != "" {
Expand Down

0 comments on commit 8698052

Please sign in to comment.