From 2046b141cfd29de290c398fa5b31a8607e19d517 Mon Sep 17 00:00:00 2001 From: dkeysil Date: Mon, 26 Aug 2024 16:28:29 +0200 Subject: [PATCH] Add get transaction count method to eth client --- ethereum/client.go | 44 ++++++++++++++++++++++++++++------- ethereum/mocks/mock_client.go | 15 ++++++++++++ 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/ethereum/client.go b/ethereum/client.go index b31d9ab..c3e15eb 100644 --- a/ethereum/client.go +++ b/ethereum/client.go @@ -62,6 +62,7 @@ type Client interface { BlockNumber(ctx context.Context) (*big.Int, error) TransactionReceipt(ctx context.Context, txHash string) (*domain.TransactionReceipt, error) ChainID(ctx context.Context) (*big.Int, error) + GetTransactionCount(ctx context.Context, address string, blockNumber *big.Int) (*big.Int, error) TraceBlock(ctx context.Context, number *big.Int) ([]domain.Trace, error) DebugTraceCall( ctx context.Context, req *domain.TraceCallTransaction, @@ -75,14 +76,15 @@ type Client interface { } const ( - blocksByNumber = "eth_getBlockByNumber" - blocksByHash = "eth_getBlockByHash" - blockNumber = "eth_blockNumber" - getLogs = "eth_getLogs" - transactionReceipt = "eth_getTransactionReceipt" - traceBlock = "trace_block" - debugTraceCall = "debug_traceCall" - chainId = "eth_chainId" + blocksByNumber = "eth_getBlockByNumber" + blocksByHash = "eth_getBlockByHash" + blockNumber = "eth_blockNumber" + getLogs = "eth_getLogs" + transactionReceipt = "eth_getTransactionReceipt" + traceBlock = "trace_block" + debugTraceCall = "debug_traceCall" + chainId = "eth_chainId" + getTransactionCount = "eth_getTransactionCount" ) const defaultRetryInterval = time.Second * 15 @@ -384,6 +386,32 @@ func (e *streamEthClient) TransactionReceipt(ctx context.Context, txHash string) return &result, err } +// GetTransactionCount returns the transaction count for an address +func (e *streamEthClient) GetTransactionCount(ctx context.Context, address string, blockNumber *big.Int) (*big.Int, error) { + name := fmt.Sprintf("%s(%s, %s)", getTransactionCount, address, blockNumber) + log.Debugf(name) + var result string + err := withBackoff(ctx, name, func(ctx context.Context) error { + err := e.rpcClient.CallContext(ctx, &result, getTransactionCount, address, blockNumber) + if err != nil { + return err + } + if result == "" { + return ErrNotFound + } + return nil + }, RetryOptions{ + MinBackoff: pointDur(e.retryInterval), + MaxElapsedTime: pointDur(12 * time.Hour), + MaxBackoff: pointDur(e.retryInterval), + }, nil, nil) + if err != nil { + return nil, err + } + + return utils.HexToBigInt(result) +} + // SubscribeToHead subscribes to the blockchain head and returns a channel which provides // the latest block headers. The channel is closed when subscription encounters an error // or becomes inactive (e.g. due to a hanging connection). diff --git a/ethereum/mocks/mock_client.go b/ethereum/mocks/mock_client.go index 54255c8..3a33480 100644 --- a/ethereum/mocks/mock_client.go +++ b/ethereum/mocks/mock_client.go @@ -599,6 +599,21 @@ func (mr *MockClientMockRecorder) GetLogs(ctx, q interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogs", reflect.TypeOf((*MockClient)(nil).GetLogs), ctx, q) } +// GetTransactionCount mocks base method. +func (m *MockClient) GetTransactionCount(ctx context.Context, address string, blockNumber *big.Int) (*big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTransactionCount", ctx, address, blockNumber) + ret0, _ := ret[0].(*big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTransactionCount indicates an expected call of GetTransactionCount. +func (mr *MockClientMockRecorder) GetTransactionCount(ctx, address, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransactionCount", reflect.TypeOf((*MockClient)(nil).GetTransactionCount), ctx, address, blockNumber) +} + // Health mocks base method. func (m *MockClient) Health() health.Reports { m.ctrl.T.Helper()