diff --git a/.changeset/stupid-worlds-marry.md b/.changeset/stupid-worlds-marry.md new file mode 100644 index 000000000..2e6571774 --- /dev/null +++ b/.changeset/stupid-worlds-marry.md @@ -0,0 +1,5 @@ +--- +"@smartcontractkit/mcms": minor +--- + +add timelock converter and inspector helpers diff --git a/.mockery.yaml b/.mockery.yaml index 959cf9e1f..d4ad3d309 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -32,6 +32,7 @@ packages: config: dir: "./sdk/ton/mocks" filename: "api.go" + github.com/smartcontractkit/mcms/chainwrappers: github.com/smartcontractkit/mcms/sdk: github.com/smartcontractkit/mcms/sdk/evm: github.com/smartcontractkit/mcms/sdk/evm/bindings: diff --git a/.tool-versions b/.tool-versions index d480b4036..67912758f 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,4 @@ -golang 1.24.4 +golang 1.25.3 golangci-lint 2.1.6 mockery 2.53.5 nodejs 20.16.0 diff --git a/chainwrappers/chainaccessor.go b/chainwrappers/chainaccessor.go new file mode 100644 index 000000000..112e549f3 --- /dev/null +++ b/chainwrappers/chainaccessor.go @@ -0,0 +1,18 @@ +package chainwrappers + +import ( + aptoslib "github.com/aptos-labs/aptos-go-sdk" + "github.com/block-vision/sui-go-sdk/sui" + solrpc "github.com/gagliardetto/solana-go/rpc" + + evmsdk "github.com/smartcontractkit/mcms/sdk/evm" + suisuisdk "github.com/smartcontractkit/mcms/sdk/sui" +) + +type ChainAccessor interface { + Selectors() []uint64 + EVMClient(selector uint64) (evmsdk.ContractDeployBackend, bool) + SolanaClient(selector uint64) (*solrpc.Client, bool) + AptosClient(selector uint64) (aptoslib.AptosRpcClient, bool) + SuiClient(selector uint64) (sui.ISuiAPI, suisuisdk.SuiSigner, bool) +} diff --git a/chainwrappers/converters.go b/chainwrappers/converters.go new file mode 100644 index 000000000..6cabb7c44 --- /dev/null +++ b/chainwrappers/converters.go @@ -0,0 +1,40 @@ +package chainwrappers + +import ( + "fmt" + + chainsel "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/mcms/sdk" + "github.com/smartcontractkit/mcms/sdk/aptos" + "github.com/smartcontractkit/mcms/sdk/evm" + "github.com/smartcontractkit/mcms/sdk/solana" + "github.com/smartcontractkit/mcms/types" +) + +// BuildConverters constructs a map of chain selectors to their respective timelock converters based on the provided timelock proposal. +func BuildConverters(chainMetadata map[types.ChainSelector]types.ChainMetadata) (map[types.ChainSelector]sdk.TimelockConverter, error) { + converters := make(map[types.ChainSelector]sdk.TimelockConverter) + for chainMeta := range chainMetadata { + fam, err := types.GetChainSelectorFamily(chainMeta) + if err != nil { + return nil, fmt.Errorf("error getting chain family: %w", err) + } + + var converter sdk.TimelockConverter + switch fam { + case chainsel.FamilyEVM: + converter = evm.NewTimelockConverter() + case chainsel.FamilySolana: + converter = solana.NewTimelockConverter() + case chainsel.FamilyAptos: + converter = aptos.NewTimelockConverter() + default: + return nil, fmt.Errorf("unsupported chain family %s", fam) + } + + converters[chainMeta] = converter + } + + return converters, nil +} diff --git a/chainwrappers/converters_test.go b/chainwrappers/converters_test.go new file mode 100644 index 000000000..01dd72997 --- /dev/null +++ b/chainwrappers/converters_test.go @@ -0,0 +1,75 @@ +package chainwrappers + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/mcms/internal/testutils/chaintest" + "github.com/smartcontractkit/mcms/sdk/aptos" + "github.com/smartcontractkit/mcms/sdk/evm" + "github.com/smartcontractkit/mcms/sdk/solana" + "github.com/smartcontractkit/mcms/types" +) + +func TestBuildConverters(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + metadata map[types.ChainSelector]types.ChainMetadata + expectTypes map[types.ChainSelector]any + expectErr string + }{ + { + name: "supported families", + metadata: map[types.ChainSelector]types.ChainMetadata{ + chaintest.Chain2Selector: {}, + chaintest.Chain4Selector: {}, + chaintest.Chain5Selector: {}, + }, + expectTypes: map[types.ChainSelector]any{ + chaintest.Chain2Selector: (*evm.TimelockConverter)(nil), + chaintest.Chain4Selector: (*solana.TimelockConverter)(nil), + chaintest.Chain5Selector: (*aptos.TimelockConverter)(nil), + }, + }, + { + name: "unsupported family", + metadata: map[types.ChainSelector]types.ChainMetadata{ + chaintest.Chain6Selector: {}, + }, + expectErr: "unsupported chain family", + }, + { + name: "invalid selector", + metadata: map[types.ChainSelector]types.ChainMetadata{ + chaintest.ChainInvalidSelector: {}, + }, + expectErr: "error getting chain family", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + converters, err := BuildConverters(tc.metadata) + + if tc.expectErr != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectErr) + + return + } + + require.NoError(t, err) + require.Len(t, converters, len(tc.expectTypes)) + for selector, expectedType := range tc.expectTypes { + converter, ok := converters[selector] + require.True(t, ok) + require.IsType(t, expectedType, converter) + } + }) + } +} diff --git a/chainwrappers/inspectors.go b/chainwrappers/inspectors.go new file mode 100644 index 000000000..c15ef8033 --- /dev/null +++ b/chainwrappers/inspectors.go @@ -0,0 +1,90 @@ +package chainwrappers + +import ( + "fmt" + + chainsel "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/mcms/sdk" + "github.com/smartcontractkit/mcms/sdk/aptos" + "github.com/smartcontractkit/mcms/sdk/evm" + "github.com/smartcontractkit/mcms/sdk/solana" + sdkSui "github.com/smartcontractkit/mcms/sdk/sui" + "github.com/smartcontractkit/mcms/types" +) + +// BuildInspectors gets a map of inspectors for the given chain metadata and chain clients +func BuildInspectors( + chains ChainAccessor, + chainMetadata map[types.ChainSelector]types.ChainMetadata, + action types.TimelockAction) (map[types.ChainSelector]sdk.Inspector, error) { + inspectors := map[types.ChainSelector]sdk.Inspector{} + for chainSelector, metadata := range chainMetadata { + inspector, err := BuildInspector(chains, chainSelector, action, metadata) + if err != nil { + return nil, err + } + inspectors[chainSelector] = inspector + } + + return inspectors, nil +} + +// BuildInspector constructs a chain-family-specific Inspector from ChainAccessor plus metadata. +func BuildInspector( + chains ChainAccessor, + selector types.ChainSelector, + action types.TimelockAction, + metadata types.ChainMetadata, +) (sdk.Inspector, error) { + if chains == nil { + return nil, fmt.Errorf("chain access is required") + } + + family, err := types.GetChainSelectorFamily(selector) + if err != nil { + return nil, fmt.Errorf("error getting chain family: %w", err) + } + + rawSelector := uint64(selector) + switch family { + case chainsel.FamilyEVM: + client, ok := chains.EVMClient(rawSelector) + if !ok { + return nil, fmt.Errorf("missing EVM chain client for selector %d", rawSelector) + } + + return evm.NewInspector(client), nil + case chainsel.FamilySolana: + client, ok := chains.SolanaClient(rawSelector) + if !ok { + return nil, fmt.Errorf("missing Solana chain client for selector %d", rawSelector) + } + + return solana.NewInspector(client), nil + case chainsel.FamilyAptos: + client, ok := chains.AptosClient(rawSelector) + if !ok { + return nil, fmt.Errorf("missing Aptos chain client for selector %d", rawSelector) + } + role, err := aptos.AptosRoleFromAction(action) + if err != nil { + return nil, fmt.Errorf("error determining aptos role: %w", err) + } + + return aptos.NewInspector(client, role), nil + case chainsel.FamilySui: + client, signer, ok := chains.SuiClient(rawSelector) + if !ok { + return nil, fmt.Errorf("missing Sui chain client for selector %d", rawSelector) + } + suiMetadata, err := sdkSui.SuiMetadata(metadata) + if err != nil { + return nil, fmt.Errorf("error parsing sui metadata: %w", err) + } + + return sdkSui.NewInspector(client, signer, suiMetadata.McmsPackageID, suiMetadata.Role) + default: + return nil, fmt.Errorf("unsupported chain family %s", family) + } +} diff --git a/chainwrappers/inspectors_test.go b/chainwrappers/inspectors_test.go new file mode 100644 index 000000000..72af7b5bd --- /dev/null +++ b/chainwrappers/inspectors_test.go @@ -0,0 +1,78 @@ +package chainwrappers + +import ( + "testing" + + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/mcms/chainwrappers/mocks" + + mcmsTypes "github.com/smartcontractkit/mcms/types" +) + +func TestMCMInspectorBuilder_BuildInspectors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + chainMetadata map[mcmsTypes.ChainSelector]mcmsTypes.ChainMetadata + chainAccess *mocks.ChainAccessor + setup func(access *mocks.ChainAccessor) + expectErr bool + errContains string + expectedInspectorsCount int + }{ + { + name: "empty input", + chainMetadata: map[mcmsTypes.ChainSelector]mcmsTypes.ChainMetadata{}, + chainAccess: mocks.NewChainAccessor(t), + expectErr: false, + }, + { + name: "missing chain client", + chainMetadata: map[mcmsTypes.ChainSelector]mcmsTypes.ChainMetadata{ + 1: {MCMAddress: "0xabc", StartingOpCount: 0}, + }, + chainAccess: mocks.NewChainAccessor(t), + expectErr: true, + errContains: "error getting chain family: chain family not found for selector 1", + }, + { + name: "valid input", + chainMetadata: map[mcmsTypes.ChainSelector]mcmsTypes.ChainMetadata{ + mcmsTypes.ChainSelector(chainsel.ETHEREUM_TESTNET_SEPOLIA.Selector): {MCMAddress: "0xabc", StartingOpCount: 0}, + mcmsTypes.ChainSelector(chainsel.SOLANA_DEVNET.Selector): {MCMAddress: "0xabc", StartingOpCount: 0}, + }, + chainAccess: mocks.NewChainAccessor(t), + expectErr: false, + setup: func(access *mocks.ChainAccessor) { + access.EXPECT().EVMClient(mock.Anything).Return(nil, true) + access.EXPECT().SolanaClient(mock.Anything).Return(nil, true) + }, + expectedInspectorsCount: 2, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if tc.chainAccess == nil { + tc.chainAccess = mocks.NewChainAccessor(t) + } + if tc.expectedInspectorsCount > 0 { + tc.setup(tc.chainAccess) + } + + inspectors, err := BuildInspectors(tc.chainAccess, tc.chainMetadata, mcmsTypes.TimelockActionSchedule) + if tc.expectErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errContains) + } else { + require.NoError(t, err) + require.Len(t, inspectors, tc.expectedInspectorsCount) + } + }) + } +} diff --git a/chainwrappers/mocks/chain_accessor.go b/chainwrappers/mocks/chain_accessor.go new file mode 100644 index 000000000..58f1d7be6 --- /dev/null +++ b/chainwrappers/mocks/chain_accessor.go @@ -0,0 +1,332 @@ +// Code generated by mockery v2.53.5. DO NOT EDIT. + +package mocks + +import ( + aptos "github.com/aptos-labs/aptos-go-sdk" + + evm "github.com/smartcontractkit/mcms/sdk/evm" + + mock "github.com/stretchr/testify/mock" + + rpc "github.com/gagliardetto/solana-go/rpc" + + sdksui "github.com/smartcontractkit/mcms/sdk/sui" + + sui "github.com/block-vision/sui-go-sdk/sui" +) + +// ChainAccessor is an autogenerated mock type for the ChainAccessor type +type ChainAccessor struct { + mock.Mock +} + +type ChainAccessor_Expecter struct { + mock *mock.Mock +} + +func (_m *ChainAccessor) EXPECT() *ChainAccessor_Expecter { + return &ChainAccessor_Expecter{mock: &_m.Mock} +} + +// AptosClient provides a mock function with given fields: selector +func (_m *ChainAccessor) AptosClient(selector uint64) (aptos.AptosRpcClient, bool) { + ret := _m.Called(selector) + + if len(ret) == 0 { + panic("no return value specified for AptosClient") + } + + var r0 aptos.AptosRpcClient + var r1 bool + if rf, ok := ret.Get(0).(func(uint64) (aptos.AptosRpcClient, bool)); ok { + return rf(selector) + } + if rf, ok := ret.Get(0).(func(uint64) aptos.AptosRpcClient); ok { + r0 = rf(selector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(aptos.AptosRpcClient) + } + } + + if rf, ok := ret.Get(1).(func(uint64) bool); ok { + r1 = rf(selector) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// ChainAccessor_AptosClient_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AptosClient' +type ChainAccessor_AptosClient_Call struct { + *mock.Call +} + +// AptosClient is a helper method to define mock.On call +// - selector uint64 +func (_e *ChainAccessor_Expecter) AptosClient(selector interface{}) *ChainAccessor_AptosClient_Call { + return &ChainAccessor_AptosClient_Call{Call: _e.mock.On("AptosClient", selector)} +} + +func (_c *ChainAccessor_AptosClient_Call) Run(run func(selector uint64)) *ChainAccessor_AptosClient_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(uint64)) + }) + return _c +} + +func (_c *ChainAccessor_AptosClient_Call) Return(_a0 aptos.AptosRpcClient, _a1 bool) *ChainAccessor_AptosClient_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ChainAccessor_AptosClient_Call) RunAndReturn(run func(uint64) (aptos.AptosRpcClient, bool)) *ChainAccessor_AptosClient_Call { + _c.Call.Return(run) + return _c +} + +// EVMClient provides a mock function with given fields: selector +func (_m *ChainAccessor) EVMClient(selector uint64) (evm.ContractDeployBackend, bool) { + ret := _m.Called(selector) + + if len(ret) == 0 { + panic("no return value specified for EVMClient") + } + + var r0 evm.ContractDeployBackend + var r1 bool + if rf, ok := ret.Get(0).(func(uint64) (evm.ContractDeployBackend, bool)); ok { + return rf(selector) + } + if rf, ok := ret.Get(0).(func(uint64) evm.ContractDeployBackend); ok { + r0 = rf(selector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(evm.ContractDeployBackend) + } + } + + if rf, ok := ret.Get(1).(func(uint64) bool); ok { + r1 = rf(selector) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// ChainAccessor_EVMClient_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVMClient' +type ChainAccessor_EVMClient_Call struct { + *mock.Call +} + +// EVMClient is a helper method to define mock.On call +// - selector uint64 +func (_e *ChainAccessor_Expecter) EVMClient(selector interface{}) *ChainAccessor_EVMClient_Call { + return &ChainAccessor_EVMClient_Call{Call: _e.mock.On("EVMClient", selector)} +} + +func (_c *ChainAccessor_EVMClient_Call) Run(run func(selector uint64)) *ChainAccessor_EVMClient_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(uint64)) + }) + return _c +} + +func (_c *ChainAccessor_EVMClient_Call) Return(_a0 evm.ContractDeployBackend, _a1 bool) *ChainAccessor_EVMClient_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ChainAccessor_EVMClient_Call) RunAndReturn(run func(uint64) (evm.ContractDeployBackend, bool)) *ChainAccessor_EVMClient_Call { + _c.Call.Return(run) + return _c +} + +// Selectors provides a mock function with no fields +func (_m *ChainAccessor) Selectors() []uint64 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Selectors") + } + + var r0 []uint64 + if rf, ok := ret.Get(0).(func() []uint64); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]uint64) + } + } + + return r0 +} + +// ChainAccessor_Selectors_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Selectors' +type ChainAccessor_Selectors_Call struct { + *mock.Call +} + +// Selectors is a helper method to define mock.On call +func (_e *ChainAccessor_Expecter) Selectors() *ChainAccessor_Selectors_Call { + return &ChainAccessor_Selectors_Call{Call: _e.mock.On("Selectors")} +} + +func (_c *ChainAccessor_Selectors_Call) Run(run func()) *ChainAccessor_Selectors_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ChainAccessor_Selectors_Call) Return(_a0 []uint64) *ChainAccessor_Selectors_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ChainAccessor_Selectors_Call) RunAndReturn(run func() []uint64) *ChainAccessor_Selectors_Call { + _c.Call.Return(run) + return _c +} + +// SolanaClient provides a mock function with given fields: selector +func (_m *ChainAccessor) SolanaClient(selector uint64) (*rpc.Client, bool) { + ret := _m.Called(selector) + + if len(ret) == 0 { + panic("no return value specified for SolanaClient") + } + + var r0 *rpc.Client + var r1 bool + if rf, ok := ret.Get(0).(func(uint64) (*rpc.Client, bool)); ok { + return rf(selector) + } + if rf, ok := ret.Get(0).(func(uint64) *rpc.Client); ok { + r0 = rf(selector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*rpc.Client) + } + } + + if rf, ok := ret.Get(1).(func(uint64) bool); ok { + r1 = rf(selector) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// ChainAccessor_SolanaClient_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SolanaClient' +type ChainAccessor_SolanaClient_Call struct { + *mock.Call +} + +// SolanaClient is a helper method to define mock.On call +// - selector uint64 +func (_e *ChainAccessor_Expecter) SolanaClient(selector interface{}) *ChainAccessor_SolanaClient_Call { + return &ChainAccessor_SolanaClient_Call{Call: _e.mock.On("SolanaClient", selector)} +} + +func (_c *ChainAccessor_SolanaClient_Call) Run(run func(selector uint64)) *ChainAccessor_SolanaClient_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(uint64)) + }) + return _c +} + +func (_c *ChainAccessor_SolanaClient_Call) Return(_a0 *rpc.Client, _a1 bool) *ChainAccessor_SolanaClient_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ChainAccessor_SolanaClient_Call) RunAndReturn(run func(uint64) (*rpc.Client, bool)) *ChainAccessor_SolanaClient_Call { + _c.Call.Return(run) + return _c +} + +// SuiClient provides a mock function with given fields: selector +func (_m *ChainAccessor) SuiClient(selector uint64) (sui.ISuiAPI, sdksui.SuiSigner, bool) { + ret := _m.Called(selector) + + if len(ret) == 0 { + panic("no return value specified for SuiClient") + } + + var r0 sui.ISuiAPI + var r1 sdksui.SuiSigner + var r2 bool + if rf, ok := ret.Get(0).(func(uint64) (sui.ISuiAPI, sdksui.SuiSigner, bool)); ok { + return rf(selector) + } + if rf, ok := ret.Get(0).(func(uint64) sui.ISuiAPI); ok { + r0 = rf(selector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(sui.ISuiAPI) + } + } + + if rf, ok := ret.Get(1).(func(uint64) sdksui.SuiSigner); ok { + r1 = rf(selector) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(sdksui.SuiSigner) + } + } + + if rf, ok := ret.Get(2).(func(uint64) bool); ok { + r2 = rf(selector) + } else { + r2 = ret.Get(2).(bool) + } + + return r0, r1, r2 +} + +// ChainAccessor_SuiClient_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuiClient' +type ChainAccessor_SuiClient_Call struct { + *mock.Call +} + +// SuiClient is a helper method to define mock.On call +// - selector uint64 +func (_e *ChainAccessor_Expecter) SuiClient(selector interface{}) *ChainAccessor_SuiClient_Call { + return &ChainAccessor_SuiClient_Call{Call: _e.mock.On("SuiClient", selector)} +} + +func (_c *ChainAccessor_SuiClient_Call) Run(run func(selector uint64)) *ChainAccessor_SuiClient_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(uint64)) + }) + return _c +} + +func (_c *ChainAccessor_SuiClient_Call) Return(_a0 sui.ISuiAPI, _a1 sdksui.SuiSigner, _a2 bool) *ChainAccessor_SuiClient_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *ChainAccessor_SuiClient_Call) RunAndReturn(run func(uint64) (sui.ISuiAPI, sdksui.SuiSigner, bool)) *ChainAccessor_SuiClient_Call { + _c.Call.Return(run) + return _c +} + +// NewChainAccessor creates a new instance of ChainAccessor. 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 NewChainAccessor(t interface { + mock.TestingT + Cleanup(func()) +}) *ChainAccessor { + mock := &ChainAccessor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/docs/docs/contributing/integrating-new-chain-guide.md b/docs/docs/contributing/integrating-new-chain-guide.md index 432faed82..fe8ac5da8 100644 --- a/docs/docs/contributing/integrating-new-chain-guide.md +++ b/docs/docs/contributing/integrating-new-chain-guide.md @@ -43,6 +43,18 @@ All chain family integrations must implement interfaces defined in the `/sdk` fo | `TimelockInspector` | **Required** | Query timelock contract state | [timelock_inspector.go](https://github.com/smartcontractkit/mcms/blob/main/sdk/timelock_inspector.go) | | `TimelockConverter` | **Required** | Convert batch operations to timelock operations | [timelock_converter.go](https://github.com/smartcontractkit/mcms/blob/main/sdk/timelock_converter.go) | +### ChainAccess Registry Adapter + +The MCMS SDK intentionally avoids importing chain-registry implementations (for example, the Chainlink Deployments Framework). Instead, shared tooling must expose the `ChainAccess` interface defined in [`sdk/chainclient.go`](https://github.com/smartcontractkit/mcms/blob/main/sdk/chainclient.go) so inspectors and proposal tooling can fetch RPC clients without pulling in external dependencies. + +Your adapter should: + +- Implement `Selectors() []uint64` and the per-family lookup helpers (`EVMClient`, `SolanaClient`, `AptosClient`, `Sui`) by delegating to your registry. +- Return chain clients that satisfy `bind.ContractBackend`/`bind.DeployBackend` for EVM, `*solrpc.Client` for Solana, `aptoslib.AptosRpcClient` for Aptos, and `(sui.ISuiAPI, SuiSigner)` for Sui, etc. +- Live in the repository that already depends on your registry (e.g., CLDF or deployment tooling) so `mcms` itself stays agnostic. + +This boundary keeps MCMS reusable across environments while still allowing downstream systems to map their chain catalogs into MCMS inspectors. + ## Required Interfaces ### Executor Interface @@ -360,6 +372,7 @@ Use the `/sdk/errors/` package for standardized error handling: Each interface implementation needs a corresponding `_test.go` file with comprehensive coverage (>80%). Test all public methods with both success and failure cases using table-driven tests. Mock external dependencies (RPC clients, contracts) in `sdk//mocks/`. **Test Examples:** + - [EVM Tests](https://github.com/smartcontractkit/mcms/blob/main/sdk/evm/executor_test.go) | [Mock Examples](https://github.com/smartcontractkit/mcms/tree/main/sdk/evm/mocks) - [Solana Tests](https://github.com/smartcontractkit/mcms/blob/main/sdk/solana/encoder_test.go) | [Mocks](https://github.com/smartcontractkit/mcms/tree/main/sdk/solana/mocks) - [Aptos Tests](https://github.com/smartcontractkit/mcms/blob/main/sdk/aptos/inspector_test.go) | [Mocks](https://github.com/smartcontractkit/mcms/tree/main/sdk/aptos/mocks) @@ -368,19 +381,20 @@ Each interface implementation needs a corresponding `_test.go` file with compreh Create test suite under `/e2e/tests//` covering: -| Test Category | Example | Key Coverage | -|---------------|---------|--------------| -| **Config Management** | [solana/set_config.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/set_config.go) | Set/update config, retrieve and verify, clearRoot flag | -| **Root Operations** | [solana/set_root.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/set_root.go) | Set root with signatures, quorum requirements, expiration | -| **Operation Execution** | [solana/execute.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/execute.go) | Execute with valid proof, verify effects, test invalid proofs | -| **Contract Inspection** | [solana/inspection.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/inspection.go) | Query config, op count, root, metadata | -| **Simulation** (optional) | [solana/simulator.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/simulator.go) | Simulate valid/invalid ops, verify no state changes | -| **Timelock Conversion** (optional) | [solana/timelock_converter.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/timelock_converter.go) | Convert batch to timelock ops, verify IDs and actions | -| **Timelock Execution** (optional) | [solana/timelock_execution.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/timelock_execution.go) | Schedule with delay, execute after delay, predecessors | -| **Timelock Inspection** (optional) | [solana/timelock_inspection.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/timelock_inspection.go) | Query roles, operation status, minimum delay | -| **Timelock Cancellation** (optional) | [aptos/timelock_cancel.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/aptos/timelock_cancel.go) | Cancel pending ops, verify cancellation | +| Test Category | Example | Key Coverage | +|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| **Config Management** | [solana/set_config.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/set_config.go) | Set/update config, retrieve and verify, clearRoot flag | +| **Root Operations** | [solana/set_root.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/set_root.go) | Set root with signatures, quorum requirements, expiration | +| **Operation Execution** | [solana/execute.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/execute.go) | Execute with valid proof, verify effects, test invalid proofs | +| **Contract Inspection** | [solana/inspection.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/inspection.go) | Query config, op count, root, metadata | +| **Simulation** (optional) | [solana/simulator.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/simulator.go) | Simulate valid/invalid ops, verify no state changes | +| **Timelock Conversion** (optional) | [solana/timelock_converter.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/timelock_converter.go) | Convert batch to timelock ops, verify IDs and actions | +| **Timelock Execution** (optional) | [solana/timelock_execution.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/timelock_execution.go) | Schedule with delay, execute after delay, predecessors | +| **Timelock Inspection** (optional) | [solana/timelock_inspection.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/solana/timelock_inspection.go) | Query roles, operation status, minimum delay | +| **Timelock Cancellation** (optional) | [aptos/timelock_cancel.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/aptos/timelock_cancel.go) | Cancel pending ops, verify cancellation | **Test Suite Setup:** + 1. Create `e2e/config..toml` ([example](https://github.com/smartcontractkit/mcms/blob/main/e2e/config.evm.toml)) 2. Update [e2e/tests/setup.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/setup.go) with blockchain node and RPC clients 3. Add suite to [e2e/tests/runner_test.go](https://github.com/smartcontractkit/mcms/blob/main/e2e/tests/runner_test.go) diff --git a/errors.go b/errors.go index 2ce62bd39..a827b8c5e 100644 --- a/errors.go +++ b/errors.go @@ -109,7 +109,7 @@ func NewQuorumNotReachedError(sel types.ChainSelector) *QuorumNotReachedError { return &QuorumNotReachedError{ChainSelector: sel} } -func (e *QuorumNotReachedError) Error() string { +func (e QuorumNotReachedError) Error() string { return fmt.Sprintf("quorum not reached for chain %d", e.ChainSelector) } diff --git a/sdk/aptos/aptos_helpers.go b/sdk/aptos/aptos_helpers.go new file mode 100644 index 000000000..5757a6d21 --- /dev/null +++ b/sdk/aptos/aptos_helpers.go @@ -0,0 +1,20 @@ +package aptos + +import ( + "errors" + + "github.com/smartcontractkit/mcms/types" +) + +func AptosRoleFromAction(action types.TimelockAction) (TimelockRole, error) { + switch action { + case types.TimelockActionBypass: + return TimelockRoleBypasser, nil + case types.TimelockActionSchedule: + return TimelockRoleProposer, nil + case types.TimelockActionCancel: + return TimelockRoleCanceller, nil + default: + return 0, errors.New("unknown timelock action") + } +} diff --git a/sdk/aptos/aptos_helpers_test.go b/sdk/aptos/aptos_helpers_test.go new file mode 100644 index 000000000..8542d7706 --- /dev/null +++ b/sdk/aptos/aptos_helpers_test.go @@ -0,0 +1,63 @@ +package aptos + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + mcmsTypes "github.com/smartcontractkit/mcms/types" +) + +func TestAptosRoleFromAction(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + action mcmsTypes.TimelockAction + expectedRole TimelockRole + expectError bool + }{ + { + name: "bypass action returns bypasser role", + action: mcmsTypes.TimelockActionBypass, + expectedRole: TimelockRoleBypasser, + expectError: false, + }, + { + name: "schedule action returns proposer role", + action: mcmsTypes.TimelockActionSchedule, + expectedRole: TimelockRoleProposer, + expectError: false, + }, + { + name: "cancel action returns canceller role", + action: mcmsTypes.TimelockActionCancel, + expectedRole: TimelockRoleCanceller, + expectError: false, + }, + { + name: "unknown action returns error", + action: mcmsTypes.TimelockAction("unknown"), + expectedRole: 0, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + role, err := AptosRoleFromAction(tt.action) + + if tt.expectError { + require.Error(t, err) + assert.Equal(t, "unknown timelock action", err.Error()) + assert.Equal(t, tt.expectedRole, role) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expectedRole, role) + } + }) + } +} diff --git a/sdk/aptos/configurer.go b/sdk/aptos/configurer.go index b54c5a39c..46d6e3e48 100644 --- a/sdk/aptos/configurer.go +++ b/sdk/aptos/configurer.go @@ -12,7 +12,6 @@ import ( "github.com/smartcontractkit/chainlink-aptos/bindings/mcms" "github.com/smartcontractkit/mcms/sdk" - "github.com/smartcontractkit/mcms/sdk/evm" "github.com/smartcontractkit/mcms/types" ) @@ -68,7 +67,7 @@ func (c Configurer) SetConfig(ctx context.Context, mcmsAddr string, cfg *types.C mcmsBinding := c.bindingFn(mcmsAddress, c.client) opts := &bind.TransactOpts{Signer: c.auth} - groupQuorum, groupParents, signerAddresses, signerGroups, err := evm.ExtractSetConfigInputs(cfg) + groupQuorum, groupParents, signerAddresses, signerGroups, err := sdk.ExtractSetConfigInputs(cfg) if err != nil { return types.TransactionResult{}, fmt.Errorf("unable to extract set config inputs: %w", err) } diff --git a/sdk/confighelpers.go b/sdk/confighelpers.go new file mode 100644 index 000000000..750768351 --- /dev/null +++ b/sdk/confighelpers.go @@ -0,0 +1,102 @@ +package sdk + +import ( + "fmt" + "math/big" + "slices" + + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/mcms/internal/utils/safecast" + "github.com/smartcontractkit/mcms/sdk/evm/bindings" + "github.com/smartcontractkit/mcms/types" +) + +// ExtractSetConfigInputs flattens a nested `*types.Config` into: +// 1. groupQuorums: [32]uint8 where each index i holds the quorum for group i (zero-padded). +// 2. groupParents: [32]uint8 where each index i holds the parent group’s index (or a sentinel). +// 3. orderedSignerAddresses: a sorted slice of all signer addresses. +// 4. orderedSignerGroups: a parallel slice of group indices for each signer. +// +// Returns an error if the structure cannot be flattened (e.g., too many groups). +func ExtractSetConfigInputs( + group *types.Config, +) ([32]uint8, [32]uint8, []common.Address, []uint8, error) { + var groupQuorums, groupParents, signerGroups = []uint8{}, []uint8{}, []uint8{} + var signerAddrs = []common.Address{} + + err := extractGroupsAndSigners(group, 0, &groupQuorums, &groupParents, &signerAddrs, &signerGroups) + if err != nil { + return [32]uint8{}, [32]uint8{}, []common.Address{}, []uint8{}, err + } + + // fill the rest of the arrays with 0s + for i := len(groupQuorums); i < 32; i++ { + groupQuorums = append(groupQuorums, 0) + groupParents = append(groupParents, 0) + } + + // Combine SignerAddresses and SignerGroups into a slice of Signer structs + bindSigners := make([]bindings.ManyChainMultiSigSigner, len(signerAddrs)) + for i := range signerAddrs { + bindSigners[i] = bindings.ManyChainMultiSigSigner{ + Addr: signerAddrs[i], + Group: signerGroups[i], + } + } + + // Sort signers by their addresses in ascending order + slices.SortFunc(bindSigners, func(i, j bindings.ManyChainMultiSigSigner) int { + addressA := new(big.Int).SetBytes(i.Addr.Bytes()) + addressB := new(big.Int).SetBytes(j.Addr.Bytes()) + + return addressA.Cmp(addressB) + }) + + // Extract the ordered addresses and groups after sorting + orderedSignerAddresses := make([]common.Address, len(signerAddrs)) + orderedSignerGroups := make([]uint8, len(signerAddrs)) + for i, signer := range bindSigners { + orderedSignerAddresses[i] = signer.Addr + orderedSignerGroups[i] = signer.Group + } + + return [32]uint8(groupQuorums), [32]uint8(groupParents), orderedSignerAddresses, orderedSignerGroups, nil +} + +func extractGroupsAndSigners( + group *types.Config, + parentIdx uint8, + groupQuorums *[]uint8, + groupParents *[]uint8, + signerAddrs *[]common.Address, + signerGroups *[]uint8, +) error { + // Append the group's quorum and parent index to the respective slices + *groupQuorums = append(*groupQuorums, group.Quorum) + *groupParents = append(*groupParents, parentIdx) + + // Assign the current group index + currentGroupIdx := len(*groupQuorums) - 1 + + // Safe to cast currentGroupIdx to uint8 + currentGroupIdxUint8, err := safecast.IntToUint8(currentGroupIdx) + if err != nil { + return fmt.Errorf("group index %d exceeds uint8 range", currentGroupIdx) + } + + // For each string signer, append the signer and its group index + for _, signerAddr := range group.Signers { + *signerAddrs = append(*signerAddrs, signerAddr) + *signerGroups = append(*signerGroups, currentGroupIdxUint8) + } + + // Recursively handle the nested multisig groups + for _, groupSigner := range group.GroupSigners { + if err := extractGroupsAndSigners(&groupSigner, currentGroupIdxUint8, groupQuorums, groupParents, signerAddrs, signerGroups); err != nil { + return err + } + } + + return nil +} diff --git a/sdk/evm/config_transformer.go b/sdk/evm/config_transformer.go index bffec74fb..ecd1138ab 100644 --- a/sdk/evm/config_transformer.go +++ b/sdk/evm/config_transformer.go @@ -1,13 +1,8 @@ package evm import ( - "fmt" - "math/big" - "slices" - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/mcms/internal/utils/safecast" "github.com/smartcontractkit/mcms/sdk" sdkerrors "github.com/smartcontractkit/mcms/sdk/errors" "github.com/smartcontractkit/mcms/sdk/evm/bindings" @@ -69,7 +64,7 @@ func (e *ConfigTransformer) ToChainConfig( ) (bindings.ManyChainMultiSigConfig, error) { var bindConfig bindings.ManyChainMultiSigConfig - groupQuorums, groupParents, signerAddrs, signerGroups, err := ExtractSetConfigInputs(&cfg) + groupQuorums, groupParents, signerAddrs, signerGroups, err := sdk.ExtractSetConfigInputs(&cfg) if err != nil { return bindConfig, err } @@ -97,92 +92,3 @@ func (e *ConfigTransformer) ToChainConfig( Signers: bindSigners, }, nil } - -// ExtractSetConfigInputs flattens a nested `*types.Config` into: -// 1. groupQuorums: [32]uint8 where each index i holds the quorum for group i (zero-padded). -// 2. groupParents: [32]uint8 where each index i holds the parent group’s index (or a sentinel). -// 3. orderedSignerAddresses: a sorted slice of all signer addresses. -// 4. orderedSignerGroups: a parallel slice of group indices for each signer. -// -// Returns an error if the structure cannot be flattened (e.g., too many groups). -func ExtractSetConfigInputs( - group *types.Config, -) ([32]uint8, [32]uint8, []common.Address, []uint8, error) { - var groupQuorums, groupParents, signerGroups = []uint8{}, []uint8{}, []uint8{} - var signerAddrs = []common.Address{} - - err := extractGroupsAndSigners(group, 0, &groupQuorums, &groupParents, &signerAddrs, &signerGroups) - if err != nil { - return [32]uint8{}, [32]uint8{}, []common.Address{}, []uint8{}, err - } - - // fill the rest of the arrays with 0s - for i := len(groupQuorums); i < 32; i++ { - groupQuorums = append(groupQuorums, 0) - groupParents = append(groupParents, 0) - } - - // Combine SignerAddresses and SignerGroups into a slice of Signer structs - bindSigners := make([]bindings.ManyChainMultiSigSigner, len(signerAddrs)) - for i := range signerAddrs { - bindSigners[i] = bindings.ManyChainMultiSigSigner{ - Addr: signerAddrs[i], - Group: signerGroups[i], - } - } - - // Sort signers by their addresses in ascending order - slices.SortFunc(bindSigners, func(i, j bindings.ManyChainMultiSigSigner) int { - addressA := new(big.Int).SetBytes(i.Addr.Bytes()) - addressB := new(big.Int).SetBytes(j.Addr.Bytes()) - - return addressA.Cmp(addressB) - }) - - // Extract the ordered addresses and groups after sorting - orderedSignerAddresses := make([]common.Address, len(signerAddrs)) - orderedSignerGroups := make([]uint8, len(signerAddrs)) - for i, signer := range bindSigners { - orderedSignerAddresses[i] = signer.Addr - orderedSignerGroups[i] = signer.Group - } - - return [32]uint8(groupQuorums), [32]uint8(groupParents), orderedSignerAddresses, orderedSignerGroups, nil -} - -func extractGroupsAndSigners( - group *types.Config, - parentIdx uint8, - groupQuorums *[]uint8, - groupParents *[]uint8, - signerAddrs *[]common.Address, - signerGroups *[]uint8, -) error { - // Append the group's quorum and parent index to the respective slices - *groupQuorums = append(*groupQuorums, group.Quorum) - *groupParents = append(*groupParents, parentIdx) - - // Assign the current group index - currentGroupIdx := len(*groupQuorums) - 1 - - // Safe to cast currentGroupIdx to uint8 - currentGroupIdxUint8, err := safecast.IntToUint8(currentGroupIdx) - if err != nil { - return fmt.Errorf("group index %d exceeds uint8 range", currentGroupIdx) - } - - // For each string signer, append the signer and its group index - for _, signerAddr := range group.Signers { - *signerAddrs = append(*signerAddrs, signerAddr) - *signerGroups = append(*signerGroups, currentGroupIdxUint8) - } - - // Recursively handle the nested multisig groups - for _, groupSigner := range group.GroupSigners { - if err := extractGroupsAndSigners(&groupSigner, currentGroupIdxUint8, groupQuorums, groupParents, signerAddrs, signerGroups); err != nil { - return err - } - } - - return nil -} diff --git a/sdk/evm/configurer.go b/sdk/evm/configurer.go index e7cdc8e80..9cbbc39f0 100644 --- a/sdk/evm/configurer.go +++ b/sdk/evm/configurer.go @@ -37,7 +37,7 @@ func (c *Configurer) SetConfig(ctx context.Context, mcmAddr string, cfg *types.C return types.TransactionResult{}, err } - groupQuorums, groupParents, signerAddrs, signerGroups, err := ExtractSetConfigInputs(cfg) + groupQuorums, groupParents, signerAddrs, signerGroups, err := sdk.ExtractSetConfigInputs(cfg) if err != nil { return types.TransactionResult{}, err } diff --git a/sdk/evm/simulator.go b/sdk/evm/simulator.go index d049abcec..2dbd203a0 100644 --- a/sdk/evm/simulator.go +++ b/sdk/evm/simulator.go @@ -1,12 +1,11 @@ package evm import ( + "context" "encoding/json" "errors" "math/big" - "context" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" diff --git a/sdk/evm/utils.go b/sdk/evm/utils.go index f238534fa..05bab6f6c 100644 --- a/sdk/evm/utils.go +++ b/sdk/evm/utils.go @@ -1,14 +1,13 @@ package evm import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" cselectors "github.com/smartcontractkit/chain-selectors" sdkerrors "github.com/smartcontractkit/mcms/sdk/errors" "github.com/smartcontractkit/mcms/types" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/mcms/sdk/evm/bindings" ) diff --git a/sdk/mocks/contract_deploy_backend.go b/sdk/mocks/contract_deploy_backend.go new file mode 100644 index 000000000..fd248d1be --- /dev/null +++ b/sdk/mocks/contract_deploy_backend.go @@ -0,0 +1,736 @@ +// Code generated by mockery v2.53.5. DO NOT EDIT. + +package mocks + +import ( + context "context" + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + ethereum "github.com/ethereum/go-ethereum" + + mock "github.com/stretchr/testify/mock" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// ContractDeployBackend is an autogenerated mock type for the ContractDeployBackend type +type ContractDeployBackend struct { + mock.Mock +} + +type ContractDeployBackend_Expecter struct { + mock *mock.Mock +} + +func (_m *ContractDeployBackend) EXPECT() *ContractDeployBackend_Expecter { + return &ContractDeployBackend_Expecter{mock: &_m.Mock} +} + +// CallContract provides a mock function with given fields: ctx, call, blockNumber +func (_m *ContractDeployBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, call, 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, ethereum.CallMsg, *big.Int) ([]byte, error)); ok { + return rf(ctx, call, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) []byte); ok { + r0 = rf(ctx, call, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg, *big.Int) error); ok { + r1 = rf(ctx, call, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ContractDeployBackend_CallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContract' +type ContractDeployBackend_CallContract_Call struct { + *mock.Call +} + +// CallContract is a helper method to define mock.On call +// - ctx context.Context +// - call ethereum.CallMsg +// - blockNumber *big.Int +func (_e *ContractDeployBackend_Expecter) CallContract(ctx interface{}, call interface{}, blockNumber interface{}) *ContractDeployBackend_CallContract_Call { + return &ContractDeployBackend_CallContract_Call{Call: _e.mock.On("CallContract", ctx, call, blockNumber)} +} + +func (_c *ContractDeployBackend_CallContract_Call) Run(run func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int)) *ContractDeployBackend_CallContract_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.CallMsg), args[2].(*big.Int)) + }) + return _c +} + +func (_c *ContractDeployBackend_CallContract_Call) Return(_a0 []byte, _a1 error) *ContractDeployBackend_CallContract_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_CallContract_Call) RunAndReturn(run func(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error)) *ContractDeployBackend_CallContract_Call { + _c.Call.Return(run) + return _c +} + +// CodeAt provides a mock function with given fields: ctx, contract, blockNumber +func (_m *ContractDeployBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, contract, 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, contract, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { + r0 = rf(ctx, contract, 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, contract, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ContractDeployBackend_CodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CodeAt' +type ContractDeployBackend_CodeAt_Call struct { + *mock.Call +} + +// CodeAt is a helper method to define mock.On call +// - ctx context.Context +// - contract common.Address +// - blockNumber *big.Int +func (_e *ContractDeployBackend_Expecter) CodeAt(ctx interface{}, contract interface{}, blockNumber interface{}) *ContractDeployBackend_CodeAt_Call { + return &ContractDeployBackend_CodeAt_Call{Call: _e.mock.On("CodeAt", ctx, contract, blockNumber)} +} + +func (_c *ContractDeployBackend_CodeAt_Call) Run(run func(ctx context.Context, contract common.Address, blockNumber *big.Int)) *ContractDeployBackend_CodeAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) + }) + return _c +} + +func (_c *ContractDeployBackend_CodeAt_Call) Return(_a0 []byte, _a1 error) *ContractDeployBackend_CodeAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_CodeAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) ([]byte, error)) *ContractDeployBackend_CodeAt_Call { + _c.Call.Return(run) + return _c +} + +// EstimateGas provides a mock function with given fields: ctx, call +func (_m *ContractDeployBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (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, ethereum.CallMsg) (uint64, error)); ok { + return rf(ctx, call) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg) uint64); ok { + r0 = rf(ctx, call) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg) error); ok { + r1 = rf(ctx, call) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ContractDeployBackend_EstimateGas_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGas' +type ContractDeployBackend_EstimateGas_Call struct { + *mock.Call +} + +// EstimateGas is a helper method to define mock.On call +// - ctx context.Context +// - call ethereum.CallMsg +func (_e *ContractDeployBackend_Expecter) EstimateGas(ctx interface{}, call interface{}) *ContractDeployBackend_EstimateGas_Call { + return &ContractDeployBackend_EstimateGas_Call{Call: _e.mock.On("EstimateGas", ctx, call)} +} + +func (_c *ContractDeployBackend_EstimateGas_Call) Run(run func(ctx context.Context, call ethereum.CallMsg)) *ContractDeployBackend_EstimateGas_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.CallMsg)) + }) + return _c +} + +func (_c *ContractDeployBackend_EstimateGas_Call) Return(_a0 uint64, _a1 error) *ContractDeployBackend_EstimateGas_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_EstimateGas_Call) RunAndReturn(run func(context.Context, ethereum.CallMsg) (uint64, error)) *ContractDeployBackend_EstimateGas_Call { + _c.Call.Return(run) + return _c +} + +// FilterLogs provides a mock function with given fields: ctx, q +func (_m *ContractDeployBackend) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + ret := _m.Called(ctx, q) + + if len(ret) == 0 { + panic("no return value specified for FilterLogs") + } + + var r0 []types.Log + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]types.Log, error)); ok { + return rf(ctx, q) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []types.Log); ok { + r0 = rf(ctx, q) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Log) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery) error); ok { + r1 = rf(ctx, q) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ContractDeployBackend_FilterLogs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterLogs' +type ContractDeployBackend_FilterLogs_Call struct { + *mock.Call +} + +// FilterLogs is a helper method to define mock.On call +// - ctx context.Context +// - q ethereum.FilterQuery +func (_e *ContractDeployBackend_Expecter) FilterLogs(ctx interface{}, q interface{}) *ContractDeployBackend_FilterLogs_Call { + return &ContractDeployBackend_FilterLogs_Call{Call: _e.mock.On("FilterLogs", ctx, q)} +} + +func (_c *ContractDeployBackend_FilterLogs_Call) Run(run func(ctx context.Context, q ethereum.FilterQuery)) *ContractDeployBackend_FilterLogs_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.FilterQuery)) + }) + return _c +} + +func (_c *ContractDeployBackend_FilterLogs_Call) Return(_a0 []types.Log, _a1 error) *ContractDeployBackend_FilterLogs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_FilterLogs_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery) ([]types.Log, error)) *ContractDeployBackend_FilterLogs_Call { + _c.Call.Return(run) + return _c +} + +// HeaderByNumber provides a mock function with given fields: ctx, number +func (_m *ContractDeployBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + + var r0 *types.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Header); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + 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 +} + +// ContractDeployBackend_HeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByNumber' +type ContractDeployBackend_HeaderByNumber_Call struct { + *mock.Call +} + +// HeaderByNumber is a helper method to define mock.On call +// - ctx context.Context +// - number *big.Int +func (_e *ContractDeployBackend_Expecter) HeaderByNumber(ctx interface{}, number interface{}) *ContractDeployBackend_HeaderByNumber_Call { + return &ContractDeployBackend_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, number)} +} + +func (_c *ContractDeployBackend_HeaderByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *ContractDeployBackend_HeaderByNumber_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*big.Int)) + }) + return _c +} + +func (_c *ContractDeployBackend_HeaderByNumber_Call) Return(_a0 *types.Header, _a1 error) *ContractDeployBackend_HeaderByNumber_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*types.Header, error)) *ContractDeployBackend_HeaderByNumber_Call { + _c.Call.Return(run) + return _c +} + +// PendingCodeAt provides a mock function with given fields: ctx, account +func (_m *ContractDeployBackend) 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 +} + +// ContractDeployBackend_PendingCodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCodeAt' +type ContractDeployBackend_PendingCodeAt_Call struct { + *mock.Call +} + +// PendingCodeAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +func (_e *ContractDeployBackend_Expecter) PendingCodeAt(ctx interface{}, account interface{}) *ContractDeployBackend_PendingCodeAt_Call { + return &ContractDeployBackend_PendingCodeAt_Call{Call: _e.mock.On("PendingCodeAt", ctx, account)} +} + +func (_c *ContractDeployBackend_PendingCodeAt_Call) Run(run func(ctx context.Context, account common.Address)) *ContractDeployBackend_PendingCodeAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address)) + }) + return _c +} + +func (_c *ContractDeployBackend_PendingCodeAt_Call) Return(_a0 []byte, _a1 error) *ContractDeployBackend_PendingCodeAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_PendingCodeAt_Call) RunAndReturn(run func(context.Context, common.Address) ([]byte, error)) *ContractDeployBackend_PendingCodeAt_Call { + _c.Call.Return(run) + return _c +} + +// PendingNonceAt provides a mock function with given fields: ctx, account +func (_m *ContractDeployBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + ret := _m.Called(ctx, account) + + if len(ret) == 0 { + panic("no return value specified for PendingNonceAt") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) (uint64, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) uint64); ok { + r0 = rf(ctx, account) + } else { + r0 = ret.Get(0).(uint64) + } + + 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 +} + +// ContractDeployBackend_PendingNonceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingNonceAt' +type ContractDeployBackend_PendingNonceAt_Call struct { + *mock.Call +} + +// PendingNonceAt is a helper method to define mock.On call +// - ctx context.Context +// - account common.Address +func (_e *ContractDeployBackend_Expecter) PendingNonceAt(ctx interface{}, account interface{}) *ContractDeployBackend_PendingNonceAt_Call { + return &ContractDeployBackend_PendingNonceAt_Call{Call: _e.mock.On("PendingNonceAt", ctx, account)} +} + +func (_c *ContractDeployBackend_PendingNonceAt_Call) Run(run func(ctx context.Context, account common.Address)) *ContractDeployBackend_PendingNonceAt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address)) + }) + return _c +} + +func (_c *ContractDeployBackend_PendingNonceAt_Call) Return(_a0 uint64, _a1 error) *ContractDeployBackend_PendingNonceAt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_PendingNonceAt_Call) RunAndReturn(run func(context.Context, common.Address) (uint64, error)) *ContractDeployBackend_PendingNonceAt_Call { + _c.Call.Return(run) + return _c +} + +// SendTransaction provides a mock function with given fields: ctx, tx +func (_m *ContractDeployBackend) SendTransaction(ctx context.Context, tx *types.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, *types.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ContractDeployBackend_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' +type ContractDeployBackend_SendTransaction_Call struct { + *mock.Call +} + +// SendTransaction is a helper method to define mock.On call +// - ctx context.Context +// - tx *types.Transaction +func (_e *ContractDeployBackend_Expecter) SendTransaction(ctx interface{}, tx interface{}) *ContractDeployBackend_SendTransaction_Call { + return &ContractDeployBackend_SendTransaction_Call{Call: _e.mock.On("SendTransaction", ctx, tx)} +} + +func (_c *ContractDeployBackend_SendTransaction_Call) Run(run func(ctx context.Context, tx *types.Transaction)) *ContractDeployBackend_SendTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.Transaction)) + }) + return _c +} + +func (_c *ContractDeployBackend_SendTransaction_Call) Return(_a0 error) *ContractDeployBackend_SendTransaction_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ContractDeployBackend_SendTransaction_Call) RunAndReturn(run func(context.Context, *types.Transaction) error) *ContractDeployBackend_SendTransaction_Call { + _c.Call.Return(run) + return _c +} + +// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch +func (_m *ContractDeployBackend) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.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<- types.Log) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- types.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<- types.Log) error); ok { + r1 = rf(ctx, q, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ContractDeployBackend_SubscribeFilterLogs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeFilterLogs' +type ContractDeployBackend_SubscribeFilterLogs_Call struct { + *mock.Call +} + +// SubscribeFilterLogs is a helper method to define mock.On call +// - ctx context.Context +// - q ethereum.FilterQuery +// - ch chan<- types.Log +func (_e *ContractDeployBackend_Expecter) SubscribeFilterLogs(ctx interface{}, q interface{}, ch interface{}) *ContractDeployBackend_SubscribeFilterLogs_Call { + return &ContractDeployBackend_SubscribeFilterLogs_Call{Call: _e.mock.On("SubscribeFilterLogs", ctx, q, ch)} +} + +func (_c *ContractDeployBackend_SubscribeFilterLogs_Call) Run(run func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log)) *ContractDeployBackend_SubscribeFilterLogs_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(ethereum.FilterQuery), args[2].(chan<- types.Log)) + }) + return _c +} + +func (_c *ContractDeployBackend_SubscribeFilterLogs_Call) Return(_a0 ethereum.Subscription, _a1 error) *ContractDeployBackend_SubscribeFilterLogs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_SubscribeFilterLogs_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery, chan<- types.Log) (ethereum.Subscription, error)) *ContractDeployBackend_SubscribeFilterLogs_Call { + _c.Call.Return(run) + return _c +} + +// SuggestGasPrice provides a mock function with given fields: ctx +func (_m *ContractDeployBackend) 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 +} + +// ContractDeployBackend_SuggestGasPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasPrice' +type ContractDeployBackend_SuggestGasPrice_Call struct { + *mock.Call +} + +// SuggestGasPrice is a helper method to define mock.On call +// - ctx context.Context +func (_e *ContractDeployBackend_Expecter) SuggestGasPrice(ctx interface{}) *ContractDeployBackend_SuggestGasPrice_Call { + return &ContractDeployBackend_SuggestGasPrice_Call{Call: _e.mock.On("SuggestGasPrice", ctx)} +} + +func (_c *ContractDeployBackend_SuggestGasPrice_Call) Run(run func(ctx context.Context)) *ContractDeployBackend_SuggestGasPrice_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *ContractDeployBackend_SuggestGasPrice_Call) Return(_a0 *big.Int, _a1 error) *ContractDeployBackend_SuggestGasPrice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_SuggestGasPrice_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *ContractDeployBackend_SuggestGasPrice_Call { + _c.Call.Return(run) + return _c +} + +// SuggestGasTipCap provides a mock function with given fields: ctx +func (_m *ContractDeployBackend) 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 +} + +// ContractDeployBackend_SuggestGasTipCap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasTipCap' +type ContractDeployBackend_SuggestGasTipCap_Call struct { + *mock.Call +} + +// SuggestGasTipCap is a helper method to define mock.On call +// - ctx context.Context +func (_e *ContractDeployBackend_Expecter) SuggestGasTipCap(ctx interface{}) *ContractDeployBackend_SuggestGasTipCap_Call { + return &ContractDeployBackend_SuggestGasTipCap_Call{Call: _e.mock.On("SuggestGasTipCap", ctx)} +} + +func (_c *ContractDeployBackend_SuggestGasTipCap_Call) Run(run func(ctx context.Context)) *ContractDeployBackend_SuggestGasTipCap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *ContractDeployBackend_SuggestGasTipCap_Call) Return(_a0 *big.Int, _a1 error) *ContractDeployBackend_SuggestGasTipCap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_SuggestGasTipCap_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *ContractDeployBackend_SuggestGasTipCap_Call { + _c.Call.Return(run) + return _c +} + +// TransactionReceipt provides a mock function with given fields: ctx, txHash +func (_m *ContractDeployBackend) 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 +} + +// ContractDeployBackend_TransactionReceipt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceipt' +type ContractDeployBackend_TransactionReceipt_Call struct { + *mock.Call +} + +// TransactionReceipt is a helper method to define mock.On call +// - ctx context.Context +// - txHash common.Hash +func (_e *ContractDeployBackend_Expecter) TransactionReceipt(ctx interface{}, txHash interface{}) *ContractDeployBackend_TransactionReceipt_Call { + return &ContractDeployBackend_TransactionReceipt_Call{Call: _e.mock.On("TransactionReceipt", ctx, txHash)} +} + +func (_c *ContractDeployBackend_TransactionReceipt_Call) Run(run func(ctx context.Context, txHash common.Hash)) *ContractDeployBackend_TransactionReceipt_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *ContractDeployBackend_TransactionReceipt_Call) Return(_a0 *types.Receipt, _a1 error) *ContractDeployBackend_TransactionReceipt_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ContractDeployBackend_TransactionReceipt_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Receipt, error)) *ContractDeployBackend_TransactionReceipt_Call { + _c.Call.Return(run) + return _c +} + +// NewContractDeployBackend creates a new instance of ContractDeployBackend. 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 NewContractDeployBackend(t interface { + mock.TestingT + Cleanup(func()) +}) *ContractDeployBackend { + mock := &ContractDeployBackend{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sdk/mocks/sui_signer.go b/sdk/mocks/sui_signer.go new file mode 100644 index 000000000..52c10a36d --- /dev/null +++ b/sdk/mocks/sui_signer.go @@ -0,0 +1,145 @@ +// Code generated by mockery v2.53.5. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// SuiSigner is an autogenerated mock type for the SuiSigner type +type SuiSigner struct { + mock.Mock +} + +type SuiSigner_Expecter struct { + mock *mock.Mock +} + +func (_m *SuiSigner) EXPECT() *SuiSigner_Expecter { + return &SuiSigner_Expecter{mock: &_m.Mock} +} + +// GetAddress provides a mock function with no fields +func (_m *SuiSigner) GetAddress() (string, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetAddress") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func() (string, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuiSigner_GetAddress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAddress' +type SuiSigner_GetAddress_Call struct { + *mock.Call +} + +// GetAddress is a helper method to define mock.On call +func (_e *SuiSigner_Expecter) GetAddress() *SuiSigner_GetAddress_Call { + return &SuiSigner_GetAddress_Call{Call: _e.mock.On("GetAddress")} +} + +func (_c *SuiSigner_GetAddress_Call) Run(run func()) *SuiSigner_GetAddress_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *SuiSigner_GetAddress_Call) Return(_a0 string, _a1 error) *SuiSigner_GetAddress_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SuiSigner_GetAddress_Call) RunAndReturn(run func() (string, error)) *SuiSigner_GetAddress_Call { + _c.Call.Return(run) + return _c +} + +// Sign provides a mock function with given fields: message +func (_m *SuiSigner) Sign(message []byte) ([]string, error) { + ret := _m.Called(message) + + if len(ret) == 0 { + panic("no return value specified for Sign") + } + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func([]byte) ([]string, error)); ok { + return rf(message) + } + if rf, ok := ret.Get(0).(func([]byte) []string); ok { + r0 = rf(message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuiSigner_Sign_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sign' +type SuiSigner_Sign_Call struct { + *mock.Call +} + +// Sign is a helper method to define mock.On call +// - message []byte +func (_e *SuiSigner_Expecter) Sign(message interface{}) *SuiSigner_Sign_Call { + return &SuiSigner_Sign_Call{Call: _e.mock.On("Sign", message)} +} + +func (_c *SuiSigner_Sign_Call) Run(run func(message []byte)) *SuiSigner_Sign_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte)) + }) + return _c +} + +func (_c *SuiSigner_Sign_Call) Return(_a0 []string, _a1 error) *SuiSigner_Sign_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SuiSigner_Sign_Call) RunAndReturn(run func([]byte) ([]string, error)) *SuiSigner_Sign_Call { + _c.Call.Return(run) + return _c +} + +// NewSuiSigner creates a new instance of SuiSigner. 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 NewSuiSigner(t interface { + mock.TestingT + Cleanup(func()) +}) *SuiSigner { + mock := &SuiSigner{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/sdk/solana/config_transformer.go b/sdk/solana/config_transformer.go index ceee25cac..858a29905 100644 --- a/sdk/solana/config_transformer.go +++ b/sdk/solana/config_transformer.go @@ -3,8 +3,9 @@ package solana import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/mcms/sdk" sdkerrors "github.com/smartcontractkit/mcms/sdk/errors" - "github.com/smartcontractkit/mcms/sdk/evm" + "github.com/smartcontractkit/mcms/types" "github.com/gagliardetto/solana-go" @@ -77,7 +78,7 @@ func (e *ConfigTransformer) ToChainConfig(cfg types.Config, solanaConfig Additio ProposedOwner: solanaConfig.ProposedOwner, } // Populate the signers: we can reuse the evm implementation here as the signers structure is the same - groupQuorums, groupParents, signerAddrs, signerGroups, err := evm.ExtractSetConfigInputs(&cfg) + groupQuorums, groupParents, signerAddrs, signerGroups, err := sdk.ExtractSetConfigInputs(&cfg) if err != nil { return bindings.MultisigConfig{}, err } diff --git a/sdk/solana/configurer.go b/sdk/solana/configurer.go index 0a94feef6..29bfb1283 100644 --- a/sdk/solana/configurer.go +++ b/sdk/solana/configurer.go @@ -9,7 +9,7 @@ import ( evmCommon "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/mcms/sdk" - evmsdk "github.com/smartcontractkit/mcms/sdk/evm" + "github.com/smartcontractkit/mcms/types" "github.com/gagliardetto/solana-go" @@ -89,7 +89,7 @@ func (c *Configurer) SetConfig( return types.TransactionResult{}, err } - groupQuorums, groupParents, signerAddresses, signerGroups, err := evmsdk.ExtractSetConfigInputs(cfg) + groupQuorums, groupParents, signerAddresses, signerGroups, err := sdk.ExtractSetConfigInputs(cfg) if err != nil { return types.TransactionResult{}, fmt.Errorf("unable to extract set config inputs: %w", err) } diff --git a/sdk/sui/configurer.go b/sdk/sui/configurer.go index df95fb8f2..1ddaf8d85 100644 --- a/sdk/sui/configurer.go +++ b/sdk/sui/configurer.go @@ -14,7 +14,6 @@ import ( bindutils "github.com/smartcontractkit/chainlink-sui/bindings/utils" "github.com/smartcontractkit/mcms/sdk" - "github.com/smartcontractkit/mcms/sdk/evm" "github.com/smartcontractkit/mcms/types" ) @@ -51,7 +50,7 @@ func (c Configurer) SetConfig(ctx context.Context, mcmsAddr string, cfg *types.C return types.TransactionResult{}, err } chainIDBig := new(big.Int).SetUint64(chainID) - groupQuorum, groupParents, signerAddresses, signerGroups, err := evm.ExtractSetConfigInputs(cfg) + groupQuorum, groupParents, signerAddresses, signerGroups, err := sdk.ExtractSetConfigInputs(cfg) if err != nil { return types.TransactionResult{}, fmt.Errorf("unable to extract set config inputs: %w", err) diff --git a/sdk/sui/mocks/mcms/imcms.go b/sdk/sui/mocks/mcms/imcms.go index 81c3e3d51..55ff6629e 100644 --- a/sdk/sui/mocks/mcms/imcms.go +++ b/sdk/sui/mocks/mcms/imcms.go @@ -664,67 +664,6 @@ func (_c *IMcms_DevInspect_Call) RunAndReturn(run func() module_mcms.IMcmsDevIns return _c } -// DispatchTimelockBlockFunction provides a mock function with given fields: ctx, opts, timelock, timelockCallbackParams -func (_m *IMcms) DispatchTimelockBlockFunction(ctx context.Context, opts *bind.CallOpts, timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error) { - ret := _m.Called(ctx, opts, timelock, timelockCallbackParams) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockBlockFunction") - } - - var r0 *models.SuiTransactionBlockResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error)); ok { - return rf(ctx, opts, timelock, timelockCallbackParams) - } - if rf, ok := ret.Get(0).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) *models.SuiTransactionBlockResponse); ok { - r0 = rf(ctx, opts, timelock, timelockCallbackParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.SuiTransactionBlockResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) error); ok { - r1 = rf(ctx, opts, timelock, timelockCallbackParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IMcms_DispatchTimelockBlockFunction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockBlockFunction' -type IMcms_DispatchTimelockBlockFunction_Call struct { - *mock.Call -} - -// DispatchTimelockBlockFunction is a helper method to define mock.On call -// - ctx context.Context -// - opts *bind.CallOpts -// - timelock bind.Object -// - timelockCallbackParams module_mcms.TimelockCallbackParams -func (_e *IMcms_Expecter) DispatchTimelockBlockFunction(ctx interface{}, opts interface{}, timelock interface{}, timelockCallbackParams interface{}) *IMcms_DispatchTimelockBlockFunction_Call { - return &IMcms_DispatchTimelockBlockFunction_Call{Call: _e.mock.On("DispatchTimelockBlockFunction", ctx, opts, timelock, timelockCallbackParams)} -} - -func (_c *IMcms_DispatchTimelockBlockFunction_Call) Run(run func(ctx context.Context, opts *bind.CallOpts, timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams)) *IMcms_DispatchTimelockBlockFunction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*bind.CallOpts), args[2].(bind.Object), args[3].(module_mcms.TimelockCallbackParams)) - }) - return _c -} - -func (_c *IMcms_DispatchTimelockBlockFunction_Call) Return(_a0 *models.SuiTransactionBlockResponse, _a1 error) *IMcms_DispatchTimelockBlockFunction_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *IMcms_DispatchTimelockBlockFunction_Call) RunAndReturn(run func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error)) *IMcms_DispatchTimelockBlockFunction_Call { - _c.Call.Return(run) - return _c -} - // DispatchTimelockBypasserExecuteBatch provides a mock function with given fields: ctx, opts, timelockCallbackParams func (_m *IMcms) DispatchTimelockBypasserExecuteBatch(ctx context.Context, opts *bind.CallOpts, timelockCallbackParams module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error) { ret := _m.Called(ctx, opts, timelockCallbackParams) @@ -971,128 +910,6 @@ func (_c *IMcms_DispatchTimelockScheduleBatch_Call) RunAndReturn(run func(contex return _c } -// DispatchTimelockUnblockFunction provides a mock function with given fields: ctx, opts, timelock, timelockCallbackParams -func (_m *IMcms) DispatchTimelockUnblockFunction(ctx context.Context, opts *bind.CallOpts, timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error) { - ret := _m.Called(ctx, opts, timelock, timelockCallbackParams) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockUnblockFunction") - } - - var r0 *models.SuiTransactionBlockResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error)); ok { - return rf(ctx, opts, timelock, timelockCallbackParams) - } - if rf, ok := ret.Get(0).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) *models.SuiTransactionBlockResponse); ok { - r0 = rf(ctx, opts, timelock, timelockCallbackParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.SuiTransactionBlockResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) error); ok { - r1 = rf(ctx, opts, timelock, timelockCallbackParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IMcms_DispatchTimelockUnblockFunction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockUnblockFunction' -type IMcms_DispatchTimelockUnblockFunction_Call struct { - *mock.Call -} - -// DispatchTimelockUnblockFunction is a helper method to define mock.On call -// - ctx context.Context -// - opts *bind.CallOpts -// - timelock bind.Object -// - timelockCallbackParams module_mcms.TimelockCallbackParams -func (_e *IMcms_Expecter) DispatchTimelockUnblockFunction(ctx interface{}, opts interface{}, timelock interface{}, timelockCallbackParams interface{}) *IMcms_DispatchTimelockUnblockFunction_Call { - return &IMcms_DispatchTimelockUnblockFunction_Call{Call: _e.mock.On("DispatchTimelockUnblockFunction", ctx, opts, timelock, timelockCallbackParams)} -} - -func (_c *IMcms_DispatchTimelockUnblockFunction_Call) Run(run func(ctx context.Context, opts *bind.CallOpts, timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams)) *IMcms_DispatchTimelockUnblockFunction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*bind.CallOpts), args[2].(bind.Object), args[3].(module_mcms.TimelockCallbackParams)) - }) - return _c -} - -func (_c *IMcms_DispatchTimelockUnblockFunction_Call) Return(_a0 *models.SuiTransactionBlockResponse, _a1 error) *IMcms_DispatchTimelockUnblockFunction_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *IMcms_DispatchTimelockUnblockFunction_Call) RunAndReturn(run func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error)) *IMcms_DispatchTimelockUnblockFunction_Call { - _c.Call.Return(run) - return _c -} - -// DispatchTimelockUpdateMinDelay provides a mock function with given fields: ctx, opts, timelock, timelockCallbackParams -func (_m *IMcms) DispatchTimelockUpdateMinDelay(ctx context.Context, opts *bind.CallOpts, timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error) { - ret := _m.Called(ctx, opts, timelock, timelockCallbackParams) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockUpdateMinDelay") - } - - var r0 *models.SuiTransactionBlockResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error)); ok { - return rf(ctx, opts, timelock, timelockCallbackParams) - } - if rf, ok := ret.Get(0).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) *models.SuiTransactionBlockResponse); ok { - r0 = rf(ctx, opts, timelock, timelockCallbackParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.SuiTransactionBlockResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) error); ok { - r1 = rf(ctx, opts, timelock, timelockCallbackParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IMcms_DispatchTimelockUpdateMinDelay_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockUpdateMinDelay' -type IMcms_DispatchTimelockUpdateMinDelay_Call struct { - *mock.Call -} - -// DispatchTimelockUpdateMinDelay is a helper method to define mock.On call -// - ctx context.Context -// - opts *bind.CallOpts -// - timelock bind.Object -// - timelockCallbackParams module_mcms.TimelockCallbackParams -func (_e *IMcms_Expecter) DispatchTimelockUpdateMinDelay(ctx interface{}, opts interface{}, timelock interface{}, timelockCallbackParams interface{}) *IMcms_DispatchTimelockUpdateMinDelay_Call { - return &IMcms_DispatchTimelockUpdateMinDelay_Call{Call: _e.mock.On("DispatchTimelockUpdateMinDelay", ctx, opts, timelock, timelockCallbackParams)} -} - -func (_c *IMcms_DispatchTimelockUpdateMinDelay_Call) Run(run func(ctx context.Context, opts *bind.CallOpts, timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams)) *IMcms_DispatchTimelockUpdateMinDelay_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*bind.CallOpts), args[2].(bind.Object), args[3].(module_mcms.TimelockCallbackParams)) - }) - return _c -} - -func (_c *IMcms_DispatchTimelockUpdateMinDelay_Call) Return(_a0 *models.SuiTransactionBlockResponse, _a1 error) *IMcms_DispatchTimelockUpdateMinDelay_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *IMcms_DispatchTimelockUpdateMinDelay_Call) RunAndReturn(run func(context.Context, *bind.CallOpts, bind.Object, module_mcms.TimelockCallbackParams) (*models.SuiTransactionBlockResponse, error)) *IMcms_DispatchTimelockUpdateMinDelay_Call { - _c.Call.Return(run) - return _c -} - // Encoder provides a mock function with no fields func (_m *IMcms) Encoder() module_mcms.McmsEncoder { ret := _m.Called() diff --git a/sdk/sui/mocks/mcms/mcmsencoder.go b/sdk/sui/mocks/mcms/mcmsencoder.go index 811711cef..dcf2be7ea 100644 --- a/sdk/sui/mocks/mcms/mcmsencoder.go +++ b/sdk/sui/mocks/mcms/mcmsencoder.go @@ -1151,132 +1151,6 @@ func (_c *McmsEncoder_DataWithArgs_Call) RunAndReturn(run func(...interface{}) ( return _c } -// DispatchTimelockBlockFunction provides a mock function with given fields: timelock, timelockCallbackParams -func (_m *McmsEncoder) DispatchTimelockBlockFunction(timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error) { - ret := _m.Called(timelock, timelockCallbackParams) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockBlockFunction") - } - - var r0 *bind.EncodedCall - var r1 error - if rf, ok := ret.Get(0).(func(bind.Object, module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error)); ok { - return rf(timelock, timelockCallbackParams) - } - if rf, ok := ret.Get(0).(func(bind.Object, module_mcms.TimelockCallbackParams) *bind.EncodedCall); ok { - r0 = rf(timelock, timelockCallbackParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*bind.EncodedCall) - } - } - - if rf, ok := ret.Get(1).(func(bind.Object, module_mcms.TimelockCallbackParams) error); ok { - r1 = rf(timelock, timelockCallbackParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// McmsEncoder_DispatchTimelockBlockFunction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockBlockFunction' -type McmsEncoder_DispatchTimelockBlockFunction_Call struct { - *mock.Call -} - -// DispatchTimelockBlockFunction is a helper method to define mock.On call -// - timelock bind.Object -// - timelockCallbackParams module_mcms.TimelockCallbackParams -func (_e *McmsEncoder_Expecter) DispatchTimelockBlockFunction(timelock interface{}, timelockCallbackParams interface{}) *McmsEncoder_DispatchTimelockBlockFunction_Call { - return &McmsEncoder_DispatchTimelockBlockFunction_Call{Call: _e.mock.On("DispatchTimelockBlockFunction", timelock, timelockCallbackParams)} -} - -func (_c *McmsEncoder_DispatchTimelockBlockFunction_Call) Run(run func(timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams)) *McmsEncoder_DispatchTimelockBlockFunction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bind.Object), args[1].(module_mcms.TimelockCallbackParams)) - }) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockBlockFunction_Call) Return(_a0 *bind.EncodedCall, _a1 error) *McmsEncoder_DispatchTimelockBlockFunction_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockBlockFunction_Call) RunAndReturn(run func(bind.Object, module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error)) *McmsEncoder_DispatchTimelockBlockFunction_Call { - _c.Call.Return(run) - return _c -} - -// DispatchTimelockBlockFunctionWithArgs provides a mock function with given fields: args -func (_m *McmsEncoder) DispatchTimelockBlockFunctionWithArgs(args ...interface{}) (*bind.EncodedCall, error) { - var _ca []interface{} - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockBlockFunctionWithArgs") - } - - var r0 *bind.EncodedCall - var r1 error - if rf, ok := ret.Get(0).(func(...interface{}) (*bind.EncodedCall, error)); ok { - return rf(args...) - } - if rf, ok := ret.Get(0).(func(...interface{}) *bind.EncodedCall); ok { - r0 = rf(args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*bind.EncodedCall) - } - } - - if rf, ok := ret.Get(1).(func(...interface{}) error); ok { - r1 = rf(args...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockBlockFunctionWithArgs' -type McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call struct { - *mock.Call -} - -// DispatchTimelockBlockFunctionWithArgs is a helper method to define mock.On call -// - args ...interface{} -func (_e *McmsEncoder_Expecter) DispatchTimelockBlockFunctionWithArgs(args ...interface{}) *McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call { - return &McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call{Call: _e.mock.On("DispatchTimelockBlockFunctionWithArgs", - append([]interface{}{}, args...)...)} -} - -func (_c *McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call) Run(run func(args ...interface{})) *McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call) Return(_a0 *bind.EncodedCall, _a1 error) *McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call) RunAndReturn(run func(...interface{}) (*bind.EncodedCall, error)) *McmsEncoder_DispatchTimelockBlockFunctionWithArgs_Call { - _c.Call.Return(run) - return _c -} - // DispatchTimelockBypasserExecuteBatch provides a mock function with given fields: timelockCallbackParams func (_m *McmsEncoder) DispatchTimelockBypasserExecuteBatch(timelockCallbackParams module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error) { ret := _m.Called(timelockCallbackParams) @@ -1783,258 +1657,6 @@ func (_c *McmsEncoder_DispatchTimelockScheduleBatchWithArgs_Call) RunAndReturn(r return _c } -// DispatchTimelockUnblockFunction provides a mock function with given fields: timelock, timelockCallbackParams -func (_m *McmsEncoder) DispatchTimelockUnblockFunction(timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error) { - ret := _m.Called(timelock, timelockCallbackParams) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockUnblockFunction") - } - - var r0 *bind.EncodedCall - var r1 error - if rf, ok := ret.Get(0).(func(bind.Object, module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error)); ok { - return rf(timelock, timelockCallbackParams) - } - if rf, ok := ret.Get(0).(func(bind.Object, module_mcms.TimelockCallbackParams) *bind.EncodedCall); ok { - r0 = rf(timelock, timelockCallbackParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*bind.EncodedCall) - } - } - - if rf, ok := ret.Get(1).(func(bind.Object, module_mcms.TimelockCallbackParams) error); ok { - r1 = rf(timelock, timelockCallbackParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// McmsEncoder_DispatchTimelockUnblockFunction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockUnblockFunction' -type McmsEncoder_DispatchTimelockUnblockFunction_Call struct { - *mock.Call -} - -// DispatchTimelockUnblockFunction is a helper method to define mock.On call -// - timelock bind.Object -// - timelockCallbackParams module_mcms.TimelockCallbackParams -func (_e *McmsEncoder_Expecter) DispatchTimelockUnblockFunction(timelock interface{}, timelockCallbackParams interface{}) *McmsEncoder_DispatchTimelockUnblockFunction_Call { - return &McmsEncoder_DispatchTimelockUnblockFunction_Call{Call: _e.mock.On("DispatchTimelockUnblockFunction", timelock, timelockCallbackParams)} -} - -func (_c *McmsEncoder_DispatchTimelockUnblockFunction_Call) Run(run func(timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams)) *McmsEncoder_DispatchTimelockUnblockFunction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bind.Object), args[1].(module_mcms.TimelockCallbackParams)) - }) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUnblockFunction_Call) Return(_a0 *bind.EncodedCall, _a1 error) *McmsEncoder_DispatchTimelockUnblockFunction_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUnblockFunction_Call) RunAndReturn(run func(bind.Object, module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error)) *McmsEncoder_DispatchTimelockUnblockFunction_Call { - _c.Call.Return(run) - return _c -} - -// DispatchTimelockUnblockFunctionWithArgs provides a mock function with given fields: args -func (_m *McmsEncoder) DispatchTimelockUnblockFunctionWithArgs(args ...interface{}) (*bind.EncodedCall, error) { - var _ca []interface{} - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockUnblockFunctionWithArgs") - } - - var r0 *bind.EncodedCall - var r1 error - if rf, ok := ret.Get(0).(func(...interface{}) (*bind.EncodedCall, error)); ok { - return rf(args...) - } - if rf, ok := ret.Get(0).(func(...interface{}) *bind.EncodedCall); ok { - r0 = rf(args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*bind.EncodedCall) - } - } - - if rf, ok := ret.Get(1).(func(...interface{}) error); ok { - r1 = rf(args...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockUnblockFunctionWithArgs' -type McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call struct { - *mock.Call -} - -// DispatchTimelockUnblockFunctionWithArgs is a helper method to define mock.On call -// - args ...interface{} -func (_e *McmsEncoder_Expecter) DispatchTimelockUnblockFunctionWithArgs(args ...interface{}) *McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call { - return &McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call{Call: _e.mock.On("DispatchTimelockUnblockFunctionWithArgs", - append([]interface{}{}, args...)...)} -} - -func (_c *McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call) Run(run func(args ...interface{})) *McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call) Return(_a0 *bind.EncodedCall, _a1 error) *McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call) RunAndReturn(run func(...interface{}) (*bind.EncodedCall, error)) *McmsEncoder_DispatchTimelockUnblockFunctionWithArgs_Call { - _c.Call.Return(run) - return _c -} - -// DispatchTimelockUpdateMinDelay provides a mock function with given fields: timelock, timelockCallbackParams -func (_m *McmsEncoder) DispatchTimelockUpdateMinDelay(timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error) { - ret := _m.Called(timelock, timelockCallbackParams) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockUpdateMinDelay") - } - - var r0 *bind.EncodedCall - var r1 error - if rf, ok := ret.Get(0).(func(bind.Object, module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error)); ok { - return rf(timelock, timelockCallbackParams) - } - if rf, ok := ret.Get(0).(func(bind.Object, module_mcms.TimelockCallbackParams) *bind.EncodedCall); ok { - r0 = rf(timelock, timelockCallbackParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*bind.EncodedCall) - } - } - - if rf, ok := ret.Get(1).(func(bind.Object, module_mcms.TimelockCallbackParams) error); ok { - r1 = rf(timelock, timelockCallbackParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// McmsEncoder_DispatchTimelockUpdateMinDelay_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockUpdateMinDelay' -type McmsEncoder_DispatchTimelockUpdateMinDelay_Call struct { - *mock.Call -} - -// DispatchTimelockUpdateMinDelay is a helper method to define mock.On call -// - timelock bind.Object -// - timelockCallbackParams module_mcms.TimelockCallbackParams -func (_e *McmsEncoder_Expecter) DispatchTimelockUpdateMinDelay(timelock interface{}, timelockCallbackParams interface{}) *McmsEncoder_DispatchTimelockUpdateMinDelay_Call { - return &McmsEncoder_DispatchTimelockUpdateMinDelay_Call{Call: _e.mock.On("DispatchTimelockUpdateMinDelay", timelock, timelockCallbackParams)} -} - -func (_c *McmsEncoder_DispatchTimelockUpdateMinDelay_Call) Run(run func(timelock bind.Object, timelockCallbackParams module_mcms.TimelockCallbackParams)) *McmsEncoder_DispatchTimelockUpdateMinDelay_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bind.Object), args[1].(module_mcms.TimelockCallbackParams)) - }) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUpdateMinDelay_Call) Return(_a0 *bind.EncodedCall, _a1 error) *McmsEncoder_DispatchTimelockUpdateMinDelay_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUpdateMinDelay_Call) RunAndReturn(run func(bind.Object, module_mcms.TimelockCallbackParams) (*bind.EncodedCall, error)) *McmsEncoder_DispatchTimelockUpdateMinDelay_Call { - _c.Call.Return(run) - return _c -} - -// DispatchTimelockUpdateMinDelayWithArgs provides a mock function with given fields: args -func (_m *McmsEncoder) DispatchTimelockUpdateMinDelayWithArgs(args ...interface{}) (*bind.EncodedCall, error) { - var _ca []interface{} - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for DispatchTimelockUpdateMinDelayWithArgs") - } - - var r0 *bind.EncodedCall - var r1 error - if rf, ok := ret.Get(0).(func(...interface{}) (*bind.EncodedCall, error)); ok { - return rf(args...) - } - if rf, ok := ret.Get(0).(func(...interface{}) *bind.EncodedCall); ok { - r0 = rf(args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*bind.EncodedCall) - } - } - - if rf, ok := ret.Get(1).(func(...interface{}) error); ok { - r1 = rf(args...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchTimelockUpdateMinDelayWithArgs' -type McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call struct { - *mock.Call -} - -// DispatchTimelockUpdateMinDelayWithArgs is a helper method to define mock.On call -// - args ...interface{} -func (_e *McmsEncoder_Expecter) DispatchTimelockUpdateMinDelayWithArgs(args ...interface{}) *McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call { - return &McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call{Call: _e.mock.On("DispatchTimelockUpdateMinDelayWithArgs", - append([]interface{}{}, args...)...)} -} - -func (_c *McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call) Run(run func(args ...interface{})) *McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call) Return(_a0 *bind.EncodedCall, _a1 error) *McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call) RunAndReturn(run func(...interface{}) (*bind.EncodedCall, error)) *McmsEncoder_DispatchTimelockUpdateMinDelayWithArgs_Call { - _c.Call.Return(run) - return _c -} - // Execute provides a mock function with given fields: state, clock, role, chainId, multisigAddr, nonce, to, moduleName, functionName, data, proof func (_m *McmsEncoder) Execute(state bind.Object, clock bind.Object, role byte, chainId *big.Int, multisigAddr string, nonce uint64, to string, moduleName string, functionName string, data []byte, proof [][]byte) (*bind.EncodedCall, error) { ret := _m.Called(state, clock, role, chainId, multisigAddr, nonce, to, moduleName, functionName, data, proof) diff --git a/sdk/sui/sui_helpers.go b/sdk/sui/sui_helpers.go new file mode 100644 index 000000000..1c167c146 --- /dev/null +++ b/sdk/sui/sui_helpers.go @@ -0,0 +1,23 @@ +package sui + +import ( + "encoding/json" + "fmt" + + "github.com/smartcontractkit/mcms/types" +) + +func SuiMetadata(chainMetadata types.ChainMetadata) (AdditionalFieldsMetadata, error) { + var metadata AdditionalFieldsMetadata + err := json.Unmarshal([]byte(chainMetadata.AdditionalFields), &metadata) + if err != nil { + return AdditionalFieldsMetadata{}, fmt.Errorf("error unmarshaling sui chain metadata: %w", err) + } + + err = metadata.Validate() + if err != nil { + return AdditionalFieldsMetadata{}, fmt.Errorf("error validating sui chain metadata: %w", err) + } + + return metadata, nil +} diff --git a/sdk/sui/sui_helpers_test.go b/sdk/sui/sui_helpers_test.go new file mode 100644 index 000000000..468dcdf01 --- /dev/null +++ b/sdk/sui/sui_helpers_test.go @@ -0,0 +1,76 @@ +package sui + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/mcms/types" +) + +func TestSuiMetadata(t *testing.T) { + t.Parallel() + + validMetadata := types.ChainMetadata{ + AdditionalFields: []byte(`{"mcms_package_id":"0x1","role":1,"account_obj":"0x2","registry_obj":"0x3","timelock_obj":"0x4","deployer_state_obj":"0x5"}`), + } + + tests := []struct { + name string + metadata types.ChainMetadata + expectError bool + errorMsg string + }{ + { + name: "valid metadata returns success", + metadata: validMetadata, + }, + { + name: "invalid JSON returns error", + metadata: types.ChainMetadata{ + AdditionalFields: []byte(`{"mcms_package_id":"0x1","role":1`), + }, + expectError: true, + errorMsg: "error unmarshaling sui chain metadata", + }, + { + name: "missing required fields returns validation error", + metadata: types.ChainMetadata{ + AdditionalFields: []byte(`{"role":1}`), + }, + expectError: true, + errorMsg: "error validating sui chain metadata", + }, + { + name: "empty additional fields returns unmarshaling error", + metadata: types.ChainMetadata{ + AdditionalFields: nil, + }, + expectError: true, + errorMsg: "error unmarshaling sui chain metadata", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + metadata, err := SuiMetadata(tt.metadata) + if tt.expectError { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errorMsg) + assert.Equal(t, AdditionalFieldsMetadata{}, metadata) + + return + } + + require.NoError(t, err) + assert.Equal(t, "0x1", metadata.McmsPackageID) + assert.Equal(t, TimelockRole(1), metadata.Role) + assert.Equal(t, "0x2", metadata.AccountObj) + assert.Equal(t, "0x3", metadata.RegistryObj) + assert.Equal(t, "0x4", metadata.TimelockObj) + }) + } +} diff --git a/sdk/sui/utils.go b/sdk/sui/utils.go index 0782cd745..477f346cb 100644 --- a/sdk/sui/utils.go +++ b/sdk/sui/utils.go @@ -8,6 +8,15 @@ import ( const AddressLen = 32 +// TODO: this interface should come from chainlink-sui when available +type SuiSigner interface { + // Sign signs the given message and returns the serialized signature. + Sign(message []byte) ([]string, error) + + // GetAddress returns the Sui address derived from the signer's public key + GetAddress() (string, error) +} + type Address [AddressLen]uint8 func AddressFromHex(str string) (*Address, error) { diff --git a/sdk/ton/config_transformer.go b/sdk/ton/config_transformer.go index 69349c2c6..0b274a96d 100644 --- a/sdk/ton/config_transformer.go +++ b/sdk/ton/config_transformer.go @@ -33,7 +33,7 @@ func NewConfigTransformer() ConfigTransformer { return &configTransformer{} } // ToChainConfig converts the chain agnostic config to the chain-specific config func (e *configTransformer) ToChainConfig(cfg types.Config, _ any) (mcms.Config, error) { - groupQuorum, groupParents, signerAddrs, signerGroups, err := evm.ExtractSetConfigInputs(&cfg) + groupQuorum, groupParents, signerAddrs, signerGroups, err := sdk.ExtractSetConfigInputs(&cfg) if err != nil { return mcms.Config{}, fmt.Errorf("unable to extract set config inputs: %w", err) } diff --git a/sdk/ton/configurer.go b/sdk/ton/configurer.go index d49ccc5a9..89c6b880d 100644 --- a/sdk/ton/configurer.go +++ b/sdk/ton/configurer.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" "github.com/smartcontractkit/mcms/sdk" - "github.com/smartcontractkit/mcms/sdk/evm" + "github.com/smartcontractkit/mcms/types" "github.com/xssnick/tonutils-go/address" @@ -62,7 +62,7 @@ func (c configurer) SetConfig(ctx context.Context, mcmsAddr string, cfg *types.C return types.TransactionResult{}, fmt.Errorf("invalid mcms address: %w", err) } - groupQuorum, groupParents, signerAddresses, _signerGroups, err := evm.ExtractSetConfigInputs(cfg) + groupQuorum, groupParents, signerAddresses, _signerGroups, err := sdk.ExtractSetConfigInputs(cfg) if err != nil { return types.TransactionResult{}, fmt.Errorf("unable to extract set config inputs: %w", err) } diff --git a/signable_test.go b/signable_test.go index d3b8df919..672b7e7af 100644 --- a/signable_test.go +++ b/signable_test.go @@ -460,8 +460,8 @@ func TestSignable_SingleChainMultipleSignerMultipleTX_FailureMissingQuorum(t *te // Validate the signatures quorumMet, err := signable.ValidateSignatures(ctx) require.Error(t, err) - //nolint:testifylint // Allow IsType for error type checking - require.IsType(t, &QuorumNotReachedError{}, err) + + require.EqualError(t, QuorumNotReachedError{ChainSelector: chaintest.Chain1Selector}, err.Error()) require.False(t, quorumMet) }