From 28a8b69057843b190b7bc234a739e25e351e04ad Mon Sep 17 00:00:00 2001 From: ilija Date: Thu, 12 Dec 2024 12:55:19 -0500 Subject: [PATCH] Connect codec interface tests and refactor codec to interface like EVM one --- go.mod | 2 +- pkg/solana/codec/codec_entry.go | 90 ++++++ pkg/solana/codec/codec_test.go | 135 +++++++++ pkg/solana/codec/decoder.go | 42 +++ pkg/solana/codec/discriminator.go | 16 +- pkg/solana/codec/encoder.go | 51 ++++ pkg/solana/codec/parsed_types.go | 48 +++ pkg/solana/codec/solana.go | 184 ++++++++---- pkg/solana/codec/solana_test.go | 72 ----- pkg/solana/codec/testutils/converters.go | 148 +++++++++ .../codec/testutils/itemArray1TypeIDL.json | 108 +++++++ .../codec/testutils/itemArray2TypeIDL.json | 107 +++++++ .../testutils/itemWithConfigExtraIDL.json | 91 ++++++ pkg/solana/codec/testutils/nilTypeIDL.json | 12 + .../codec/testutils/sizeItemTypeIDL.json | 38 +++ pkg/solana/codec/testutils/testIDL.json | 242 +++------------ .../itemSliceTypeIDL.json | 104 +++++++ .../testutils/test_item_slice_type/types.go | 283 ++++++++++++++++++ .../testutils/test_item_type/accounts.go | 140 +++++++++ .../testutils/test_item_type/itemIDL.json | 88 ++++++ .../codec/testutils/test_item_type/types.go | 173 +++++++++++ pkg/solana/codec/testutils/types.go | 45 ++- pkg/solana/codec/types.go | 15 + pkg/solana/config/chain_reader.go | 4 +- 24 files changed, 1901 insertions(+), 337 deletions(-) create mode 100644 pkg/solana/codec/codec_entry.go create mode 100644 pkg/solana/codec/codec_test.go create mode 100644 pkg/solana/codec/decoder.go create mode 100644 pkg/solana/codec/encoder.go create mode 100644 pkg/solana/codec/parsed_types.go create mode 100644 pkg/solana/codec/testutils/converters.go create mode 100644 pkg/solana/codec/testutils/itemArray1TypeIDL.json create mode 100644 pkg/solana/codec/testutils/itemArray2TypeIDL.json create mode 100644 pkg/solana/codec/testutils/itemWithConfigExtraIDL.json create mode 100644 pkg/solana/codec/testutils/nilTypeIDL.json create mode 100644 pkg/solana/codec/testutils/sizeItemTypeIDL.json create mode 100644 pkg/solana/codec/testutils/test_item_slice_type/itemSliceTypeIDL.json create mode 100644 pkg/solana/codec/testutils/test_item_slice_type/types.go create mode 100644 pkg/solana/codec/testutils/test_item_type/accounts.go create mode 100644 pkg/solana/codec/testutils/test_item_type/itemIDL.json create mode 100644 pkg/solana/codec/testutils/test_item_type/types.go create mode 100644 pkg/solana/codec/types.go diff --git a/go.mod b/go.mod index f9363c1cc..feef696a5 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/sync v0.8.0 - golang.org/x/text v0.18.0 ) require ( @@ -124,6 +123,7 @@ require ( golang.org/x/net v0.29.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect diff --git a/pkg/solana/codec/codec_entry.go b/pkg/solana/codec/codec_entry.go new file mode 100644 index 000000000..bd24ea3d8 --- /dev/null +++ b/pkg/solana/codec/codec_entry.go @@ -0,0 +1,90 @@ +package codec + +import ( + "reflect" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + commonencodings "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" +) + +type Entry interface { + Encode(value any, into []byte) ([]byte, error) + Decode(encoded []byte) (any, []byte, error) + GetCodecType() commonencodings.TypeCodec + GetType() reflect.Type + + Modifier() codec.Modifier +} + +// TODO this can also be an event entry, but anchor-go defines events differently, maybe just have a separate struct and method that satisfy entry interface for events. +func NewEntry(idlAccount IdlTypeDef, idlTypes IdlTypeDefSlice, includeDiscriminator bool, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) { + refs := &codecRefs{ + builder: builder, + codecs: make(map[string]commonencodings.TypeCodec), + typeDefs: idlTypes, + dependencies: make(map[string][]string), + } + + if mod == nil { + mod = codec.MultiModifier{} + } + + _, accCodec, err := createCodecType(idlAccount, refs, false) + if err != nil { + return nil, err + } + + entry := &codecEntry{name: idlAccount.Name, includeDiscriminator: includeDiscriminator, codecType: accCodec, typ: accCodec.GetType(), mod: mod} + if entry.includeDiscriminator { + entry.Discriminator = commonencodings.NamedTypeCodec{Name: "Discriminator" + idlAccount.Name, Codec: NewDiscriminator(idlAccount.Name)} + } + + return entry, nil +} + +type codecEntry struct { + name string + includeDiscriminator bool + Discriminator commonencodings.NamedTypeCodec + typ reflect.Type + codecType commonencodings.TypeCodec + mod codec.Modifier +} + +func (entry *codecEntry) GetType() reflect.Type { + return entry.typ +} + +func (entry *codecEntry) GetCodecType() commonencodings.TypeCodec { + return entry.codecType +} + +func (entry *codecEntry) Encode(value any, into []byte) ([]byte, error) { + encodedVal, err := entry.codecType.Encode(value, into) + if err != nil { + return nil, err + } + + if entry.includeDiscriminator { + var byt []byte + disc := NewDiscriminator(entry.name) + encodedDisc, err := disc.Encode(&disc.hashPrefix, byt) + if err != nil { + return nil, err + } + return append(encodedDisc, encodedVal...), nil + } + + return encodedVal, nil +} + +func (entry *codecEntry) Decode(encoded []byte) (any, []byte, error) { + if entry.includeDiscriminator { + encoded = encoded[discriminatorLength:] + } + return entry.codecType.Decode(encoded) +} + +func (entry *codecEntry) Modifier() codec.Modifier { + return entry.mod +} diff --git a/pkg/solana/codec/codec_test.go b/pkg/solana/codec/codec_test.go new file mode 100644 index 000000000..3fc121cb0 --- /dev/null +++ b/pkg/solana/codec/codec_test.go @@ -0,0 +1,135 @@ +package codec_test + +import ( + "bytes" + _ "embed" + "slices" + "testing" + + bin "github.com/gagliardetto/binary" + "github.com/gagliardetto/solana-go" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/require" + + commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" + clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils/test_item_type" +) + +const anyExtraValue = 3 + +func TestCodec(t *testing.T) { + tester := &codecInterfaceTester{} + RunCodecInterfaceTests(t, tester) + //RunCodecInterfaceTests(t, looptestutils.WrapCodecTesterForLoop(tester)) +} + +type codecInterfaceTester struct { + TestSelectionSupport +} + +func (it *codecInterfaceTester) Setup(_ *testing.T) {} + +func (it *codecInterfaceTester) GetAccountBytes(i int) []byte { + pk, _ := solana.NewRandomPrivateKey() + return pk.PublicKey().Bytes() +} + +func (it *codecInterfaceTester) GetAccountString(i int) string { + return solana.PublicKeyFromBytes(it.GetAccountBytes(i)).String() +} + +func (it *codecInterfaceTester) EncodeFields(t *testing.T, request *EncodeRequest) []byte { + if request.TestOn == testutils.TestItemType { + return encodeFieldsOnItem(t, request) + } + + return encodeFieldsOnSliceOrArray(t, request) +} + +func encodeFieldsOnItem(t *testing.T, request *EncodeRequest) ocr2types.Report { + buf := new(bytes.Buffer) + if err := testutils.EncodeRequestToTestStruct(request).MarshalWithEncoder(bin.NewBorshEncoder(buf)); err != nil { + require.NoError(t, err) + } + return buf.Bytes() +} + +func encodeFieldsOnSliceOrArray(_ *testing.T, request *EncodeRequest) []byte { + args := make([]any, 1) + switch request.TestOn { + case testutils.TestItemArray1Type: + args[0] = [1]test_item_type.TestStruct{testutils.ToInternalType(request.TestStructs[0])} + case testutils.TestItemArray2Type: + args[0] = [2]test_item_type.TestStruct{testutils.ToInternalType(request.TestStructs[0]), testutils.ToInternalType(request.TestStructs[1])} + default: + tmp := make([]test_item_type.TestStruct, len(request.TestStructs)) + for i, ts := range request.TestStructs { + tmp[i] = testutils.ToInternalType(ts) + } + args[0] = tmp + } + + return []byte{} +} + +func (it *codecInterfaceTester) GetCodec(t *testing.T) clcommontypes.Codec { + codecConfig := codec.Config{Configs: map[string]codec.ChainConfig{}} + testStruct := CreateTestStruct[*testing.T](0, it) + for k, v := range testutils.CodecDefs { + entry := codecConfig.Configs[k] + entry.IDL = v + + if slices.Contains([]string{testutils.TestItemSliceType, testutils.TestItemArray1Type, testutils.TestItemArray2Type}, k) { + entry.ModifierConfigs = commoncodec.ModifiersConfig{ + &commoncodec.RenameModifierConfig{Fields: map[string]string{"Items.NestedDynamicStruct.Inner.IntVal": "I"}}, + &commoncodec.RenameModifierConfig{Fields: map[string]string{"Items.NestedStaticStruct.Inner.IntVal": "I"}}, + &commoncodec.AddressBytesToStringModifierConfig{ + Fields: []string{"Items.AccountStruct.AccountStr"}, + Modifier: codec.SolanaAddressModifier{}, + }, + &commoncodec.WrapperModifierConfig{Fields: map[string]string{"Items.NestedStaticStruct.Inner.IntVal": "I"}}, + } + } else if k != testutils.SizeItemType && k != testutils.NilType { + entry.ModifierConfigs = commoncodec.ModifiersConfig{ + &commoncodec.RenameModifierConfig{Fields: map[string]string{"NestedDynamicStruct.Inner.IntVal": "I"}}, + &commoncodec.RenameModifierConfig{Fields: map[string]string{"NestedStaticStruct.Inner.IntVal": "I"}}, + } + } + + if slices.Contains([]string{testutils.TestItemType, testutils.TestItemWithConfigExtra}, k) { + addressByteModifier := &commoncodec.AddressBytesToStringModifierConfig{ + Fields: []string{"AccountStruct.AccountStr"}, + Modifier: codec.SolanaAddressModifier{}, + } + entry.ModifierConfigs = append(entry.ModifierConfigs, addressByteModifier) + } + + if k == testutils.TestItemWithConfigExtra { + hardCode := &commoncodec.HardCodeModifierConfig{ + OnChainValues: map[string]any{ + "BigField": testStruct.BigField.String(), + "AccountStruct.Account": solana.PublicKeyFromBytes(testStruct.AccountStruct.Account), + }, + OffChainValues: map[string]any{"ExtraField": anyExtraValue}, + } + entry.ModifierConfigs = append(entry.ModifierConfigs, hardCode) + } + codecConfig.Configs[k] = entry + } + + c, err := codec.NewCodec(codecConfig) + require.NoError(t, err) + + return c +} + +func (it *codecInterfaceTester) IncludeArrayEncodingSizeEnforcement() bool { + return true +} +func (it *codecInterfaceTester) Name() string { + return "Solana" +} diff --git a/pkg/solana/codec/decoder.go b/pkg/solana/codec/decoder.go new file mode 100644 index 000000000..9ad6b201b --- /dev/null +++ b/pkg/solana/codec/decoder.go @@ -0,0 +1,42 @@ +package codec + +import ( + "context" + "fmt" + "reflect" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type decoder struct { + Definitions map[string]Entry +} + +var _ commontypes.Decoder = &decoder{} + +func (m *decoder) Decode(_ context.Context, raw []byte, into any, itemType string) (err error) { + item, ok := m.Definitions[itemType] + if !ok { + return fmt.Errorf("%w: cannot find type %s", commontypes.ErrInvalidType, itemType) + } + + val, remaining, err := item.Decode(raw) + if err != nil { + return err + } + + if len(remaining) != 0 { + return fmt.Errorf("%w: remaining bytes after decoding %s", commontypes.ErrInvalidEncoding, itemType) + } + + return codec.Convert(reflect.ValueOf(val), reflect.ValueOf(into), nil) +} + +func (m *decoder) GetMaxDecodingSize(_ context.Context, n int, itemType string) (int, error) { + entry, ok := m.Definitions[itemType] + if !ok { + return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) + } + return entry.GetCodecType().Size(n) +} diff --git a/pkg/solana/codec/discriminator.go b/pkg/solana/codec/discriminator.go index f712a3f68..9bc363ae7 100644 --- a/pkg/solana/codec/discriminator.go +++ b/pkg/solana/codec/discriminator.go @@ -12,16 +12,16 @@ import ( const discriminatorLength = 8 -func NewDiscriminator(name string) encodings.TypeCodec { +func NewDiscriminator(name string) *Discriminator { sum := sha256.Sum256([]byte("account:" + name)) - return &discriminator{hashPrefix: sum[:discriminatorLength]} + return &Discriminator{hashPrefix: sum[:discriminatorLength]} } -type discriminator struct { +type Discriminator struct { hashPrefix []byte } -func (d discriminator) Encode(value any, into []byte) ([]byte, error) { +func (d Discriminator) Encode(value any, into []byte) ([]byte, error) { if value == nil { return append(into, d.hashPrefix...), nil } @@ -44,7 +44,7 @@ func (d discriminator) Encode(value any, into []byte) ([]byte, error) { return append(into, *raw...), nil } -func (d discriminator) Decode(encoded []byte) (any, []byte, error) { +func (d Discriminator) Decode(encoded []byte) (any, []byte, error) { raw, remaining, err := encodings.SafeDecode(encoded, discriminatorLength, func(raw []byte) []byte { return raw }) if err != nil { return nil, nil, err @@ -57,15 +57,15 @@ func (d discriminator) Decode(encoded []byte) (any, []byte, error) { return &raw, remaining, nil } -func (d discriminator) GetType() reflect.Type { +func (d Discriminator) GetType() reflect.Type { // Pointer type so that nil can inject values and so that the NamedCodec won't wrap with no-nil pointer. return reflect.TypeOf(&[]byte{}) } -func (d discriminator) Size(_ int) (int, error) { +func (d Discriminator) Size(_ int) (int, error) { return discriminatorLength, nil } -func (d discriminator) FixedSize() (int, error) { +func (d Discriminator) FixedSize() (int, error) { return discriminatorLength, nil } diff --git a/pkg/solana/codec/encoder.go b/pkg/solana/codec/encoder.go new file mode 100644 index 000000000..2605bc9ee --- /dev/null +++ b/pkg/solana/codec/encoder.go @@ -0,0 +1,51 @@ +package codec + +import ( + "context" + "fmt" + "reflect" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type encoder struct { + Definitions map[string]Entry +} + +var _ commontypes.Encoder = &encoder{} + +func (e *encoder) Encode(_ context.Context, item any, itemType string) (res []byte, err error) { + info, ok := e.Definitions[itemType] + if !ok { + return nil, fmt.Errorf("%w: cannot find definition for %s", commontypes.ErrInvalidType, itemType) + } + + if item != nil { + rItem := reflect.ValueOf(item) + myType := info.GetCodecType().GetType() + if rItem.Kind() == reflect.Pointer && myType.Kind() != reflect.Pointer { + rItem = reflect.Indirect(rItem) + } + + if !rItem.IsZero() && rItem.Type() != myType { + tmp := reflect.New(myType) + if err := codec.Convert(rItem, tmp, nil); err != nil { + return nil, err + } + item = tmp.Elem().Interface() + } else { + item = rItem.Interface() + } + } + + return info.Encode(item, nil) +} + +func (e *encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) (int, error) { + entry, ok := e.Definitions[itemType] + if !ok { + return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) + } + return entry.GetCodecType().Size(n) +} diff --git a/pkg/solana/codec/parsed_types.go b/pkg/solana/codec/parsed_types.go new file mode 100644 index 000000000..09f20390a --- /dev/null +++ b/pkg/solana/codec/parsed_types.go @@ -0,0 +1,48 @@ +package codec + +import ( + "fmt" + "reflect" + + commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type ParsedTypes struct { + EncoderDefs map[string]Entry + DecoderDefs map[string]Entry +} + +func (parsed *ParsedTypes) ToCodec() (commontypes.RemoteCodec, error) { + modByTypeName := map[string]commoncodec.Modifier{} + if err := AddEntries(parsed.EncoderDefs, modByTypeName); err != nil { + return nil, err + } + if err := AddEntries(parsed.DecoderDefs, modByTypeName); err != nil { + return nil, err + } + + mod, err := commoncodec.NewByItemTypeModifier(modByTypeName) + if err != nil { + return nil, err + } + underlying := &solanaCodec{ + encoder: &encoder{Definitions: parsed.EncoderDefs}, + decoder: &decoder{Definitions: parsed.DecoderDefs}, + ParsedTypes: parsed, + } + return commoncodec.NewModifierCodec(underlying, mod, DecoderHooks...) +} + +// AddEntries extracts the mods from codecEntry and adds them to modByTypeName use with codec.NewByItemTypeModifier +// Since each input/output can have its own modifications, we need to keep track of them by type name +func AddEntries(defs map[string]Entry, modByTypeName map[string]commoncodec.Modifier) error { + for k, def := range defs { + modByTypeName[k] = def.Modifier() + _, err := def.Modifier().RetypeToOffChain(reflect.PointerTo(def.GetType()), k) + if err != nil { + return fmt.Errorf("%w: cannot retype %v: %w", commontypes.ErrInvalidConfig, k, err) + } + } + return nil +} diff --git a/pkg/solana/codec/solana.go b/pkg/solana/codec/solana.go index 71e2f7f06..203f1ad39 100644 --- a/pkg/solana/codec/solana.go +++ b/pkg/solana/codec/solana.go @@ -20,16 +20,17 @@ Modifiers can be provided to assist in modifying property names, adding properti package codec import ( + "encoding/json" "fmt" "math" + "reflect" "github.com/go-viper/mapstructure/v2" - "golang.org/x/text/cases" - "golang.org/x/text/language" - "github.com/smartcontractkit/chainlink-common/pkg/codec" - "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" - "github.com/smartcontractkit/chainlink-common/pkg/types" + commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" + commonencodings "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" + "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings/binary" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" ) const ( @@ -42,15 +43,85 @@ const ( // Eg: int32 in a go struct from a plugin could require a *big.Int in Pack for int24, if it fits, we shouldn't care. // SliceToArrayVerifySizeHook verifies that slices have the correct size when converting to an array // EpochToTimeHook allows multiple conversions: time.Time -> int64; int64 -> time.Time; *big.Int -> time.Time; and more -var DecoderHooks = []mapstructure.DecodeHookFunc{codec.EpochToTimeHook, codec.BigIntHook, codec.SliceToArrayVerifySizeHook} +var DecoderHooks = []mapstructure.DecodeHookFunc{commoncodec.EpochToTimeHook, commoncodec.BigIntHook, commoncodec.SliceToArrayVerifySizeHook} -func NewNamedModifierCodec(original types.RemoteCodec, itemType string, modifier codec.Modifier) (types.RemoteCodec, error) { - mod, err := codec.NewByItemTypeModifier(map[string]codec.Modifier{itemType: modifier}) +type solanaCodec struct { + *encoder + *decoder + *ParsedTypes +} + +func (s solanaCodec) CreateType(itemType string, forEncoding bool) (any, error) { + var itemTypes map[string]Entry + if forEncoding { + itemTypes = s.EncoderDefs + } else { + itemTypes = s.DecoderDefs + } + + def, ok := itemTypes[itemType] + if !ok { + return nil, fmt.Errorf("%w: cannot find type name %q", commontypes.ErrInvalidType, itemType) + } + + // we don't need double pointers, and they can also mess up reflection variable creation and mapstruct decode + if def.GetType().Kind() == reflect.Pointer { + return reflect.New(def.GetCodecType().GetType().Elem()).Interface(), nil + } + + return reflect.New(def.GetType()).Interface(), nil +} + +// NewCodec creates a new [commoncommontypes.RemoteCodec] for EVM. +// Note that names in the ABI are converted to Go names using [abi.ToCamelCase], +// this is per convention in [abi.MakeTopics], [abi.Arguments.Pack] etc. +// This allows names on-chain to be in go convention when generated. +// It means that if you need to use a [commoncodec.Modifier] to reference a field +// you need to use the Go name instead of the name on-chain. +// eg: rename FooBar -> Bar, not foo_bar_ to Bar if the name on-chain is foo_bar_ +func NewCodec(conf Config) (commontypes.RemoteCodec, error) { + parsed := &ParsedTypes{ + EncoderDefs: map[string]Entry{}, + DecoderDefs: map[string]Entry{}, + } + + for k, v := range conf.Configs { + var idl IDL + if err := json.Unmarshal([]byte(v.IDL), &idl); err != nil { + return nil, err + } + + mod, err := v.ModifierConfigs.ToModifier(DecoderHooks...) + if err != nil { + return nil, err + } + + var newEntry Entry + if v.AccountDef != nil && v.EventDef != nil { + return nil, fmt.Errorf("can't have both account and event definitions in codec config") + } else if v.AccountDef != nil { + newEntry, err = NewEntry(*v.AccountDef, idl.Types, true, mod, binary.LittleEndian()) + if err != nil { + return nil, err + } + } else { + // TODO + return nil, fmt.Errorf("event definition parsing is not implemented") + } + parsed.EncoderDefs[k] = newEntry + parsed.DecoderDefs[k] = newEntry + } + + return parsed.ToCodec() +} + +func NewNamedModifierCodec(original commontypes.RemoteCodec, itemType string, modifier commoncodec.Modifier) (commontypes.RemoteCodec, error) { + mod, err := commoncodec.NewByItemTypeModifier(map[string]commoncodec.Modifier{itemType: modifier}) if err != nil { return nil, err } - modCodec, err := codec.NewModifierCodec(original, mod, DecoderHooks...) + modCodec, err := commoncodec.NewModifierCodec(original, mod, DecoderHooks...) if err != nil { return nil, err } @@ -60,18 +131,17 @@ func NewNamedModifierCodec(original types.RemoteCodec, itemType string, modifier return modCodec, err } -func NewIDLInstructionsCodec(idl IDL, builder encodings.Builder) (types.RemoteCodec, error) { - typeCodecs := make(encodings.LenientCodecFromTypeCodec) - caser := cases.Title(language.English) +func NewIDLInstructionsCodec(idl IDL, builder commonencodings.Builder) (commontypes.RemoteCodec, error) { + typeCodecs := make(commonencodings.LenientCodecFromTypeCodec) refs := &codecRefs{ builder: builder, - codecs: make(map[string]encodings.TypeCodec), + codecs: make(map[string]commonencodings.TypeCodec), typeDefs: idl.Types, dependencies: make(map[string][]string), } for _, instruction := range idl.Instructions { - name, instCodec, err := asStruct(instruction.Args, refs, instruction.Name, caser, false) + name, instCodec, err := asStruct(instruction.Args, refs, instruction.Name, false) if err != nil { return nil, err } @@ -83,21 +153,21 @@ func NewIDLInstructionsCodec(idl IDL, builder encodings.Builder) (types.RemoteCo } // NewIDLAccountCodec is for Anchor custom types -func NewIDLAccountCodec(idl IDL, builder encodings.Builder) (types.RemoteCodec, error) { +func NewIDLAccountCodec(idl IDL, builder commonencodings.Builder) (commontypes.RemoteCodec, error) { return newIDLCoded(idl, builder, idl.Accounts, true) } -func NewIDLDefinedTypesCodec(idl IDL, builder encodings.Builder) (types.RemoteCodec, error) { +func NewIDLDefinedTypesCodec(idl IDL, builder commonencodings.Builder) (commontypes.RemoteCodec, error) { return newIDLCoded(idl, builder, idl.Types, false) } func newIDLCoded( - idl IDL, builder encodings.Builder, from IdlTypeDefSlice, includeDiscriminator bool) (types.RemoteCodec, error) { - typeCodecs := make(encodings.LenientCodecFromTypeCodec) + idl IDL, builder commonencodings.Builder, from IdlTypeDefSlice, includeDiscriminator bool) (commontypes.RemoteCodec, error) { + typeCodecs := make(commonencodings.LenientCodecFromTypeCodec) refs := &codecRefs{ builder: builder, - codecs: make(map[string]encodings.TypeCodec), + codecs: make(map[string]commonencodings.TypeCodec), typeDefs: idl.Types, dependencies: make(map[string][]string), } @@ -105,11 +175,11 @@ func newIDLCoded( for _, def := range from { var ( name string - accCodec encodings.TypeCodec + accCodec commonencodings.TypeCodec err error ) - name, accCodec, err = createNamedCodec(def, refs, includeDiscriminator) + name, accCodec, err = createCodecType(def, refs, includeDiscriminator) if err != nil { return nil, err } @@ -121,32 +191,31 @@ func newIDLCoded( } type codecRefs struct { - builder encodings.Builder - codecs map[string]encodings.TypeCodec + builder commonencodings.Builder + codecs map[string]commonencodings.TypeCodec typeDefs IdlTypeDefSlice dependencies map[string][]string } -func createNamedCodec( +func createCodecType( def IdlTypeDef, refs *codecRefs, includeDiscriminator bool, -) (string, encodings.TypeCodec, error) { - caser := cases.Title(language.English) +) (string, commonencodings.TypeCodec, error) { name := def.Name switch def.Type.Kind { case IdlTypeDefTyKindStruct: - return asStruct(*def.Type.Fields, refs, name, caser, includeDiscriminator) + return asStruct(*def.Type.Fields, refs, name, includeDiscriminator) case IdlTypeDefTyKindEnum: variants := def.Type.Variants if !variants.IsAllUint8() { - return name, nil, fmt.Errorf("%w: variants are not supported", types.ErrInvalidConfig) + return name, nil, fmt.Errorf("%w: variants are not supported", commontypes.ErrInvalidConfig) } return name, refs.builder.Uint8(), nil default: - return name, nil, fmt.Errorf(unknownIDLFormat, types.ErrInvalidConfig, def.Type.Kind) + return name, nil, fmt.Errorf(unknownIDLFormat, commontypes.ErrInvalidConfig, def.Type.Kind) } } @@ -154,17 +223,16 @@ func asStruct( fields []IdlField, refs *codecRefs, name string, // name is the struct name and can be used in dependency checks - caser cases.Caser, includeDiscriminator bool, -) (string, encodings.TypeCodec, error) { +) (string, commonencodings.TypeCodec, error) { desLen := 0 if includeDiscriminator { desLen = 1 } - named := make([]encodings.NamedTypeCodec, len(fields)+desLen) + named := make([]commonencodings.NamedTypeCodec, len(fields)+desLen) if includeDiscriminator { - named[0] = encodings.NamedTypeCodec{Name: "Discriminator" + name, Codec: NewDiscriminator(name)} + named[0] = commonencodings.NamedTypeCodec{Name: "Discriminator" + name, Codec: NewDiscriminator(name)} } for idx, field := range fields { @@ -175,10 +243,10 @@ func asStruct( return name, nil, err } - named[idx+desLen] = encodings.NamedTypeCodec{Name: caser.String(fieldName), Codec: typedCodec} + named[idx+desLen] = commonencodings.NamedTypeCodec{Name: fieldName, Codec: typedCodec} } - structCodec, err := encodings.NewStructCodec(named) + structCodec, err := commonencodings.NewStructCodec(named) if err != nil { return name, nil, err } @@ -186,7 +254,7 @@ func asStruct( return name, structCodec, nil } -func processFieldType(parentTypeName string, idlType IdlType, refs *codecRefs) (encodings.TypeCodec, error) { +func processFieldType(parentTypeName string, idlType IdlType, refs *codecRefs) (commonencodings.TypeCodec, error) { switch true { case idlType.IsString(): return getCodecByStringType(idlType.GetString(), refs.builder) @@ -201,13 +269,13 @@ func processFieldType(parentTypeName string, idlType IdlType, refs *codecRefs) ( case idlType.IsIdlTypeVec(): return asVec(parentTypeName, idlType.GetIdlTypeVec(), refs) default: - return nil, fmt.Errorf("%w: unknown IDL type def", types.ErrInvalidConfig) + return nil, fmt.Errorf("%w: unknown IDL type def", commontypes.ErrInvalidConfig) } } -func asDefined(parentTypeName string, definedName *IdlTypeDefined, refs *codecRefs) (encodings.TypeCodec, error) { +func asDefined(parentTypeName string, definedName *IdlTypeDefined, refs *codecRefs) (commonencodings.TypeCodec, error) { if definedName == nil { - return nil, fmt.Errorf("%w: defined type name should not be nil", types.ErrInvalidConfig) + return nil, fmt.Errorf("%w: defined type name should not be nil", commontypes.ErrInvalidConfig) } // already exists as a type in the typed codecs @@ -217,19 +285,19 @@ func asDefined(parentTypeName string, definedName *IdlTypeDefined, refs *codecRe // nextDef should not have a dependency on definedName if !validDependency(refs, parentTypeName, definedName.Defined) { - return nil, fmt.Errorf("%w: circular dependency detected on %s -> %s relation", types.ErrInvalidConfig, parentTypeName, definedName.Defined) + return nil, fmt.Errorf("%w: circular dependency detected on %s -> %s relation", commontypes.ErrInvalidConfig, parentTypeName, definedName.Defined) } // codec by defined type doesn't exist // process it using the provided typeDefs nextDef := refs.typeDefs.GetByName(definedName.Defined) if nextDef == nil { - return nil, fmt.Errorf("%w: IDL type does not exist for name %s", types.ErrInvalidConfig, definedName.Defined) + return nil, fmt.Errorf("%w: IDL type does not exist for name %s", commontypes.ErrInvalidConfig, definedName.Defined) } saveDependency(refs, parentTypeName, definedName.Defined) - newTypeName, newTypeCodec, err := createNamedCodec(*nextDef, refs, false) + newTypeName, newTypeCodec, err := createCodecType(*nextDef, refs, false) if err != nil { return nil, err } @@ -240,16 +308,16 @@ func asDefined(parentTypeName string, definedName *IdlTypeDefined, refs *codecRe return newTypeCodec, nil } -func asArray(parentTypeName string, idlArray *IdlTypeArray, refs *codecRefs) (encodings.TypeCodec, error) { +func asArray(parentTypeName string, idlArray *IdlTypeArray, refs *codecRefs) (commonencodings.TypeCodec, error) { codec, err := processFieldType(parentTypeName, idlArray.Thing, refs) if err != nil { return nil, err } - return encodings.NewArray(idlArray.Num, codec) + return commonencodings.NewArray(idlArray.Num, codec) } -func asVec(parentTypeName string, idlVec *IdlTypeVec, refs *codecRefs) (encodings.TypeCodec, error) { +func asVec(parentTypeName string, idlVec *IdlTypeVec, refs *codecRefs) (commonencodings.TypeCodec, error) { codec, err := processFieldType(parentTypeName, idlVec.Vec, refs) if err != nil { return nil, err @@ -260,10 +328,10 @@ func asVec(parentTypeName string, idlVec *IdlTypeVec, refs *codecRefs) (encoding return nil, err } - return encodings.NewSlice(codec, b) + return commonencodings.NewSlice(codec, b) } -func getCodecByStringType(curType IdlTypeAsString, builder encodings.Builder) (encodings.TypeCodec, error) { +func getCodecByStringType(curType IdlTypeAsString, builder commonencodings.Builder) (commonencodings.TypeCodec, error) { switch curType { case IdlTypeBool: return builder.Bool(), nil @@ -278,11 +346,11 @@ func getCodecByStringType(curType IdlTypeAsString, builder encodings.Builder) (e case IdlTypeBytes, IdlTypePublicKey, IdlTypeHash: return getByteCodecByStringType(curType, builder) default: - return nil, fmt.Errorf(unknownIDLFormat, types.ErrInvalidConfig, curType) + return nil, fmt.Errorf(unknownIDLFormat, commontypes.ErrInvalidConfig, curType) } } -func getIntCodecByStringType(curType IdlTypeAsString, builder encodings.Builder) (encodings.TypeCodec, error) { +func getIntCodecByStringType(curType IdlTypeAsString, builder commonencodings.Builder) (commonencodings.TypeCodec, error) { switch curType { case IdlTypeI8: return builder.Int8(), nil @@ -295,11 +363,11 @@ func getIntCodecByStringType(curType IdlTypeAsString, builder encodings.Builder) case IdlTypeI128: return builder.BigInt(16, true) default: - return nil, fmt.Errorf(unknownIDLFormat, types.ErrInvalidConfig, curType) + return nil, fmt.Errorf(unknownIDLFormat, commontypes.ErrInvalidConfig, curType) } } -func getUIntCodecByStringType(curType IdlTypeAsString, builder encodings.Builder) (encodings.TypeCodec, error) { +func getUIntCodecByStringType(curType IdlTypeAsString, builder commonencodings.Builder) (commonencodings.TypeCodec, error) { switch curType { case IdlTypeU8: return builder.Uint8(), nil @@ -312,22 +380,22 @@ func getUIntCodecByStringType(curType IdlTypeAsString, builder encodings.Builder case IdlTypeU128: return builder.BigInt(16, true) default: - return nil, fmt.Errorf(unknownIDLFormat, types.ErrInvalidConfig, curType) + return nil, fmt.Errorf(unknownIDLFormat, commontypes.ErrInvalidConfig, curType) } } -func getTimeCodecByStringType(curType IdlTypeAsString, builder encodings.Builder) (encodings.TypeCodec, error) { +func getTimeCodecByStringType(curType IdlTypeAsString, builder commonencodings.Builder) (commonencodings.TypeCodec, error) { switch curType { case IdlTypeUnixTimestamp: return builder.Int64(), nil case IdlTypeDuration: return NewDuration(builder), nil default: - return nil, fmt.Errorf(unknownIDLFormat, types.ErrInvalidConfig, curType) + return nil, fmt.Errorf(unknownIDLFormat, commontypes.ErrInvalidConfig, curType) } } -func getByteCodecByStringType(curType IdlTypeAsString, builder encodings.Builder) (encodings.TypeCodec, error) { +func getByteCodecByStringType(curType IdlTypeAsString, builder commonencodings.Builder) (commonencodings.TypeCodec, error) { switch curType { case IdlTypeBytes: b, err := builder.Int(4) @@ -335,11 +403,11 @@ func getByteCodecByStringType(curType IdlTypeAsString, builder encodings.Builder return nil, err } - return encodings.NewSlice(builder.Uint8(), b) + return commonencodings.NewSlice(builder.Uint8(), b) case IdlTypePublicKey, IdlTypeHash: - return encodings.NewArray(DefaultHashBitLength, builder.Uint8()) + return commonencodings.NewArray(DefaultHashBitLength, builder.Uint8()) default: - return nil, fmt.Errorf(unknownIDLFormat, types.ErrInvalidConfig, curType) + return nil, fmt.Errorf(unknownIDLFormat, commontypes.ErrInvalidConfig, curType) } } diff --git a/pkg/solana/codec/solana_test.go b/pkg/solana/codec/solana_test.go index 4dd116691..ffefd5047 100644 --- a/pkg/solana/codec/solana_test.go +++ b/pkg/solana/codec/solana_test.go @@ -3,13 +3,10 @@ package codec_test import ( "encoding/json" "testing" - "time" - ag_solana "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - codeccommon "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings/binary" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" @@ -60,75 +57,6 @@ func TestNewIDLDefinedTypesCodecCodec(t *testing.T) { require.Equal(t, expected, decoded) } -func TestNewIDLCodec_WithModifiers(t *testing.T) { - t.Parallel() - - ctx := tests.Context(t) - _, _, idlCodec := newTestIDLAndCodec(t, true) - modConfig := codeccommon.ModifiersConfig{ - &codeccommon.RenameModifierConfig{Fields: map[string]string{"Value": "V"}}, - } - - renameMod, err := modConfig.ToModifier(codec.DecoderHooks...) - require.NoError(t, err) - - idlCodecWithMods, err := codec.NewNamedModifierCodec(idlCodec, testutils.TestStructWithNestedStruct, renameMod) - require.NoError(t, err) - - type modifiedTestStruct struct { - V uint8 - InnerStruct testutils.ObjectRef1 - BasicNestedArray [][]uint32 - Option *string - DefinedArray []testutils.ObjectRef2 - BasicVector []string - TimeVal int64 - DurationVal time.Duration - PublicKey ag_solana.PublicKey - EnumVal uint8 - } - - expected := modifiedTestStruct{ - V: testutils.DefaultTestStruct.Value, - InnerStruct: testutils.DefaultTestStruct.InnerStruct, - BasicNestedArray: testutils.DefaultTestStruct.BasicNestedArray, - Option: testutils.DefaultTestStruct.Option, - DefinedArray: testutils.DefaultTestStruct.DefinedArray, - BasicVector: testutils.DefaultTestStruct.BasicVector, - TimeVal: testutils.DefaultTestStruct.TimeVal, - DurationVal: testutils.DefaultTestStruct.DurationVal, - PublicKey: testutils.DefaultTestStruct.PublicKey, - EnumVal: testutils.DefaultTestStruct.EnumVal, - } - - withModsBts, err := idlCodecWithMods.Encode(ctx, expected, testutils.TestStructWithNestedStruct) - require.NoError(t, err) - - noModsBts, err := idlCodec.Encode(ctx, testutils.DefaultTestStruct, testutils.TestStructWithNestedStruct) - - // the codec without modifiers should encode an unmodified struct to the same bytes - // as the codec with modifiers encodes a modified struct - require.NoError(t, err) - require.Equal(t, withModsBts, noModsBts) - - var decoded modifiedTestStruct - - // the codec with modifiers should decode from unmodified bytes into a modified struct - require.NoError(t, idlCodecWithMods.Decode(ctx, noModsBts, &decoded, testutils.TestStructWithNestedStruct)) - require.Equal(t, expected, decoded) - - var unmodifiedDecoded testutils.StructWithNestedStruct - - // the codec without modifiers should decode from unmodified bytes to the same values as - // modified struct - require.NoError(t, idlCodec.Decode(ctx, noModsBts, &unmodifiedDecoded, testutils.TestStructWithNestedStruct)) - require.Equal(t, expected.V, unmodifiedDecoded.Value) - require.Equal(t, expected.TimeVal, unmodifiedDecoded.TimeVal) - require.Equal(t, expected.DurationVal, unmodifiedDecoded.DurationVal) - require.Equal(t, expected.PublicKey, unmodifiedDecoded.PublicKey) - require.Equal(t, expected.EnumVal, unmodifiedDecoded.EnumVal) -} - func TestNewIDLCodec_CircularDependency(t *testing.T) { t.Parallel() diff --git a/pkg/solana/codec/testutils/converters.go b/pkg/solana/codec/testutils/converters.go new file mode 100644 index 000000000..ae79ca945 --- /dev/null +++ b/pkg/solana/codec/testutils/converters.go @@ -0,0 +1,148 @@ +package testutils + +import ( + "math/big" + + bin "github.com/gagliardetto/binary" + "github.com/gagliardetto/solana-go" + "github.com/smartcontractkit/libocr/commontypes" + + "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils/test_item_type" +) + +func EncodeRequestToTestStruct(request *interfacetests.EncodeRequest) test_item_type.TestStruct { + byt := [32]byte{} + for i, v := range request.TestStructs[0].OracleIDs { + byt[i] = byte(v) + } + + k, _ := solana.PublicKeyFromBase58(request.TestStructs[0].AccountStruct.AccountStr) + + accs := make([]solana.PublicKey, len(request.TestStructs[0].Accounts)) + for i, v := range request.TestStructs[0].Accounts { + accs[i] = solana.PublicKeyFromBytes(v) + } + + testStruct := test_item_type.TestStruct{ + Field: *request.TestStructs[0].Field, + OracleId: uint8(request.TestStructs[0].OracleID), + OracleIds: byt, + AccountStruct: test_item_type.AccountStruct{ + Account: solana.PublicKeyFromBytes(request.TestStructs[0].AccountStruct.Account), + AccountStr: k, + }, + Accounts: accs, + DifferentField: request.TestStructs[0].DifferentField, + BigField: bigIntToBinInt128(request.TestStructs[0].BigField), + NestedDynamicStruct: test_item_type.NestedDynamic{ + FixedBytes: request.TestStructs[0].NestedDynamicStruct.FixedBytes, + Inner: test_item_type.InnerDynamic{ + IntVal: int64(request.TestStructs[0].NestedDynamicStruct.Inner.I), + S: request.TestStructs[0].NestedDynamicStruct.Inner.S, + }, + }, + NestedStaticStruct: test_item_type.NestedStatic{ + FixedBytes: request.TestStructs[0].NestedStaticStruct.FixedBytes, + Inner: test_item_type.InnerStatic{ + IntVal: int64(request.TestStructs[0].NestedStaticStruct.Inner.I), + A: solana.PublicKeyFromBytes(request.TestStructs[0].NestedStaticStruct.Inner.A), + }, + }, + } + return testStruct +} + +func bigIntToBinInt128(val *big.Int) bin.Int128 { + return bin.Int128{ + Lo: val.Uint64(), + Hi: new(big.Int).Rsh(val, 64).Uint64(), + } +} + +func argsFromTestStruct(ts interfacetests.TestStruct) []any { + return []any{ + ts.Field, + ts.DifferentField, + uint8(ts.OracleID), + getOracleIDs(ts), + accountStructToInternalType(ts.AccountStruct), + getAccounts(ts), + ts.BigField, + midDynamicToInternalType(ts.NestedDynamicStruct), + midStaticToInternalType(ts.NestedStaticStruct), + } +} + +func getOracleIDs(first interfacetests.TestStruct) [32]byte { + oracleIDs := [32]byte{} + for i, oracleID := range first.OracleIDs { + oracleIDs[i] = byte(oracleID) + } + return oracleIDs +} + +func oracleIDsToBytes(oracleIDs [32]commontypes.OracleID) [32]byte { + convertedIDs := [32]byte{} + for i, id := range oracleIDs { + convertedIDs[i] = byte(id) + } + return convertedIDs +} + +func ToInternalType(testStruct interfacetests.TestStruct) test_item_type.TestStruct { + return test_item_type.TestStruct{ + Field: *testStruct.Field, + DifferentField: testStruct.DifferentField, + OracleId: byte(testStruct.OracleID), + OracleIds: oracleIDsToBytes(testStruct.OracleIDs), + AccountStruct: accountStructToInternalType(testStruct.AccountStruct), + Accounts: convertAccounts(testStruct.Accounts), + BigField: bigIntToBinInt128(testStruct.BigField), + NestedDynamicStruct: midDynamicToInternalType(testStruct.NestedDynamicStruct), + NestedStaticStruct: midStaticToInternalType(testStruct.NestedStaticStruct), + } +} + +func accountStructToInternalType(a interfacetests.AccountStruct) test_item_type.AccountStruct { + return test_item_type.AccountStruct{ + Account: solana.PublicKeyFromBytes(a.Account), + AccountStr: solana.MustPublicKeyFromBase58(a.AccountStr), + } +} + +func convertAccounts(accounts [][]byte) []solana.PublicKey { + convertedAccounts := make([]solana.PublicKey, len(accounts)) + for i, a := range accounts { + convertedAccounts[i] = solana.PublicKeyFromBytes(a) + } + return convertedAccounts +} + +func midDynamicToInternalType(m interfacetests.MidLevelDynamicTestStruct) test_item_type.NestedDynamic { + return test_item_type.NestedDynamic{ + FixedBytes: m.FixedBytes, + Inner: test_item_type.InnerDynamic{ + IntVal: int64(m.Inner.I), + S: m.Inner.S, + }, + } +} + +func midStaticToInternalType(m interfacetests.MidLevelStaticTestStruct) test_item_type.NestedStatic { + return test_item_type.NestedStatic{ + FixedBytes: m.FixedBytes, + Inner: test_item_type.InnerStatic{ + IntVal: int64(m.Inner.I), + A: solana.PublicKeyFromBytes(m.Inner.A), + }, + } +} + +func getAccounts(first interfacetests.TestStruct) []solana.PublicKey { + accountBytes := make([]solana.PublicKey, len(first.Accounts)) + for i, account := range first.Accounts { + accountBytes[i] = solana.PublicKeyFromBytes(account) + } + return accountBytes +} diff --git a/pkg/solana/codec/testutils/itemArray1TypeIDL.json b/pkg/solana/codec/testutils/itemArray1TypeIDL.json new file mode 100644 index 000000000..589c7e0a6 --- /dev/null +++ b/pkg/solana/codec/testutils/itemArray1TypeIDL.json @@ -0,0 +1,108 @@ +{ + "version": "0.1.0", + "name": "test_item_array1_type", + "instructions": [ + { + "name": "ProcessTestItemArray1Type", + "accounts": [ + { + "name": "testItemArray", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "TestItemArray", + "type": { + "kind": "struct", + "fields": [ + { + "name": "Items", + "type": { + "array": [ + { + "defined": "TestItem" + }, + 1 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "TestItem", + "type": { + "kind": "struct", + "fields": [ + { "name": "Field", "type": "i32" }, + { "name": "DifferentField", "type": "string" }, + { "name": "OracleId", "type": "u8" }, + { "name": "OracleIds", "type": { "array": ["u8", 32] } }, + { "name": "AccountStruct", "type": { "defined": "AccountStruct" } }, + { "name": "Accounts", "type": { "vec": "publicKey" } }, + { "name": "DifferentField", "type": "string" }, + { "name": "BigField", "type": "i128" }, + { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, + { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } } + ] + } + }, + { + "name": "AccountStruct", + "type": { + "kind": "struct", + "fields": [ + { "name": "Account", "type": "publicKey" }, + { "name": "AccountStr", "type": "publicKey" } + ] + } + }, + { + "name": "InnerDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "S", "type": "string" } + ] + } + }, + { + "name": "NestedDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerDynamic" } } + ] + } + }, + { + "name": "InnerStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "A", "type": "publicKey" } + ] + } + }, + { + "name": "NestedStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerStatic" } } + ] + } + } + ] +} diff --git a/pkg/solana/codec/testutils/itemArray2TypeIDL.json b/pkg/solana/codec/testutils/itemArray2TypeIDL.json new file mode 100644 index 000000000..0dcc0a0d4 --- /dev/null +++ b/pkg/solana/codec/testutils/itemArray2TypeIDL.json @@ -0,0 +1,107 @@ +{ + "version": "0.1.0", + "name": "test_item_array2_type", + "instructions": [ + { + "name": "ProcessTestItemArray2Type", + "accounts": [ + { + "name": "TestItemArray", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "TestItemArray", + "type": { + "kind": "struct", + "fields": [ + { + "name": "Items", + "type": { + "array": [ + { + "defined": "TestItem" + }, + 2 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "TestItem", + "type": { + "kind": "struct", + "fields": [ + { "name": "Field", "type": "i32" }, + { "name": "DifferentField", "type": "string" }, + { "name": "OracleId", "type": "u8" }, + { "name": "OracleIds", "type": { "array": ["u8", 32] } }, + { "name": "AccountStruct", "type": { "defined": "AccountStruct" } }, + { "name": "Accounts", "type": { "vec": "publicKey" } }, + { "name": "BigField", "type": "i128" }, + { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, + { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } } + ] + } + }, + { + "name": "AccountStruct", + "type": { + "kind": "struct", + "fields": [ + { "name": "Account", "type": "publicKey" }, + { "name": "AccountStr", "type": "publicKey" } + ] + } + }, + { + "name": "InnerDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "S", "type": "string" } + ] + } + }, + { + "name": "NestedDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerDynamic" } } + ] + } + }, + { + "name": "InnerStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "A", "type": "publicKey" } + ] + } + }, + { + "name": "NestedStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerStatic" } } + ] + } + } + ] +} diff --git a/pkg/solana/codec/testutils/itemWithConfigExtraIDL.json b/pkg/solana/codec/testutils/itemWithConfigExtraIDL.json new file mode 100644 index 000000000..05405f3eb --- /dev/null +++ b/pkg/solana/codec/testutils/itemWithConfigExtraIDL.json @@ -0,0 +1,91 @@ +{ + "version": "0.1.0", + "name": "test_item_with_config_extra", + "instructions": [ + { + "name": "ProcessTestItemWithConfigExtra", + "accounts": [ + { + "name": "TestItemWithConfigExtra", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "TestItemWithConfigExtra", + "type": { + "kind": "struct", + "fields": [ + { "name": "Field", "type": "i32" }, + { "name": "DifferentField", "type": "string" }, + { "name": "OracleId", "type": "u8" }, + { "name": "OracleIds", "type": { "array": ["u8", 32] } }, + { "name": "AccountStruct", "type": { "defined": "AccountStruct" } }, + { "name": "Accounts", "type": { "vec": "publicKey" } }, + { "name": "BigField", "type": "i128" }, + { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, + { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } }, + { "name": "ConfigParam1", "type": "u64" }, + { "name": "ConfigParam2", "type": "bool" }, + { "name": "ConfigParam3", "type": { "option": "string" } } + ] + } + } + ], + "types": [ + { + "name": "AccountStruct", + "type": { + "kind": "struct", + "fields": [ + { "name": "Account", "type": "publicKey" }, + { "name": "AccountStr", "type": "publicKey" } + ] + } + }, + { + "name": "InnerDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "S", "type": "string" } + ] + } + }, + { + "name": "NestedDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerDynamic" } } + ] + } + }, + { + "name": "InnerStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "A", "type": "publicKey" } + ] + } + }, + { + "name": "NestedStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerStatic" } } + ] + } + } + ] +} \ No newline at end of file diff --git a/pkg/solana/codec/testutils/nilTypeIDL.json b/pkg/solana/codec/testutils/nilTypeIDL.json new file mode 100644 index 000000000..ca1afd638 --- /dev/null +++ b/pkg/solana/codec/testutils/nilTypeIDL.json @@ -0,0 +1,12 @@ +{ + "name": "NilType", + "accounts": [ + { + "name": "NilAccount", + "type": { + "kind": "struct", + "fields": [] + } + } + ] +} \ No newline at end of file diff --git a/pkg/solana/codec/testutils/sizeItemTypeIDL.json b/pkg/solana/codec/testutils/sizeItemTypeIDL.json new file mode 100644 index 000000000..fdb73115e --- /dev/null +++ b/pkg/solana/codec/testutils/sizeItemTypeIDL.json @@ -0,0 +1,38 @@ +{ + "version": "0.1.0", + "name": "item_for_size", + "instructions": [ + { + "name": "ProcessItemForSize", + "accounts": [ + { + "name": "ItemForSize", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "ItemForSize", + "type": { + "kind": "struct", + "fields": [ + { + "name": "Stuff", + "type": { + "vec": "i128" + } + }, + { + "name": "OtherStuff", + "type": "i128" + } + ] + } + } + ], + "types": [] +} diff --git a/pkg/solana/codec/testutils/testIDL.json b/pkg/solana/codec/testutils/testIDL.json index 0ab037721..9efb85063 100644 --- a/pkg/solana/codec/testutils/testIDL.json +++ b/pkg/solana/codec/testutils/testIDL.json @@ -1,232 +1,88 @@ { "version": "0.1.0", - "name": "some_test_idl", + "name": "test_item_type", + "instructions": [ + { + "name": "processTestItemType", + "accounts": [ + { + "name": "testItem", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], "accounts": [ { - "name": "StructWithNestedStruct", + "name": "TestItem", "type": { "kind": "struct", "fields": [ - { - "name": "value", - "type": "u8" - }, - { - "name": "innerStruct", - "type": { - "defined": "ObjectRef1" - } - }, - { - "name": "basicNestedArray", - "type": { - "array": [ - { - "array": [ - "u32", - 3 - ] - }, - 3 - ] - } - }, - { - "name": "option", - "type": { - "option": "string" - } - }, - { - "name": "definedArray", - "type": { - "array": [ - { - "defined": "ObjectRef2" - }, - 2 - ] - } - }, - { - "name": "basicVector", - "type": { - "vec": "string" - } - }, - { - "name": "timeVal", - "type": "unixTimestamp" - }, - { - "name": "durationVal", - "type": "duration" - }, - { - "name": "publicKey", - "type": "publicKey" - }, - { - "name": "enumVal", - "type": { - "defined": "SimpleEnum" - } - } + { "name": "field", "type": "i32" }, + { "name": "differentField", "type": "string" }, + { "name": "oracleId", "type": "u8" }, + { "name": "oracleIds", "type": { "array": ["u8", 32] } }, + { "name": "accountStruct", "type": { "defined": "AccountStruct" } }, + { "name": "accounts", "type": { "vec": "publicKey" } }, + { "name": "bigField", "type": "i128" }, + { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, + { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } } ] } } ], "types": [ { - "name": "StructWithNestedStructType", + "name": "AccountStruct", + "type": { + "kind": "struct", + "fields": [ + { "name": "account", "type": "publicKey" }, + { "name": "accountStr", "type": "publicKey" } + ] + } + }, + { + "name": "InnerDynamic", "type": { "kind": "struct", "fields": [ - { - "name": "value", - "type": "u8" - }, - { - "name": "innerStruct", - "type": { - "defined": "ObjectRef1" - } - }, - { - "name": "basicNestedArray", - "type": { - "array": [ - { - "array": [ - "u32", - 3 - ] - }, - 3 - ] - } - }, - { - "name": "option", - "type": { - "option": "string" - } - }, - { - "name": "definedArray", - "type": { - "array": [ - { - "defined": "ObjectRef2" - }, - 2 - ] - } - }, - { - "name": "basicVector", - "type": { - "vec": "string" - } - }, - { - "name": "timeVal", - "type": "unixTimestamp" - }, - { - "name": "durationVal", - "type": "duration" - }, - { - "name": "publicKey", - "type": "publicKey" - }, - { - "name": "enumVal", - "type": { - "defined": "SimpleEnum" - } - } + { "name": "intVal", "type": "i64" }, + { "name": "s", "type": "string" } ] } }, { - "name": "ObjectRef1", + "name": "NestedDynamic", "type": { "kind": "struct", "fields": [ - { - "name": "prop1", - "type": "i8" - }, - { - "name": "prop2", - "type": "string" - }, - { - "name": "prop3", - "type": "u128" - }, - { - "name": "prop4", - "type": "u16" - }, - { - "name": "prop5", - "type": "u64" - }, - { - "name": "prop6", - "type": "bool" - } + { "name": "fixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "inner", "type": { "defined": "InnerDynamic" } } ] } }, { - "name": "ObjectRef2", + "name": "InnerStatic", "type": { "kind": "struct", "fields": [ - { - "name": "prop1", - "type": "u32" - }, - { - "name": "prop2", - "type": "i128" - }, - { - "name": "prop3", - "type": "i16" - }, - { - "name": "prop4", - "type": "i32" - }, - { - "name": "prop5", - "type": "i64" - }, - { - "name": "prop6", - "type": "bytes" - } + { "name": "intVal", "type": "i64" }, + { "name": "a", "type": "publicKey" } ] } }, { - "name": "SimpleEnum", + "name": "NestedStatic", "type": { - "kind": "enum", - "variants": [ - { - "name": "A" - }, - { - "name": "B" - } + "kind": "struct", + "fields": [ + { "name": "fixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "inner", "type": { "defined": "InnerStatic" } } ] } } ] -} \ No newline at end of file +} diff --git a/pkg/solana/codec/testutils/test_item_slice_type/itemSliceTypeIDL.json b/pkg/solana/codec/testutils/test_item_slice_type/itemSliceTypeIDL.json new file mode 100644 index 000000000..8f4d42bd2 --- /dev/null +++ b/pkg/solana/codec/testutils/test_item_slice_type/itemSliceTypeIDL.json @@ -0,0 +1,104 @@ + { + "version": "0.1.0", + "name": "test_item_slice_type", + "instructions": [ + { + "name": "ProcessTestItemSliceType", + "accounts": [ + { + "name": "TestItemSlice", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "TestItemSlice", + "type": { + "kind": "struct", + "fields": [ + { + "name": "Items", + "type": { + "vec": { + "defined": "TestItem" + } + } + } + ] + } + } + ], + "types": [ + { + "name": "TestItem", + "type": { + "kind": "struct", + "fields": [ + { "name": "Field", "type": "i32" }, + { "name": "DifferentField", "type": "string" }, + { "name": "OracleId", "type": "u8" }, + { "name": "OracleIds", "type": { "array": ["u8", 32] } }, + { "name": "AccountStruct", "type": { "defined": "AccountStruct" } }, + { "name": "Accounts", "type": { "vec": "publicKey" } }, + { "name": "BigField", "type": "i128" }, + { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, + { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } } + ] + } + }, + { + "name": "AccountStruct", + "type": { + "kind": "struct", + "fields": [ + { "name": "Account", "type": "publicKey" }, + { "name": "AccountStr", "type": "publicKey" } + ] + } + }, + { + "name": "InnerDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "S", "type": "string" } + ] + } + }, + { + "name": "NestedDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerDynamic" } } + ] + } + }, + { + "name": "InnerStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "A", "type": "publicKey" } + ] + } + }, + { + "name": "NestedStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerStatic" } } + ] + } + } + ] + } diff --git a/pkg/solana/codec/testutils/test_item_slice_type/types.go b/pkg/solana/codec/testutils/test_item_slice_type/types.go new file mode 100644 index 000000000..16c0ce105 --- /dev/null +++ b/pkg/solana/codec/testutils/test_item_slice_type/types.go @@ -0,0 +1,283 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package test_item_slice_type + +import ( + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" +) + +type TestItem struct { + Field int32 + DifferentField string + OracleId uint8 + OracleIds [32]uint8 + AccountStruct AccountStruct + Accounts []ag_solanago.PublicKey + BigField ag_binary.Int128 + NestedDynamicStruct NestedDynamic + NestedStaticStruct NestedStatic +} + +func (obj TestItem) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Field` param: + err = encoder.Encode(obj.Field) + if err != nil { + return err + } + // Serialize `DifferentField` param: + err = encoder.Encode(obj.DifferentField) + if err != nil { + return err + } + // Serialize `OracleId` param: + err = encoder.Encode(obj.OracleId) + if err != nil { + return err + } + // Serialize `OracleIds` param: + err = encoder.Encode(obj.OracleIds) + if err != nil { + return err + } + // Serialize `AccountStruct` param: + err = encoder.Encode(obj.AccountStruct) + if err != nil { + return err + } + // Serialize `Accounts` param: + err = encoder.Encode(obj.Accounts) + if err != nil { + return err + } + // Serialize `BigField` param: + err = encoder.Encode(obj.BigField) + if err != nil { + return err + } + // Serialize `NestedDynamicStruct` param: + err = encoder.Encode(obj.NestedDynamicStruct) + if err != nil { + return err + } + // Serialize `NestedStaticStruct` param: + err = encoder.Encode(obj.NestedStaticStruct) + if err != nil { + return err + } + return nil +} + +func (obj *TestItem) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Field`: + err = decoder.Decode(&obj.Field) + if err != nil { + return err + } + // Deserialize `DifferentField`: + err = decoder.Decode(&obj.DifferentField) + if err != nil { + return err + } + // Deserialize `OracleId`: + err = decoder.Decode(&obj.OracleId) + if err != nil { + return err + } + // Deserialize `OracleIds`: + err = decoder.Decode(&obj.OracleIds) + if err != nil { + return err + } + // Deserialize `AccountStruct`: + err = decoder.Decode(&obj.AccountStruct) + if err != nil { + return err + } + // Deserialize `Accounts`: + err = decoder.Decode(&obj.Accounts) + if err != nil { + return err + } + // Deserialize `BigField`: + err = decoder.Decode(&obj.BigField) + if err != nil { + return err + } + // Deserialize `NestedDynamicStruct`: + err = decoder.Decode(&obj.NestedDynamicStruct) + if err != nil { + return err + } + // Deserialize `NestedStaticStruct`: + err = decoder.Decode(&obj.NestedStaticStruct) + if err != nil { + return err + } + return nil +} + +type AccountStruct struct { + Account ag_solanago.PublicKey + AccountStr ag_solanago.PublicKey +} + +func (obj AccountStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Account` param: + err = encoder.Encode(obj.Account) + if err != nil { + return err + } + // Serialize `AccountStr` param: + err = encoder.Encode(obj.AccountStr) + if err != nil { + return err + } + return nil +} + +func (obj *AccountStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Account`: + err = decoder.Decode(&obj.Account) + if err != nil { + return err + } + // Deserialize `AccountStr`: + err = decoder.Decode(&obj.AccountStr) + if err != nil { + return err + } + return nil +} + +type InnerDynamic struct { + IntVal int64 + S string +} + +func (obj InnerDynamic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `IntVal` param: + err = encoder.Encode(obj.IntVal) + if err != nil { + return err + } + // Serialize `S` param: + err = encoder.Encode(obj.S) + if err != nil { + return err + } + return nil +} + +func (obj *InnerDynamic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `IntVal`: + err = decoder.Decode(&obj.IntVal) + if err != nil { + return err + } + // Deserialize `S`: + err = decoder.Decode(&obj.S) + if err != nil { + return err + } + return nil +} + +type NestedDynamic struct { + FixedBytes [2]uint8 + Inner InnerDynamic +} + +func (obj NestedDynamic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *NestedDynamic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} + +type InnerStatic struct { + IntVal int64 + A ag_solanago.PublicKey +} + +func (obj InnerStatic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `IntVal` param: + err = encoder.Encode(obj.IntVal) + if err != nil { + return err + } + // Serialize `A` param: + err = encoder.Encode(obj.A) + if err != nil { + return err + } + return nil +} + +func (obj *InnerStatic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `IntVal`: + err = decoder.Decode(&obj.IntVal) + if err != nil { + return err + } + // Deserialize `A`: + err = decoder.Decode(&obj.A) + if err != nil { + return err + } + return nil +} + +type NestedStatic struct { + FixedBytes [2]uint8 + Inner InnerStatic +} + +func (obj NestedStatic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *NestedStatic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} diff --git a/pkg/solana/codec/testutils/test_item_type/accounts.go b/pkg/solana/codec/testutils/test_item_type/accounts.go new file mode 100644 index 000000000..728945e62 --- /dev/null +++ b/pkg/solana/codec/testutils/test_item_type/accounts.go @@ -0,0 +1,140 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package test_item_type + +import ( + "fmt" + + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" +) + +type TestStruct struct { + Field int32 + OracleId uint8 + OracleIds [32]uint8 + AccountStruct AccountStruct + Accounts []ag_solanago.PublicKey + DifferentField string + BigField ag_binary.Int128 + NestedDynamicStruct NestedDynamic + NestedStaticStruct NestedStatic +} + +var TestStructDiscriminator = [8]byte{243, 149, 82, 70, 154, 54, 107, 6} + +func (obj TestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(TestStructDiscriminator[:], false) + if err != nil { + return err + } + // Serialize `Field` param: + err = encoder.Encode(obj.Field) + if err != nil { + return err + } + // Serialize `OracleId` param: + err = encoder.Encode(obj.OracleId) + if err != nil { + return err + } + // Serialize `OracleIds` param: + err = encoder.Encode(obj.OracleIds) + if err != nil { + return err + } + // Serialize `AccountStruct` param: + err = encoder.Encode(obj.AccountStruct) + if err != nil { + return err + } + // Serialize `Accounts` param: + err = encoder.Encode(obj.Accounts) + if err != nil { + return err + } + // Serialize `DifferentField` param: + err = encoder.Encode(obj.DifferentField) + if err != nil { + return err + } + // Serialize `BigField` param: + err = encoder.Encode(obj.BigField) + if err != nil { + return err + } + // Serialize `NestedDynamicStruct` param: + err = encoder.Encode(obj.NestedDynamicStruct) + if err != nil { + return err + } + // Serialize `NestedStaticStruct` param: + err = encoder.Encode(obj.NestedStaticStruct) + if err != nil { + return err + } + return nil +} + +func (obj *TestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(TestStructDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[243 149 82 70 154 54 107 6]", + fmt.Sprint(discriminator[:])) + } + } + // Deserialize `Field`: + err = decoder.Decode(&obj.Field) + if err != nil { + return err + } + // Deserialize `OracleId`: + err = decoder.Decode(&obj.OracleId) + if err != nil { + return err + } + // Deserialize `OracleIds`: + err = decoder.Decode(&obj.OracleIds) + if err != nil { + return err + } + // Deserialize `AccountStruct`: + err = decoder.Decode(&obj.AccountStruct) + if err != nil { + return err + } + // Deserialize `Accounts`: + err = decoder.Decode(&obj.Accounts) + if err != nil { + return err + } + // Deserialize `DifferentField`: + err = decoder.Decode(&obj.DifferentField) + if err != nil { + return err + } + // Deserialize `BigField`: + err = decoder.Decode(&obj.BigField) + if err != nil { + return err + } + // Deserialize `NestedDynamicStruct`: + err = decoder.Decode(&obj.NestedDynamicStruct) + if err != nil { + return err + } + // Deserialize `NestedStaticStruct`: + err = decoder.Decode(&obj.NestedStaticStruct) + if err != nil { + return err + } + return nil +} diff --git a/pkg/solana/codec/testutils/test_item_type/itemIDL.json b/pkg/solana/codec/testutils/test_item_type/itemIDL.json new file mode 100644 index 000000000..529ae23a7 --- /dev/null +++ b/pkg/solana/codec/testutils/test_item_type/itemIDL.json @@ -0,0 +1,88 @@ +{ + "version": "0.1.0", + "name": "test_item_type", + "instructions": [ + { + "name": "processTestItemType", + "accounts": [ + { + "name": "TestStruct", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "TestStruct", + "type": { + "kind": "struct", + "fields": [ + { "name": "Field", "type": "i32" }, + { "name": "OracleId", "type": "u8" }, + { "name": "OracleIds", "type": { "array": ["u8", 32] } }, + { "name": "AccountStruct", "type": { "defined": "AccountStruct" } }, + { "name": "Accounts", "type": { "vec": "publicKey" } }, + { "name": "DifferentField", "type": "string" }, + { "name": "BigField", "type": "i128" }, + { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, + { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } } + ] + } + } + ], + "types": [ + { + "name": "AccountStruct", + "type": { + "kind": "struct", + "fields": [ + { "name": "Account", "type": "publicKey" }, + { "name": "AccountStr", "type": "publicKey" } + ] + } + }, + { + "name": "InnerDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "S", "type": "string" } + ] + } + }, + { + "name": "NestedDynamic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerDynamic" } } + ] + } + }, + { + "name": "InnerStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "IntVal", "type": "i64" }, + { "name": "A", "type": "publicKey" } + ] + } + }, + { + "name": "NestedStatic", + "type": { + "kind": "struct", + "fields": [ + { "name": "FixedBytes", "type": { "array": ["u8", 2] } }, + { "name": "Inner", "type": { "defined": "InnerStatic" } } + ] + } + } + ] +} diff --git a/pkg/solana/codec/testutils/test_item_type/types.go b/pkg/solana/codec/testutils/test_item_type/types.go new file mode 100644 index 000000000..a5049aa53 --- /dev/null +++ b/pkg/solana/codec/testutils/test_item_type/types.go @@ -0,0 +1,173 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package test_item_type + +import ( + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" +) + +type AccountStruct struct { + Account ag_solanago.PublicKey + AccountStr ag_solanago.PublicKey +} + +func (obj AccountStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Account` param: + err = encoder.Encode(obj.Account) + if err != nil { + return err + } + // Serialize `AccountStr` param: + err = encoder.Encode(obj.AccountStr) + if err != nil { + return err + } + return nil +} + +func (obj *AccountStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Account`: + err = decoder.Decode(&obj.Account) + if err != nil { + return err + } + // Deserialize `AccountStr`: + err = decoder.Decode(&obj.AccountStr) + if err != nil { + return err + } + return nil +} + +type InnerDynamic struct { + IntVal int64 + S string +} + +func (obj InnerDynamic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `IntVal` param: + err = encoder.Encode(obj.IntVal) + if err != nil { + return err + } + // Serialize `S` param: + err = encoder.Encode(obj.S) + if err != nil { + return err + } + return nil +} + +func (obj *InnerDynamic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `IntVal`: + err = decoder.Decode(&obj.IntVal) + if err != nil { + return err + } + // Deserialize `S`: + err = decoder.Decode(&obj.S) + if err != nil { + return err + } + return nil +} + +type NestedDynamic struct { + FixedBytes [2]uint8 + Inner InnerDynamic +} + +func (obj NestedDynamic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *NestedDynamic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} + +type InnerStatic struct { + IntVal int64 + A ag_solanago.PublicKey +} + +func (obj InnerStatic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `IntVal` param: + err = encoder.Encode(obj.IntVal) + if err != nil { + return err + } + // Serialize `A` param: + err = encoder.Encode(obj.A) + if err != nil { + return err + } + return nil +} + +func (obj *InnerStatic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `IntVal`: + err = decoder.Decode(&obj.IntVal) + if err != nil { + return err + } + // Deserialize `A`: + err = decoder.Decode(&obj.A) + if err != nil { + return err + } + return nil +} + +type NestedStatic struct { + FixedBytes [2]uint8 + Inner InnerStatic +} + +func (obj NestedStatic) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *NestedStatic) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} diff --git a/pkg/solana/codec/testutils/types.go b/pkg/solana/codec/testutils/types.go index 533e88b0b..3bdce672d 100644 --- a/pkg/solana/codec/testutils/types.go +++ b/pkg/solana/codec/testutils/types.go @@ -5,7 +5,7 @@ import ( "math/big" "time" - ag_solana "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go" ) var ( @@ -45,9 +45,15 @@ var ( BasicVector: []string{"some string", "another string"}, TimeVal: 683_100_000, DurationVal: 42 * time.Second, - PublicKey: ag_solana.NewWallet().PublicKey(), + PublicKey: solana.NewWallet().PublicKey(), EnumVal: 0, } + TestItemType = "TestItem" + TestItemSliceType = "TestItemSliceType" + TestItemArray1Type = "TestItemArray1Type" + TestItemArray2Type = "TestItemArray2Type" + TestItemWithConfigExtra = "TestItemWithConfigExtra" + NilType = "NilType" ) type StructWithNestedStruct struct { @@ -59,7 +65,7 @@ type StructWithNestedStruct struct { BasicVector []string TimeVal int64 DurationVal time.Duration - PublicKey ag_solana.PublicKey + PublicKey solana.PublicKey EnumVal uint8 } @@ -86,3 +92,36 @@ var JSONIDLWithAllTypes string //go:embed circularDepIDL.json var CircularDepIDL string + +const SizeItemType = "item for size" + +//go:embed test_item_type/itemIDL.json +var itemTypeJSONIDL string + +//go:embed test_item_slice_type/itemSliceTypeIDL.json +var itemSliceTypeJSONIDL string + +//go:embed itemArray1TypeIDL.json +var itemArray1TypeJSONIDL string + +//go:embed itemArray2TypeIDL.json +var itemArray2TypeJSONIDL string + +//go:embed sizeItemTypeIDL.json +var sizeItemTypeJSONIDL string + +//go:embed itemWithConfigExtraIDL.json +var itemWithConfigExtraJSONIDL string + +//go:embed nilTypeIDL.json +var nilTypeJSONIDL string + +var CodecDefs = map[string]string{ + //TestItemType: itemTypeJSONIDL, + TestItemSliceType: itemSliceTypeJSONIDL, + //TestItemArray1Type: itemArray1TypeJSONIDL, + //TestItemArray2Type: itemArray2TypeJSONIDL, + //sizeItemType: sizeItemTypeJSONIDL, + //TestItemWithConfigExtra: itemWithConfigExtraJSONIDL, + //NilType: nilTypeJSONIDL, +} diff --git a/pkg/solana/codec/types.go b/pkg/solana/codec/types.go new file mode 100644 index 000000000..16af437af --- /dev/null +++ b/pkg/solana/codec/types.go @@ -0,0 +1,15 @@ +package codec + +import commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" + +type Config struct { + // Configs key is the type's name for the codec + Configs map[string]ChainConfig `json:"configs" toml:"configs"` +} + +type ChainConfig struct { + IDL string `json:"IDL" toml:"IDL"` + AccountDef *IdlTypeDef `json:"account" toml:"IDL"` + EventDef *IdlEvent `json:"event" toml:"IDL"` + ModifierConfigs commoncodec.ModifiersConfig `json:"modifierConfigs,omitempty" toml:"modifierConfigs,omitempty"` +} diff --git a/pkg/solana/config/chain_reader.go b/pkg/solana/config/chain_reader.go index dbe9ef4ab..4251624fe 100644 --- a/pkg/solana/config/chain_reader.go +++ b/pkg/solana/config/chain_reader.go @@ -7,7 +7,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/smartcontractkit/chainlink-common/pkg/codec" + commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings/binary" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -82,7 +82,7 @@ type chainDataProcedureFields struct { IDLAccount string `json:"idlAccount,omitempty"` // OutputModifications provides modifiers to convert chain data format to custom // output formats. - OutputModifications codec.ModifiersConfig `json:"outputModifications,omitempty"` + OutputModifications commoncodec.ModifiersConfig `json:"outputModifications,omitempty"` // RPCOpts provides optional configurations for commitment, encoding, and data // slice offsets. RPCOpts *RPCOpts `json:"rpcOpts,omitempty"`