diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index 72c6eb19029..465445b8092 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -17,12 +17,12 @@ import ( ) // mockRPC is an autogenerated mock type for the RPC type -type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH]] struct { +type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { mock.Mock } // BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) { ret := _m.Called(ctx, accountAddress, blockNumber) if len(ret) == 0 { @@ -52,7 +52,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // BatchCallContext provides a mock function with given fields: ctx, b -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BatchCallContext(ctx context.Context, b []interface{}) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { ret := _m.Called(ctx, b) if len(ret) == 0 { @@ -60,7 +60,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, []BATCH_ELEM) error); ok { r0 = rf(ctx, b) } else { r0 = ret.Error(0) @@ -70,7 +70,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // BlockByHash provides a mock function with given fields: ctx, hash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) { ret := _m.Called(ctx, hash) if len(ret) == 0 { @@ -98,7 +98,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // BlockByNumber provides a mock function with given fields: ctx, number -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) { ret := _m.Called(ctx, number) if len(ret) == 0 { @@ -126,7 +126,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // CallContext provides a mock function with given fields: ctx, result, method, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { var _ca []interface{} _ca = append(_ca, ctx, result, method) _ca = append(_ca, args...) @@ -147,7 +147,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // CallContract provides a mock function with given fields: ctx, msg, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, msg, blockNumber) if len(ret) == 0 { @@ -177,7 +177,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // ChainID provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ChainID(ctx context.Context) (CHAIN_ID, error) { ret := _m.Called(ctx) if len(ret) == 0 { @@ -205,7 +205,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // ClientVersion provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) ClientVersion(_a0 context.Context) (string, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ClientVersion(_a0 context.Context) (string, error) { ret := _m.Called(_a0) if len(ret) == 0 { @@ -233,12 +233,12 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // Close provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Close() { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Close() { _m.Called() } // CodeAt provides a mock function with given fields: ctx, account, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, account, blockNumber) if len(ret) == 0 { @@ -268,7 +268,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // Dial provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Dial(ctx context.Context) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Dial(ctx context.Context) error { ret := _m.Called(ctx) if len(ret) == 0 { @@ -286,7 +286,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // DialHTTP provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) DialHTTP() error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DialHTTP() error { ret := _m.Called() if len(ret) == 0 { @@ -304,12 +304,12 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // DisconnectAll provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) DisconnectAll() { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DisconnectAll() { _m.Called() } // EstimateGas provides a mock function with given fields: ctx, call -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { ret := _m.Called(ctx, call) if len(ret) == 0 { @@ -337,7 +337,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // FilterEvents provides a mock function with given fields: ctx, query -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) { ret := _m.Called(ctx, query) if len(ret) == 0 { @@ -367,7 +367,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { ret := _m.Called(ctx, accountAddress, linkAddress) if len(ret) == 0 { @@ -397,7 +397,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // LatestBlockHeight provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { ret := _m.Called(_a0) if len(ret) == 0 { @@ -427,7 +427,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // PendingCallContract provides a mock function with given fields: ctx, msg -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { ret := _m.Called(ctx, msg) if len(ret) == 0 { @@ -457,7 +457,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // PendingSequenceAt provides a mock function with given fields: ctx, addr -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) { ret := _m.Called(ctx, addr) if len(ret) == 0 { @@ -485,7 +485,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SendEmptyTransaction(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR) (string, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendEmptyTransaction(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR) (string, error) { ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) if len(ret) == 0 { @@ -513,7 +513,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SendTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SendTransaction(ctx context.Context, tx TX) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { ret := _m.Called(ctx, tx) if len(ret) == 0 { @@ -531,7 +531,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) { ret := _m.Called(ctx, accountAddress, blockNumber) if len(ret) == 0 { @@ -559,12 +559,12 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SetAliveLoopSub(_a0 types.Subscription) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SetAliveLoopSub(_a0 types.Subscription) { _m.Called(_a0) } // SimulateTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SimulateTransaction(ctx context.Context, tx TX) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { ret := _m.Called(ctx, tx) if len(ret) == 0 { @@ -582,7 +582,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { var _ca []interface{} _ca = append(_ca, ctx, channel) _ca = append(_ca, args...) @@ -615,7 +615,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SubscribersCount provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SubscribersCount() int32 { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribersCount() int32 { ret := _m.Called() if len(ret) == 0 { @@ -633,7 +633,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) { ret := _m.Called(ctx, accountAddress, tokenAddress) if len(ret) == 0 { @@ -663,7 +663,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // TransactionByHash provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) { ret := _m.Called(ctx, txHash) if len(ret) == 0 { @@ -691,7 +691,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // TransactionReceipt provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) { ret := _m.Called(ctx, txHash) if len(ret) == 0 { @@ -719,17 +719,17 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) UnsubscribeAllExceptAliveLoop() { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) UnsubscribeAllExceptAliveLoop() { _m.Called() } // newMockRPC creates a new instance of mockRPC. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func newMockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH]](t interface { +func newMockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}](t interface { mock.TestingT Cleanup(func()) -}) *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD] { - mock := &mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]{} +}) *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { + mock := &mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index ae9b3afd0d4..e86a7631982 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -50,7 +50,8 @@ type MultiNode[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], + BATCH_ELEM any, ] interface { clientAPI[ CHAIN_ID, @@ -64,12 +65,13 @@ type MultiNode[ TX_RECEIPT, FEE, HEAD, + BATCH_ELEM, ] Close() error NodeStates() map[string]string SelectNodeRPC() (RPC_CLIENT, error) - BatchCallContextAll(ctx context.Context, b []any) error + BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error ConfiguredChainID() CHAIN_ID IsL2() bool } @@ -86,7 +88,8 @@ type multiNode[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], + BATCH_ELEM any, ] struct { services.StateMachine nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] @@ -124,7 +127,8 @@ func NewMultiNode[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], + BATCH_ELEM any, ]( lggr logger.Logger, selectionMode string, @@ -137,7 +141,7 @@ func NewMultiNode[ chainFamily string, classifySendTxError func(tx TX, err error) SendTxReturnCode, sendTxSoftTimeout time.Duration, -) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT] { +) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM] { nodeSelector := newNodeSelector(selectionMode, nodes) // Prometheus' default interval is 15s, set this to under 7.5s to avoid // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) @@ -145,7 +149,7 @@ func NewMultiNode[ if sendTxSoftTimeout == 0 { sendTxSoftTimeout = QueryTimeout / 2 } - c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]{ + c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]{ nodes: nodes, sendonlys: sendonlys, chainID: chainID, @@ -171,7 +175,7 @@ func NewMultiNode[ // // Nodes handle their own redialing and runloops, so this function does not // return any error if the nodes aren't available -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Dial(ctx context.Context) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Dial(ctx context.Context) error { return c.StartOnce("MultiNode", func() (merr error) { if len(c.nodes) == 0 { return fmt.Errorf("no available nodes for chain %s", c.chainID.String()) @@ -218,7 +222,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // Close tears down the MultiNode and closes all nodes -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Close() error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Close() error { return c.StopOnce("MultiNode", func() error { close(c.chStop) c.wg.Wait() @@ -229,7 +233,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // SelectNodeRPC returns an RPC of an active node. If there are no active nodes it returns an error. // Call this method from your chain-specific client implementation to access any chain-specific rpc calls. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SelectNodeRPC() (rpc RPC_CLIENT, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SelectNodeRPC() (rpc RPC_CLIENT, err error) { n, err := c.selectNode() if err != nil { return rpc, err @@ -239,7 +243,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // selectNode returns the active Node, if it is still nodeStateAlive, otherwise it selects a new one from the NodeSelector. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) selectNode() (node Node[CHAIN_ID, HEAD, RPC_CLIENT], err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) selectNode() (node Node[CHAIN_ID, HEAD, RPC_CLIENT], err error) { c.activeMu.RLock() node = c.activeNode c.activeMu.RUnlock() @@ -269,7 +273,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // nLiveNodes returns the number of currently alive nodes, as well as the highest block number and greatest total difficulty. // totalDifficulty will be 0 if all nodes return nil. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { totalDifficulty = big.NewInt(0) for _, n := range c.nodes { if s, num, td := n.StateAndLatest(); s == nodeStateAlive { @@ -285,7 +289,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) checkLease() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLease() { bestNode := c.nodeSelector.Select() for _, n := range c.nodes { // Terminate client subscriptions. Services are responsible for reconnecting, which will be routed to the new @@ -303,7 +307,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.activeMu.Unlock() } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) checkLeaseLoop() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { defer c.wg.Done() c.leaseTicker = time.NewTicker(c.leaseDuration) defer c.leaseTicker.Stop() @@ -318,7 +322,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) runLoop() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) runLoop() { defer c.wg.Done() c.report() @@ -336,7 +340,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) report() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) report() { type nodeWithState struct { Node string State string @@ -371,7 +375,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // ClientAPI methods -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BalanceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (*big.Int, error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BalanceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (*big.Int, error) { n, err := c.selectNode() if err != nil { return nil, err @@ -379,7 +383,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().BalanceAt(ctx, account, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BatchCallContext(ctx context.Context, b []any) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { n, err := c.selectNode() if err != nil { return err @@ -391,7 +395,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // sendonlys. // CAUTION: This should only be used for mass re-transmitting transactions, it // might have unexpected effects to use it for anything else. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BatchCallContextAll(ctx context.Context, b []any) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error { var wg sync.WaitGroup defer wg.Wait() @@ -429,7 +433,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return main.RPC().BatchCallContext(ctx, b) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (h HEAD, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (h HEAD, err error) { n, err := c.selectNode() if err != nil { return h, err @@ -437,7 +441,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().BlockByHash(ctx, hash) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BlockByNumber(ctx context.Context, number *big.Int) (h HEAD, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (h HEAD, err error) { n, err := c.selectNode() if err != nil { return h, err @@ -445,7 +449,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().BlockByNumber(ctx, number) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { n, err := c.selectNode() if err != nil { return err @@ -453,7 +457,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().CallContext(ctx, result, method, args...) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CallContract( +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContract( ctx context.Context, attempt interface{}, blockNumber *big.Int, @@ -465,7 +469,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().CallContract(ctx, attempt, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) PendingCallContract( +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingCallContract( ctx context.Context, attempt interface{}, ) (rpcErr []byte, extractErr error) { @@ -478,7 +482,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // ChainID makes a direct RPC call. In most cases it should be better to use the configured chain id instead by // calling ConfiguredChainID. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ChainID(ctx context.Context) (id CHAIN_ID, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ChainID(ctx context.Context) (id CHAIN_ID, err error) { n, err := c.selectNode() if err != nil { return id, err @@ -486,11 +490,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().ChainID(ctx) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ChainType() config.ChainType { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ChainType() config.ChainType { return c.chainType } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) (code []byte, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) (code []byte, err error) { n, err := c.selectNode() if err != nil { return code, err @@ -498,11 +502,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().CodeAt(ctx, account, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ConfiguredChainID() CHAIN_ID { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ConfiguredChainID() CHAIN_ID { return c.chainID } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) EstimateGas(ctx context.Context, call any) (gas uint64, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) EstimateGas(ctx context.Context, call any) (gas uint64, err error) { n, err := c.selectNode() if err != nil { return gas, err @@ -510,7 +514,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().EstimateGas(ctx, call) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) FilterEvents(ctx context.Context, query EVENT_OPS) (e []EVENT, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) (e []EVENT, err error) { n, err := c.selectNode() if err != nil { return e, err @@ -518,11 +522,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().FilterEvents(ctx, query) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) IsL2() bool { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) IsL2() bool { return c.ChainType().IsL2() } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) LatestBlockHeight(ctx context.Context) (h *big.Int, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestBlockHeight(ctx context.Context) (h *big.Int, err error) { n, err := c.selectNode() if err != nil { return h, err @@ -530,7 +534,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().LatestBlockHeight(ctx) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (b *assets.Link, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (b *assets.Link, err error) { n, err := c.selectNode() if err != nil { return b, err @@ -538,7 +542,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().LINKBalance(ctx, accountAddress, linkAddress) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) NodeStates() (states map[string]string) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) NodeStates() (states map[string]string) { states = make(map[string]string) for _, n := range c.nodes { states[n.Name()] = n.State().String() @@ -549,7 +553,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) PendingSequenceAt(ctx context.Context, addr ADDR) (s SEQ, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (s SEQ, err error) { n, err := c.selectNode() if err != nil { return s, err @@ -557,7 +561,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().PendingSequenceAt(ctx, addr) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SendEmptyTransaction( +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendEmptyTransaction( ctx context.Context, newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), seq SEQ, @@ -577,7 +581,7 @@ type sendTxResult struct { ResultCode SendTxReturnCode } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) broadcastTxAsync(ctx context.Context, +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) broadcastTxAsync(ctx context.Context, n SendOnlyNode[CHAIN_ID, RPC_CLIENT], tx TX) sendTxResult { txErr := n.RPC().SendTransaction(ctx, tx) c.lggr.Debugw("Node sent transaction", "name", n.String(), "tx", tx, "err", txErr) @@ -590,7 +594,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // collectTxResults - refer to SendTransaction comment for implementation details, -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) error { if healthyNodesNum == 0 { return ErroringNodeError } @@ -633,7 +637,7 @@ loop: } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { defer c.wg.Done() resultsByCode := map[SendTxReturnCode][]error{} // txResults eventually will be closed @@ -698,7 +702,7 @@ const sendTxQuorum = 0.7 // * If there is at least one terminal error - returns terminal error // * If there is both success and terminal error - returns success and reports invariant violation // * Otherwise, returns any (effectively random) of the errors. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SendTransaction(ctx context.Context, tx TX) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { if len(c.nodes) == 0 { return ErroringNodeError } @@ -768,7 +772,7 @@ func findFirstIn[K comparable, V any](set map[K]V, keys []K) (V, bool) { return v, false } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SequenceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (s SEQ, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SequenceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (s SEQ, err error) { n, err := c.selectNode() if err != nil { return s, err @@ -776,7 +780,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().SequenceAt(ctx, account, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SimulateTransaction(ctx context.Context, tx TX) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { n, err := c.selectNode() if err != nil { return err @@ -784,7 +788,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().SimulateTransaction(ctx, tx) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (s types.Subscription, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (s types.Subscription, err error) { n, err := c.selectNode() if err != nil { return s, err @@ -792,7 +796,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().Subscribe(ctx, channel, args...) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { n, err := c.selectNode() if err != nil { return b, err @@ -800,7 +804,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().TokenBalance(ctx, account, tokenAddr) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TransactionByHash(ctx context.Context, txHash TX_HASH) (tx TX, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (tx TX, err error) { n, err := c.selectNode() if err != nil { return tx, err @@ -808,7 +812,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().TransactionByHash(ctx, txHash) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (txr TX_RECEIPT, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (txr TX_RECEIPT, err error) { n, err := c.selectNode() if err != nil { return txr, err diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index dabaae57c5d..43e4127556a 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -22,11 +22,11 @@ import ( ) type multiNodeRPCClient RPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] type testMultiNode struct { *multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient] + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any] } type multiNodeOpts struct { @@ -49,19 +49,19 @@ func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { } result := NewMultiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient](opts.logger, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any](opts.logger, opts.selectionMode, opts.leaseDuration, opts.noNewHeadsThreshold, opts.nodes, opts.sendonlys, opts.chainID, opts.chainType, opts.chainFamily, opts.classifySendTxError, opts.sendTxSoftTimeout) return testMultiNode{ result.(*multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient]), + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any]), } } func newMultiNodeRPCClient(t *testing.T) *mockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] { + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] { return newMockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]](t) + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any](t) } func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { diff --git a/common/client/types.go b/common/client/types.go index fe9e4d7d482..c05ab238f57 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -24,7 +24,7 @@ type RPC[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - + BATCH_ELEM any, ] interface { NodeClient[ CHAIN_ID, @@ -42,6 +42,7 @@ type RPC[ TX_RECEIPT, FEE, HEAD, + BATCH_ELEM, ] } @@ -84,6 +85,7 @@ type clientAPI[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], + BATCH_ELEM any, ] interface { connection[CHAIN_ID, HEAD] @@ -118,7 +120,7 @@ type clientAPI[ FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) // Misc - BatchCallContext(ctx context.Context, b []any) error + BatchCallContext(ctx context.Context, b []BATCH_ELEM) error CallContract( ctx context.Context, msg interface{}, diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index b16054b69a8..33deed1df32 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -36,6 +36,7 @@ type chainClient struct { *assets.Wei, *evmtypes.Head, RPCCLient, + rpc.BatchElem, ] logger logger.SugaredLogger } @@ -88,20 +89,16 @@ func (c *chainClient) BalanceAt(ctx context.Context, account common.Address, blo return c.multiNode.BalanceAt(ctx, account, blockNumber) } +// Request specific errors for batch calls are returned to the individual BatchElem. +// Ensure the same BatchElem slice provided by the caller is passed through the call stack +// to ensure the caller has access to the errors. func (c *chainClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - batch := make([]any, len(b)) - for i, arg := range b { - batch[i] = any(arg) - } - return c.multiNode.BatchCallContext(ctx, batch) + return c.multiNode.BatchCallContext(ctx, b) } +// Similar to BatchCallContext, ensure the provided BatchElem slice is passed through func (c *chainClient) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error { - batch := make([]any, len(b)) - for i, arg := range b { - batch[i] = any(arg) - } - return c.multiNode.BatchCallContextAll(ctx, batch) + return c.multiNode.BatchCallContextAll(ctx, b) } // TODO-1663: return custom Block type instead of geth's once client.go is deprecated. diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go new file mode 100644 index 00000000000..6af9a67ee1c --- /dev/null +++ b/core/chains/evm/client/chain_client_test.go @@ -0,0 +1,70 @@ +package client_test + +import ( + "errors" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" +) + +func newMockRpc(t *testing.T) *mocks.RPCCLient { + mockRpc := mocks.NewRPCCLient(t) + mockRpc.On("Dial", mock.Anything).Return(nil).Once() + mockRpc.On("Close").Return(nil).Once() + mockRpc.On("ChainID", mock.Anything).Return(testutils.FixtureChainID, nil).Once() + mockRpc.On("Subscribe", mock.Anything, mock.Anything, mock.Anything).Return(client.NewMockSubscription(), nil).Once() + mockRpc.On("SetAliveLoopSub", mock.Anything).Return().Once() + return mockRpc +} + +func TestChainClient_BatchCallContext(t *testing.T) { + t.Parallel() + + t.Run("batch requests return errors", func(t *testing.T) { + ctx := testutils.Context(t) + rpcError := errors.New("something went wrong") + blockNumResp := "" + blockNum := hexutil.EncodeBig(big.NewInt(42)) + b := []rpc.BatchElem{ + { + Method: "eth_getBlockByNumber", + Args: []interface{}{blockNum, true}, + Result: &types.Block{}, + }, + { + Method: "eth_blockNumber", + Result: &blockNumResp, + }, + } + + mockRpc := newMockRpc(t) + mockRpc.On("BatchCallContext", mock.Anything, b).Run(func(args mock.Arguments) { + reqs := args.Get(1).([]rpc.BatchElem) + for i := 0; i < len(reqs); i++ { + elem := &reqs[i] + elem.Error = rpcError + } + }).Return(nil).Once() + + client := client.NewChainClientWithMockedRpc(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID, mockRpc) + err := client.Dial(ctx) + require.NoError(t, err) + + err = client.BatchCallContext(ctx, b) + require.NoError(t, err) + for _, elem := range b { + require.ErrorIs(t, rpcError, elem.Error) + } + }) +} diff --git a/core/chains/evm/client/chain_id_sub_test.go b/core/chains/evm/client/chain_id_sub_test.go index c71b45c489e..f959376acca 100644 --- a/core/chains/evm/client/chain_id_sub_test.go +++ b/core/chains/evm/client/chain_id_sub_test.go @@ -11,22 +11,6 @@ import ( ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) -type mockSubscription struct { - unsubscribed bool - Errors chan error -} - -func newMockSubscription() *mockSubscription { - return &mockSubscription{Errors: make(chan error)} -} - -func (mes *mockSubscription) Err() <-chan error { return mes.Errors } - -func (mes *mockSubscription) Unsubscribe() { - mes.unsubscribed = true - close(mes.Errors) -} - func TestChainIDSubForwarder(t *testing.T) { t.Parallel() @@ -37,7 +21,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) forwarder.Unsubscribe() @@ -54,7 +38,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) sub.Errors <- errors.New("boo") @@ -72,7 +56,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) forwarder.srcCh <- &evmtypes.Head{} @@ -90,7 +74,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() errIn := errors.New("foo") errOut := forwarder.start(sub, errIn) assert.Equal(t, errIn, errOut) @@ -101,7 +85,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index c2f60e13f55..467195e11e8 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -127,6 +127,32 @@ func NewChainClientWithEmptyNode( return c } +func NewChainClientWithMockedRpc( + t *testing.T, + selectionMode string, + leaseDuration time.Duration, + noNewHeadsThreshold time.Duration, + chainID *big.Int, + rpc RPCCLient, +) Client { + + lggr := logger.Test(t) + + var chainType commonconfig.ChainType + + cfg := TestNodePoolConfig{ + NodeSelectionMode: NodeSelectionMode_RoundRobin, + } + parsed, _ := url.ParseRequestURI("ws://test") + + n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCCLient]( + cfg, noNewHeadsThreshold, lggr, *parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") + primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCCLient]{n} + c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType) + t.Cleanup(c.Close) + return c +} + type TestableSendOnlyNode interface { SendOnlyNode SetEthClient(newBatchSender BatchSender, newSender TxSender) @@ -137,3 +163,19 @@ const HeadResult = `{"difficulty":"0xf3a00","extraData":"0xd88301050384676574688 func IsDialed(s SendOnlyNode) bool { return s.(*sendOnlyNode).dialed } + +type mockSubscription struct { + unsubscribed bool + Errors chan error +} + +func NewMockSubscription() *mockSubscription { + return &mockSubscription{Errors: make(chan error)} +} + +func (mes *mockSubscription) Err() <-chan error { return mes.Errors } + +func (mes *mockSubscription) Unsubscribe() { + mes.unsubscribed = true + close(mes.Errors) +} diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go new file mode 100644 index 00000000000..186fd2534e3 --- /dev/null +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -0,0 +1,1028 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + big "math/big" + + assets "github.com/smartcontractkit/chainlink-common/pkg/assets" + + common "github.com/ethereum/go-ethereum/common" + + commontypes "github.com/smartcontractkit/chainlink/v2/common/types" + + context "context" + + coretypes "github.com/ethereum/go-ethereum/core/types" + + ethereum "github.com/ethereum/go-ethereum" + + evmassets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + + mock "github.com/stretchr/testify/mock" + + rpc "github.com/ethereum/go-ethereum/rpc" + + types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" +) + +// RPCCLient is an autogenerated mock type for the RPCCLient type +type RPCCLient struct { + mock.Mock +} + +// BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber +func (_m *RPCCLient) BalanceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (*big.Int, error) { + ret := _m.Called(ctx, accountAddress, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for BalanceAt") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (*big.Int, error)); ok { + return rf(ctx, accountAddress, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) *big.Int); ok { + r0 = rf(ctx, accountAddress, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, accountAddress, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BatchCallContext provides a mock function with given fields: ctx, b +func (_m *RPCCLient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { + ret := _m.Called(ctx, b) + + if len(ret) == 0 { + panic("no return value specified for BatchCallContext") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []rpc.BatchElem) error); ok { + r0 = rf(ctx, b) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// BlockByHash provides a mock function with given fields: ctx, hash +func (_m *RPCCLient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Head, error) { + ret := _m.Called(ctx, hash) + + if len(ret) == 0 { + panic("no return value specified for BlockByHash") + } + + var r0 *types.Head + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Head, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Head); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Head) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByHashGeth provides a mock function with given fields: ctx, hash +func (_m *RPCCLient) BlockByHashGeth(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { + ret := _m.Called(ctx, hash) + + if len(ret) == 0 { + panic("no return value specified for BlockByHashGeth") + } + + var r0 *coretypes.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Block, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Block); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Block) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByNumber provides a mock function with given fields: ctx, number +func (_m *RPCCLient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Head, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for BlockByNumber") + } + + var r0 *types.Head + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Head, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Head); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Head) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByNumberGeth provides a mock function with given fields: ctx, number +func (_m *RPCCLient) BlockByNumberGeth(ctx context.Context, number *big.Int) (*coretypes.Block, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for BlockByNumberGeth") + } + + var r0 *coretypes.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Block, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Block); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Block) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CallContext provides a mock function with given fields: ctx, result, method, args +func (_m *RPCCLient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + var _ca []interface{} + _ca = append(_ca, ctx, result, method) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CallContext") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { + r0 = rf(ctx, result, method, args...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CallContract provides a mock function with given fields: ctx, msg, blockNumber +func (_m *RPCCLient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, msg, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for CallContract") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) ([]byte, error)); ok { + return rf(ctx, msg, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) []byte); ok { + r0 = rf(ctx, msg, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}, *big.Int) error); ok { + r1 = rf(ctx, msg, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ChainID provides a mock function with given fields: ctx +func (_m *RPCCLient) ChainID(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ClientVersion provides a mock function with given fields: _a0 +func (_m *RPCCLient) ClientVersion(_a0 context.Context) (string, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for ClientVersion") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Close provides a mock function with given fields: +func (_m *RPCCLient) Close() { + _m.Called() +} + +// CodeAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *RPCCLient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, account, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for CodeAt") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]byte, error)); ok { + return rf(ctx, account, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { + r0 = rf(ctx, account, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, account, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Dial provides a mock function with given fields: ctx +func (_m *RPCCLient) Dial(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Dial") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DialHTTP provides a mock function with given fields: +func (_m *RPCCLient) DialHTTP() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for DialHTTP") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DisconnectAll provides a mock function with given fields: +func (_m *RPCCLient) DisconnectAll() { + _m.Called() +} + +// EstimateGas provides a mock function with given fields: ctx, call +func (_m *RPCCLient) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { + ret := _m.Called(ctx, call) + + if len(ret) == 0 { + panic("no return value specified for EstimateGas") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}) (uint64, error)); ok { + return rf(ctx, call) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}) uint64); ok { + r0 = rf(ctx, call) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { + r1 = rf(ctx, call) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FilterEvents provides a mock function with given fields: ctx, query +func (_m *RPCCLient) FilterEvents(ctx context.Context, query ethereum.FilterQuery) ([]coretypes.Log, error) { + ret := _m.Called(ctx, query) + + if len(ret) == 0 { + panic("no return value specified for FilterEvents") + } + + var r0 []coretypes.Log + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)); ok { + return rf(ctx, query) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []coretypes.Log); ok { + r0 = rf(ctx, query) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]coretypes.Log) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery) error); ok { + r1 = rf(ctx, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HeaderByHash provides a mock function with given fields: ctx, h +func (_m *RPCCLient) HeaderByHash(ctx context.Context, h common.Hash) (*coretypes.Header, error) { + ret := _m.Called(ctx, h) + + if len(ret) == 0 { + panic("no return value specified for HeaderByHash") + } + + var r0 *coretypes.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Header, error)); ok { + return rf(ctx, h) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Header); ok { + r0 = rf(ctx, h) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, h) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HeaderByNumber provides a mock function with given fields: ctx, n +func (_m *RPCCLient) HeaderByNumber(ctx context.Context, n *big.Int) (*coretypes.Header, error) { + ret := _m.Called(ctx, n) + + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + + var r0 *coretypes.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Header, error)); ok { + return rf(ctx, n) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Header); ok { + r0 = rf(ctx, n) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, n) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress +func (_m *RPCCLient) LINKBalance(ctx context.Context, accountAddress common.Address, linkAddress common.Address) (*assets.Link, error) { + ret := _m.Called(ctx, accountAddress, linkAddress) + + if len(ret) == 0 { + panic("no return value specified for LINKBalance") + } + + var r0 *assets.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*assets.Link, error)); ok { + return rf(ctx, accountAddress, linkAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *assets.Link); ok { + r0 = rf(ctx, accountAddress, linkAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*assets.Link) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { + r1 = rf(ctx, accountAddress, linkAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LatestBlockHeight provides a mock function with given fields: _a0 +func (_m *RPCCLient) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for LatestBlockHeight") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingCallContract provides a mock function with given fields: ctx, msg +func (_m *RPCCLient) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { + ret := _m.Called(ctx, msg) + + if len(ret) == 0 { + panic("no return value specified for PendingCallContract") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}) ([]byte, error)); ok { + return rf(ctx, msg) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}) []byte); ok { + r0 = rf(ctx, msg) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { + r1 = rf(ctx, msg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingCodeAt provides a mock function with given fields: ctx, account +func (_m *RPCCLient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + ret := _m.Called(ctx, account) + + if len(ret) == 0 { + panic("no return value specified for PendingCodeAt") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) ([]byte, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) []byte); ok { + r0 = rf(ctx, account) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, account) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingSequenceAt provides a mock function with given fields: ctx, addr +func (_m *RPCCLient) PendingSequenceAt(ctx context.Context, addr common.Address) (types.Nonce, error) { + ret := _m.Called(ctx, addr) + + if len(ret) == 0 { + panic("no return value specified for PendingSequenceAt") + } + + var r0 types.Nonce + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) (types.Nonce, error)); ok { + return rf(ctx, addr) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) types.Nonce); ok { + r0 = rf(ctx, addr) + } else { + r0 = ret.Get(0).(types.Nonce) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, addr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress +func (_m *RPCCLient) SendEmptyTransaction(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address) (string, error) { + ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + + if len(ret) == 0 { + panic("no return value specified for SendEmptyTransaction") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) (string, error)); ok { + return rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) string); ok { + r0 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) error); ok { + r1 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SendTransaction provides a mock function with given fields: ctx, tx +func (_m *RPCCLient) SendTransaction(ctx context.Context, tx *coretypes.Transaction) error { + ret := _m.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for SendTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber +func (_m *RPCCLient) SequenceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (types.Nonce, error) { + ret := _m.Called(ctx, accountAddress, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for SequenceAt") + } + + var r0 types.Nonce + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (types.Nonce, error)); ok { + return rf(ctx, accountAddress, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) types.Nonce); ok { + r0 = rf(ctx, accountAddress, blockNumber) + } else { + r0 = ret.Get(0).(types.Nonce) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, accountAddress, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SetAliveLoopSub provides a mock function with given fields: _a0 +func (_m *RPCCLient) SetAliveLoopSub(_a0 commontypes.Subscription) { + _m.Called(_a0) +} + +// SimulateTransaction provides a mock function with given fields: ctx, tx +func (_m *RPCCLient) SimulateTransaction(ctx context.Context, tx *coretypes.Transaction) error { + ret := _m.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for SimulateTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Subscribe provides a mock function with given fields: ctx, channel, args +func (_m *RPCCLient) Subscribe(ctx context.Context, channel chan<- *types.Head, args ...interface{}) (commontypes.Subscription, error) { + var _ca []interface{} + _ca = append(_ca, ctx, channel) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + + var r0 commontypes.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head, ...interface{}) (commontypes.Subscription, error)); ok { + return rf(ctx, channel, args...) + } + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head, ...interface{}) commontypes.Subscription); ok { + r0 = rf(ctx, channel, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(commontypes.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head, ...interface{}) error); ok { + r1 = rf(ctx, channel, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch +func (_m *RPCCLient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { + ret := _m.Called(ctx, q, ch) + + if len(ret) == 0 { + panic("no return value specified for SubscribeFilterLogs") + } + + var r0 ethereum.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { + r0 = rf(ctx, q, ch) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ethereum.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { + r1 = rf(ctx, q, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribersCount provides a mock function with given fields: +func (_m *RPCCLient) SubscribersCount() int32 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for SubscribersCount") + } + + var r0 int32 + if rf, ok := ret.Get(0).(func() int32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int32) + } + + return r0 +} + +// SuggestGasPrice provides a mock function with given fields: ctx +func (_m *RPCCLient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SuggestGasPrice") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuggestGasTipCap provides a mock function with given fields: ctx +func (_m *RPCCLient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SuggestGasTipCap") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress +func (_m *RPCCLient) TokenBalance(ctx context.Context, accountAddress common.Address, tokenAddress common.Address) (*big.Int, error) { + ret := _m.Called(ctx, accountAddress, tokenAddress) + + if len(ret) == 0 { + panic("no return value specified for TokenBalance") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*big.Int, error)); ok { + return rf(ctx, accountAddress, tokenAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *big.Int); ok { + r0 = rf(ctx, accountAddress, tokenAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { + r1 = rf(ctx, accountAddress, tokenAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionByHash provides a mock function with given fields: ctx, txHash +func (_m *RPCCLient) TransactionByHash(ctx context.Context, txHash common.Hash) (*coretypes.Transaction, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionByHash") + } + + var r0 *coretypes.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Transaction, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Transaction); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionReceipt provides a mock function with given fields: ctx, txHash +func (_m *RPCCLient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionReceipt") + } + + var r0 *types.Receipt + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Receipt, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Receipt); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Receipt) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionReceiptGeth provides a mock function with given fields: ctx, txHash +func (_m *RPCCLient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionReceiptGeth") + } + + var r0 *coretypes.Receipt + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Receipt, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Receipt); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Receipt) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: +func (_m *RPCCLient) UnsubscribeAllExceptAliveLoop() { + _m.Called() +} + +// NewRPCCLient creates a new instance of RPCCLient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRPCCLient(t interface { + mock.TestingT + Cleanup(func()) +}) *RPCCLient { + mock := &RPCCLient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 54656cf1d3e..29c5d8fbb8b 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -42,6 +42,7 @@ type RPCCLient interface { *evmtypes.Receipt, *assets.Wei, *evmtypes.Head, + rpc.BatchElem, ] BlockByHashGeth(ctx context.Context, hash common.Hash) (b *types.Block, err error) BlockByNumberGeth(ctx context.Context, number *big.Int) (b *types.Block, err error) @@ -315,24 +316,20 @@ func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method return err } -func (r *rpcClient) BatchCallContext(ctx context.Context, b []any) error { +func (r *rpcClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) if err != nil { return err } - batch := make([]rpc.BatchElem, len(b)) - for i, arg := range b { - batch[i] = arg.(rpc.BatchElem) - } defer cancel() lggr := r.newRqLggr().With("nBatchElems", len(b), "batchElems", b) lggr.Trace("RPC call: evmclient.Client#BatchCallContext") start := time.Now() if http != nil { - err = r.wrapHTTP(http.rpc.BatchCallContext(ctx, batch)) + err = r.wrapHTTP(http.rpc.BatchCallContext(ctx, b)) } else { - err = r.wrapWS(ws.rpc.BatchCallContext(ctx, batch)) + err = r.wrapWS(ws.rpc.BatchCallContext(ctx, b)) } duration := time.Since(start) diff --git a/core/chains/evm/client/send_only_node_test.go b/core/chains/evm/client/send_only_node_test.go index c2fdad06ec1..61db09a448c 100644 --- a/core/chains/evm/client/send_only_node_test.go +++ b/core/chains/evm/client/send_only_node_test.go @@ -20,7 +20,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -37,7 +36,7 @@ func TestNewSendOnlyNode(t *testing.T) { name := "TestNewSendOnlyNode" chainID := testutils.NewRandomEVMChainID() - node := evmclient.NewSendOnlyNode(lggr, *url, name, chainID) + node := client.NewSendOnlyNode(lggr, *url, name, chainID) assert.NotNil(t, node) // Must contain name & url with redacted password @@ -54,7 +53,7 @@ func TestStartSendOnlyNode(t *testing.T) { r := chainIDResp{chainID.Int64(), nil} url := r.newHTTPServer(t) lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - s := evmclient.NewSendOnlyNode(lggr, *url, t.Name(), chainID) + s := client.NewSendOnlyNode(lggr, *url, t.Name(), chainID) defer func() { assert.NoError(t, s.Close()) }() err := s.Start(testutils.Context(t)) assert.NoError(t, err) // No errors expected @@ -67,7 +66,7 @@ func TestStartSendOnlyNode(t *testing.T) { chainID := testutils.FixtureChainID r := chainIDResp{chainID.Int64(), nil} url := r.newHTTPServer(t) - s := evmclient.NewSendOnlyNode(lggr, *url, t.Name(), testutils.FixtureChainID) + s := client.NewSendOnlyNode(lggr, *url, t.Name(), testutils.FixtureChainID) defer func() { assert.NoError(t, s.Close()) }() err := s.Start(testutils.Context(t)) @@ -80,7 +79,7 @@ func TestStartSendOnlyNode(t *testing.T) { t.Parallel() lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) invalidURL := url.URL{Scheme: "some rubbish", Host: "not a valid host"} - s := evmclient.NewSendOnlyNode(lggr, invalidURL, t.Name(), testutils.FixtureChainID) + s := client.NewSendOnlyNode(lggr, invalidURL, t.Name(), testutils.FixtureChainID) defer func() { assert.NoError(t, s.Close()) }() err := s.Start(testutils.Context(t)) @@ -112,10 +111,10 @@ func TestSendTransaction(t *testing.T) { chainID := testutils.FixtureChainID lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) url := testutils.MustParseURL(t, "http://place.holder") - s := evmclient.NewSendOnlyNode(lggr, + s := client.NewSendOnlyNode(lggr, *url, t.Name(), - testutils.FixtureChainID).(evmclient.TestableSendOnlyNode) + testutils.FixtureChainID).(client.TestableSendOnlyNode) require.NotNil(t, s) signedTx := createSignedTx(t, chainID, 1, []byte{1, 2, 3}) @@ -139,10 +138,10 @@ func TestBatchCallContext(t *testing.T) { lggr := logger.Test(t) chainID := testutils.FixtureChainID url := testutils.MustParseURL(t, "http://place.holder") - s := evmclient.NewSendOnlyNode( + s := client.NewSendOnlyNode( lggr, *url, "TestBatchCallContext", - chainID).(evmclient.TestableSendOnlyNode) + chainID).(client.TestableSendOnlyNode) blockNum := hexutil.EncodeBig(big.NewInt(42)) req := []rpc.BatchElem{