Skip to content

Commit 9b8023e

Browse files
committed
Updated codec usage
1 parent dcb53a2 commit 9b8023e

File tree

4 files changed

+83
-73
lines changed

4 files changed

+83
-73
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

+42-38
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package chainwriter_test
33
import (
44
"bytes"
55
"errors"
6-
"io/ioutil"
6+
"fmt"
77
"math/big"
88
"os"
99
"reflect"
@@ -27,6 +27,12 @@ import (
2727
txmMocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm/mocks"
2828
)
2929

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

@@ -87,7 +93,7 @@ func TestChainWriter_GetAddresses(t *testing.T) {
8793
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
8894
Seeds: []chainwriter.Seed{
8995
// extract seed2 for PDA lookup
90-
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
96+
{Dynamic: chainwriter.AccountLookup{Name: "Seed2", Location: "Seed2"}},
9197
},
9298
IsSigner: derivedTablePdaLookupMeta.IsSigner,
9399
IsWritable: derivedTablePdaLookupMeta.IsWritable,
@@ -107,10 +113,10 @@ func TestChainWriter_GetAddresses(t *testing.T) {
107113
// correlates to DerivedTable index in account lookup config
108114
derivedTablePdaLookupMeta.PublicKey = storedPubKeys[0]
109115

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

116122
accountLookupConfig := []chainwriter.Lookup{
@@ -122,7 +128,7 @@ func TestChainWriter_GetAddresses(t *testing.T) {
122128
},
123129
chainwriter.AccountLookup{
124130
Name: "LookupTable",
125-
Location: "lookup_table",
131+
Location: "LookupTable",
126132
IsSigner: accountLookupMeta.IsSigner,
127133
IsWritable: accountLookupMeta.IsWritable,
128134
},
@@ -131,7 +137,7 @@ func TestChainWriter_GetAddresses(t *testing.T) {
131137
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: solana.SystemProgramID.String()},
132138
Seeds: []chainwriter.Seed{
133139
// extract seed1 for PDA lookup
134-
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
140+
{Dynamic: chainwriter.AccountLookup{Name: "Seed1", Location: "Seed1"}},
135141
},
136142
IsSigner: pdaLookupMeta.IsSigner,
137143
IsWritable: pdaLookupMeta.IsWritable,
@@ -177,8 +183,8 @@ func TestChainWriter_GetAddresses(t *testing.T) {
177183
})
178184

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

184190
accountLookupConfig := []chainwriter.Lookup{
@@ -202,8 +208,8 @@ func TestChainWriter_GetAddresses(t *testing.T) {
202208
})
203209

204210
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,
211+
args := Arguments{
212+
Seed2: seed2,
207213
}
208214

209215
accountLookupConfig := []chainwriter.Lookup{
@@ -274,7 +280,7 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
274280
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
275281
Seeds: []chainwriter.Seed{
276282
// extract seed1 for PDA lookup
277-
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
283+
{Dynamic: chainwriter.AccountLookup{Name: "Seed1", Location: "Seed1"}},
278284
},
279285
IsSigner: true,
280286
IsWritable: true,
@@ -291,7 +297,7 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
291297
PublicKey: chainwriter.AccountConstant{Name: "UnusedAccount", Address: unusedProgramID.String()},
292298
Seeds: []chainwriter.Seed{
293299
// extract seed2 for PDA lookup
294-
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
300+
{Dynamic: chainwriter.AccountLookup{Name: "Seed2", Location: "Seed2"}},
295301
},
296302
IsSigner: true,
297303
IsWritable: true,
@@ -305,9 +311,9 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
305311
StaticLookupTables: []solana.PublicKey{staticLookupTablePubkey1, staticLookupTablePubkey2},
306312
}
307313

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

313319
t.Run("returns filtered map with only relevant addresses required by account lookup config", func(t *testing.T) {
@@ -403,6 +409,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
403409
seed2 := []byte("seed2")
404410
programID := solana.MustPublicKeyFromBase58("6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE")
405411
derivedTablePda := mustFindPdaProgramAddress(t, [][]byte{seed2}, programID)
412+
fmt.Println("pda:", derivedTablePda)
406413
// mock data account response from program
407414
derivedLookupTablePubkey := mockDataAccountLookupTable(t, rw, derivedTablePda)
408415
// mock fetch lookup table addresses call
@@ -414,19 +421,14 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
414421
staticLookupKeys := chainwriter.CreateTestPubKeys(t, 2)
415422
mockFetchLookupTableAddresses(t, rw, staticLookupTablePubkey, staticLookupKeys)
416423

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

425427
testContractIDLJson := string(data)
426428

427429
cwConfig := chainwriter.ChainWriterConfig{
428430
Programs: map[string]chainwriter.ProgramConfig{
429-
"contractReaderInterface": {
431+
"contract_reader_interface": {
430432
Methods: map[string]chainwriter.MethodConfig{
431433
"initializeLookupTable": {
432434
FromAddress: admin.String(),
@@ -440,7 +442,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
440442
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
441443
Seeds: []chainwriter.Seed{
442444
// extract seed2 for PDA lookup
443-
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
445+
{Dynamic: chainwriter.AccountLookup{Name: "Seed2", Location: "Seed2"}},
444446
},
445447
IsSigner: false,
446448
IsWritable: false,
@@ -462,7 +464,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
462464
},
463465
chainwriter.AccountLookup{
464466
Name: "LookupTable",
465-
Location: "lookup_table",
467+
Location: "LookupTable",
466468
IsSigner: false,
467469
IsWritable: false,
468470
},
@@ -471,7 +473,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
471473
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: solana.SystemProgramID.String()},
472474
Seeds: []chainwriter.Seed{
473475
// extract seed1 for PDA lookup
474-
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
476+
{Dynamic: chainwriter.AccountLookup{Name: "Seed1", Location: "Seed1"}},
475477
},
476478
IsSigner: false,
477479
IsWritable: false,
@@ -514,22 +516,24 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
514516

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

522525
t.Run("fails if invalid contract name provided", func(t *testing.T) {
523526
txID := uuid.NewString()
524-
args := map[string]interface{}{}
527+
args := Arguments{}
525528
submitErr := cw.SubmitTransaction(ctx, "badContract", "initializeLookupTable", args, txID, programID.String(), nil, nil)
526529
require.Error(t, submitErr)
527530
})
528531

529532
t.Run("fails if invalid method provided", func(t *testing.T) {
530533
txID := uuid.NewString()
531-
args := map[string]interface{}{}
532-
submitErr := cw.SubmitTransaction(ctx, "contractReaderInterface", "badMethod", args, txID, programID.String(), nil, nil)
534+
535+
args := Arguments{}
536+
submitErr := cw.SubmitTransaction(ctx, "contract_reader_interface", "badMethod", args, txID, programID.String(), nil, nil)
533537
require.Error(t, submitErr)
534538
})
535539

@@ -555,13 +559,13 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
555559
return true
556560
}), &txID, mock.Anything).Return(nil).Once()
557561

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

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
}

0 commit comments

Comments
 (0)