Skip to content

Commit d5d9b16

Browse files
authoredFeb 24, 2025··
feat(solana)!: enable calling SetConfig without sending the transaction (#310)
This PR enables clients to call SetConfig() without sending the instructions to the blockchain. Its purpose is to retrieve the instructions that _would_ be sent -- to, for instance, use this instructions to build an mcms proposal. The instructions are returned in the `TransactionResult` object, in the `RawData` attribute (which used to be called `RawTransaction` and was renamed to reflect the fact it can hold other arbitrary types). In order to "activate" this mode, clients must set the optional argument `SkipTransaction`. For instance: ```go // standard SetConfig, instructions are sent on-chain result, err := NewConfigurer(client, auth, chainSelector).SetConfig(...) assert.True(t, solana.IsSignature(result.Hash)) // SkipTransaction() added to SetConfig() -- instructions are NOT sent on-chain result, err := NewConfigurer(client, auth, chainSelector, SkipTransaction()).SetConfig(...) assert.Equal(t, result.Hash, "") assert.GreaterOrEqual(t, len(result.RawData.([]solana.Instruction)), 4) ``` BREAKING_CHANGE: `RawTransaction` renamed to `RawData`
1 parent d8c7115 commit d5d9b16

14 files changed

+298
-93
lines changed
 

‎.changeset/many-paws-type.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smartcontractkit/mcms": minor
3+
---
4+
5+
feat: enable calling SetConfig without sending the transaction

‎e2e/ledger/ledger_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (s *ManualLedgerSigningTestSuite) setRootEVM(
102102
configurer := evm.NewConfigurer(s.ClientA, s.authEVM)
103103
tx, err := configurer.SetConfig(ctx, instance.Address().Hex(), &mcmConfig, true)
104104
s.Require().NoError(err, "Failed to set contract configuration")
105-
_, err = bind.WaitMined(ctx, s.ClientA, tx.RawTransaction.(*gethTypes.Transaction))
105+
_, err = bind.WaitMined(ctx, s.ClientA, tx.RawData.(*gethTypes.Transaction))
106106
s.Require().NoError(err, "Failed to mine set config transaction")
107107

108108
// set root

‎e2e/tests/solana/set_config.go

+49-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import (
88

99
"github.com/ethereum/go-ethereum/common"
1010
"github.com/gagliardetto/solana-go"
11+
12+
"github.com/gagliardetto/solana-go/rpc"
1113
"github.com/google/go-cmp/cmp"
14+
solanaCommon "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common"
1215

1316
mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana"
1417
"github.com/smartcontractkit/mcms/types"
@@ -69,16 +72,51 @@ func (s *SolanaTestSuite) Test_Solana_SetConfig() {
6972
},
7073
}
7174

72-
// --- act ---
73-
configurer := mcmsSolana.NewConfigurer(s.SolanaClient, auth, s.ChainSelector)
74-
signature, err := configurer.SetConfig(ctx, mcmAddress, &config, true)
75-
s.Require().NoError(err)
76-
_, err = solana.SignatureFromBase58(signature.Hash)
77-
s.Require().NoError(err)
75+
s.Run("send and confirm instructions to the blockchain", func() {
76+
// --- act ---
77+
configurer := mcmsSolana.NewConfigurer(s.SolanaClient, auth, s.ChainSelector)
78+
result, err := configurer.SetConfig(ctx, mcmAddress, &config, true)
79+
s.Require().NoError(err)
80+
_, err = solana.SignatureFromBase58(result.Hash)
81+
s.Require().NoError(err)
7882

79-
// --- assert ---
80-
gotConfig, err := mcmsSolana.NewInspector(s.SolanaClient).GetConfig(ctx, mcmAddress)
81-
s.Require().NoError(err)
82-
s.Require().NotNil(gotConfig)
83-
s.Require().Empty(cmp.Diff(config, *gotConfig))
83+
// --- assert ---
84+
gotConfig, err := mcmsSolana.NewInspector(s.SolanaClient).GetConfig(ctx, mcmAddress)
85+
s.Require().NoError(err)
86+
s.Require().NotNil(gotConfig)
87+
s.Require().Empty(cmp.Diff(config, *gotConfig))
88+
})
89+
90+
s.Run("do NOT send transactions to the blockchain", func() {
91+
skipTxConfig := types.Config{
92+
Quorum: 1,
93+
Signers: []common.Address{
94+
testEVMAccounts[0].Address,
95+
},
96+
GroupSigners: []types.Config{},
97+
}
98+
99+
// --- act ---
100+
configurer := mcmsSolana.NewConfigurer(s.SolanaClient, auth, s.ChainSelector, mcmsSolana.WithDoNotSendInstructionsOnChain())
101+
result, err := configurer.SetConfig(ctx, mcmAddress, &skipTxConfig, true)
102+
s.Require().NoError(err)
103+
104+
// --- assert ---
105+
s.Require().Empty(err, result.Hash)
106+
107+
gotConfig, err := mcmsSolana.NewInspector(s.SolanaClient).GetConfig(ctx, mcmAddress)
108+
s.Require().NoError(err)
109+
s.Require().NotNil(gotConfig)
110+
s.Require().Empty(cmp.Diff(config, *gotConfig)) // previous config should still be valid
111+
112+
// manually send instructions from the response and confirm that they modified the config
113+
instructions := result.RawData.([]solana.Instruction)
114+
_, err = solanaCommon.SendAndConfirm(ctx, s.SolanaClient, instructions, auth, rpc.CommitmentConfirmed)
115+
s.Require().NoError(err)
116+
117+
gotConfig, err = mcmsSolana.NewInspector(s.SolanaClient).GetConfig(ctx, mcmAddress)
118+
s.Require().NoError(err)
119+
s.Require().NotNil(gotConfig)
120+
s.Require().Empty(cmp.Diff(skipTxConfig, *gotConfig))
121+
})
84122
}

‎executable_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func TestExecutor_ExecuteE2E_SingleChainSingleSignerSingleTX_Success(t *testing.
194194
require.NoError(t, err)
195195
require.NotNil(t, tx)
196196
require.NotEmpty(t, tx.Hash)
197-
require.NotNil(t, tx.RawTransaction)
197+
require.NotNil(t, tx.RawData)
198198
sim.Backend.Commit()
199199

200200
// Validate Contract State and verify root was set
@@ -208,7 +208,7 @@ func TestExecutor_ExecuteE2E_SingleChainSingleSignerSingleTX_Success(t *testing.
208208
require.NoError(t, err)
209209
require.NotNil(t, tx)
210210
require.NotEmpty(t, tx.Hash)
211-
require.NotNil(t, tx.RawTransaction)
211+
require.NotNil(t, tx.RawData)
212212
sim.Backend.Commit()
213213

214214
// Check the state of the MCMS contract
@@ -322,7 +322,7 @@ func TestExecutor_ExecuteE2E_SingleChainMultipleSignerSingleTX_Success(t *testin
322322
require.NoError(t, err)
323323
require.NotNil(t, tx)
324324
require.NotEmpty(t, tx.Hash)
325-
require.NotNil(t, tx.RawTransaction)
325+
require.NotNil(t, tx.RawData)
326326
sim.Backend.Commit()
327327

328328
// Validate Contract State and verify root was set
@@ -335,7 +335,7 @@ func TestExecutor_ExecuteE2E_SingleChainMultipleSignerSingleTX_Success(t *testin
335335
tx, err = executable.Execute(ctx, 0)
336336
require.NoError(t, err)
337337
require.NotNil(t, tx)
338-
require.NotNil(t, tx.RawTransaction)
338+
require.NotNil(t, tx.RawData)
339339
require.NotEqual(t, "", tx.Hash)
340340
sim.Backend.Commit()
341341

@@ -456,7 +456,7 @@ func TestExecutor_ExecuteE2E_SingleChainSingleSignerMultipleTX_Success(t *testin
456456
tx, err := executable.SetRoot(ctx, chaintest.Chain1Selector)
457457
require.NoError(t, err)
458458
require.NotNil(t, tx)
459-
require.NotNil(t, tx.RawTransaction)
459+
require.NotNil(t, tx.RawData)
460460
require.NotEmpty(t, tx.Hash)
461461
sim.Backend.Commit()
462462

@@ -472,7 +472,7 @@ func TestExecutor_ExecuteE2E_SingleChainSingleSignerMultipleTX_Success(t *testin
472472
tx, err = executable.Execute(ctx, i)
473473
require.NoError(t, err)
474474
require.NotNil(t, tx)
475-
require.NotNil(t, tx.RawTransaction)
475+
require.NotNil(t, tx.RawData)
476476
require.NotEqual(t, "", tx.Hash)
477477

478478
sim.Backend.Commit()
@@ -600,7 +600,7 @@ func TestExecutor_ExecuteE2E_SingleChainMultipleSignerMultipleTX_Success(t *test
600600
tx, err := executable.SetRoot(ctx, chaintest.Chain1Selector)
601601
require.NoError(t, err)
602602
require.NotNil(t, tx)
603-
require.NotNil(t, tx.RawTransaction)
603+
require.NotNil(t, tx.RawData)
604604
require.NotEmpty(t, tx.Hash)
605605

606606
sim.Backend.Commit()
@@ -617,7 +617,7 @@ func TestExecutor_ExecuteE2E_SingleChainMultipleSignerMultipleTX_Success(t *test
617617
tx, err = executable.Execute(ctx, i)
618618
require.NoError(t, err)
619619
require.NotNil(t, tx)
620-
require.NotNil(t, tx.RawTransaction)
620+
require.NotNil(t, tx.RawData)
621621
require.NotEqual(t, "", tx.Hash)
622622

623623
sim.Backend.Commit()

‎sdk/evm/configurer.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ func (c *Configurer) SetConfig(ctx context.Context, mcmAddr string, cfg *types.C
5858
}
5959

6060
return types.TransactionResult{
61-
Hash: tx.Hash().Hex(),
62-
ChainFamily: chain_selectors.FamilyEVM,
63-
RawTransaction: tx,
61+
Hash: tx.Hash().Hex(),
62+
ChainFamily: chain_selectors.FamilyEVM,
63+
RawData: tx,
6464
}, nil
6565
}

‎sdk/evm/executor.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ func (e *Executor) ExecuteOperation(
5959
}
6060

6161
return types.TransactionResult{
62-
Hash: tx.Hash().Hex(),
63-
ChainFamily: chain_selectors.FamilyEVM,
64-
RawTransaction: tx,
62+
Hash: tx.Hash().Hex(),
63+
ChainFamily: chain_selectors.FamilyEVM,
64+
RawData: tx,
6565
}, err
6666
}
6767

@@ -103,8 +103,8 @@ func (e *Executor) SetRoot(
103103
}
104104

105105
return types.TransactionResult{
106-
Hash: tx.Hash().Hex(),
107-
ChainFamily: chain_selectors.FamilyEVM,
108-
RawTransaction: tx,
106+
Hash: tx.Hash().Hex(),
107+
ChainFamily: chain_selectors.FamilyEVM,
108+
RawData: tx,
109109
}, err
110110
}

‎sdk/evm/timelock_executor.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ func (t *TimelockExecutor) Execute(
6666
}
6767

6868
return types.TransactionResult{
69-
Hash: tx.Hash().Hex(),
70-
ChainFamily: chain_selectors.FamilyEVM,
71-
RawTransaction: tx,
69+
Hash: tx.Hash().Hex(),
70+
ChainFamily: chain_selectors.FamilyEVM,
71+
RawData: tx,
7272
}, nil
7373
}

‎sdk/solana/common.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,18 @@ func sendAndConfirm(
153153
return "", nil, fmt.Errorf("unable to validate and build instruction: %w", err)
154154
}
155155

156-
result, err := solanaCommon.SendAndConfirm(ctx, client, []solana.Instruction{instruction}, auth, commitmentType)
156+
return sendAndConfirmInstructions(ctx, client, auth, []solana.Instruction{instruction}, commitmentType)
157+
}
158+
159+
// sendAndConfirm contains the default logic for sending and confirming instructions.
160+
func sendAndConfirmInstructions(
161+
ctx context.Context,
162+
client *rpc.Client,
163+
auth solana.PrivateKey,
164+
instructions []solana.Instruction,
165+
commitmentType rpc.CommitmentType,
166+
) (string, *rpc.GetTransactionResult, error) {
167+
result, err := solanaCommon.SendAndConfirm(ctx, client, instructions, auth, commitmentType)
157168
if err != nil {
158169
return "", nil, fmt.Errorf("unable to send instruction: %w", err)
159170
}

‎sdk/solana/configurer.go

+113-25
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,53 @@ var _ sdk.Configurer = &Configurer{}
2020

2121
// Configurer configures the MCM contract for Solana chains.
2222
type Configurer struct {
23+
instructionCollection
2324
chainSelector types.ChainSelector
2425
client *rpc.Client
2526
auth solana.PrivateKey
27+
skipSend bool
2628
}
2729

2830
// NewConfigurer creates a new Configurer for Solana chains.
29-
func NewConfigurer(client *rpc.Client, auth solana.PrivateKey, chainSelector types.ChainSelector) *Configurer {
30-
return &Configurer{
31+
//
32+
// options:
33+
//
34+
// WithDoNotSendInstructionsOnChain: when selected, the Configurer instance will not
35+
// send the Solana instructions to the blockchain.
36+
func NewConfigurer(
37+
client *rpc.Client, auth solana.PrivateKey, chainSelector types.ChainSelector, options ...configurerOption,
38+
) *Configurer {
39+
configurer := &Configurer{
3140
client: client,
3241
auth: auth,
3342
chainSelector: chainSelector,
43+
skipSend: false,
44+
}
45+
for _, opt := range options {
46+
opt(configurer)
47+
}
48+
49+
return configurer
50+
}
51+
52+
type configurerOption func(*Configurer)
53+
54+
func WithDoNotSendInstructionsOnChain() configurerOption {
55+
return func(c *Configurer) {
56+
c.skipSend = true
3457
}
3558
}
3659

3760
// SetConfig sets the configuration for the MCM contract on the Solana chain.
38-
func (c *Configurer) SetConfig(ctx context.Context, mcmAddress string, cfg *types.Config, clearRoot bool) (types.TransactionResult, error) {
61+
//
62+
// The list of instructions needed to set the configuration is returned in the
63+
// `RawData` field. And if the instructions were sent on chain (which they are
64+
// unless the `WithDoNotSendInstructionsOnChain` option was selected in the
65+
// constructor), the signature of the last instruction is returned in the
66+
// `Hash` field.
67+
func (c *Configurer) SetConfig(
68+
ctx context.Context, mcmAddress string, cfg *types.Config, clearRoot bool,
69+
) (types.TransactionResult, error) {
3970
programID, pdaSeed, err := ParseContractAddress(mcmAddress)
4071
if err != nil {
4172
return types.TransactionResult{}, err
@@ -71,12 +102,15 @@ func (c *Configurer) SetConfig(ctx context.Context, mcmAddress string, cfg *type
71102
return types.TransactionResult{}, err
72103
}
73104

74-
err = c.preloadSigners(ctx, pdaSeed, solanaSignerAddresses(signerAddresses), configPDA, configSignersPDA)
105+
clear(c.instructions)
106+
defer clear(c.instructions)
107+
108+
err = c.preloadSigners(pdaSeed, solanaSignerAddresses(signerAddresses), configPDA, configSignersPDA)
75109
if err != nil {
76110
return types.TransactionResult{}, fmt.Errorf("unable to preload signatures: %w", err)
77111
}
78112

79-
setConfigInstruction := bindings.NewSetConfigInstruction(
113+
err = c.addInstruction("setConfig", bindings.NewSetConfigInstruction(
80114
pdaSeed,
81115
signerGroups,
82116
groupQuorums,
@@ -87,47 +121,50 @@ func (c *Configurer) SetConfig(ctx context.Context, mcmAddress string, cfg *type
87121
rootMetadataPDA,
88122
expiringRootAndOpCountPDA,
89123
c.auth.PublicKey(),
90-
solana.SystemProgramID)
91-
signature, tx, err := sendAndConfirm(ctx, c.client, c.auth, setConfigInstruction, rpc.CommitmentConfirmed)
124+
solana.SystemProgramID))
92125
if err != nil {
93-
return types.TransactionResult{}, fmt.Errorf("unable to set config: %w", err)
126+
return types.TransactionResult{}, err
127+
}
128+
129+
var signature string
130+
if !c.skipSend {
131+
signature, err = c.sendInstructions(ctx, c.client, c.auth)
132+
if err != nil {
133+
return types.TransactionResult{}, fmt.Errorf("unable to set config: %w", err)
134+
}
94135
}
95136

96137
return types.TransactionResult{
97-
Hash: signature,
98-
ChainFamily: chain_selectors.FamilySolana,
99-
RawTransaction: tx,
138+
Hash: signature,
139+
ChainFamily: chain_selectors.FamilySolana,
140+
RawData: c.solanaInstructions(),
100141
}, nil
101142
}
102143

103144
func (c *Configurer) preloadSigners(
104-
ctx context.Context,
105145
mcmName [32]byte,
106146
signerAddresses [][20]uint8,
107147
configPDA solana.PublicKey,
108148
configSignersPDA solana.PublicKey,
109149
) error {
110-
initSignersInstruction := bindings.NewInitSignersInstruction(mcmName, uint8(len(signerAddresses)), configPDA, //nolint:gosec
111-
configSignersPDA, c.auth.PublicKey(), solana.SystemProgramID)
112-
_, _, err := sendAndConfirm(ctx, c.client, c.auth, initSignersInstruction, rpc.CommitmentConfirmed)
150+
err := c.addInstruction("initSigners", bindings.NewInitSignersInstruction(mcmName, uint8(len(signerAddresses)), //nolint:gosec
151+
configPDA, configSignersPDA, c.auth.PublicKey(), solana.SystemProgramID))
113152
if err != nil {
114-
return fmt.Errorf("unable to initialize signers: %w", err)
153+
return err
115154
}
116155

117156
for i, chunkIndex := range chunkIndexes(len(signerAddresses), config.MaxAppendSignerBatchSize) {
118-
appendSignersInstructions := bindings.NewAppendSignersInstruction(mcmName,
119-
signerAddresses[chunkIndex[0]:chunkIndex[1]], configPDA, configSignersPDA, c.auth.PublicKey())
120-
_, _, aerr := sendAndConfirm(ctx, c.client, c.auth, appendSignersInstructions, rpc.CommitmentConfirmed)
121-
if aerr != nil {
122-
return fmt.Errorf("unable to append signers (%d): %w", i, aerr)
157+
err = c.addInstruction(fmt.Sprintf("appendSigners%d", i), bindings.NewAppendSignersInstruction(mcmName,
158+
signerAddresses[chunkIndex[0]:chunkIndex[1]], configPDA, configSignersPDA, c.auth.PublicKey()))
159+
if err != nil {
160+
return err
123161
}
124162
}
125163

126-
finalizeSignersInstruction := bindings.NewFinalizeSignersInstruction(mcmName, configPDA, configSignersPDA,
127-
c.auth.PublicKey())
128-
_, _, err = sendAndConfirm(ctx, c.client, c.auth, finalizeSignersInstruction, rpc.CommitmentConfirmed)
164+
err = c.addInstruction("finalizeSigners", bindings.NewFinalizeSignersInstruction(mcmName, configPDA,
165+
configSignersPDA, c.auth.PublicKey()))
129166
if err != nil {
130-
return fmt.Errorf("unable to finalize signers: %w", err)
167+
return err
131168
}
132169

133170
return nil
@@ -141,3 +178,54 @@ func solanaSignerAddresses(evmAddresses []evmCommon.Address) [][20]uint8 {
141178

142179
return solanaAddresses
143180
}
181+
182+
type labeledInstruction struct {
183+
solana.Instruction
184+
label string
185+
}
186+
187+
type instructionCollection struct {
188+
instructions []labeledInstruction
189+
}
190+
191+
func (c *instructionCollection) solanaInstructions() []solana.Instruction {
192+
solanaInstructions := make([]solana.Instruction, len(c.instructions))
193+
for i, instruction := range c.instructions {
194+
solanaInstructions[i] = instruction.Instruction
195+
}
196+
197+
return solanaInstructions
198+
}
199+
200+
func (c *instructionCollection) addInstruction(label string, instructionBuilder any) error {
201+
instruction, err := validateAndBuildSolanaInstruction(instructionBuilder)
202+
if err != nil {
203+
return fmt.Errorf("unable to validate and build %s instruction: %w", label, err)
204+
}
205+
206+
c.instructions = append(c.instructions, labeledInstruction{instruction, label})
207+
208+
return nil
209+
}
210+
211+
func (c *instructionCollection) sendInstructions(
212+
ctx context.Context,
213+
client *rpc.Client,
214+
auth solana.PrivateKey,
215+
) (string, error) {
216+
if len(auth) == 0 {
217+
return "", nil
218+
}
219+
220+
var signature string
221+
var err error
222+
for i, instruction := range c.instructions {
223+
signature, _, err = sendAndConfirmInstructions(ctx, client, auth,
224+
[]solana.Instruction{instruction}, rpc.CommitmentConfirmed)
225+
if err != nil {
226+
return "", fmt.Errorf("unable to send instruction %d - %s: %w", i, instruction.label, err)
227+
}
228+
}
229+
230+
return signature, nil
231+
}

‎sdk/solana/configurer_test.go

+80-17
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import (
66
"testing"
77

88
"github.com/ethereum/go-ethereum/common"
9+
ag_binary "github.com/gagliardetto/binary"
910
"github.com/gagliardetto/solana-go"
1011
"github.com/gagliardetto/solana-go/rpc"
1112
"github.com/google/go-cmp/cmp"
13+
"github.com/google/go-cmp/cmp/cmpopts"
1214
cselectors "github.com/smartcontractkit/chain-selectors"
15+
bindings "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/mcm"
1316
"github.com/stretchr/testify/require"
1417

1518
"github.com/smartcontractkit/mcms/sdk/solana/mocks"
@@ -23,9 +26,36 @@ func Test_NewConfigurer(t *testing.T) {
2326
auth := solana.MustPrivateKeyFromBase58("DmPfeHBC8Brf8s5qQXi25bmJ996v6BHRtaLc6AH51yFGSqQpUMy1oHkbbXobPNBdgGH2F29PAmoq9ZZua4K9vCc")
2427
chainSelector := types.ChainSelector(cselectors.SOLANA_DEVNET.Selector)
2528

26-
configurer := NewConfigurer(client, auth, chainSelector)
29+
tests := []struct {
30+
name string
31+
constructorFn func() *Configurer
32+
wantSkipTransaction bool
33+
}{
34+
{
35+
name: "standard args",
36+
constructorFn: func() *Configurer {
37+
return NewConfigurer(client, auth, chainSelector)
38+
},
39+
wantSkipTransaction: false,
40+
},
41+
{
42+
name: "skip transaction option",
43+
constructorFn: func() *Configurer {
44+
return NewConfigurer(client, auth, chainSelector, WithDoNotSendInstructionsOnChain())
45+
},
46+
wantSkipTransaction: true,
47+
},
48+
}
2749

28-
require.NotNil(t, configurer)
50+
for _, tt := range tests {
51+
t.Run(tt.name, func(t *testing.T) {
52+
t.Parallel()
53+
configurer := tt.constructorFn()
54+
55+
require.NotNil(t, configurer)
56+
require.Equal(t, tt.wantSkipTransaction, configurer.skipSend)
57+
})
58+
}
2959
}
3060

3161
func TestConfigurer_SetConfig(t *testing.T) {
@@ -39,14 +69,18 @@ func TestConfigurer_SetConfig(t *testing.T) {
3969
clearRoot := false
4070

4171
tests := []struct {
42-
name string
43-
mcmConfig *types.Config
44-
setup func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient)
45-
want string
46-
wantErr string
72+
name string
73+
auth solana.PrivateKey
74+
options []configurerOption
75+
mcmConfig *types.Config
76+
setup func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient)
77+
wantHash string
78+
wantInstructions []solana.Instruction
79+
wantErr string
4780
}{
4881
{
49-
name: "success",
82+
name: "success - send instructions",
83+
auth: auth,
5084
mcmConfig: defaultMcmConfig,
5185
setup: func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient) {
5286
t.Helper()
@@ -62,16 +96,38 @@ func TestConfigurer_SetConfig(t *testing.T) {
6296
mockSolanaTransaction(t, mockJSONRPCClient, 13, 23,
6397
"52f3VmvW7m9uTQu3PtyibgxnAvEuXDmm9umuHherGjS4pzRR7QXRDKnZhh6b95P7pQxzTgvE1muMNKYEY7YWsS3G", nil, nil)
6498
},
65-
want: "52f3VmvW7m9uTQu3PtyibgxnAvEuXDmm9umuHherGjS4pzRR7QXRDKnZhh6b95P7pQxzTgvE1muMNKYEY7YWsS3G",
99+
wantHash: "52f3VmvW7m9uTQu3PtyibgxnAvEuXDmm9umuHherGjS4pzRR7QXRDKnZhh6b95P7pQxzTgvE1muMNKYEY7YWsS3G",
100+
wantInstructions: []solana.Instruction{
101+
bindings.NewInitSignersInstructionBuilder().Build(),
102+
bindings.NewAppendSignersInstructionBuilder().Build(),
103+
bindings.NewFinalizeSignersInstructionBuilder().Build(),
104+
bindings.NewSetConfigInstructionBuilder().Build(),
105+
},
106+
},
107+
{
108+
name: "success - do not send instructions",
109+
auth: auth,
110+
options: []configurerOption{WithDoNotSendInstructionsOnChain()},
111+
mcmConfig: defaultMcmConfig,
112+
setup: func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient) { t.Helper() },
113+
wantHash: "",
114+
wantInstructions: []solana.Instruction{
115+
bindings.NewInitSignersInstructionBuilder().Build(),
116+
bindings.NewAppendSignersInstructionBuilder().Build(),
117+
bindings.NewFinalizeSignersInstructionBuilder().Build(),
118+
bindings.NewSetConfigInstructionBuilder().Build(),
119+
},
66120
},
67121
{
68122
name: "failure: too many signers",
123+
auth: auth,
69124
mcmConfig: &types.Config{Quorum: 1, Signers: generateSigners(t, 181)},
70125
setup: func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient) { t.Helper() },
71126
wantErr: "too many signers (max 180)",
72127
},
73128
{
74129
name: "failure: initialize signers error",
130+
auth: auth,
75131
mcmConfig: defaultMcmConfig,
76132
setup: func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient) {
77133
t.Helper()
@@ -80,10 +136,11 @@ func TestConfigurer_SetConfig(t *testing.T) {
80136
"4PQcRHQJT4cRQZooAhZMAP9ZXJsAka9DeKvXeYvXAvPpHb4Qkc5rmTSHDA2SZSh9aKPBguBx4kmcyHHbkytoAiRr",
81137
nil, fmt.Errorf("initialize signers error"))
82138
},
83-
wantErr: "unable to initialize signers: unable to send instruction: initialize signers error",
139+
wantErr: "unable to set config: unable to send instruction 0 - initSigners: unable to send instruction: initialize signers error",
84140
},
85141
{
86142
name: "failure: append signers error",
143+
auth: auth,
87144
mcmConfig: defaultMcmConfig,
88145
setup: func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient) {
89146
t.Helper()
@@ -97,10 +154,11 @@ func TestConfigurer_SetConfig(t *testing.T) {
97154
"7D9XEYRnCn1D5JFrrYMPUaHfog7Vnj5rbPdj7kbULa4hKq7GsnA7Q8KNQfLEgfCawBsW4dcH2MQAp4km1dnjr6V",
98155
nil, fmt.Errorf("append signers error"))
99156
},
100-
wantErr: "unable to append signers (0): unable to send instruction: append signers error",
157+
wantErr: "unable to set config: unable to send instruction 1 - appendSigners0: unable to send instruction: append signers error",
101158
},
102159
{
103160
name: "failure: finalize signers error",
161+
auth: auth,
104162
mcmConfig: defaultMcmConfig,
105163
setup: func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient) {
106164
t.Helper()
@@ -116,10 +174,11 @@ func TestConfigurer_SetConfig(t *testing.T) {
116174
"2iEeniu3QUgXNsjau8r7fZ7XLb2g1F3q9VJJKvRyyFz4hHgVvhGkLgSUdmRumfXKWv8spJ9ihudGFyPZsPGdp4Ya",
117175
nil, fmt.Errorf("finalize signers error"))
118176
},
119-
wantErr: "unable to finalize signers: unable to send instruction: finalize signers error",
177+
wantErr: "unable to set config: unable to send instruction 2 - finalizeSigners: unable to send instruction: finalize signers error",
120178
},
121179
{
122180
name: "failure: set config error",
181+
auth: auth,
123182
mcmConfig: &types.Config{Quorum: 1, Signers: []common.Address{common.HexToAddress("0x1")}},
124183
setup: func(t *testing.T, configurer *Configurer, mockJSONRPCClient *mocks.JSONRPCClient) {
125184
t.Helper()
@@ -137,21 +196,23 @@ func TestConfigurer_SetConfig(t *testing.T) {
137196
"52f3VmvW7m9uTQu3PtyibgxnAvEuXDmm9umuHherGjS4pzRR7QXRDKnZhh6b95P7pQxzTgvE1muMNKYEY7YWsS3G",
138197
nil, fmt.Errorf("set config error"))
139198
},
140-
wantErr: "unable to set config: unable to send instruction: set config error",
199+
wantErr: "unable to set config: unable to send instruction 3 - setConfig: unable to send instruction: set config error",
141200
},
142201
}
143202
for _, tt := range tests {
144203
t.Run(tt.name, func(t *testing.T) {
145204
t.Parallel()
146205

147-
configurer, mockJSONRPCClient := newTestConfigurer(t, auth, chainSelector)
206+
configurer, mockJSONRPCClient := newTestConfigurer(t, tt.auth, chainSelector, tt.options...)
148207
tt.setup(t, configurer, mockJSONRPCClient)
149208

150209
got, err := configurer.SetConfig(ctx, ContractAddress(testMCMProgramID, testPDASeed), tt.mcmConfig, clearRoot)
151210

152211
if tt.wantErr == "" {
153212
require.NoError(t, err)
154-
require.Empty(t, cmp.Diff(tt.want, got.Hash))
213+
require.Empty(t, cmp.Diff(tt.wantHash, got.Hash))
214+
require.Empty(t, cmp.Diff(tt.wantInstructions, got.RawData.([]solana.Instruction),
215+
cmpopts.IgnoreFields(ag_binary.BaseVariant{}, "Impl")))
155216
} else {
156217
require.ErrorContains(t, err, tt.wantErr)
157218
}
@@ -161,11 +222,13 @@ func TestConfigurer_SetConfig(t *testing.T) {
161222

162223
// ----- helpers -----
163224

164-
func newTestConfigurer(t *testing.T, auth solana.PrivateKey, chainSelector types.ChainSelector) (*Configurer, *mocks.JSONRPCClient) {
225+
func newTestConfigurer(
226+
t *testing.T, auth solana.PrivateKey, chainSelector types.ChainSelector, options ...configurerOption,
227+
) (*Configurer, *mocks.JSONRPCClient) {
165228
t.Helper()
166229

167230
mockJSONRPCClient := mocks.NewJSONRPCClient(t)
168231
client := rpc.NewWithCustomRPCClient(mockJSONRPCClient)
169232

170-
return NewConfigurer(client, auth, chainSelector), mockJSONRPCClient
233+
return NewConfigurer(client, auth, chainSelector, options...), mockJSONRPCClient
171234
}

‎sdk/solana/executor.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ func (e *Executor) ExecuteOperation(
113113
}
114114

115115
return types.TransactionResult{
116-
Hash: signature,
117-
ChainFamily: chain_selectors.FamilySolana,
118-
RawTransaction: tx,
116+
Hash: signature,
117+
ChainFamily: chain_selectors.FamilySolana,
118+
RawData: tx,
119119
}, nil
120120
}
121121

@@ -186,9 +186,9 @@ func (e *Executor) SetRoot(
186186
}
187187

188188
return types.TransactionResult{
189-
Hash: signature,
190-
ChainFamily: chain_selectors.FamilySolana,
191-
RawTransaction: tx,
189+
Hash: signature,
190+
ChainFamily: chain_selectors.FamilySolana,
191+
RawData: tx,
192192
}, nil
193193
}
194194

‎sdk/solana/executor_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ func TestExecutor_ExecuteOperation(t *testing.T) {
180180
} else {
181181
require.NoError(t, err)
182182
require.NotNil(t, got)
183-
require.NotNil(t, got.RawTransaction)
183+
require.NotNil(t, got.RawData)
184184
require.Equalf(t, tt.want, got.Hash, "%q. Executor.ExecuteOperation()", tt.name)
185185
}
186186
})
@@ -357,7 +357,7 @@ func TestExecutor_SetRoot(t *testing.T) {
357357
if tt.wantErr == "" {
358358
require.NoError(t, err)
359359
require.NotNil(t, got)
360-
require.NotNil(t, got.RawTransaction)
360+
require.NotNil(t, got.RawData)
361361
require.Empty(t, cmp.Diff(tt.want, got.Hash))
362362
} else {
363363
require.ErrorContains(t, err, tt.wantErr)

‎sdk/solana/timelock_executor.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ func (e *TimelockExecutor) Execute(
9595
}
9696

9797
return types.TransactionResult{
98-
Hash: signature,
99-
ChainFamily: chain_selectors.FamilySolana,
100-
RawTransaction: tx,
98+
Hash: signature,
99+
ChainFamily: chain_selectors.FamilySolana,
100+
RawData: tx,
101101
}, nil
102102
}

‎types/transaction.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ package types
44
// It contains the hash of the transaction and the transaction itself.
55
// Users of this struct should cast the transaction to the appropriate type.
66
type TransactionResult struct {
7-
Hash string `json:"hash"`
8-
ChainFamily string `json:"chainFamily"`
9-
RawTransaction any `json:"tx"`
7+
Hash string `json:"hash"`
8+
ChainFamily string `json:"chainFamily"`
9+
RawData any `json:"rawData"`
1010
}
1111

12-
func NewTransactionResult(hash string, tx any, cf string) TransactionResult {
12+
func NewTransactionResult(hash string, rawData any, cf string) TransactionResult {
1313
return TransactionResult{
14-
Hash: hash,
15-
ChainFamily: cf,
16-
RawTransaction: tx,
14+
Hash: hash,
15+
ChainFamily: cf,
16+
RawData: rawData,
1717
}
1818
}

0 commit comments

Comments
 (0)
Please sign in to comment.