Skip to content

Commit fbcb658

Browse files
committed
Updated codec usage
1 parent dcb53a2 commit fbcb658

File tree

5 files changed

+81
-74
lines changed

5 files changed

+81
-74
lines changed

pkg/solana/chainwriter/ccip_example_config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func TestConfig() {
3737
Name: "RegistryTokenState",
3838
// In this case, the user configured the lookup table accounts to use a PDALookup, which
3939
// generates a list of one of more PDA accounts based on the input parameters. Specifically,
40-
// there will be multple PDA accounts if there are multiple addresses in the message, otherwise,
40+
// there will be multiple PDA accounts if there are multiple addresses in the message, otherwise,
4141
// there will only be one PDA account to read from. The PDA account corresponds to the lookup table.
4242
Accounts: PDALookups{
4343
Name: "RegistryTokenState",

pkg/solana/chainwriter/chain_writer.go

+39-34
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/gagliardetto/solana-go/rpc"
1212

1313
commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec"
14-
"github.com/smartcontractkit/chainlink-common/pkg/codec/encodings/binary"
1514
"github.com/smartcontractkit/chainlink-common/pkg/logger"
1615
"github.com/smartcontractkit/chainlink-common/pkg/services"
1716
"github.com/smartcontractkit/chainlink-common/pkg/types"
@@ -31,7 +30,8 @@ type SolanaChainWriterService struct {
3130
ge fees.Estimator
3231
config ChainWriterConfig
3332

34-
codecs map[string]types.Codec
33+
parsed *codec.ParsedTypes
34+
encoder types.Encoder
3535

3636
services.StateMachine
3737
}
@@ -62,48 +62,54 @@ type MethodConfig struct {
6262
}
6363

6464
func NewSolanaChainWriterService(logger logger.Logger, reader client.Reader, txm txm.TxManager, ge fees.Estimator, config ChainWriterConfig) (*SolanaChainWriterService, error) {
65-
codecs, err := parseIDLCodecs(config)
66-
if err != nil {
67-
return nil, fmt.Errorf("failed to parse IDL codecs: %w", err)
68-
}
69-
70-
return &SolanaChainWriterService{
65+
w := SolanaChainWriterService{
7166
lggr: logger,
7267
reader: reader,
7368
txm: txm,
7469
ge: ge,
7570
config: config,
76-
codecs: codecs,
77-
}, nil
71+
parsed: &codec.ParsedTypes{EncoderDefs: map[string]codec.Entry{}, DecoderDefs: map[string]codec.Entry{}},
72+
}
73+
74+
if err := w.parsePrograms(config); err != nil {
75+
return nil, fmt.Errorf("failed to parse programs: %w", err)
76+
}
77+
78+
var err error
79+
if w.encoder, err = w.parsed.ToCodec(); err != nil {
80+
return nil, fmt.Errorf("%w: failed to create codec", err)
81+
}
82+
83+
return &w, nil
7884
}
7985

80-
func parseIDLCodecs(config ChainWriterConfig) (map[string]types.Codec, error) {
81-
codecs := make(map[string]types.Codec)
86+
func (s *SolanaChainWriterService) parsePrograms(config ChainWriterConfig) error {
8287
for program, programConfig := range config.Programs {
8388
var idl codec.IDL
8489
if err := json.Unmarshal([]byte(programConfig.IDL), &idl); err != nil {
85-
return nil, fmt.Errorf("failed to unmarshal IDL for program: %s, error: %w", program, err)
86-
}
87-
idlCodec, err := codec.NewIDLInstructionsCodec(idl, binary.LittleEndian())
88-
if err != nil {
89-
return nil, fmt.Errorf("failed to create codec from IDL for program: %s, error: %w", program, err)
90+
return fmt.Errorf("failed to unmarshal IDL for program: %s, error: %w", program, err)
9091
}
9192
for method, methodConfig := range programConfig.Methods {
92-
if methodConfig.InputModifications != nil {
93-
modConfig, err := methodConfig.InputModifications.ToModifier(codec.DecoderHooks...)
94-
if err != nil {
95-
return nil, fmt.Errorf("failed to create input modifications for method %s.%s, error: %w", program, method, err)
96-
}
97-
// add mods to codec
98-
idlCodec, err = codec.NewNamedModifierCodec(idlCodec, method, modConfig)
99-
if err != nil {
100-
return nil, fmt.Errorf("failed to create named codec for method %s.%s, error: %w", program, method, err)
101-
}
93+
idlDef, err := codec.FindDefinitionFromIDL(codec.ChainConfigTypeInstructionDef, methodConfig.ChainSpecificName, idl)
94+
if err != nil {
95+
return err
10296
}
97+
98+
inputMod, err := methodConfig.InputModifications.ToModifier(codec.DecoderHooks...)
99+
if err != nil {
100+
return fmt.Errorf("failed to create input modifications for method %s.%s, error: %w", program, method, err)
101+
}
102+
103+
input, err := codec.CreateCodecEntry(idlDef, methodConfig.ChainSpecificName, idl, inputMod)
104+
if err != nil {
105+
return fmt.Errorf("failed to create codec entry for method %s.%s, error: %w", program, method, err)
106+
}
107+
108+
s.parsed.EncoderDefs[codec.WrapItemType(true, program, method, "")] = input
103109
}
104-
codecs[program] = idlCodec
105110
}
106-
return codecs, nil
111+
112+
return nil
107113
}
108114

109115
/*
@@ -250,16 +256,15 @@ func (s *SolanaChainWriterService) SubmitTransaction(ctx context.Context, contra
250256
}
251257
}
252258

253-
codec := s.codecs[contractName]
254-
encodedPayload, err := codec.Encode(ctx, args, method)
255-
256-
discriminator := GetDiscriminator(methodConfig.ChainSpecificName)
257-
encodedPayload = append(discriminator[:], encodedPayload...)
259+
encodedPayload, err := s.encoder.Encode(ctx, args, codec.WrapItemType(true, contractName, method, ""))
258260

259261
if err != nil {
260262
return errorWithDebugID(fmt.Errorf("error encoding transaction payload: %w", err), debugID)
261263
}
262264

265+
discriminator := GetDiscriminator(methodConfig.ChainSpecificName)
266+
encodedPayload = append(discriminator[:], encodedPayload...)
267+
263268
// Fetch derived and static table maps
264269
derivedTableMap, staticTableMap, err := s.ResolveLookupTables(ctx, args, methodConfig.LookupTables)
265270
if err != nil {

pkg/solana/chainwriter/chain_writer_test.go

+40-38
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package chainwriter_test
33
import (
44
"bytes"
55
"errors"
6-
"io/ioutil"
76
"math/big"
87
"os"
98
"reflect"
@@ -27,6 +26,12 @@ import (
2726
txmMocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm/mocks"
2827
)
2928

29+
type Arguments struct {
30+
LookupTable solana.PublicKey
31+
Seed1 []byte
32+
Seed2 []byte
33+
}
34+
3035
func TestChainWriter_GetAddresses(t *testing.T) {
3136
ctx := tests.Context(t)
3237

@@ -87,7 +92,7 @@ func TestChainWriter_GetAddresses(t *testing.T) {
8792
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
8893
Seeds: []chainwriter.Seed{
8994
// extract seed2 for PDA lookup
90-
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
95+
{Dynamic: chainwriter.AccountLookup{Name: "Seed2", Location: "Seed2"}},
9196
},
9297
IsSigner: derivedTablePdaLookupMeta.IsSigner,
9398
IsWritable: derivedTablePdaLookupMeta.IsWritable,
@@ -107,10 +112,10 @@ func TestChainWriter_GetAddresses(t *testing.T) {
107112
// correlates to DerivedTable index in account lookup config
108113
derivedTablePdaLookupMeta.PublicKey = storedPubKeys[0]
109114

110-
args := map[string]interface{}{
111-
"lookup_table": accountLookupMeta.PublicKey.Bytes(),
112-
"seed1": seed1,
113-
"seed2": seed2,
115+
args := Arguments{
116+
LookupTable: accountLookupMeta.PublicKey,
117+
Seed1: seed1,
118+
Seed2: seed2,
114119
}
115120

116121
accountLookupConfig := []chainwriter.Lookup{
@@ -122,7 +127,7 @@ func TestChainWriter_GetAddresses(t *testing.T) {
122127
},
123128
chainwriter.AccountLookup{
124129
Name: "LookupTable",
125-
Location: "lookup_table",
130+
Location: "LookupTable",
126131
IsSigner: accountLookupMeta.IsSigner,
127132
IsWritable: accountLookupMeta.IsWritable,
128133
},
@@ -131,7 +136,7 @@ func TestChainWriter_GetAddresses(t *testing.T) {
131136
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: solana.SystemProgramID.String()},
132137
Seeds: []chainwriter.Seed{
133138
// extract seed1 for PDA lookup
134-
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
139+
{Dynamic: chainwriter.AccountLookup{Name: "Seed1", Location: "Seed1"}},
135140
},
136141
IsSigner: pdaLookupMeta.IsSigner,
137142
IsWritable: pdaLookupMeta.IsWritable,
@@ -177,8 +182,8 @@ func TestChainWriter_GetAddresses(t *testing.T) {
177182
})
178183

179184
t.Run("resolve addresses for multiple indices from derived lookup table", func(t *testing.T) {
180-
args := map[string]interface{}{
181-
"seed2": seed2,
185+
args := Arguments{
186+
Seed2: seed2,
182187
}
183188

184189
accountLookupConfig := []chainwriter.Lookup{
@@ -202,8 +207,8 @@ func TestChainWriter_GetAddresses(t *testing.T) {
202207
})
203208

204209
t.Run("resolve all addresses from derived lookup table if indices not specified", func(t *testing.T) {
205-
args := map[string]interface{}{
206-
"seed2": seed2,
210+
args := Arguments{
211+
Seed2: seed2,
207212
}
208213

209214
accountLookupConfig := []chainwriter.Lookup{
@@ -274,7 +279,7 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
274279
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
275280
Seeds: []chainwriter.Seed{
276281
// extract seed1 for PDA lookup
277-
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
282+
{Dynamic: chainwriter.AccountLookup{Name: "Seed1", Location: "Seed1"}},
278283
},
279284
IsSigner: true,
280285
IsWritable: true,
@@ -291,7 +296,7 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
291296
PublicKey: chainwriter.AccountConstant{Name: "UnusedAccount", Address: unusedProgramID.String()},
292297
Seeds: []chainwriter.Seed{
293298
// extract seed2 for PDA lookup
294-
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
299+
{Dynamic: chainwriter.AccountLookup{Name: "Seed2", Location: "Seed2"}},
295300
},
296301
IsSigner: true,
297302
IsWritable: true,
@@ -305,9 +310,9 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
305310
StaticLookupTables: []solana.PublicKey{staticLookupTablePubkey1, staticLookupTablePubkey2},
306311
}
307312

308-
args := map[string]interface{}{
309-
"seed1": seed1,
310-
"seed2": seed2,
313+
args := Arguments{
314+
Seed1: seed1,
315+
Seed2: seed2,
311316
}
312317

313318
t.Run("returns filtered map with only relevant addresses required by account lookup config", func(t *testing.T) {
@@ -414,19 +419,14 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
414419
staticLookupKeys := chainwriter.CreateTestPubKeys(t, 2)
415420
mockFetchLookupTableAddresses(t, rw, staticLookupTablePubkey, staticLookupKeys)
416421

417-
jsonFile, err := os.Open("testContractIDL.json")
418-
require.NoError(t, err)
419-
420-
defer jsonFile.Close()
421-
422-
data, err := ioutil.ReadAll(jsonFile)
422+
data, err := os.ReadFile("testContractIDL.json")
423423
require.NoError(t, err)
424424

425425
testContractIDLJson := string(data)
426426

427427
cwConfig := chainwriter.ChainWriterConfig{
428428
Programs: map[string]chainwriter.ProgramConfig{
429-
"contractReaderInterface": {
429+
"contract_reader_interface": {
430430
Methods: map[string]chainwriter.MethodConfig{
431431
"initializeLookupTable": {
432432
FromAddress: admin.String(),
@@ -440,7 +440,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
440440
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
441441
Seeds: []chainwriter.Seed{
442442
// extract seed2 for PDA lookup
443-
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
443+
{Dynamic: chainwriter.AccountLookup{Name: "Seed2", Location: "Seed2"}},
444444
},
445445
IsSigner: false,
446446
IsWritable: false,
@@ -462,7 +462,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
462462
},
463463
chainwriter.AccountLookup{
464464
Name: "LookupTable",
465-
Location: "lookup_table",
465+
Location: "LookupTable",
466466
IsSigner: false,
467467
IsWritable: false,
468468
},
@@ -471,7 +471,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
471471
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: solana.SystemProgramID.String()},
472472
Seeds: []chainwriter.Seed{
473473
// extract seed1 for PDA lookup
474-
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
474+
{Dynamic: chainwriter.AccountLookup{Name: "Seed1", Location: "Seed1"}},
475475
},
476476
IsSigner: false,
477477
IsWritable: false,
@@ -514,22 +514,24 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
514514

515515
t.Run("fails to encode payload if args with missing values provided", func(t *testing.T) {
516516
txID := uuid.NewString()
517-
args := map[string]interface{}{}
518-
submitErr := cw.SubmitTransaction(ctx, "contractReaderInterface", "initializeLookupTable", args, txID, programID.String(), nil, nil)
517+
type InvalidArgs struct{}
518+
args := InvalidArgs{}
519+
submitErr := cw.SubmitTransaction(ctx, "contract_reader_interface", "initializeLookupTable", args, txID, programID.String(), nil, nil)
519520
require.Error(t, submitErr)
520521
})
521522

522523
t.Run("fails if invalid contract name provided", func(t *testing.T) {
523524
txID := uuid.NewString()
524-
args := map[string]interface{}{}
525+
args := Arguments{}
525526
submitErr := cw.SubmitTransaction(ctx, "badContract", "initializeLookupTable", args, txID, programID.String(), nil, nil)
526527
require.Error(t, submitErr)
527528
})
528529

529530
t.Run("fails if invalid method provided", func(t *testing.T) {
530531
txID := uuid.NewString()
531-
args := map[string]interface{}{}
532-
submitErr := cw.SubmitTransaction(ctx, "contractReaderInterface", "badMethod", args, txID, programID.String(), nil, nil)
532+
533+
args := Arguments{}
534+
submitErr := cw.SubmitTransaction(ctx, "contract_reader_interface", "badMethod", args, txID, programID.String(), nil, nil)
533535
require.Error(t, submitErr)
534536
})
535537

@@ -555,13 +557,13 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
555557
return true
556558
}), &txID, mock.Anything).Return(nil).Once()
557559

558-
args := map[string]interface{}{
559-
"lookupTable": chainwriter.GetRandomPubKey(t).Bytes(),
560-
"lookup_table": account2.Bytes(),
561-
"seed1": seed1,
562-
"seed2": seed2,
560+
args := Arguments{
561+
LookupTable: account2,
562+
Seed1: seed1,
563+
Seed2: seed2,
563564
}
564-
submitErr := cw.SubmitTransaction(ctx, "contractReaderInterface", "initializeLookupTable", args, txID, programID.String(), nil, nil)
565+
566+
submitErr := cw.SubmitTransaction(ctx, "contract_reader_interface", "initializeLookupTable", args, txID, programID.String(), nil, nil)
565567
require.NoError(t, submitErr)
566568
})
567569
}

pkg/solana/chainwriter/lookups.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/smartcontractkit/chainlink-solana/pkg/solana/client"
1313
)
1414

15+
// Lookup is an interface that defines a method to resolve an address (or multiple addresses) from a given definition.
1516
type Lookup interface {
1617
Resolve(ctx context.Context, args any, derivedTableMap map[string]map[string][]*solana.AccountMeta, reader client.Reader) ([]*solana.AccountMeta, error)
1718
}

pkg/solana/utils/utils.go

-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ func FundAccounts(t *testing.T, accounts []solana.PrivateKey, solanaGoClient *rp
187187
}
188188
}
189189
remaining = unconfirmedTxCount
190-
fmt.Printf("Waiting for finalized funding on %d addresses\n", remaining)
191190

192191
time.Sleep(500 * time.Millisecond)
193192
if count > 60 {

0 commit comments

Comments
 (0)