From ac585baa87295be5f90793bee4157a7d0645eaef Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 24 Jan 2025 17:56:40 -0600 Subject: [PATCH 01/15] Updated interface tests to initialize ContractReader and ContractWriter configs with a test index as a PDA seed --- .../relayinterface/chain_components_test.go | 261 ++++++++++-------- .../chainreader/account_read_binding.go | 6 +- pkg/solana/chainreader/chain_reader.go | 2 +- pkg/solana/chainreader/chain_reader_test.go | 12 +- pkg/solana/codec/anchoridl.go | 2 +- 5 files changed, 151 insertions(+), 132 deletions(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 0ccc36b1f..d3a3e9649 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "sync" + "sync/atomic" "testing" "time" @@ -30,14 +31,13 @@ import ( commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" - contract "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" "github.com/smartcontractkit/chainlink-solana/integration-tests/solclient" "github.com/smartcontractkit/chainlink-solana/integration-tests/utils" "github.com/smartcontractkit/chainlink-solana/pkg/solana/chainreader" "github.com/smartcontractkit/chainlink-solana/pkg/solana/chainwriter" "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm" keyMocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm/mocks" @@ -51,7 +51,7 @@ func TestChainComponents(t *testing.T) { t.Run("RunChainComponentsSolanaTests", func(t *testing.T) { t.Parallel() - it := &SolanaChainComponentsInterfaceTester[*testing.T]{Helper: helper} + it := &SolanaChainComponentsInterfaceTester[*testing.T]{Helper: helper, testContext: make(map[string]uint64), testContextMu: &sync.RWMutex{}, testIdx: &atomic.Uint64{}} DisableTests(it) it.Setup(t) RunChainComponentsSolanaTests(t, it) @@ -59,7 +59,7 @@ func TestChainComponents(t *testing.T) { t.Run("RunChainComponentsInLoopSolanaTests", func(t *testing.T) { t.Parallel() - it := &SolanaChainComponentsInterfaceTester[*testing.T]{Helper: helper} + it := &SolanaChainComponentsInterfaceTester[*testing.T]{Helper: helper, testContext: make(map[string]uint64), testContextMu: &sync.RWMutex{}, testIdx: &atomic.Uint64{}} DisableTests(it) wrapped := commontestutils.WrapContractReaderTesterForLoop(it) wrapped.Setup(t) @@ -101,17 +101,17 @@ func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { }) } -func RunChainComponentsSolanaTests[T TestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) { +func RunChainComponentsSolanaTests[T WrappedTestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) { RunContractReaderSolanaTests(t, it) // Add ChainWriter tests here } -func RunChainComponentsInLoopSolanaTests[T TestingT[T]](t T, it ChainComponentsInterfaceTester[T]) { +func RunChainComponentsInLoopSolanaTests[T WrappedTestingT[T]](t T, it ChainComponentsInterfaceTester[T]) { RunContractReaderInLoopTests(t, it) // Add ChainWriter tests here } -func RunContractReaderSolanaTests[T TestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) { +func RunContractReaderSolanaTests[T WrappedTestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) { RunContractReaderInterfaceTests(t, it, false, true) var testCases []Testcase[T] @@ -119,7 +119,7 @@ func RunContractReaderSolanaTests[T TestingT[T]](t T, it *SolanaChainComponentsI RunTests(t, it, testCases) } -func RunContractReaderInLoopTests[T TestingT[T]](t T, it ChainComponentsInterfaceTester[T]) { +func RunContractReaderInLoopTests[T WrappedTestingT[T]](t T, it ChainComponentsInterfaceTester[T]) { RunContractReaderInterfaceTests(t, it, false, true) var testCases []Testcase[T] @@ -127,96 +127,34 @@ func RunContractReaderInLoopTests[T TestingT[T]](t T, it ChainComponentsInterfac RunTests(t, it, testCases) } -type SolanaChainComponentsInterfaceTesterHelper[T TestingT[T]] interface { +type SolanaChainComponentsInterfaceTesterHelper[T WrappedTestingT[T]] interface { Init(t T) RPCClient() *chainreader.RPCClientWrapper Context(t T) context.Context Logger(t T) logger.Logger GetJSONEncodedIDL(t T) []byte - CreateAccount(t T, it SolanaChainComponentsInterfaceTester[T], value uint64) solana.PublicKey + CreateAccount(t T, it SolanaChainComponentsInterfaceTester[T], contractName string, value uint64) solana.PublicKey TXM() *txm.TxManager SolanaClient() *client.Client } -type SolanaChainComponentsInterfaceTester[T TestingT[T]] struct { +type WrappedTestingT[T any] interface { + TestingT[T] + Name() string +} + +type SolanaChainComponentsInterfaceTester[T WrappedTestingT[T]] struct { TestSelectionSupport - Helper SolanaChainComponentsInterfaceTesterHelper[T] - cr *chainreader.SolanaChainReaderService - contractReaderConfig config.ContractReader - chainWriterConfig chainwriter.ChainWriterConfig + Helper SolanaChainComponentsInterfaceTesterHelper[T] + testContext map[string]uint64 + testContextMu *sync.RWMutex + testIdx *atomic.Uint64 } +// ContractReaderConfig and ContractWriterConfig are created when GetContractReader and GetContractWriter are called, respectively, +// so that a test index can be injected as a PDA seed for each test func (it *SolanaChainComponentsInterfaceTester[T]) Setup(t T) { t.Cleanup(func() {}) - - it.contractReaderConfig = config.ContractReader{ - Namespaces: map[string]config.ChainContractReader{ - AnyContractName: { - IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), - Reads: map[string]config.ReadDefinition{ - MethodReturningUint64: { - ChainSpecificName: "DataAccount", - ReadType: config.Account, - OutputModifications: commoncodec.ModifiersConfig{ - &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, - }, - }, - MethodReturningUint64Slice: { - ChainSpecificName: "DataAccount", - OutputModifications: commoncodec.ModifiersConfig{ - &commoncodec.PropertyExtractorConfig{FieldName: "U64Slice"}, - }, - }, - }, - }, - AnySecondContractName: { - IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), - Reads: map[string]config.ReadDefinition{ - MethodReturningUint64: { - ChainSpecificName: "DataAccount", - OutputModifications: commoncodec.ModifiersConfig{ - &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, - }, - }, - }, - }, - }, - } - - it.chainWriterConfig = chainwriter.ChainWriterConfig{ - Programs: map[string]chainwriter.ProgramConfig{ - AnyContractName: { - IDL: string(it.Helper.GetJSONEncodedIDL(t)), - Methods: map[string]chainwriter.MethodConfig{ - "initialize": { - FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), - InputModifications: nil, - ChainSpecificName: "initialize", - LookupTables: chainwriter.LookupTables{}, - Accounts: []chainwriter.Lookup{ - chainwriter.PDALookups{ - Name: "Account", - PublicKey: chainwriter.AccountConstant{ - Name: "ProgramID", - Address: programPubKey, - }, - Seeds: []chainwriter.Seed{ - {Static: []byte("data")}, - {Dynamic: chainwriter.AccountLookup{ - Name: "TestIDX", - Location: "TestIdx", - }}, - }, - IsWritable: true, - IsSigner: false, - }, - }, - DebugIDLocation: "", - }, - }, - }, - }, - } } func (it *SolanaChainComponentsInterfaceTester[T]) Name() string { @@ -232,34 +170,40 @@ func (it *SolanaChainComponentsInterfaceTester[T]) GetAccountString(i int) strin } func (it *SolanaChainComponentsInterfaceTester[T]) GetContractReader(t T) types.ContractReader { - ctx := it.Helper.Context(t) - if it.cr != nil { - return it.cr - } - - svc, err := chainreader.NewChainReaderService(it.Helper.Logger(t), it.Helper.RPCClient(), it.contractReaderConfig) + contractReaderConfig := it.buildContractReaderConfig(t) + svc, err := chainreader.NewChainReaderService(it.Helper.Logger(t), it.Helper.RPCClient(), contractReaderConfig) require.NoError(t, err) - require.NoError(t, svc.Start(ctx)) - - it.cr = svc + servicetest.Run(t, svc) return svc } func (it *SolanaChainComponentsInterfaceTester[T]) GetContractWriter(t T) types.ContractWriter { - cw, err := chainwriter.NewSolanaChainWriterService(it.Helper.Logger(t), it.Helper.SolanaClient(), *it.Helper.TXM(), nil, it.chainWriterConfig) + chainWriterConfig := it.buildContractWriterConfig(t) + cw, err := chainwriter.NewSolanaChainWriterService(it.Helper.Logger(t), it.Helper.SolanaClient(), *it.Helper.TXM(), nil, chainWriterConfig) require.NoError(t, err) servicetest.Run(t, cw) return cw } +func (it *SolanaChainComponentsInterfaceTester[T]) getTestIdx(name string) uint64 { + it.testContextMu.Lock() + defer it.testContextMu.Unlock() + idx, exists := it.testContext[name] + if !exists { + idx = it.testIdx.Add(1) // new index is needed so increment the existing + it.testContext[name] = idx // set new index in map + } + return idx +} + func (it *SolanaChainComponentsInterfaceTester[T]) GetBindings(t T) []types.BoundContract { // Create a new account with fresh state for each test return []types.BoundContract{ - {Name: AnyContractName, Address: it.Helper.CreateAccount(t, *it, AnyValueToReadWithoutAnArgument).String()}, - {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnyDifferentValueToReadWithoutAnArgument).String()}, + {Name: AnyContractName, Address: it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument).String()}, + {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument).String()}, } } @@ -280,8 +224,6 @@ type helper struct { rpcClient *rpc.Client wsClient *ws.Client idlBts []byte - nonce uint64 - nonceMu sync.Mutex txm txm.TxManager sc *client.Client } @@ -371,24 +313,11 @@ func (h *helper) GetJSONEncodedIDL(t *testing.T) []byte { return h.idlBts } -func (h *helper) CreateAccount(t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], value uint64) solana.PublicKey { +func (h *helper) CreateAccount(t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], contractName string, value uint64) solana.PublicKey { t.Helper() - // avoid collisions in parallel tests - h.nonceMu.Lock() - h.nonce++ - nonce := h.nonce - h.nonceMu.Unlock() - - bts := make([]byte, 8) - binary.LittleEndian.PutUint64(bts, nonce*value) - - pubKey, _, err := solana.FindProgramAddress([][]byte{[]byte("data"), bts}, h.programID) - require.NoError(t, err) - - h.runInitialize(t, it, nonce, value) - - return pubKey + h.runInitialize(t, it, contractName, value) + return h.programID } type InitializeArgs struct { @@ -399,25 +328,115 @@ type InitializeArgs struct { func (h *helper) runInitialize( t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], - nonce uint64, + contractName string, value uint64, ) { t.Helper() cw := it.GetContractWriter(t) + // Fetch test index from map + it.testContextMu.RLock() + defer it.testContextMu.RUnlock() + testIdx, exists := it.testContext[t.Name()] + if !exists { + return + } + args := InitializeArgs{ - TestIdx: nonce * value, - Value: value, + TestIdx: testIdx, + Value: value, + } + + SubmitTransactionToCW(t, &it, cw, "initialize", args, types.BoundContract{Name: contractName, Address: h.programID.String()}, types.Finalized) +} + +func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T) config.ContractReader { + idx := it.getTestIdx(t.Name()) + pdaPrefix := []byte("data") + pdaPrefix = binary.LittleEndian.AppendUint64(pdaPrefix, idx) + return config.ContractReader{ + Namespaces: map[string]config.ChainContractReader{ + AnyContractName: { + IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), + Reads: map[string]config.ReadDefinition{ + MethodReturningUint64: { + ChainSpecificName: "DataAccount", + ReadType: config.Account, + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, + }, + }, + MethodReturningUint64Slice: { + ChainSpecificName: "DataAccount", + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Slice"}, + }, + }, + }, + }, + AnySecondContractName: { + IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), + Reads: map[string]config.ReadDefinition{ + MethodReturningUint64: { + ChainSpecificName: "DataAccount", + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, + }, + }, + }, + }, + }, } - buf := make([]byte, 8) - binary.LittleEndian.PutUint64(buf, nonce*value) +} - SubmitTransactionToCW(t, &it, cw, "initialize", args, types.BoundContract{Name: AnyContractName, Address: h.programID.String()}, types.Finalized) +func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T) chainwriter.ChainWriterConfig { + idx := it.getTestIdx(t.Name()) + testIdx := binary.LittleEndian.AppendUint64([]byte{}, idx) + return chainwriter.ChainWriterConfig{ + Programs: map[string]chainwriter.ProgramConfig{ + AnyContractName: { + IDL: string(it.Helper.GetJSONEncodedIDL(t)), + Methods: map[string]chainwriter.MethodConfig{ + "initialize": { + FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), + InputModifications: nil, + ChainSpecificName: "initialize", + LookupTables: chainwriter.LookupTables{}, + Accounts: []chainwriter.Lookup{ + chainwriter.PDALookups{ + Name: "Account", + PublicKey: chainwriter.AccountConstant{ + Name: "ProgramID", + Address: programPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("data")}, + {Static: testIdx}, + }, + IsWritable: true, + IsSigner: false, + }, + }, + DebugIDLocation: "", + }, + }, + }, + }, + } } -func mustUnmarshalIDL[T TestingT[T]](t T, rawIDL string) codec.IDL { +func mustUnmarshalIDL[T WrappedTestingT[T]](t T, rawIDL string) codec.IDL { var idl codec.IDL if err := json.Unmarshal([]byte(rawIDL), &idl); err != nil { t.Errorf("failed to unmarshal test IDL", err) diff --git a/pkg/solana/chainreader/account_read_binding.go b/pkg/solana/chainreader/account_read_binding.go index b8854b38c..69fdc1328 100644 --- a/pkg/solana/chainreader/account_read_binding.go +++ b/pkg/solana/chainreader/account_read_binding.go @@ -17,10 +17,10 @@ type accountReadBinding struct { codec types.RemoteCodec key solana.PublicKey isPda bool // flag to signify whether or not the account read is for a PDA - prefix string // only used for PDA public key calculation + prefix []byte // only used for PDA public key calculation } -func newAccountReadBinding(namespace, genericName, prefix string, isPda bool) *accountReadBinding { +func newAccountReadBinding(namespace, genericName string, prefix []byte, isPda bool) *accountReadBinding { return &accountReadBinding{ namespace: namespace, genericName: genericName, @@ -68,7 +68,7 @@ func (b *accountReadBinding) Decode(ctx context.Context, bts []byte, outVal any) func (b *accountReadBinding) buildSeedsSlice(ctx context.Context, params any) ([][]byte, error) { flattenedSeeds := make([]byte, 0, solana.MaxSeeds*solana.MaxSeedLength) // Append the static prefix string first - flattenedSeeds = append(flattenedSeeds, []byte(b.prefix)...) + flattenedSeeds = append(flattenedSeeds, b.prefix...) // Encode the seeds provided in the params encodedParamSeeds, err := b.codec.Encode(ctx, params, codec.WrapItemType(true, b.namespace, b.genericName, "")) if err != nil { diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index 41d70161b..f76d2d4cd 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -298,7 +298,7 @@ func (s *SolanaChainReaderService) addAccountRead(namespace string, genericName reader = newAccountReadBinding(namespace, genericName, readDefinition.PDADefiniton.Prefix, true) } else { inputAccountIDLDef = codec.NilIdlTypeDefTy - reader = newAccountReadBinding(namespace, genericName, "", false) + reader = newAccountReadBinding(namespace, genericName, nil, false) } if err := s.addCodecDef(true, namespace, genericName, codec.ChainConfigTypeAccountDef, idl, inputAccountIDLDef, readDefinition.InputModifications); err != nil { return err diff --git a/pkg/solana/chainreader/chain_reader_test.go b/pkg/solana/chainreader/chain_reader_test.go index 019ef3e09..4d17ab60f 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -294,7 +294,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) { { name: "happy path", pdaDefinition: codec.PDATypeDef{ - Prefix: prefixString, + Prefix: []byte(prefixString), Seeds: []codec.PDASeed{ { Name: "PubKey", @@ -315,7 +315,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) { { name: "with modifier and random field", pdaDefinition: codec.PDATypeDef{ - Prefix: prefixString, + Prefix: []byte(prefixString), Seeds: []codec.PDASeed{ { Name: "PubKey", @@ -340,7 +340,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) { { name: "only prefix", pdaDefinition: codec.PDATypeDef{ - Prefix: prefixString, + Prefix: []byte(prefixString), }, expected: mustFindProgramAddress(t, programID, [][]byte{[]byte(prefixString)}), params: nil, @@ -348,7 +348,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) { { name: "no prefix", pdaDefinition: codec.PDATypeDef{ - Prefix: "", + Prefix: nil, Seeds: []codec.PDASeed{ { Name: "PubKey", @@ -369,7 +369,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) { { name: "public key seed provided as bytes", pdaDefinition: codec.PDATypeDef{ - Prefix: prefixString, + Prefix: []byte(prefixString), Seeds: []codec.PDASeed{ { Name: "PubKey", @@ -429,7 +429,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) { ChainSpecificName: testutils.TestStructWithNestedStruct, ReadType: config.Account, PDADefiniton: codec.PDATypeDef{ - Prefix: prefixString, + Prefix: []byte(prefixString), Seeds: []codec.PDASeed{ { Name: "PubKey", diff --git a/pkg/solana/codec/anchoridl.go b/pkg/solana/codec/anchoridl.go index 0ea1322ad..d3a991be6 100644 --- a/pkg/solana/codec/anchoridl.go +++ b/pkg/solana/codec/anchoridl.go @@ -143,7 +143,7 @@ type IdlField struct { // PDA is a struct that does not correlate to an official IDL type // It is needed to encode seeds to calculate the address for PDA account reads type PDATypeDef struct { - Prefix string `json:"prefix,omitempty"` + Prefix []byte `json:"prefix,omitempty"` Seeds []PDASeed `json:"seeds,omitempty"` } From 9d64ddd7bfef71a941536f60902bd3fbc9e94485 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 31 Jan 2025 10:54:11 -0600 Subject: [PATCH 02/15] Disabled tests that require a second program --- integration-tests/relayinterface/chain_components_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index d3a3e9649..c37d3b5d7 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -69,6 +69,9 @@ func TestChainComponents(t *testing.T) { func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { it.DisableTests([]string{ + // disable tests that require secondary program + ContractReaderGetLatestValueFromMultipleContractsNamesSameFunction, + ContractReaderBatchGetLatestValueMultipleContractNamesSameFunction, // disable tests that set values ContractReaderGetLatestValueBasedOnConfidenceLevel, // disable anything returning a struct or requiring input params for now @@ -203,7 +206,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) GetBindings(t T) []types.Boun // Create a new account with fresh state for each test return []types.BoundContract{ {Name: AnyContractName, Address: it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument).String()}, - {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument).String()}, + // {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument).String()}, } } From 3d609172d08d5250c6e75b59e377cdd7bfef4403 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 31 Jan 2025 11:28:34 -0600 Subject: [PATCH 03/15] Updated contract reader test programs to enable interface tests --- contracts/Anchor.toml | 1 + contracts/Cargo.lock | 7 + .../contract_reader_interface/Initialize.go | 31 ++- .../contract_reader_interface/StoreVal.go | 165 +++++++++++++++ .../StoreVal_test.go | 32 +++ .../contract_reader_interface/accounts.go | 42 ++++ .../contract_reader_interface/instructions.go | 7 + .../Initialize.go | 188 ++++++++++++++++++ .../Initialize_test.go | 32 +++ .../accounts.go | 72 +++++++ .../instructions.go | 117 +++++++++++ .../testing_utils.go | 20 ++ .../types.go | 3 + .../Cargo.toml | 19 ++ .../Xargo.toml | 2 + .../src/lib.rs | 42 ++++ .../contract-reader-interface/src/lib.rs | 37 ++++ .../relayinterface/chain_components_test.go | 183 +++++++++++++---- 18 files changed, 960 insertions(+), 40 deletions(-) create mode 100644 contracts/generated/contract_reader_interface/StoreVal.go create mode 100644 contracts/generated/contract_reader_interface/StoreVal_test.go create mode 100644 contracts/generated/contract_reader_interface_secondary/Initialize.go create mode 100644 contracts/generated/contract_reader_interface_secondary/Initialize_test.go create mode 100644 contracts/generated/contract_reader_interface_secondary/accounts.go create mode 100644 contracts/generated/contract_reader_interface_secondary/instructions.go create mode 100644 contracts/generated/contract_reader_interface_secondary/testing_utils.go create mode 100644 contracts/generated/contract_reader_interface_secondary/types.go create mode 100644 contracts/programs/contract-reader-interface-secondary/Cargo.toml create mode 100644 contracts/programs/contract-reader-interface-secondary/Xargo.toml create mode 100644 contracts/programs/contract-reader-interface-secondary/src/lib.rs diff --git a/contracts/Anchor.toml b/contracts/Anchor.toml index 30b788caa..9e43812e4 100644 --- a/contracts/Anchor.toml +++ b/contracts/Anchor.toml @@ -28,6 +28,7 @@ test = "pnpm run test" [programs.localnet] access_controller = "9xi644bRR8birboDGdTiwBq3C7VEeR7VuamRYYXCubUW" contract-reader-interface = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" +contract-reader-interface-secondary = "9SFyk8NmGYh5D612mJwUYhguCRY9cFgaS2vksrigepjf" log-read-test = "J1zQwrBNBngz26jRPNWsUSZMHJwBwpkoDitXRV95LdK4" ocr_2 = "cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ" # need to rename the idl to satisfy anchor.js... store = "HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny" \ No newline at end of file diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 0209f1dbb..ae8d5fbff 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -726,6 +726,13 @@ dependencies = [ "anchor-lang", ] +[[package]] +name = "contract-reader-interface-secondary" +version = "0.1.0" +dependencies = [ + "anchor-lang", +] + [[package]] name = "cpufeatures" version = "0.2.12" diff --git a/contracts/generated/contract_reader_interface/Initialize.go b/contracts/generated/contract_reader_interface/Initialize.go index 03e13f579..fa7b441ea 100644 --- a/contracts/generated/contract_reader_interface/Initialize.go +++ b/contracts/generated/contract_reader_interface/Initialize.go @@ -19,14 +19,16 @@ type Initialize struct { // // [1] = [WRITE] data // - // [2] = [] systemProgram + // [2] = [WRITE] value + // + // [3] = [] systemProgram ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` } // NewInitializeInstructionBuilder creates a new `Initialize` instruction builder. func NewInitializeInstructionBuilder() *Initialize { nd := &Initialize{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 4), } return nd } @@ -65,15 +67,26 @@ func (inst *Initialize) GetDataAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[1] } +// SetValueAccount sets the "value" account. +func (inst *Initialize) SetValueAccount(value ag_solanago.PublicKey) *Initialize { + inst.AccountMetaSlice[2] = ag_solanago.Meta(value).WRITE() + return inst +} + +// GetValueAccount gets the "value" account. +func (inst *Initialize) GetValueAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + // SetSystemProgramAccount sets the "systemProgram" account. func (inst *Initialize) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *Initialize { - inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) + inst.AccountMetaSlice[3] = ag_solanago.Meta(systemProgram) return inst } // GetSystemProgramAccount gets the "systemProgram" account. func (inst *Initialize) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] + return inst.AccountMetaSlice[3] } func (inst Initialize) Build() *Instruction { @@ -113,6 +126,9 @@ func (inst *Initialize) Validate() error { return errors.New("accounts.Data is not set") } if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.Value is not set") + } + if inst.AccountMetaSlice[3] == nil { return errors.New("accounts.SystemProgram is not set") } } @@ -134,10 +150,11 @@ func (inst *Initialize) EncodeToTree(parent ag_treeout.Branches) { }) // Accounts of the instruction: - instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + instructionBranch.Child("Accounts[len=4]").ParentFunc(func(accountsBranch ag_treeout.Branches) { accountsBranch.Child(ag_format.Meta(" signer", inst.AccountMetaSlice[0])) accountsBranch.Child(ag_format.Meta(" data", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta(" value", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[3])) }) }) }) @@ -178,11 +195,13 @@ func NewInitializeInstruction( // Accounts: signer ag_solanago.PublicKey, data ag_solanago.PublicKey, + valueAccount ag_solanago.PublicKey, systemProgram ag_solanago.PublicKey) *Initialize { return NewInitializeInstructionBuilder(). SetTestIdx(testIdx). SetValue(value). SetSignerAccount(signer). SetDataAccount(data). + SetValueAccount(valueAccount). SetSystemProgramAccount(systemProgram) } diff --git a/contracts/generated/contract_reader_interface/StoreVal.go b/contracts/generated/contract_reader_interface/StoreVal.go new file mode 100644 index 000000000..d59fe3405 --- /dev/null +++ b/contracts/generated/contract_reader_interface/StoreVal.go @@ -0,0 +1,165 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface + +import ( + "errors" + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +// StoreVal is the `storeVal` instruction. +type StoreVal struct { + Value *uint64 + + // [0] = [WRITE, SIGNER] signer + // + // [1] = [WRITE] value + // + // [2] = [] systemProgram + ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` +} + +// NewStoreValInstructionBuilder creates a new `StoreVal` instruction builder. +func NewStoreValInstructionBuilder() *StoreVal { + nd := &StoreVal{ + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), + } + return nd +} + +// SetValue sets the "value" parameter. +func (inst *StoreVal) SetValue(value uint64) *StoreVal { + inst.Value = &value + return inst +} + +// SetSignerAccount sets the "signer" account. +func (inst *StoreVal) SetSignerAccount(signer ag_solanago.PublicKey) *StoreVal { + inst.AccountMetaSlice[0] = ag_solanago.Meta(signer).WRITE().SIGNER() + return inst +} + +// GetSignerAccount gets the "signer" account. +func (inst *StoreVal) GetSignerAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[0] +} + +// SetValueAccount sets the "value" account. +func (inst *StoreVal) SetValueAccount(value ag_solanago.PublicKey) *StoreVal { + inst.AccountMetaSlice[1] = ag_solanago.Meta(value).WRITE() + return inst +} + +// GetValueAccount gets the "value" account. +func (inst *StoreVal) GetValueAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + +// SetSystemProgramAccount sets the "systemProgram" account. +func (inst *StoreVal) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *StoreVal { + inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) + return inst +} + +// GetSystemProgramAccount gets the "systemProgram" account. +func (inst *StoreVal) GetSystemProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +func (inst StoreVal) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: Instruction_StoreVal, + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst StoreVal) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *StoreVal) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.Value == nil { + return errors.New("Value parameter is not set") + } + } + + // Check whether all (required) accounts are set: + { + if inst.AccountMetaSlice[0] == nil { + return errors.New("accounts.Signer is not set") + } + if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.Value is not set") + } + if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.SystemProgram is not set") + } + } + return nil +} + +func (inst *StoreVal) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("StoreVal")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params[len=1]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("Value", *inst.Value)) + }) + + // Accounts of the instruction: + instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" signer", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta(" value", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) + }) + }) + }) +} + +func (obj StoreVal) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Value` param: + err = encoder.Encode(obj.Value) + if err != nil { + return err + } + return nil +} +func (obj *StoreVal) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Value`: + err = decoder.Decode(&obj.Value) + if err != nil { + return err + } + return nil +} + +// NewStoreValInstruction declares a new StoreVal instruction with the provided parameters and accounts. +func NewStoreValInstruction( + // Parameters: + value uint64, + // Accounts: + signer ag_solanago.PublicKey, + valueAccount ag_solanago.PublicKey, + systemProgram ag_solanago.PublicKey) *StoreVal { + return NewStoreValInstructionBuilder(). + SetValue(value). + SetSignerAccount(signer). + SetValueAccount(valueAccount). + SetSystemProgramAccount(systemProgram) +} diff --git a/contracts/generated/contract_reader_interface/StoreVal_test.go b/contracts/generated/contract_reader_interface/StoreVal_test.go new file mode 100644 index 000000000..2f4e6350f --- /dev/null +++ b/contracts/generated/contract_reader_interface/StoreVal_test.go @@ -0,0 +1,32 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface + +import ( + "bytes" + ag_gofuzz "github.com/gagliardetto/gofuzz" + ag_require "github.com/stretchr/testify/require" + "strconv" + "testing" +) + +func TestEncodeDecode_StoreVal(t *testing.T) { + fu := ag_gofuzz.New().NilChance(0) + for i := 0; i < 1; i++ { + t.Run("StoreVal"+strconv.Itoa(i), func(t *testing.T) { + { + params := new(StoreVal) + fu.Fuzz(params) + params.AccountMetaSlice = nil + buf := new(bytes.Buffer) + err := encodeT(*params, buf) + ag_require.NoError(t, err) + got := new(StoreVal) + err = decodeT(got, buf.Bytes()) + got.AccountMetaSlice = nil + ag_require.NoError(t, err) + ag_require.Equal(t, params, got) + } + }) + } +} diff --git a/contracts/generated/contract_reader_interface/accounts.go b/contracts/generated/contract_reader_interface/accounts.go index d02ccb874..c5606c730 100644 --- a/contracts/generated/contract_reader_interface/accounts.go +++ b/contracts/generated/contract_reader_interface/accounts.go @@ -157,3 +157,45 @@ func (obj *DataAccount) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err er } return nil } + +type Value struct { + U64Value uint64 +} + +var ValueDiscriminator = [8]byte{135, 158, 244, 117, 72, 203, 24, 194} + +func (obj Value) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(ValueDiscriminator[:], false) + if err != nil { + return err + } + // Serialize `U64Value` param: + err = encoder.Encode(obj.U64Value) + if err != nil { + return err + } + return nil +} + +func (obj *Value) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(ValueDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[135 158 244 117 72 203 24 194]", + fmt.Sprint(discriminator[:])) + } + } + // Deserialize `U64Value`: + err = decoder.Decode(&obj.U64Value) + if err != nil { + return err + } + return nil +} diff --git a/contracts/generated/contract_reader_interface/instructions.go b/contracts/generated/contract_reader_interface/instructions.go index c86960a41..b49a9fb24 100644 --- a/contracts/generated/contract_reader_interface/instructions.go +++ b/contracts/generated/contract_reader_interface/instructions.go @@ -31,6 +31,8 @@ var ( Instruction_Initialize = ag_binary.TypeID([8]byte{175, 175, 109, 31, 13, 152, 155, 237}) Instruction_InitializeLookupTable = ag_binary.TypeID([8]byte{149, 120, 10, 249, 212, 185, 177, 216}) + + Instruction_StoreVal = ag_binary.TypeID([8]byte{158, 54, 7, 226, 137, 227, 73, 242}) ) // InstructionIDToName returns the name of the instruction given its ID. @@ -40,6 +42,8 @@ func InstructionIDToName(id ag_binary.TypeID) string { return "Initialize" case Instruction_InitializeLookupTable: return "InitializeLookupTable" + case Instruction_StoreVal: + return "StoreVal" default: return "" } @@ -66,6 +70,9 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( { "initialize_lookup_table", (*InitializeLookupTable)(nil), }, + { + "store_val", (*StoreVal)(nil), + }, }, ) diff --git a/contracts/generated/contract_reader_interface_secondary/Initialize.go b/contracts/generated/contract_reader_interface_secondary/Initialize.go new file mode 100644 index 000000000..7f26475cb --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/Initialize.go @@ -0,0 +1,188 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary + +import ( + "errors" + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +// Initialize is the `initialize` instruction. +type Initialize struct { + TestIdx *uint64 + Value *uint64 + + // [0] = [WRITE, SIGNER] signer + // + // [1] = [WRITE] data + // + // [2] = [] systemProgram + ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` +} + +// NewInitializeInstructionBuilder creates a new `Initialize` instruction builder. +func NewInitializeInstructionBuilder() *Initialize { + nd := &Initialize{ + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), + } + return nd +} + +// SetTestIdx sets the "testIdx" parameter. +func (inst *Initialize) SetTestIdx(testIdx uint64) *Initialize { + inst.TestIdx = &testIdx + return inst +} + +// SetValue sets the "value" parameter. +func (inst *Initialize) SetValue(value uint64) *Initialize { + inst.Value = &value + return inst +} + +// SetSignerAccount sets the "signer" account. +func (inst *Initialize) SetSignerAccount(signer ag_solanago.PublicKey) *Initialize { + inst.AccountMetaSlice[0] = ag_solanago.Meta(signer).WRITE().SIGNER() + return inst +} + +// GetSignerAccount gets the "signer" account. +func (inst *Initialize) GetSignerAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[0] +} + +// SetDataAccount sets the "data" account. +func (inst *Initialize) SetDataAccount(data ag_solanago.PublicKey) *Initialize { + inst.AccountMetaSlice[1] = ag_solanago.Meta(data).WRITE() + return inst +} + +// GetDataAccount gets the "data" account. +func (inst *Initialize) GetDataAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + +// SetSystemProgramAccount sets the "systemProgram" account. +func (inst *Initialize) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *Initialize { + inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) + return inst +} + +// GetSystemProgramAccount gets the "systemProgram" account. +func (inst *Initialize) GetSystemProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +func (inst Initialize) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: Instruction_Initialize, + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst Initialize) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *Initialize) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.TestIdx == nil { + return errors.New("TestIdx parameter is not set") + } + if inst.Value == nil { + return errors.New("Value parameter is not set") + } + } + + // Check whether all (required) accounts are set: + { + if inst.AccountMetaSlice[0] == nil { + return errors.New("accounts.Signer is not set") + } + if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.Data is not set") + } + if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.SystemProgram is not set") + } + } + return nil +} + +func (inst *Initialize) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("Initialize")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("TestIdx", *inst.TestIdx)) + paramsBranch.Child(ag_format.Param(" Value", *inst.Value)) + }) + + // Accounts of the instruction: + instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" signer", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta(" data", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) + }) + }) + }) +} + +func (obj Initialize) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `TestIdx` param: + err = encoder.Encode(obj.TestIdx) + if err != nil { + return err + } + // Serialize `Value` param: + err = encoder.Encode(obj.Value) + if err != nil { + return err + } + return nil +} +func (obj *Initialize) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `TestIdx`: + err = decoder.Decode(&obj.TestIdx) + if err != nil { + return err + } + // Deserialize `Value`: + err = decoder.Decode(&obj.Value) + if err != nil { + return err + } + return nil +} + +// NewInitializeInstruction declares a new Initialize instruction with the provided parameters and accounts. +func NewInitializeInstruction( + // Parameters: + testIdx uint64, + value uint64, + // Accounts: + signer ag_solanago.PublicKey, + data ag_solanago.PublicKey, + systemProgram ag_solanago.PublicKey) *Initialize { + return NewInitializeInstructionBuilder(). + SetTestIdx(testIdx). + SetValue(value). + SetSignerAccount(signer). + SetDataAccount(data). + SetSystemProgramAccount(systemProgram) +} diff --git a/contracts/generated/contract_reader_interface_secondary/Initialize_test.go b/contracts/generated/contract_reader_interface_secondary/Initialize_test.go new file mode 100644 index 000000000..d01946a9b --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/Initialize_test.go @@ -0,0 +1,32 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary + +import ( + "bytes" + ag_gofuzz "github.com/gagliardetto/gofuzz" + ag_require "github.com/stretchr/testify/require" + "strconv" + "testing" +) + +func TestEncodeDecode_Initialize(t *testing.T) { + fu := ag_gofuzz.New().NilChance(0) + for i := 0; i < 1; i++ { + t.Run("Initialize"+strconv.Itoa(i), func(t *testing.T) { + { + params := new(Initialize) + fu.Fuzz(params) + params.AccountMetaSlice = nil + buf := new(bytes.Buffer) + err := encodeT(*params, buf) + ag_require.NoError(t, err) + got := new(Initialize) + err = decodeT(got, buf.Bytes()) + got.AccountMetaSlice = nil + ag_require.NoError(t, err) + ag_require.Equal(t, params, got) + } + }) + } +} diff --git a/contracts/generated/contract_reader_interface_secondary/accounts.go b/contracts/generated/contract_reader_interface_secondary/accounts.go new file mode 100644 index 000000000..2ddebc9d9 --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/accounts.go @@ -0,0 +1,72 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary + +import ( + "fmt" + ag_binary "github.com/gagliardetto/binary" +) + +type Data struct { + U64Value uint64 + Idx uint64 + Bump uint8 +} + +var DataDiscriminator = [8]byte{206, 156, 59, 188, 18, 79, 240, 232} + +func (obj Data) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(DataDiscriminator[:], false) + if err != nil { + return err + } + // Serialize `U64Value` param: + err = encoder.Encode(obj.U64Value) + if err != nil { + return err + } + // Serialize `Idx` param: + err = encoder.Encode(obj.Idx) + if err != nil { + return err + } + // Serialize `Bump` param: + err = encoder.Encode(obj.Bump) + if err != nil { + return err + } + return nil +} + +func (obj *Data) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(DataDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[206 156 59 188 18 79 240 232]", + fmt.Sprint(discriminator[:])) + } + } + // Deserialize `U64Value`: + err = decoder.Decode(&obj.U64Value) + if err != nil { + return err + } + // Deserialize `Idx`: + err = decoder.Decode(&obj.Idx) + if err != nil { + return err + } + // Deserialize `Bump`: + err = decoder.Decode(&obj.Bump) + if err != nil { + return err + } + return nil +} diff --git a/contracts/generated/contract_reader_interface_secondary/instructions.go b/contracts/generated/contract_reader_interface_secondary/instructions.go new file mode 100644 index 000000000..9398b7a0f --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/instructions.go @@ -0,0 +1,117 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary + +import ( + "bytes" + "fmt" + ag_spew "github.com/davecgh/go-spew/spew" + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_text "github.com/gagliardetto/solana-go/text" + ag_treeout "github.com/gagliardetto/treeout" +) + +var ProgramID ag_solanago.PublicKey + +func SetProgramID(pubkey ag_solanago.PublicKey) { + ProgramID = pubkey + ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction) +} + +const ProgramName = "ContractReaderInterfaceSecondary" + +func init() { + if !ProgramID.IsZero() { + ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction) + } +} + +var ( + Instruction_Initialize = ag_binary.TypeID([8]byte{175, 175, 109, 31, 13, 152, 155, 237}) +) + +// InstructionIDToName returns the name of the instruction given its ID. +func InstructionIDToName(id ag_binary.TypeID) string { + switch id { + case Instruction_Initialize: + return "Initialize" + default: + return "" + } +} + +type Instruction struct { + ag_binary.BaseVariant +} + +func (inst *Instruction) EncodeToTree(parent ag_treeout.Branches) { + if enToTree, ok := inst.Impl.(ag_text.EncodableToTree); ok { + enToTree.EncodeToTree(parent) + } else { + parent.Child(ag_spew.Sdump(inst)) + } +} + +var InstructionImplDef = ag_binary.NewVariantDefinition( + ag_binary.AnchorTypeIDEncoding, + []ag_binary.VariantType{ + { + "initialize", (*Initialize)(nil), + }, + }, +) + +func (inst *Instruction) ProgramID() ag_solanago.PublicKey { + return ProgramID +} + +func (inst *Instruction) Accounts() (out []*ag_solanago.AccountMeta) { + return inst.Impl.(ag_solanago.AccountsGettable).GetAccounts() +} + +func (inst *Instruction) Data() ([]byte, error) { + buf := new(bytes.Buffer) + if err := ag_binary.NewBorshEncoder(buf).Encode(inst); err != nil { + return nil, fmt.Errorf("unable to encode instruction: %w", err) + } + return buf.Bytes(), nil +} + +func (inst *Instruction) TextEncode(encoder *ag_text.Encoder, option *ag_text.Option) error { + return encoder.Encode(inst.Impl, option) +} + +func (inst *Instruction) UnmarshalWithDecoder(decoder *ag_binary.Decoder) error { + return inst.BaseVariant.UnmarshalBinaryVariant(decoder, InstructionImplDef) +} + +func (inst *Instruction) MarshalWithEncoder(encoder *ag_binary.Encoder) error { + err := encoder.WriteBytes(inst.TypeID.Bytes(), false) + if err != nil { + return fmt.Errorf("unable to write variant type: %w", err) + } + return encoder.Encode(inst.Impl) +} + +func registryDecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (interface{}, error) { + inst, err := DecodeInstruction(accounts, data) + if err != nil { + return nil, err + } + return inst, nil +} + +func DecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (*Instruction, error) { + inst := new(Instruction) + if err := ag_binary.NewBorshDecoder(data).Decode(inst); err != nil { + return nil, fmt.Errorf("unable to decode instruction: %w", err) + } + if v, ok := inst.Impl.(ag_solanago.AccountsSettable); ok { + err := v.SetAccounts(accounts) + if err != nil { + return nil, fmt.Errorf("unable to set accounts for instruction: %w", err) + } + } + return inst, nil +} diff --git a/contracts/generated/contract_reader_interface_secondary/testing_utils.go b/contracts/generated/contract_reader_interface_secondary/testing_utils.go new file mode 100644 index 000000000..462a96f1e --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/testing_utils.go @@ -0,0 +1,20 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary + +import ( + "bytes" + "fmt" + ag_binary "github.com/gagliardetto/binary" +) + +func encodeT(data interface{}, buf *bytes.Buffer) error { + if err := ag_binary.NewBorshEncoder(buf).Encode(data); err != nil { + return fmt.Errorf("unable to encode instruction: %w", err) + } + return nil +} + +func decodeT(dst interface{}, data []byte) error { + return ag_binary.NewBorshDecoder(data).Decode(dst) +} diff --git a/contracts/generated/contract_reader_interface_secondary/types.go b/contracts/generated/contract_reader_interface_secondary/types.go new file mode 100644 index 000000000..1a669df33 --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/types.go @@ -0,0 +1,3 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary diff --git a/contracts/programs/contract-reader-interface-secondary/Cargo.toml b/contracts/programs/contract-reader-interface-secondary/Cargo.toml new file mode 100644 index 000000000..494865214 --- /dev/null +++ b/contracts/programs/contract-reader-interface-secondary/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "contract-reader-interface-secondary" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "contract_reader_interface_secondary" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.29.0" diff --git a/contracts/programs/contract-reader-interface-secondary/Xargo.toml b/contracts/programs/contract-reader-interface-secondary/Xargo.toml new file mode 100644 index 000000000..475fb71ed --- /dev/null +++ b/contracts/programs/contract-reader-interface-secondary/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/contracts/programs/contract-reader-interface-secondary/src/lib.rs b/contracts/programs/contract-reader-interface-secondary/src/lib.rs new file mode 100644 index 000000000..bc43f4df7 --- /dev/null +++ b/contracts/programs/contract-reader-interface-secondary/src/lib.rs @@ -0,0 +1,42 @@ +use anchor_lang::prelude::*; +use std::mem::size_of; + +declare_id!("9SFyk8NmGYh5D612mJwUYhguCRY9cFgaS2vksrigepjf"); + +#[program] +pub mod contract_reader_interface_secondary { + use super::*; + + pub fn initialize(ctx: Context, test_idx: u64, value: u64) -> Result<()> { + let account = &mut ctx.accounts.data; + account.u64_value = value; + account.idx = test_idx; + account.bump = ctx.bumps.data; + Ok(()) + } +} + +#[derive(Accounts)] +#[instruction(test_idx: u64)] +pub struct Initialize<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + // derived test PDA + #[account( + init, + payer = signer, + space = size_of::() + 8, + seeds=[b"data".as_ref(), test_idx.to_le_bytes().as_ref()], + bump)] + pub data: Account<'info, Data>, + + pub system_program: Program<'info, System>, +} + +#[account] +pub struct Data { + pub u64_value: u64, + pub idx: u64, + pub bump: u8 +} diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 51c06a33e..752712c21 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -15,6 +15,8 @@ pub mod contract_reader_interface { account.idx = test_idx; account.bump = ctx.bumps.data; + ctx.accounts.value.u64_value = 0; + Ok(()) } @@ -30,6 +32,12 @@ pub mod contract_reader_interface { Ok(()) } + + pub fn store_val(ctx: Context, value: u64) -> Result<()> { + let val = &mut ctx.accounts.value; + val.u64_value = value; + Ok(()) + } } #[derive(Accounts)] @@ -47,6 +55,15 @@ pub struct Initialize<'info> { bump)] pub data: Account<'info, DataAccount>, + // derived test PDA + #[account( + init, + payer = signer, + space = size_of::() + 8, + seeds=[b"val"], + bump)] + pub value: Account<'info, Value>, + pub system_program: Program<'info, System>, } @@ -70,6 +87,21 @@ pub struct InitializeLookupTableData<'info> { pub system_program: Program<'info, System>, } +#[derive(Accounts)] +pub struct StoreVal<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + // derived test PDA + #[account( + mut, + seeds=[b"val"], + bump)] + pub value: Account<'info, Value>, + + pub system_program: Program<'info, System>, +} + #[account] pub struct LookupTableDataAccount { pub version: u8, // Version of the data account @@ -85,3 +117,8 @@ pub struct DataAccount { pub u64_value: u64, pub u64_slice: Vec, } + +#[account] +pub struct Value { + pub u64_value: u64 +} \ No newline at end of file diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index c37d3b5d7..ee2e1a5ca 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -31,7 +31,8 @@ import ( commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - contract "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" + contractprimary "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" + contractsecondary "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface_secondary" "github.com/smartcontractkit/chainlink-solana/integration-tests/solclient" "github.com/smartcontractkit/chainlink-solana/integration-tests/utils" "github.com/smartcontractkit/chainlink-solana/pkg/solana/chainreader" @@ -69,8 +70,7 @@ func TestChainComponents(t *testing.T) { func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { it.DisableTests([]string{ - // disable tests that require secondary program - ContractReaderGetLatestValueFromMultipleContractsNamesSameFunction, + // disable failing test ContractReaderBatchGetLatestValueMultipleContractNamesSameFunction, // disable tests that set values ContractReaderGetLatestValueBasedOnConfidenceLevel, @@ -135,7 +135,8 @@ type SolanaChainComponentsInterfaceTesterHelper[T WrappedTestingT[T]] interface RPCClient() *chainreader.RPCClientWrapper Context(t T) context.Context Logger(t T) logger.Logger - GetJSONEncodedIDL(t T) []byte + GetPrimaryIDL(t T) []byte + GetSecondaryIDL(t T) []byte CreateAccount(t T, it SolanaChainComponentsInterfaceTester[T], contractName string, value uint64) solana.PublicKey TXM() *txm.TxManager SolanaClient() *client.Client @@ -206,7 +207,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) GetBindings(t T) []types.Boun // Create a new account with fresh state for each test return []types.BoundContract{ {Name: AnyContractName, Address: it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument).String()}, - // {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument).String()}, + {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument).String()}, } } @@ -221,14 +222,16 @@ func (it *SolanaChainComponentsInterfaceTester[T]) GenerateBlocksTillConfidenceL } type helper struct { - programID solana.PublicKey - rpcURL string - wsURL string - rpcClient *rpc.Client - wsClient *ws.Client - idlBts []byte - txm txm.TxManager - sc *client.Client + primaryProgramID solana.PublicKey + secondaryProgramID solana.PublicKey + rpcURL string + wsURL string + rpcClient *rpc.Client + wsClient *ws.Client + primaryIdlBts []byte + secondaryIdlBts []byte + txm txm.TxManager + sc *client.Client } func (h *helper) Init(t *testing.T) { @@ -237,7 +240,7 @@ func (h *helper) Init(t *testing.T) { privateKey, err := solana.PrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]) require.NoError(t, err) - h.rpcURL, h.wsURL = solanautils.SetupTestValidatorWithAnchorPrograms(t, privateKey.PublicKey().String(), []string{"contract-reader-interface"}) + h.rpcURL, h.wsURL = solanautils.SetupTestValidatorWithAnchorPrograms(t, privateKey.PublicKey().String(), []string{"contract-reader-interface", "contract-reader-interface-secondary"}) h.wsClient, err = ws.Connect(tests.Context(t), h.wsURL) h.rpcClient = rpc.New(h.rpcURL) @@ -266,11 +269,16 @@ func (h *helper) Init(t *testing.T) { h.txm = txm - pubkey, err := solana.PublicKeyFromBase58(programPubKey) + primaryPubkey, err := solana.PublicKeyFromBase58(primaryProgramPubKey) require.NoError(t, err) + contractprimary.SetProgramID(primaryPubkey) - contract.SetProgramID(pubkey) - h.programID = pubkey + secondaryPubkey, err := solana.PublicKeyFromBase58(secondaryProgramPubKey) + require.NoError(t, err) + contractsecondary.SetProgramID(secondaryPubkey) + + h.primaryProgramID = primaryPubkey + h.secondaryProgramID = secondaryPubkey } func (h *helper) RPCClient() *chainreader.RPCClientWrapper { @@ -293,14 +301,34 @@ func (h *helper) Logger(t *testing.T) logger.Logger { return logger.Test(t) } -func (h *helper) GetJSONEncodedIDL(t *testing.T) []byte { +func (h *helper) GetPrimaryIDL(t *testing.T) []byte { t.Helper() - if h.idlBts != nil { - return h.idlBts + if h.primaryIdlBts != nil { + return h.primaryIdlBts } - soPath := filepath.Join(utils.IDLDir, "contract_reader_interface.json") + bts := h.GetJSONEncodedIDL(t, "contract_reader_interface.json") + h.primaryIdlBts = bts + return h.primaryIdlBts +} + +func (h *helper) GetSecondaryIDL(t *testing.T) []byte { + t.Helper() + + if h.secondaryIdlBts != nil { + return h.secondaryIdlBts + } + + bts := h.GetJSONEncodedIDL(t, "contract_reader_interface_secondary.json") + h.secondaryIdlBts = bts + return h.secondaryIdlBts +} + +func (h *helper) GetJSONEncodedIDL(t *testing.T, fileName string) []byte { + t.Helper() + + soPath := filepath.Join(utils.IDLDir, fileName) _, err := os.Stat(soPath) if err != nil { @@ -311,16 +339,22 @@ func (h *helper) GetJSONEncodedIDL(t *testing.T) []byte { bts, err := os.ReadFile(soPath) require.NoError(t, err) - h.idlBts = bts - - return h.idlBts + return bts } func (h *helper) CreateAccount(t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], contractName string, value uint64) solana.PublicKey { t.Helper() - h.runInitialize(t, it, contractName, value) - return h.programID + var programID solana.PublicKey + switch contractName { + case AnyContractName: + programID = h.primaryProgramID + case AnySecondContractName: + programID = h.secondaryProgramID + } + + h.runInitialize(t, it, contractName, programID, value) + return programID } type InitializeArgs struct { @@ -332,6 +366,7 @@ func (h *helper) runInitialize( t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], contractName string, + programID solana.PublicKey, value uint64, ) { t.Helper() @@ -351,7 +386,7 @@ func (h *helper) runInitialize( Value: value, } - SubmitTransactionToCW(t, &it, cw, "initialize", args, types.BoundContract{Name: contractName, Address: h.programID.String()}, types.Finalized) + SubmitTransactionToCW(t, &it, cw, "initialize", args, types.BoundContract{Name: contractName, Address: programID.String()}, types.Finalized) } func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T) config.ContractReader { @@ -361,7 +396,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T return config.ContractReader{ Namespaces: map[string]config.ChainContractReader{ AnyContractName: { - IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), + IDL: mustUnmarshalIDL(t, string(it.Helper.GetPrimaryIDL(t))), Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { ChainSpecificName: "DataAccount", @@ -382,13 +417,31 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T &commoncodec.PropertyExtractorConfig{FieldName: "U64Slice"}, }, }, + MethodSettingUint64: { + ChainSpecificName: "DataAccount", + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, + }, + }, + MethodReturningAlterableUint64: { + ChainSpecificName: "Value", + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, + }, + }, }, }, AnySecondContractName: { - IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), + IDL: mustUnmarshalIDL(t, string(it.Helper.GetSecondaryIDL(t))), Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { - ChainSpecificName: "DataAccount", + ChainSpecificName: "Data", PDADefiniton: codec.PDATypeDef{ Prefix: pdaPrefix, }, @@ -400,7 +453,6 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T }, }, } - } func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T) chainwriter.ChainWriterConfig { @@ -409,7 +461,67 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T return chainwriter.ChainWriterConfig{ Programs: map[string]chainwriter.ProgramConfig{ AnyContractName: { - IDL: string(it.Helper.GetJSONEncodedIDL(t)), + IDL: string(it.Helper.GetPrimaryIDL(t)), + Methods: map[string]chainwriter.MethodConfig{ + "initialize": { + FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), + InputModifications: nil, + ChainSpecificName: "initialize", + LookupTables: chainwriter.LookupTables{}, + Accounts: []chainwriter.Lookup{ + chainwriter.PDALookups{ + Name: "Account", + PublicKey: chainwriter.AccountConstant{ + Name: "ProgramID", + Address: primaryProgramPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("data")}, + {Static: testIdx}, + }, + IsWritable: true, + IsSigner: false, + }, + chainwriter.PDALookups{ + Name: "ValueAccount", + PublicKey: chainwriter.AccountConstant{ + Name: "ProgramID", + Address: primaryProgramPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("val")}, + }, + IsWritable: true, + IsSigner: false, + }, + }, + DebugIDLocation: "", + }, + MethodSettingUint64: { + FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), + InputModifications: nil, + ChainSpecificName: "storeVal", + LookupTables: chainwriter.LookupTables{}, + Accounts: []chainwriter.Lookup{ + chainwriter.PDALookups{ + Name: "Account", + PublicKey: chainwriter.AccountConstant{ + Name: "ProgramID", + Address: primaryProgramPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("val")}, + }, + IsWritable: true, + IsSigner: false, + }, + }, + DebugIDLocation: "", + }, + }, + }, + AnySecondContractName: { + IDL: string(it.Helper.GetSecondaryIDL(t)), Methods: map[string]chainwriter.MethodConfig{ "initialize": { FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), @@ -421,7 +533,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T Name: "Account", PublicKey: chainwriter.AccountConstant{ Name: "ProgramID", - Address: programPubKey, + Address: secondaryProgramPubKey, }, Seeds: []chainwriter.Seed{ {Static: []byte("data")}, @@ -449,4 +561,7 @@ func mustUnmarshalIDL[T WrappedTestingT[T]](t T, rawIDL string) codec.IDL { return idl } -const programPubKey = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" +const ( + primaryProgramPubKey = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" + secondaryProgramPubKey = "9SFyk8NmGYh5D612mJwUYhguCRY9cFgaS2vksrigepjf" +) From c68e6ab0312f7e6b87a57a3eb7be8de5eb08c370 Mon Sep 17 00:00:00 2001 From: ilija Date: Mon, 3 Feb 2025 20:22:08 +0100 Subject: [PATCH 04/15] Add Test Struct PDA --- .../contract-reader-interface/src/lib.rs | 106 +++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 752712c21..9ceb25b24 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -36,8 +36,32 @@ pub mod contract_reader_interface { pub fn store_val(ctx: Context, value: u64) -> Result<()> { let val = &mut ctx.accounts.value; val.u64_value = value; + Ok(()) } + + pub fn store_test_struct( + ctx: Context, + test_idx: u64, + data: TestStructData, + ) -> Result<()> { + let test_struct_account = &mut ctx.accounts.test_struct; + + test_struct_account.idx = test_idx; + test_struct_account.bump = ctx.bumps.test_struct; + + test_struct_account.field = data.field; + test_struct_account.oracle_id = data.oracle_id; + test_struct_account.oracle_ids = data.oracle_ids; + test_struct_account.account_struct = data.account_struct; + test_struct_account.accounts = data.accounts; + test_struct_account.different_field = data.different_field; + test_struct_account.big_field = data.big_field; + test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct; + test_struct_account.nested_static_struct = data.nested_static_struct; + + Ok(()) + } } #[derive(Accounts)] @@ -98,6 +122,26 @@ pub struct StoreVal<'info> { seeds=[b"val"], bump)] pub value: Account<'info, Value>, +} + +#[derive(Accounts)] +#[instruction(test_idx: u64)] +pub struct StoreTestStruct<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + #[account( + init, + payer = signer, + // Add extra buffer for variable fields + space = 8 + size_of::() + 400, + seeds = [ + b"test-struct", + test_idx.to_le_bytes().as_ref() + ], + bump + )] + pub test_struct: Account<'info, TestStruct>, pub system_program: Program<'info, System>, } @@ -121,4 +165,64 @@ pub struct DataAccount { #[account] pub struct Value { pub u64_value: u64 -} \ No newline at end of file +} + +#[account] +pub struct TestStruct { + pub idx: u64, + pub bump: u8, + + pub field: Option, + pub oracle_id: [u8; 32], + pub oracle_ids: [[u8; 32]; 32], + pub account_struct: AccountStruct, + pub accounts: Vec>, + pub different_field: String, + pub big_field: Option<[u8; 32]>, + + pub nested_dynamic_struct: MidLevelDynamicTestStruct, + pub nested_static_struct: MidLevelStaticTestStruct, +} + +#[account] +pub struct TestStructData { + pub field: Option, + pub oracle_id: [u8; 32], + pub oracle_ids: [[u8; 32]; 32], + pub account_struct: AccountStruct, + pub accounts: Vec>, + pub different_field: String, + pub big_field: Option<[u8; 32]>, + pub nested_dynamic_struct: MidLevelDynamicTestStruct, + pub nested_static_struct: MidLevelStaticTestStruct, +} + +#[account] +pub struct AccountStruct { + pub account: Vec, + pub account_str: String, +} + +#[account] +pub struct MidLevelDynamicTestStruct { + pub fixed_bytes: [u8; 2], + pub inner: InnerDynamicTestStruct, +} + +#[account] +pub struct InnerDynamicTestStruct { + pub i: i64, + pub s: String, +} + +#[account] +pub struct MidLevelStaticTestStruct { + pub fixed_bytes: [u8; 2], + pub inner: InnerStaticTestStruct, +} + +#[account] +pub struct InnerStaticTestStruct { + pub i: i64, + pub a: Vec, +} From ce86a85972a8a39ab32ce40ebc59b054e47d3c07 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Wed, 5 Feb 2025 15:00:21 -0600 Subject: [PATCH 05/15] Fixed interface tests for the latest ChainWriter changes --- .../contract_reader_interface/Initialize.go | 31 +- .../StoreTestStruct.go | 188 ++++++ ...oreVal_test.go => StoreTestStruct_test.go} | 8 +- .../contract_reader_interface/StoreVal.go | 165 ----- .../contract_reader_interface/accounts.go | 613 +++++++++++++++++- .../contract_reader_interface/instructions.go | 8 +- .../contract-reader-interface/src/lib.rs | 36 - .../relayinterface/chain_components_test.go | 70 +- 8 files changed, 830 insertions(+), 289 deletions(-) create mode 100644 contracts/generated/contract_reader_interface/StoreTestStruct.go rename contracts/generated/contract_reader_interface/{StoreVal_test.go => StoreTestStruct_test.go} (76%) delete mode 100644 contracts/generated/contract_reader_interface/StoreVal.go diff --git a/contracts/generated/contract_reader_interface/Initialize.go b/contracts/generated/contract_reader_interface/Initialize.go index fa7b441ea..03e13f579 100644 --- a/contracts/generated/contract_reader_interface/Initialize.go +++ b/contracts/generated/contract_reader_interface/Initialize.go @@ -19,16 +19,14 @@ type Initialize struct { // // [1] = [WRITE] data // - // [2] = [WRITE] value - // - // [3] = [] systemProgram + // [2] = [] systemProgram ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` } // NewInitializeInstructionBuilder creates a new `Initialize` instruction builder. func NewInitializeInstructionBuilder() *Initialize { nd := &Initialize{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 4), + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), } return nd } @@ -67,26 +65,15 @@ func (inst *Initialize) GetDataAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[1] } -// SetValueAccount sets the "value" account. -func (inst *Initialize) SetValueAccount(value ag_solanago.PublicKey) *Initialize { - inst.AccountMetaSlice[2] = ag_solanago.Meta(value).WRITE() - return inst -} - -// GetValueAccount gets the "value" account. -func (inst *Initialize) GetValueAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] -} - // SetSystemProgramAccount sets the "systemProgram" account. func (inst *Initialize) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *Initialize { - inst.AccountMetaSlice[3] = ag_solanago.Meta(systemProgram) + inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) return inst } // GetSystemProgramAccount gets the "systemProgram" account. func (inst *Initialize) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[3] + return inst.AccountMetaSlice[2] } func (inst Initialize) Build() *Instruction { @@ -126,9 +113,6 @@ func (inst *Initialize) Validate() error { return errors.New("accounts.Data is not set") } if inst.AccountMetaSlice[2] == nil { - return errors.New("accounts.Value is not set") - } - if inst.AccountMetaSlice[3] == nil { return errors.New("accounts.SystemProgram is not set") } } @@ -150,11 +134,10 @@ func (inst *Initialize) EncodeToTree(parent ag_treeout.Branches) { }) // Accounts of the instruction: - instructionBranch.Child("Accounts[len=4]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { accountsBranch.Child(ag_format.Meta(" signer", inst.AccountMetaSlice[0])) accountsBranch.Child(ag_format.Meta(" data", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta(" value", inst.AccountMetaSlice[2])) - accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[3])) + accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) }) }) }) @@ -195,13 +178,11 @@ func NewInitializeInstruction( // Accounts: signer ag_solanago.PublicKey, data ag_solanago.PublicKey, - valueAccount ag_solanago.PublicKey, systemProgram ag_solanago.PublicKey) *Initialize { return NewInitializeInstructionBuilder(). SetTestIdx(testIdx). SetValue(value). SetSignerAccount(signer). SetDataAccount(data). - SetValueAccount(valueAccount). SetSystemProgramAccount(systemProgram) } diff --git a/contracts/generated/contract_reader_interface/StoreTestStruct.go b/contracts/generated/contract_reader_interface/StoreTestStruct.go new file mode 100644 index 000000000..49111079d --- /dev/null +++ b/contracts/generated/contract_reader_interface/StoreTestStruct.go @@ -0,0 +1,188 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface + +import ( + "errors" + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +// StoreTestStruct is the `storeTestStruct` instruction. +type StoreTestStruct struct { + TestIdx *uint64 + Data *TestStructData + + // [0] = [WRITE, SIGNER] signer + // + // [1] = [WRITE] testStruct + // + // [2] = [] systemProgram + ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` +} + +// NewStoreTestStructInstructionBuilder creates a new `StoreTestStruct` instruction builder. +func NewStoreTestStructInstructionBuilder() *StoreTestStruct { + nd := &StoreTestStruct{ + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), + } + return nd +} + +// SetTestIdx sets the "testIdx" parameter. +func (inst *StoreTestStruct) SetTestIdx(testIdx uint64) *StoreTestStruct { + inst.TestIdx = &testIdx + return inst +} + +// SetData sets the "data" parameter. +func (inst *StoreTestStruct) SetData(data TestStructData) *StoreTestStruct { + inst.Data = &data + return inst +} + +// SetSignerAccount sets the "signer" account. +func (inst *StoreTestStruct) SetSignerAccount(signer ag_solanago.PublicKey) *StoreTestStruct { + inst.AccountMetaSlice[0] = ag_solanago.Meta(signer).WRITE().SIGNER() + return inst +} + +// GetSignerAccount gets the "signer" account. +func (inst *StoreTestStruct) GetSignerAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[0] +} + +// SetTestStructAccount sets the "testStruct" account. +func (inst *StoreTestStruct) SetTestStructAccount(testStruct ag_solanago.PublicKey) *StoreTestStruct { + inst.AccountMetaSlice[1] = ag_solanago.Meta(testStruct).WRITE() + return inst +} + +// GetTestStructAccount gets the "testStruct" account. +func (inst *StoreTestStruct) GetTestStructAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + +// SetSystemProgramAccount sets the "systemProgram" account. +func (inst *StoreTestStruct) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *StoreTestStruct { + inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) + return inst +} + +// GetSystemProgramAccount gets the "systemProgram" account. +func (inst *StoreTestStruct) GetSystemProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +func (inst StoreTestStruct) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: Instruction_StoreTestStruct, + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst StoreTestStruct) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *StoreTestStruct) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.TestIdx == nil { + return errors.New("TestIdx parameter is not set") + } + if inst.Data == nil { + return errors.New("Data parameter is not set") + } + } + + // Check whether all (required) accounts are set: + { + if inst.AccountMetaSlice[0] == nil { + return errors.New("accounts.Signer is not set") + } + if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.TestStruct is not set") + } + if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.SystemProgram is not set") + } + } + return nil +} + +func (inst *StoreTestStruct) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("StoreTestStruct")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("TestIdx", *inst.TestIdx)) + paramsBranch.Child(ag_format.Param(" Data", *inst.Data)) + }) + + // Accounts of the instruction: + instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" signer", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta(" testStruct", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) + }) + }) + }) +} + +func (obj StoreTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `TestIdx` param: + err = encoder.Encode(obj.TestIdx) + if err != nil { + return err + } + // Serialize `Data` param: + err = encoder.Encode(obj.Data) + if err != nil { + return err + } + return nil +} +func (obj *StoreTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `TestIdx`: + err = decoder.Decode(&obj.TestIdx) + if err != nil { + return err + } + // Deserialize `Data`: + err = decoder.Decode(&obj.Data) + if err != nil { + return err + } + return nil +} + +// NewStoreTestStructInstruction declares a new StoreTestStruct instruction with the provided parameters and accounts. +func NewStoreTestStructInstruction( + // Parameters: + testIdx uint64, + data TestStructData, + // Accounts: + signer ag_solanago.PublicKey, + testStruct ag_solanago.PublicKey, + systemProgram ag_solanago.PublicKey) *StoreTestStruct { + return NewStoreTestStructInstructionBuilder(). + SetTestIdx(testIdx). + SetData(data). + SetSignerAccount(signer). + SetTestStructAccount(testStruct). + SetSystemProgramAccount(systemProgram) +} diff --git a/contracts/generated/contract_reader_interface/StoreVal_test.go b/contracts/generated/contract_reader_interface/StoreTestStruct_test.go similarity index 76% rename from contracts/generated/contract_reader_interface/StoreVal_test.go rename to contracts/generated/contract_reader_interface/StoreTestStruct_test.go index 2f4e6350f..ecbd2f208 100644 --- a/contracts/generated/contract_reader_interface/StoreVal_test.go +++ b/contracts/generated/contract_reader_interface/StoreTestStruct_test.go @@ -10,18 +10,18 @@ import ( "testing" ) -func TestEncodeDecode_StoreVal(t *testing.T) { +func TestEncodeDecode_StoreTestStruct(t *testing.T) { fu := ag_gofuzz.New().NilChance(0) for i := 0; i < 1; i++ { - t.Run("StoreVal"+strconv.Itoa(i), func(t *testing.T) { + t.Run("StoreTestStruct"+strconv.Itoa(i), func(t *testing.T) { { - params := new(StoreVal) + params := new(StoreTestStruct) fu.Fuzz(params) params.AccountMetaSlice = nil buf := new(bytes.Buffer) err := encodeT(*params, buf) ag_require.NoError(t, err) - got := new(StoreVal) + got := new(StoreTestStruct) err = decodeT(got, buf.Bytes()) got.AccountMetaSlice = nil ag_require.NoError(t, err) diff --git a/contracts/generated/contract_reader_interface/StoreVal.go b/contracts/generated/contract_reader_interface/StoreVal.go deleted file mode 100644 index d59fe3405..000000000 --- a/contracts/generated/contract_reader_interface/StoreVal.go +++ /dev/null @@ -1,165 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package contract_reader_interface - -import ( - "errors" - ag_binary "github.com/gagliardetto/binary" - ag_solanago "github.com/gagliardetto/solana-go" - ag_format "github.com/gagliardetto/solana-go/text/format" - ag_treeout "github.com/gagliardetto/treeout" -) - -// StoreVal is the `storeVal` instruction. -type StoreVal struct { - Value *uint64 - - // [0] = [WRITE, SIGNER] signer - // - // [1] = [WRITE] value - // - // [2] = [] systemProgram - ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` -} - -// NewStoreValInstructionBuilder creates a new `StoreVal` instruction builder. -func NewStoreValInstructionBuilder() *StoreVal { - nd := &StoreVal{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), - } - return nd -} - -// SetValue sets the "value" parameter. -func (inst *StoreVal) SetValue(value uint64) *StoreVal { - inst.Value = &value - return inst -} - -// SetSignerAccount sets the "signer" account. -func (inst *StoreVal) SetSignerAccount(signer ag_solanago.PublicKey) *StoreVal { - inst.AccountMetaSlice[0] = ag_solanago.Meta(signer).WRITE().SIGNER() - return inst -} - -// GetSignerAccount gets the "signer" account. -func (inst *StoreVal) GetSignerAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[0] -} - -// SetValueAccount sets the "value" account. -func (inst *StoreVal) SetValueAccount(value ag_solanago.PublicKey) *StoreVal { - inst.AccountMetaSlice[1] = ag_solanago.Meta(value).WRITE() - return inst -} - -// GetValueAccount gets the "value" account. -func (inst *StoreVal) GetValueAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] -} - -// SetSystemProgramAccount sets the "systemProgram" account. -func (inst *StoreVal) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *StoreVal { - inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) - return inst -} - -// GetSystemProgramAccount gets the "systemProgram" account. -func (inst *StoreVal) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] -} - -func (inst StoreVal) Build() *Instruction { - return &Instruction{BaseVariant: ag_binary.BaseVariant{ - Impl: inst, - TypeID: Instruction_StoreVal, - }} -} - -// ValidateAndBuild validates the instruction parameters and accounts; -// if there is a validation error, it returns the error. -// Otherwise, it builds and returns the instruction. -func (inst StoreVal) ValidateAndBuild() (*Instruction, error) { - if err := inst.Validate(); err != nil { - return nil, err - } - return inst.Build(), nil -} - -func (inst *StoreVal) Validate() error { - // Check whether all (required) parameters are set: - { - if inst.Value == nil { - return errors.New("Value parameter is not set") - } - } - - // Check whether all (required) accounts are set: - { - if inst.AccountMetaSlice[0] == nil { - return errors.New("accounts.Signer is not set") - } - if inst.AccountMetaSlice[1] == nil { - return errors.New("accounts.Value is not set") - } - if inst.AccountMetaSlice[2] == nil { - return errors.New("accounts.SystemProgram is not set") - } - } - return nil -} - -func (inst *StoreVal) EncodeToTree(parent ag_treeout.Branches) { - parent.Child(ag_format.Program(ProgramName, ProgramID)). - // - ParentFunc(func(programBranch ag_treeout.Branches) { - programBranch.Child(ag_format.Instruction("StoreVal")). - // - ParentFunc(func(instructionBranch ag_treeout.Branches) { - - // Parameters of the instruction: - instructionBranch.Child("Params[len=1]").ParentFunc(func(paramsBranch ag_treeout.Branches) { - paramsBranch.Child(ag_format.Param("Value", *inst.Value)) - }) - - // Accounts of the instruction: - instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta(" signer", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta(" value", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) - }) - }) - }) -} - -func (obj StoreVal) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `Value` param: - err = encoder.Encode(obj.Value) - if err != nil { - return err - } - return nil -} -func (obj *StoreVal) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `Value`: - err = decoder.Decode(&obj.Value) - if err != nil { - return err - } - return nil -} - -// NewStoreValInstruction declares a new StoreVal instruction with the provided parameters and accounts. -func NewStoreValInstruction( - // Parameters: - value uint64, - // Accounts: - signer ag_solanago.PublicKey, - valueAccount ag_solanago.PublicKey, - systemProgram ag_solanago.PublicKey) *StoreVal { - return NewStoreValInstructionBuilder(). - SetValue(value). - SetSignerAccount(signer). - SetValueAccount(valueAccount). - SetSystemProgramAccount(systemProgram) -} diff --git a/contracts/generated/contract_reader_interface/accounts.go b/contracts/generated/contract_reader_interface/accounts.go index c5606c730..4a298a92b 100644 --- a/contracts/generated/contract_reader_interface/accounts.go +++ b/contracts/generated/contract_reader_interface/accounts.go @@ -158,42 +158,631 @@ func (obj *DataAccount) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err er return nil } -type Value struct { - U64Value uint64 +type TestStruct struct { + Idx uint64 + Bump uint8 + Field *int32 `bin:"optional"` + OracleId [32]uint8 + OracleIds [32][32]uint8 + AccountStruct AccountStruct + Accounts [][]byte + DifferentField string + BigField *[32]uint8 `bin:"optional"` + NestedDynamicStruct MidLevelDynamicTestStruct + NestedStaticStruct MidLevelStaticTestStruct } -var ValueDiscriminator = [8]byte{135, 158, 244, 117, 72, 203, 24, 194} +var TestStructDiscriminator = [8]byte{243, 149, 82, 70, 154, 54, 107, 6} -func (obj Value) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { +func (obj TestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { // Write account discriminator: - err = encoder.WriteBytes(ValueDiscriminator[:], false) + err = encoder.WriteBytes(TestStructDiscriminator[:], false) if err != nil { return err } - // Serialize `U64Value` param: - err = encoder.Encode(obj.U64Value) + // Serialize `Idx` param: + err = encoder.Encode(obj.Idx) + if err != nil { + return err + } + // Serialize `Bump` param: + err = encoder.Encode(obj.Bump) + if err != nil { + return err + } + // Serialize `Field` param (optional): + { + if obj.Field == nil { + err = encoder.WriteBool(false) + if err != nil { + return err + } + } else { + err = encoder.WriteBool(true) + if err != nil { + return err + } + 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 (optional): + { + if obj.BigField == nil { + err = encoder.WriteBool(false) + if err != nil { + return err + } + } else { + err = encoder.WriteBool(true) + if err != nil { + return err + } + 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 *Value) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { +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(ValueDiscriminator[:]) { + if !discriminator.Equal(TestStructDiscriminator[:]) { return fmt.Errorf( "wrong discriminator: wanted %s, got %s", - "[135 158 244 117 72 203 24 194]", + "[243 149 82 70 154 54 107 6]", fmt.Sprint(discriminator[:])) } } - // Deserialize `U64Value`: - err = decoder.Decode(&obj.U64Value) + // Deserialize `Idx`: + err = decoder.Decode(&obj.Idx) + if err != nil { + return err + } + // Deserialize `Bump`: + err = decoder.Decode(&obj.Bump) + if err != nil { + return err + } + // Deserialize `Field` (optional): + { + ok, err := decoder.ReadBool() + if err != nil { + return err + } + if ok { + 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` (optional): + { + ok, err := decoder.ReadBool() + if err != nil { + return err + } + if ok { + 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 TestStructData struct { + Field *int32 `bin:"optional"` + OracleId [32]uint8 + OracleIds [32][32]uint8 + AccountStruct AccountStruct + Accounts [][]byte + DifferentField string + BigField *[32]uint8 `bin:"optional"` + NestedDynamicStruct MidLevelDynamicTestStruct + NestedStaticStruct MidLevelStaticTestStruct +} + +var TestStructDataDiscriminator = [8]byte{73, 128, 255, 29, 196, 76, 189, 62} + +func (obj TestStructData) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(TestStructDataDiscriminator[:], false) + if err != nil { + return err + } + // Serialize `Field` param (optional): + { + if obj.Field == nil { + err = encoder.WriteBool(false) + if err != nil { + return err + } + } else { + err = encoder.WriteBool(true) + if err != nil { + return err + } + 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 (optional): + { + if obj.BigField == nil { + err = encoder.WriteBool(false) + if err != nil { + return err + } + } else { + err = encoder.WriteBool(true) + if err != nil { + return err + } + 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 *TestStructData) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(TestStructDataDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[73 128 255 29 196 76 189 62]", + fmt.Sprint(discriminator[:])) + } + } + // Deserialize `Field` (optional): + { + ok, err := decoder.ReadBool() + if err != nil { + return err + } + if ok { + 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` (optional): + { + ok, err := decoder.ReadBool() + if err != nil { + return err + } + if ok { + 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 []byte + AccountStr string +} + +var AccountStructDiscriminator = [8]byte{172, 54, 167, 253, 102, 242, 34, 94} + +func (obj AccountStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(AccountStructDiscriminator[:], false) + if err != nil { + return err + } + // 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) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(AccountStructDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[172 54 167 253 102 242 34 94]", + fmt.Sprint(discriminator[:])) + } + } + // 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 MidLevelDynamicTestStruct struct { + FixedBytes [2]uint8 + Inner InnerDynamicTestStruct +} + +var MidLevelDynamicTestStructDiscriminator = [8]byte{202, 138, 18, 67, 76, 103, 164, 244} + +func (obj MidLevelDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(MidLevelDynamicTestStructDiscriminator[:], false) + if err != nil { + return err + } + // 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 *MidLevelDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(MidLevelDynamicTestStructDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[202 138 18 67 76 103 164 244]", + fmt.Sprint(discriminator[:])) + } + } + // 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 InnerDynamicTestStruct struct { + I int64 + S string +} + +var InnerDynamicTestStructDiscriminator = [8]byte{111, 35, 169, 64, 130, 38, 199, 25} + +func (obj InnerDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(InnerDynamicTestStructDiscriminator[:], false) + if err != nil { + return err + } + // Serialize `I` param: + err = encoder.Encode(obj.I) + if err != nil { + return err + } + // Serialize `S` param: + err = encoder.Encode(obj.S) + if err != nil { + return err + } + return nil +} + +func (obj *InnerDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(InnerDynamicTestStructDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[111 35 169 64 130 38 199 25]", + fmt.Sprint(discriminator[:])) + } + } + // Deserialize `I`: + err = decoder.Decode(&obj.I) + if err != nil { + return err + } + // Deserialize `S`: + err = decoder.Decode(&obj.S) + if err != nil { + return err + } + return nil +} + +type MidLevelStaticTestStruct struct { + FixedBytes [2]uint8 + Inner InnerStaticTestStruct +} + +var MidLevelStaticTestStructDiscriminator = [8]byte{60, 247, 21, 8, 67, 89, 45, 66} + +func (obj MidLevelStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(MidLevelStaticTestStructDiscriminator[:], false) + if err != nil { + return err + } + // 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 *MidLevelStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(MidLevelStaticTestStructDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[60 247 21 8 67 89 45 66]", + fmt.Sprint(discriminator[:])) + } + } + // 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 InnerStaticTestStruct struct { + I int64 + A []byte +} + +var InnerStaticTestStructDiscriminator = [8]byte{34, 251, 49, 124, 37, 221, 104, 73} + +func (obj InnerStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(InnerStaticTestStructDiscriminator[:], false) + if err != nil { + return err + } + // Serialize `I` param: + err = encoder.Encode(obj.I) + if err != nil { + return err + } + // Serialize `A` param: + err = encoder.Encode(obj.A) + if err != nil { + return err + } + return nil +} + +func (obj *InnerStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(InnerStaticTestStructDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[34 251 49 124 37 221 104 73]", + fmt.Sprint(discriminator[:])) + } + } + // Deserialize `I`: + err = decoder.Decode(&obj.I) + if err != nil { + return err + } + // Deserialize `A`: + err = decoder.Decode(&obj.A) if err != nil { return err } diff --git a/contracts/generated/contract_reader_interface/instructions.go b/contracts/generated/contract_reader_interface/instructions.go index b49a9fb24..a91bc2601 100644 --- a/contracts/generated/contract_reader_interface/instructions.go +++ b/contracts/generated/contract_reader_interface/instructions.go @@ -32,7 +32,7 @@ var ( Instruction_InitializeLookupTable = ag_binary.TypeID([8]byte{149, 120, 10, 249, 212, 185, 177, 216}) - Instruction_StoreVal = ag_binary.TypeID([8]byte{158, 54, 7, 226, 137, 227, 73, 242}) + Instruction_StoreTestStruct = ag_binary.TypeID([8]byte{170, 96, 68, 47, 193, 198, 80, 69}) ) // InstructionIDToName returns the name of the instruction given its ID. @@ -42,8 +42,8 @@ func InstructionIDToName(id ag_binary.TypeID) string { return "Initialize" case Instruction_InitializeLookupTable: return "InitializeLookupTable" - case Instruction_StoreVal: - return "StoreVal" + case Instruction_StoreTestStruct: + return "StoreTestStruct" default: return "" } @@ -71,7 +71,7 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( "initialize_lookup_table", (*InitializeLookupTable)(nil), }, { - "store_val", (*StoreVal)(nil), + "store_test_struct", (*StoreTestStruct)(nil), }, }, ) diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 9ceb25b24..9b45c2613 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -15,8 +15,6 @@ pub mod contract_reader_interface { account.idx = test_idx; account.bump = ctx.bumps.data; - ctx.accounts.value.u64_value = 0; - Ok(()) } @@ -33,13 +31,6 @@ pub mod contract_reader_interface { Ok(()) } - pub fn store_val(ctx: Context, value: u64) -> Result<()> { - let val = &mut ctx.accounts.value; - val.u64_value = value; - - Ok(()) - } - pub fn store_test_struct( ctx: Context, test_idx: u64, @@ -79,15 +70,6 @@ pub struct Initialize<'info> { bump)] pub data: Account<'info, DataAccount>, - // derived test PDA - #[account( - init, - payer = signer, - space = size_of::() + 8, - seeds=[b"val"], - bump)] - pub value: Account<'info, Value>, - pub system_program: Program<'info, System>, } @@ -111,19 +93,6 @@ pub struct InitializeLookupTableData<'info> { pub system_program: Program<'info, System>, } -#[derive(Accounts)] -pub struct StoreVal<'info> { - #[account(mut)] - pub signer: Signer<'info>, - - // derived test PDA - #[account( - mut, - seeds=[b"val"], - bump)] - pub value: Account<'info, Value>, -} - #[derive(Accounts)] #[instruction(test_idx: u64)] pub struct StoreTestStruct<'info> { @@ -162,11 +131,6 @@ pub struct DataAccount { pub u64_slice: Vec, } -#[account] -pub struct Value { - pub u64_value: u64 -} - #[account] pub struct TestStruct { pub idx: u64, diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index ee2e1a5ca..3d5f5984c 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -71,6 +71,7 @@ func TestChainComponents(t *testing.T) { func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { it.DisableTests([]string{ // disable failing test + ContractReaderGetLatestValueFromMultipleContractsNamesSameFunction, ContractReaderBatchGetLatestValueMultipleContractNamesSameFunction, // disable tests that set values ContractReaderGetLatestValueBasedOnConfidenceLevel, @@ -426,15 +427,6 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, }, }, - MethodReturningAlterableUint64: { - ChainSpecificName: "Value", - PDADefiniton: codec.PDATypeDef{ - Prefix: pdaPrefix, - }, - OutputModifications: commoncodec.ModifiersConfig{ - &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, - }, - }, }, }, AnySecondContractName: { @@ -458,17 +450,24 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T) chainwriter.ChainWriterConfig { idx := it.getTestIdx(t.Name()) testIdx := binary.LittleEndian.AppendUint64([]byte{}, idx) + fromAddress := solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String() return chainwriter.ChainWriterConfig{ Programs: map[string]chainwriter.ProgramConfig{ AnyContractName: { IDL: string(it.Helper.GetPrimaryIDL(t)), Methods: map[string]chainwriter.MethodConfig{ "initialize": { - FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), + FromAddress: fromAddress, InputModifications: nil, ChainSpecificName: "initialize", LookupTables: chainwriter.LookupTables{}, Accounts: []chainwriter.Lookup{ + chainwriter.AccountConstant{ + Name: "Signer", + Address: fromAddress, + IsSigner: true, + IsWritable: true, + }, chainwriter.PDALookups{ Name: "Account", PublicKey: chainwriter.AccountConstant{ @@ -482,38 +481,11 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IsWritable: true, IsSigner: false, }, - chainwriter.PDALookups{ - Name: "ValueAccount", - PublicKey: chainwriter.AccountConstant{ - Name: "ProgramID", - Address: primaryProgramPubKey, - }, - Seeds: []chainwriter.Seed{ - {Static: []byte("val")}, - }, - IsWritable: true, - IsSigner: false, - }, - }, - DebugIDLocation: "", - }, - MethodSettingUint64: { - FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), - InputModifications: nil, - ChainSpecificName: "storeVal", - LookupTables: chainwriter.LookupTables{}, - Accounts: []chainwriter.Lookup{ - chainwriter.PDALookups{ - Name: "Account", - PublicKey: chainwriter.AccountConstant{ - Name: "ProgramID", - Address: primaryProgramPubKey, - }, - Seeds: []chainwriter.Seed{ - {Static: []byte("val")}, - }, - IsWritable: true, - IsSigner: false, + chainwriter.AccountConstant{ + Name: "SystemProgram", + Address: solana.SystemProgramID.String(), + IsSigner: false, + IsWritable: false, }, }, DebugIDLocation: "", @@ -524,11 +496,17 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IDL: string(it.Helper.GetSecondaryIDL(t)), Methods: map[string]chainwriter.MethodConfig{ "initialize": { - FromAddress: solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String(), + FromAddress: fromAddress, InputModifications: nil, ChainSpecificName: "initialize", LookupTables: chainwriter.LookupTables{}, Accounts: []chainwriter.Lookup{ + chainwriter.AccountConstant{ + Name: "Signer", + Address: fromAddress, + IsSigner: true, + IsWritable: true, + }, chainwriter.PDALookups{ Name: "Account", PublicKey: chainwriter.AccountConstant{ @@ -542,6 +520,12 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IsWritable: true, IsSigner: false, }, + chainwriter.AccountConstant{ + Name: "SystemProgram", + Address: solana.SystemProgramID.String(), + IsSigner: false, + IsWritable: false, + }, }, DebugIDLocation: "", }, From 5db30c2d6d37b49ea960f78b987cc26c812280d0 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Thu, 6 Feb 2025 23:23:13 -0600 Subject: [PATCH 06/15] Enabled test struct interface test --- contracts/Cargo.lock | 2 + .../{StoreTestStruct.go => Store.go} | 50 +- ...{StoreTestStruct_test.go => Store_test.go} | 8 +- .../contract_reader_interface/accounts.go | 559 ++---------------- .../contract_reader_interface/instructions.go | 8 +- .../contract_reader_interface/types.go | 335 +++++++++++ .../contract-reader-interface/Cargo.toml | 4 +- .../contract-reader-interface/src/lib.rs | 115 ++-- .../relayinterface/chain_components_test.go | 447 +++++++++++++- pkg/solana/codec/byte_string_modifier.go | 6 +- 10 files changed, 917 insertions(+), 617 deletions(-) rename contracts/generated/contract_reader_interface/{StoreTestStruct.go => Store.go} (69%) rename contracts/generated/contract_reader_interface/{StoreTestStruct_test.go => Store_test.go} (76%) diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index ae8d5fbff..01c246ad8 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -724,6 +724,8 @@ name = "contract-reader-interface" version = "0.1.0" dependencies = [ "anchor-lang", + "bytemuck", + "solana-program", ] [[package]] diff --git a/contracts/generated/contract_reader_interface/StoreTestStruct.go b/contracts/generated/contract_reader_interface/Store.go similarity index 69% rename from contracts/generated/contract_reader_interface/StoreTestStruct.go rename to contracts/generated/contract_reader_interface/Store.go index 49111079d..76414519e 100644 --- a/contracts/generated/contract_reader_interface/StoreTestStruct.go +++ b/contracts/generated/contract_reader_interface/Store.go @@ -10,8 +10,8 @@ import ( ag_treeout "github.com/gagliardetto/treeout" ) -// StoreTestStruct is the `storeTestStruct` instruction. -type StoreTestStruct struct { +// Store is the `store` instruction. +type Store struct { TestIdx *uint64 Data *TestStructData @@ -23,77 +23,77 @@ type StoreTestStruct struct { ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` } -// NewStoreTestStructInstructionBuilder creates a new `StoreTestStruct` instruction builder. -func NewStoreTestStructInstructionBuilder() *StoreTestStruct { - nd := &StoreTestStruct{ +// NewStoreInstructionBuilder creates a new `Store` instruction builder. +func NewStoreInstructionBuilder() *Store { + nd := &Store{ AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), } return nd } // SetTestIdx sets the "testIdx" parameter. -func (inst *StoreTestStruct) SetTestIdx(testIdx uint64) *StoreTestStruct { +func (inst *Store) SetTestIdx(testIdx uint64) *Store { inst.TestIdx = &testIdx return inst } // SetData sets the "data" parameter. -func (inst *StoreTestStruct) SetData(data TestStructData) *StoreTestStruct { +func (inst *Store) SetData(data TestStructData) *Store { inst.Data = &data return inst } // SetSignerAccount sets the "signer" account. -func (inst *StoreTestStruct) SetSignerAccount(signer ag_solanago.PublicKey) *StoreTestStruct { +func (inst *Store) SetSignerAccount(signer ag_solanago.PublicKey) *Store { inst.AccountMetaSlice[0] = ag_solanago.Meta(signer).WRITE().SIGNER() return inst } // GetSignerAccount gets the "signer" account. -func (inst *StoreTestStruct) GetSignerAccount() *ag_solanago.AccountMeta { +func (inst *Store) GetSignerAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[0] } // SetTestStructAccount sets the "testStruct" account. -func (inst *StoreTestStruct) SetTestStructAccount(testStruct ag_solanago.PublicKey) *StoreTestStruct { +func (inst *Store) SetTestStructAccount(testStruct ag_solanago.PublicKey) *Store { inst.AccountMetaSlice[1] = ag_solanago.Meta(testStruct).WRITE() return inst } // GetTestStructAccount gets the "testStruct" account. -func (inst *StoreTestStruct) GetTestStructAccount() *ag_solanago.AccountMeta { +func (inst *Store) GetTestStructAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[1] } // SetSystemProgramAccount sets the "systemProgram" account. -func (inst *StoreTestStruct) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *StoreTestStruct { +func (inst *Store) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *Store { inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) return inst } // GetSystemProgramAccount gets the "systemProgram" account. -func (inst *StoreTestStruct) GetSystemProgramAccount() *ag_solanago.AccountMeta { +func (inst *Store) GetSystemProgramAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[2] } -func (inst StoreTestStruct) Build() *Instruction { +func (inst Store) Build() *Instruction { return &Instruction{BaseVariant: ag_binary.BaseVariant{ Impl: inst, - TypeID: Instruction_StoreTestStruct, + TypeID: Instruction_Store, }} } // ValidateAndBuild validates the instruction parameters and accounts; // if there is a validation error, it returns the error. // Otherwise, it builds and returns the instruction. -func (inst StoreTestStruct) ValidateAndBuild() (*Instruction, error) { +func (inst Store) ValidateAndBuild() (*Instruction, error) { if err := inst.Validate(); err != nil { return nil, err } return inst.Build(), nil } -func (inst *StoreTestStruct) Validate() error { +func (inst *Store) Validate() error { // Check whether all (required) parameters are set: { if inst.TestIdx == nil { @@ -119,11 +119,11 @@ func (inst *StoreTestStruct) Validate() error { return nil } -func (inst *StoreTestStruct) EncodeToTree(parent ag_treeout.Branches) { +func (inst *Store) EncodeToTree(parent ag_treeout.Branches) { parent.Child(ag_format.Program(ProgramName, ProgramID)). // ParentFunc(func(programBranch ag_treeout.Branches) { - programBranch.Child(ag_format.Instruction("StoreTestStruct")). + programBranch.Child(ag_format.Instruction("Store")). // ParentFunc(func(instructionBranch ag_treeout.Branches) { @@ -143,7 +143,7 @@ func (inst *StoreTestStruct) EncodeToTree(parent ag_treeout.Branches) { }) } -func (obj StoreTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { +func (obj Store) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { // Serialize `TestIdx` param: err = encoder.Encode(obj.TestIdx) if err != nil { @@ -156,7 +156,7 @@ func (obj StoreTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err e } return nil } -func (obj *StoreTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { +func (obj *Store) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { // Deserialize `TestIdx`: err = decoder.Decode(&obj.TestIdx) if err != nil { @@ -170,16 +170,16 @@ func (obj *StoreTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (er return nil } -// NewStoreTestStructInstruction declares a new StoreTestStruct instruction with the provided parameters and accounts. -func NewStoreTestStructInstruction( +// NewStoreInstruction declares a new Store instruction with the provided parameters and accounts. +func NewStoreInstruction( // Parameters: testIdx uint64, data TestStructData, // Accounts: signer ag_solanago.PublicKey, testStruct ag_solanago.PublicKey, - systemProgram ag_solanago.PublicKey) *StoreTestStruct { - return NewStoreTestStructInstructionBuilder(). + systemProgram ag_solanago.PublicKey) *Store { + return NewStoreInstructionBuilder(). SetTestIdx(testIdx). SetData(data). SetSignerAccount(signer). diff --git a/contracts/generated/contract_reader_interface/StoreTestStruct_test.go b/contracts/generated/contract_reader_interface/Store_test.go similarity index 76% rename from contracts/generated/contract_reader_interface/StoreTestStruct_test.go rename to contracts/generated/contract_reader_interface/Store_test.go index ecbd2f208..6a0f8e181 100644 --- a/contracts/generated/contract_reader_interface/StoreTestStruct_test.go +++ b/contracts/generated/contract_reader_interface/Store_test.go @@ -10,18 +10,18 @@ import ( "testing" ) -func TestEncodeDecode_StoreTestStruct(t *testing.T) { +func TestEncodeDecode_Store(t *testing.T) { fu := ag_gofuzz.New().NilChance(0) for i := 0; i < 1; i++ { - t.Run("StoreTestStruct"+strconv.Itoa(i), func(t *testing.T) { + t.Run("Store"+strconv.Itoa(i), func(t *testing.T) { { - params := new(StoreTestStruct) + params := new(Store) fu.Fuzz(params) params.AccountMetaSlice = nil buf := new(bytes.Buffer) err := encodeT(*params, buf) ag_require.NoError(t, err) - got := new(StoreTestStruct) + got := new(Store) err = decodeT(got, buf.Bytes()) got.AccountMetaSlice = nil ag_require.NoError(t, err) diff --git a/contracts/generated/contract_reader_interface/accounts.go b/contracts/generated/contract_reader_interface/accounts.go index 4a298a92b..3a7197749 100644 --- a/contracts/generated/contract_reader_interface/accounts.go +++ b/contracts/generated/contract_reader_interface/accounts.go @@ -161,13 +161,17 @@ func (obj *DataAccount) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err er type TestStruct struct { Idx uint64 Bump uint8 - Field *int32 `bin:"optional"` - OracleId [32]uint8 - OracleIds [32][32]uint8 + Padding0 [7]uint8 + Field int32 + Padding1 [4]uint8 + OracleId uint8 + Padding2 [15]uint8 + OracleIds [32]uint8 + Accounts [2][32]uint8 + DifferentField [32]uint8 + Padding3 [8]uint8 + BigField ag_binary.Int128 AccountStruct AccountStruct - Accounts [][]byte - DifferentField string - BigField *[32]uint8 `bin:"optional"` NestedDynamicStruct MidLevelDynamicTestStruct NestedStaticStruct MidLevelStaticTestStruct } @@ -190,36 +194,33 @@ func (obj TestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) if err != nil { return err } - // Serialize `Field` param (optional): - { - if obj.Field == nil { - err = encoder.WriteBool(false) - if err != nil { - return err - } - } else { - err = encoder.WriteBool(true) - if err != nil { - return err - } - err = encoder.Encode(obj.Field) - if err != nil { - return err - } - } + // Serialize `Padding0` param: + err = encoder.Encode(obj.Padding0) + if err != nil { + return err + } + // Serialize `Field` param: + err = encoder.Encode(obj.Field) + if err != nil { + return err + } + // Serialize `Padding1` param: + err = encoder.Encode(obj.Padding1) + 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) + // Serialize `Padding2` param: + err = encoder.Encode(obj.Padding2) if err != nil { return err } - // Serialize `AccountStruct` param: - err = encoder.Encode(obj.AccountStruct) + // Serialize `OracleIds` param: + err = encoder.Encode(obj.OracleIds) if err != nil { return err } @@ -233,23 +234,20 @@ func (obj TestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) if err != nil { return err } - // Serialize `BigField` param (optional): - { - if obj.BigField == nil { - err = encoder.WriteBool(false) - if err != nil { - return err - } - } else { - err = encoder.WriteBool(true) - if err != nil { - return err - } - err = encoder.Encode(obj.BigField) - if err != nil { - return err - } - } + // Serialize `Padding3` param: + err = encoder.Encode(obj.Padding3) + if err != nil { + return err + } + // Serialize `BigField` param: + err = encoder.Encode(obj.BigField) + if err != nil { + return err + } + // Serialize `AccountStruct` param: + err = encoder.Encode(obj.AccountStruct) + if err != nil { + return err } // Serialize `NestedDynamicStruct` param: err = encoder.Encode(obj.NestedDynamicStruct) @@ -288,203 +286,33 @@ func (obj *TestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err err if err != nil { return err } - // Deserialize `Field` (optional): - { - ok, err := decoder.ReadBool() - if err != nil { - return err - } - if ok { - 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` (optional): - { - ok, err := decoder.ReadBool() - if err != nil { - return err - } - if ok { - err = decoder.Decode(&obj.BigField) - if err != nil { - return err - } - } - } - // Deserialize `NestedDynamicStruct`: - err = decoder.Decode(&obj.NestedDynamicStruct) + // Deserialize `Padding0`: + err = decoder.Decode(&obj.Padding0) if err != nil { return err } - // Deserialize `NestedStaticStruct`: - err = decoder.Decode(&obj.NestedStaticStruct) + // Deserialize `Field`: + err = decoder.Decode(&obj.Field) if err != nil { return err } - return nil -} - -type TestStructData struct { - Field *int32 `bin:"optional"` - OracleId [32]uint8 - OracleIds [32][32]uint8 - AccountStruct AccountStruct - Accounts [][]byte - DifferentField string - BigField *[32]uint8 `bin:"optional"` - NestedDynamicStruct MidLevelDynamicTestStruct - NestedStaticStruct MidLevelStaticTestStruct -} - -var TestStructDataDiscriminator = [8]byte{73, 128, 255, 29, 196, 76, 189, 62} - -func (obj TestStructData) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Write account discriminator: - err = encoder.WriteBytes(TestStructDataDiscriminator[:], false) + // Deserialize `Padding1`: + err = decoder.Decode(&obj.Padding1) if err != nil { return err } - // Serialize `Field` param (optional): - { - if obj.Field == nil { - err = encoder.WriteBool(false) - if err != nil { - return err - } - } else { - err = encoder.WriteBool(true) - if err != nil { - return err - } - 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 (optional): - { - if obj.BigField == nil { - err = encoder.WriteBool(false) - if err != nil { - return err - } - } else { - err = encoder.WriteBool(true) - if err != nil { - return err - } - 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 *TestStructData) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Read and check account discriminator: - { - discriminator, err := decoder.ReadTypeID() - if err != nil { - return err - } - if !discriminator.Equal(TestStructDataDiscriminator[:]) { - return fmt.Errorf( - "wrong discriminator: wanted %s, got %s", - "[73 128 255 29 196 76 189 62]", - fmt.Sprint(discriminator[:])) - } - } - // Deserialize `Field` (optional): - { - ok, err := decoder.ReadBool() - if err != nil { - return err - } - if ok { - 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) + // Deserialize `Padding2`: + err = decoder.Decode(&obj.Padding2) if err != nil { return err } - // Deserialize `AccountStruct`: - err = decoder.Decode(&obj.AccountStruct) + // Deserialize `OracleIds`: + err = decoder.Decode(&obj.OracleIds) if err != nil { return err } @@ -498,291 +326,28 @@ func (obj *TestStructData) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err if err != nil { return err } - // Deserialize `BigField` (optional): - { - ok, err := decoder.ReadBool() - if err != nil { - return err - } - if ok { - 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) + // Deserialize `Padding3`: + err = decoder.Decode(&obj.Padding3) if err != nil { return err } - return nil -} - -type AccountStruct struct { - Account []byte - AccountStr string -} - -var AccountStructDiscriminator = [8]byte{172, 54, 167, 253, 102, 242, 34, 94} - -func (obj AccountStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Write account discriminator: - err = encoder.WriteBytes(AccountStructDiscriminator[:], false) + // Deserialize `BigField`: + err = decoder.Decode(&obj.BigField) if err != nil { return err } - // 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) { - // Read and check account discriminator: - { - discriminator, err := decoder.ReadTypeID() - if err != nil { - return err - } - if !discriminator.Equal(AccountStructDiscriminator[:]) { - return fmt.Errorf( - "wrong discriminator: wanted %s, got %s", - "[172 54 167 253 102 242 34 94]", - fmt.Sprint(discriminator[:])) - } - } - // 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 MidLevelDynamicTestStruct struct { - FixedBytes [2]uint8 - Inner InnerDynamicTestStruct -} - -var MidLevelDynamicTestStructDiscriminator = [8]byte{202, 138, 18, 67, 76, 103, 164, 244} - -func (obj MidLevelDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Write account discriminator: - err = encoder.WriteBytes(MidLevelDynamicTestStructDiscriminator[:], false) - if err != nil { - return err - } - // 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 *MidLevelDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Read and check account discriminator: - { - discriminator, err := decoder.ReadTypeID() - if err != nil { - return err - } - if !discriminator.Equal(MidLevelDynamicTestStructDiscriminator[:]) { - return fmt.Errorf( - "wrong discriminator: wanted %s, got %s", - "[202 138 18 67 76 103 164 244]", - fmt.Sprint(discriminator[:])) - } - } - // 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 InnerDynamicTestStruct struct { - I int64 - S string -} - -var InnerDynamicTestStructDiscriminator = [8]byte{111, 35, 169, 64, 130, 38, 199, 25} - -func (obj InnerDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Write account discriminator: - err = encoder.WriteBytes(InnerDynamicTestStructDiscriminator[:], false) - if err != nil { - return err - } - // Serialize `I` param: - err = encoder.Encode(obj.I) - if err != nil { - return err - } - // Serialize `S` param: - err = encoder.Encode(obj.S) - if err != nil { - return err - } - return nil -} - -func (obj *InnerDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Read and check account discriminator: - { - discriminator, err := decoder.ReadTypeID() - if err != nil { - return err - } - if !discriminator.Equal(InnerDynamicTestStructDiscriminator[:]) { - return fmt.Errorf( - "wrong discriminator: wanted %s, got %s", - "[111 35 169 64 130 38 199 25]", - fmt.Sprint(discriminator[:])) - } - } - // Deserialize `I`: - err = decoder.Decode(&obj.I) - if err != nil { - return err - } - // Deserialize `S`: - err = decoder.Decode(&obj.S) - if err != nil { - return err - } - return nil -} - -type MidLevelStaticTestStruct struct { - FixedBytes [2]uint8 - Inner InnerStaticTestStruct -} - -var MidLevelStaticTestStructDiscriminator = [8]byte{60, 247, 21, 8, 67, 89, 45, 66} - -func (obj MidLevelStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Write account discriminator: - err = encoder.WriteBytes(MidLevelStaticTestStructDiscriminator[:], false) - if err != nil { - return err - } - // 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 *MidLevelStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Read and check account discriminator: - { - discriminator, err := decoder.ReadTypeID() - if err != nil { - return err - } - if !discriminator.Equal(MidLevelStaticTestStructDiscriminator[:]) { - return fmt.Errorf( - "wrong discriminator: wanted %s, got %s", - "[60 247 21 8 67 89 45 66]", - fmt.Sprint(discriminator[:])) - } - } - // 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 InnerStaticTestStruct struct { - I int64 - A []byte -} - -var InnerStaticTestStructDiscriminator = [8]byte{34, 251, 49, 124, 37, 221, 104, 73} - -func (obj InnerStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Write account discriminator: - err = encoder.WriteBytes(InnerStaticTestStructDiscriminator[:], false) - if err != nil { - return err - } - // Serialize `I` param: - err = encoder.Encode(obj.I) - if err != nil { - return err - } - // Serialize `A` param: - err = encoder.Encode(obj.A) + // Deserialize `AccountStruct`: + err = decoder.Decode(&obj.AccountStruct) if err != nil { return err } - return nil -} - -func (obj *InnerStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Read and check account discriminator: - { - discriminator, err := decoder.ReadTypeID() - if err != nil { - return err - } - if !discriminator.Equal(InnerStaticTestStructDiscriminator[:]) { - return fmt.Errorf( - "wrong discriminator: wanted %s, got %s", - "[34 251 49 124 37 221 104 73]", - fmt.Sprint(discriminator[:])) - } - } - // Deserialize `I`: - err = decoder.Decode(&obj.I) + // Deserialize `NestedDynamicStruct`: + err = decoder.Decode(&obj.NestedDynamicStruct) if err != nil { return err } - // Deserialize `A`: - err = decoder.Decode(&obj.A) + // Deserialize `NestedStaticStruct`: + err = decoder.Decode(&obj.NestedStaticStruct) if err != nil { return err } diff --git a/contracts/generated/contract_reader_interface/instructions.go b/contracts/generated/contract_reader_interface/instructions.go index a91bc2601..f4b7f964c 100644 --- a/contracts/generated/contract_reader_interface/instructions.go +++ b/contracts/generated/contract_reader_interface/instructions.go @@ -32,7 +32,7 @@ var ( Instruction_InitializeLookupTable = ag_binary.TypeID([8]byte{149, 120, 10, 249, 212, 185, 177, 216}) - Instruction_StoreTestStruct = ag_binary.TypeID([8]byte{170, 96, 68, 47, 193, 198, 80, 69}) + Instruction_Store = ag_binary.TypeID([8]byte{220, 28, 207, 235, 0, 234, 193, 246}) ) // InstructionIDToName returns the name of the instruction given its ID. @@ -42,8 +42,8 @@ func InstructionIDToName(id ag_binary.TypeID) string { return "Initialize" case Instruction_InitializeLookupTable: return "InitializeLookupTable" - case Instruction_StoreTestStruct: - return "StoreTestStruct" + case Instruction_Store: + return "Store" default: return "" } @@ -71,7 +71,7 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( "initialize_lookup_table", (*InitializeLookupTable)(nil), }, { - "store_test_struct", (*StoreTestStruct)(nil), + "store", (*Store)(nil), }, }, ) diff --git a/contracts/generated/contract_reader_interface/types.go b/contracts/generated/contract_reader_interface/types.go index afece0551..62c7e96d7 100644 --- a/contracts/generated/contract_reader_interface/types.go +++ b/contracts/generated/contract_reader_interface/types.go @@ -1,3 +1,338 @@ // Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. package contract_reader_interface + +import ( + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" +) + +type TestStructData struct { + Field int32 + Padding0 [4]uint8 + OracleId uint8 + Padding1 [15]uint8 + OracleIds [32]uint8 + Accounts [2][32]uint8 + DifferentField [32]uint8 + Padding2 [8]uint8 + BigField ag_binary.Int128 + AccountStruct AccountStruct + NestedDynamicStruct MidLevelDynamicTestStruct + NestedStaticStruct MidLevelStaticTestStruct +} + +func (obj TestStructData) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Field` param: + err = encoder.Encode(obj.Field) + if err != nil { + return err + } + // Serialize `Padding0` param: + err = encoder.Encode(obj.Padding0) + if err != nil { + return err + } + // Serialize `OracleId` param: + err = encoder.Encode(obj.OracleId) + if err != nil { + return err + } + // Serialize `Padding1` param: + err = encoder.Encode(obj.Padding1) + if err != nil { + return err + } + // Serialize `OracleIds` param: + err = encoder.Encode(obj.OracleIds) + 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 `Padding2` param: + err = encoder.Encode(obj.Padding2) + if err != nil { + return err + } + // Serialize `BigField` param: + err = encoder.Encode(obj.BigField) + if err != nil { + return err + } + // Serialize `AccountStruct` param: + err = encoder.Encode(obj.AccountStruct) + 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 *TestStructData) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Field`: + err = decoder.Decode(&obj.Field) + if err != nil { + return err + } + // Deserialize `Padding0`: + err = decoder.Decode(&obj.Padding0) + if err != nil { + return err + } + // Deserialize `OracleId`: + err = decoder.Decode(&obj.OracleId) + if err != nil { + return err + } + // Deserialize `Padding1`: + err = decoder.Decode(&obj.Padding1) + if err != nil { + return err + } + // Deserialize `OracleIds`: + err = decoder.Decode(&obj.OracleIds) + 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 `Padding2`: + err = decoder.Decode(&obj.Padding2) + if err != nil { + return err + } + // Deserialize `BigField`: + err = decoder.Decode(&obj.BigField) + if err != nil { + return err + } + // Deserialize `AccountStruct`: + err = decoder.Decode(&obj.AccountStruct) + 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 MidLevelDynamicTestStruct struct { + FixedBytes [2]uint8 + Padding [6]uint8 + Inner InnerDynamicTestStruct +} + +func (obj MidLevelDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Padding` param: + err = encoder.Encode(obj.Padding) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *MidLevelDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Padding`: + err = decoder.Decode(&obj.Padding) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} + +type InnerDynamicTestStruct struct { + I int64 + S [32]uint8 +} + +func (obj InnerDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `I` param: + err = encoder.Encode(obj.I) + if err != nil { + return err + } + // Serialize `S` param: + err = encoder.Encode(obj.S) + if err != nil { + return err + } + return nil +} + +func (obj *InnerDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `I`: + err = decoder.Decode(&obj.I) + if err != nil { + return err + } + // Deserialize `S`: + err = decoder.Decode(&obj.S) + if err != nil { + return err + } + return nil +} + +type MidLevelStaticTestStruct struct { + FixedBytes [2]uint8 + Padding [6]uint8 + Inner InnerStaticTestStruct +} + +func (obj MidLevelStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Padding` param: + err = encoder.Encode(obj.Padding) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *MidLevelStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Padding`: + err = decoder.Decode(&obj.Padding) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} + +type InnerStaticTestStruct struct { + I int64 + A ag_solanago.PublicKey +} + +func (obj InnerStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `I` param: + err = encoder.Encode(obj.I) + if err != nil { + return err + } + // Serialize `A` param: + err = encoder.Encode(obj.A) + if err != nil { + return err + } + return nil +} + +func (obj *InnerStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `I`: + err = decoder.Decode(&obj.I) + if err != nil { + return err + } + // Deserialize `A`: + err = decoder.Decode(&obj.A) + if err != nil { + return err + } + return nil +} diff --git a/contracts/programs/contract-reader-interface/Cargo.toml b/contracts/programs/contract-reader-interface/Cargo.toml index f422a9f15..c4e9ee925 100644 --- a/contracts/programs/contract-reader-interface/Cargo.toml +++ b/contracts/programs/contract-reader-interface/Cargo.toml @@ -16,4 +16,6 @@ cpi = ["no-entrypoint"] default = [] [dependencies] -anchor-lang = "0.29.0" +solana-program = "1.17.25" # pin solana to 1.17 +anchor-lang = { version = "0.29.0", features = ["init-if-needed"] } +bytemuck = "1.7" diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 9b45c2613..60c002eb5 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -9,7 +9,6 @@ pub mod contract_reader_interface { pub fn initialize(ctx: Context, test_idx: u64, value: u64) -> Result<()> { let account = &mut ctx.accounts.data; - account.u64_value = value; account.u64_slice = [3, 4].to_vec(); account.idx = test_idx; @@ -31,28 +30,28 @@ pub mod contract_reader_interface { Ok(()) } - pub fn store_test_struct( - ctx: Context, - test_idx: u64, - data: TestStructData, - ) -> Result<()> { - let test_struct_account = &mut ctx.accounts.test_struct; + pub fn store( + ctx: Context, + test_idx: u64, + data: TestStructData, + ) -> Result<()> { + let test_struct_account = &mut ctx.accounts.test_struct.load_init()?; - test_struct_account.idx = test_idx; - test_struct_account.bump = ctx.bumps.test_struct; + test_struct_account.idx = test_idx; + test_struct_account.bump = ctx.bumps.test_struct; - test_struct_account.field = data.field; - test_struct_account.oracle_id = data.oracle_id; - test_struct_account.oracle_ids = data.oracle_ids; - test_struct_account.account_struct = data.account_struct; - test_struct_account.accounts = data.accounts; - test_struct_account.different_field = data.different_field; - test_struct_account.big_field = data.big_field; - test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct; - test_struct_account.nested_static_struct = data.nested_static_struct; + test_struct_account.field = data.field; + test_struct_account.oracle_id = data.oracle_id; + test_struct_account.oracle_ids = data.oracle_ids; + test_struct_account.accounts = data.accounts; + test_struct_account.different_field = data.different_field; + test_struct_account.big_field = data.big_field; + test_struct_account.account_struct = data.account_struct.clone(); + test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct.clone(); + test_struct_account.nested_static_struct = data.nested_static_struct.clone(); - Ok(()) - } + Ok(()) + } } #[derive(Accounts)] @@ -100,17 +99,13 @@ pub struct StoreTestStruct<'info> { pub signer: Signer<'info>, #[account( - init, + init_if_needed, payer = signer, - // Add extra buffer for variable fields - space = 8 + size_of::() + 400, - seeds = [ - b"test-struct", - test_idx.to_le_bytes().as_ref() - ], + space = size_of::() + 8, + seeds=[b"struct_data".as_ref(), test_idx.to_le_bytes().as_ref()], bump )] - pub test_struct: Account<'info, TestStruct>, + pub test_struct: AccountLoader<'info, TestStruct>, pub system_program: Program<'info, System>, } @@ -131,62 +126,78 @@ pub struct DataAccount { pub u64_slice: Vec, } -#[account] +#[account(zero_copy)] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct TestStruct { pub idx: u64, pub bump: u8, + _padding0: [u8; 7], + pub field: i32, + _padding1: [u8; 4], + pub oracle_id: u8, + _padding2: [u8; 15], + pub oracle_ids: [u8; 32], + pub accounts: [[u8;32]; 2], + pub different_field: [u8; 32], // hiding field since string does not play well with zero copy + _padding3: [u8; 8], + pub big_field: i128, - pub field: Option, - pub oracle_id: [u8; 32], - pub oracle_ids: [[u8; 32]; 32], pub account_struct: AccountStruct, - pub accounts: Vec>, - pub different_field: String, - pub big_field: Option<[u8; 32]>, - pub nested_dynamic_struct: MidLevelDynamicTestStruct, pub nested_static_struct: MidLevelStaticTestStruct, } -#[account] +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct TestStructData { - pub field: Option, - pub oracle_id: [u8; 32], - pub oracle_ids: [[u8; 32]; 32], + pub field: i32, + _padding0: [u8; 4], + pub oracle_id: u8, + _padding1: [u8; 15], + pub oracle_ids: [u8; 32], + pub accounts: [[u8;32]; 2], + pub different_field: [u8; 32], + _padding2: [u8; 8], + pub big_field: i128, + pub account_struct: AccountStruct, - pub accounts: Vec>, - pub different_field: String, - pub big_field: Option<[u8; 32]>, pub nested_dynamic_struct: MidLevelDynamicTestStruct, pub nested_static_struct: MidLevelStaticTestStruct, } -#[account] +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct AccountStruct { - pub account: Vec, - pub account_str: String, + pub account: Pubkey, + pub account_str: Pubkey, } -#[account] +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct MidLevelDynamicTestStruct { pub fixed_bytes: [u8; 2], + pub _padding: [u8; 6], // explicit padding to avoid uninitialized bytes for zero_copy pub inner: InnerDynamicTestStruct, } -#[account] +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct InnerDynamicTestStruct { pub i: i64, - pub s: String, + pub s: [u8; 32], } -#[account] +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct MidLevelStaticTestStruct { pub fixed_bytes: [u8; 2], + pub _padding: [u8; 6], // explicit padding to avoid uninitialized bytes for zero_copy pub inner: InnerStaticTestStruct, } -#[account] +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct InnerStaticTestStruct { pub i: i64, - pub a: Vec, + pub a: Pubkey, } diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 2593a3890..40f72be5e 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -7,6 +7,7 @@ import ( "context" "encoding/binary" "encoding/json" + "fmt" "os" "path/filepath" "sync" @@ -17,6 +18,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/gagliardetto/solana-go/rpc/ws" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -29,6 +31,7 @@ import ( . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/values" contractprimary "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" contractsecondary "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface_secondary" @@ -44,6 +47,14 @@ import ( solanautils "github.com/smartcontractkit/chainlink-solana/pkg/solana/utils" ) +const ( + SolanaContractReaderGetLatestValueAsValuesDotValue = "Gets the latest value as a values.Value for Solana" + SolanaContractReaderGetLatestValue = "Gets the latest value for Solana" + SolanaContractReaderBatchGetLatestValue = "BatchGetLatestValues works for Solana" + SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder = "BatchGetLatestValues supports same read with different params and results retain order from request for Solana" + SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts = "BatchGetLatestValues supports same read with different params and results retain order from request even with multiple contracts for Solana" +) + func TestChainComponents(t *testing.T) { t.Parallel() helper := &helper{} @@ -69,26 +80,32 @@ func TestChainComponents(t *testing.T) { func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { it.DisableTests([]string{ + // solana is a no-op on confidence level + ContractReaderGetLatestValueBasedOnConfidenceLevel, + // disabling tests that required Solana specific logic. Covered in the Solana specific tests + ContractReaderGetLatestValue, + ContractReaderGetLatestValueAsValuesDotValue, + ContractReaderBatchGetLatestValue, + ContractReaderBatchGetLatestValueWithModifiersOwnMapstructureOverride, + ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, + ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, + // disable failing test ContractReaderGetLatestValueFromMultipleContractsNamesSameFunction, ContractReaderBatchGetLatestValueMultipleContractNamesSameFunction, - // disable tests that set values - ContractReaderGetLatestValueBasedOnConfidenceLevel, - // disable anything returning a struct or requiring input params for now - ContractReaderGetLatestValueAsValuesDotValue, - ContractReaderGetLatestValue, - ContractReaderGetLatestValueWithModifiersUsingOwnMapstrctureOverrides, + ContractReaderBatchGetLatestValueSetsErrorsProperly, + // disable failing tests requiring solana specific implementation + SolanaContractReaderGetLatestValue, + SolanaContractReaderGetLatestValueAsValuesDotValue, + SolanaContractReaderBatchGetLatestValue, + SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, + SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, + // events not yet supported ContractReaderGetLatestValueGetsLatestForEvent, ContractReaderGetLatestValueBasedOnConfidenceLevelForEvent, ContractReaderGetLatestValueReturnsNotFoundWhenNotTriggeredForEvent, ContractReaderGetLatestValueWithFilteringForEvent, - // disable anything in batch relating to input params or structs for now - ContractReaderBatchGetLatestValue, - ContractReaderBatchGetLatestValueWithModifiersOwnMapstructureOverride, - ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, - ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, - ContractReaderBatchGetLatestValueSetsErrorsProperly, // query key not implemented yet ContractReaderQueryKeyNotFound, ContractReaderQueryKeyReturnsData, @@ -117,7 +134,236 @@ func RunChainComponentsInLoopSolanaTests[T WrappedTestingT[T]](t T, it ChainComp func RunContractReaderSolanaTests[T WrappedTestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) { RunContractReaderInterfaceTests(t, it, false, true) - var testCases []Testcase[T] + testCases := []Testcase[T] { + { + Name: SolanaContractReaderGetLatestValue, + Test: func(t T) { + cr := it.GetContractReader(t) + cw := it.GetContractWriter(t) + contracts := it.GetBindings(t) + ctx := tests.Context(t) + firstItem := CreateTestStruct(0, it) + testIdx := it.testContext[t.Name()] + + args1 := StoreStructArgs{ + TestIdx: testIdx, + Data: firstItem, + } + _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args1, contracts[0], types.Unconfirmed) + + secondItem := CreateTestStruct(1, it) + args2 := StoreStructArgs{ + TestIdx: testIdx, + Data: secondItem, + } + _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args2, contracts[0], types.Unconfirmed) + + bound := BindingsByName(contracts, AnyContractName)[0] // minimum of one bound contract expected, otherwise panics + + require.NoError(t, cr.Bind(ctx, contracts)) + + actual := &TestStruct{} + params := &LatestParams{I: 1} + require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, actual)) + assert.Equal(t, &firstItem, actual) + + params.I = 2 + actual = &TestStruct{} + require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, actual)) + assert.Equal(t, &secondItem, actual) + }, + }, + { + Name: SolanaContractReaderGetLatestValueAsValuesDotValue, + Test: func(t T) { + cr := it.GetContractReader(t) + cw := it.GetContractWriter(t) + contracts := it.GetBindings(t) + ctx := tests.Context(t) + firstItem := CreateTestStruct(0, it) + testIdx := it.testContext[t.Name()] + args1 := StoreStructArgs{ + TestIdx: testIdx, + Data: firstItem, + } + _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args1, contracts[0], types.Unconfirmed) + + secondItem := CreateTestStruct(1, it) + args2 := StoreStructArgs{ + TestIdx: testIdx, + Data: secondItem, + } + _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args2, contracts[0], types.Unconfirmed) + + bound := BindingsByName(contracts, AnyContractName)[0] // minimum of one bound contract expected, otherwise panics + + require.NoError(t, cr.Bind(ctx, contracts)) + + params := &LatestParams{I: 1} + var value values.Value + + err := cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, &value) + require.NoError(t, err) + + actual := TestStruct{} + err = value.UnwrapTo(&actual) + require.NoError(t, err) + assert.Equal(t, &firstItem, &actual) + + params = &LatestParams{I: 2} + err = cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, &value) + require.NoError(t, err) + + actual = TestStruct{} + err = value.UnwrapTo(&actual) + require.NoError(t, err) + assert.Equal(t, &secondItem, &actual) + }, + }, + { + Name: SolanaContractReaderBatchGetLatestValue, + Test: func(t T) { + cr := it.GetContractReader(t) + cw := it.GetContractWriter(t) + bindings := it.GetBindings(t) + // setup test data + firstItem := CreateTestStruct(1, it) + testIdx := it.testContext[t.Name()] + args := StoreStructArgs{ + TestIdx: testIdx, + Data: firstItem, + } + bound := BindingsByName(bindings, AnyContractName)[0] + + batchCallEntry := make(BatchCallEntry) + batchCallEntry[bound] = ContractBatchEntry{{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args}} + batchContractWrite(t, it, cw, bindings, batchCallEntry) + + // setup call data + params, actual := &LatestParams{I: 1}, &TestStruct{} + batchGetLatestValueRequest := make(types.BatchGetLatestValuesRequest) + batchGetLatestValueRequest[bound] = []types.BatchRead{ + { + ReadName: MethodTakingLatestParamsReturningTestStruct, + Params: params, + ReturnVal: actual, + }, + } + + ctx := tests.Context(t) + + require.NoError(t, cr.Bind(ctx, bindings)) + result, err := cr.BatchGetLatestValues(ctx, batchGetLatestValueRequest) + require.NoError(t, err) + + anyContractBatch := result[bound] + returnValue, err := anyContractBatch[0].GetResult() + assert.NoError(t, err) + assert.Contains(t, anyContractBatch[0].ReadName, MethodTakingLatestParamsReturningTestStruct) + assert.Equal(t, &firstItem, returnValue) + }, + }, + { + Name: SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, + Test: func(t T) { + cr := it.GetContractReader(t) + cw := it.GetContractWriter(t) + bindings := it.GetBindings(t) + batchCallEntry := make(BatchCallEntry) + batchGetLatestValueRequest := make(types.BatchGetLatestValuesRequest) + bound := BindingsByName(bindings, AnyContractName)[0] + testIdx := it.testContext[t.Name()] + + for i := 0; i < 10; i++ { + // setup test data + ts := CreateTestStruct(i, it) + args := StoreStructArgs{ + TestIdx: testIdx, + Data: ts, + } + batchCallEntry[bound] = append(batchCallEntry[bound], ReadEntry{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args}) + // setup call data + batchGetLatestValueRequest[bound] = append( + batchGetLatestValueRequest[bound], + types.BatchRead{ReadName: MethodTakingLatestParamsReturningTestStruct, Params: &LatestParams{I: 1 + i}, ReturnVal: &TestStruct{}}, + ) + } + batchContractWrite(t, it, cw, bindings, batchCallEntry) + + ctx := tests.Context(t) + require.NoError(t, cr.Bind(ctx, bindings)) + + result, err := cr.BatchGetLatestValues(ctx, batchGetLatestValueRequest) + require.NoError(t, err) + + for i := 0; i < 10; i++ { + resultAnyContract, testDataAnyContract := result[bound], batchCallEntry[bound] + returnValue, err := resultAnyContract[i].GetResult() + assert.NoError(t, err) + assert.Contains(t, resultAnyContract[i].ReadName, MethodTakingLatestParamsReturningTestStruct) + assert.Equal(t, testDataAnyContract[i].ReturnValue, returnValue) + } + }, + }, + { + Name: SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, + Test: func(t T) { + cr := it.GetContractReader(t) + cw := it.GetContractWriter(t) + bindings := it.GetBindings(t) + batchCallEntry := make(BatchCallEntry) + batchGetLatestValueRequest := make(types.BatchGetLatestValuesRequest) + bound1 := BindingsByName(bindings, AnyContractName)[0] + bound2 := BindingsByName(bindings, AnySecondContractName)[0] + testIdx := it.testContext[t.Name()] + + for i := 0; i < 10; i++ { + // setup test data + ts1, ts2 := CreateTestStruct(i, it), CreateTestStruct(i+10, it) + args1 := StoreStructArgs{ + TestIdx: testIdx, + Data: ts1, + } + args2 := StoreStructArgs{ + TestIdx: testIdx, + Data: ts2, + } + batchCallEntry[bound1] = append(batchCallEntry[bound1], ReadEntry{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args1}) + batchCallEntry[bound2] = append(batchCallEntry[bound2], ReadEntry{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args2}) + // setup call data + batchGetLatestValueRequest[bound1] = append(batchGetLatestValueRequest[bound1], types.BatchRead{ReadName: MethodTakingLatestParamsReturningTestStruct, Params: &LatestParams{I: 1 + i}, ReturnVal: &TestStruct{}}) + batchGetLatestValueRequest[bound2] = append(batchGetLatestValueRequest[bound2], types.BatchRead{ReadName: MethodTakingLatestParamsReturningTestStruct, Params: &LatestParams{I: 1 + i}, ReturnVal: &TestStruct{}}) + } + batchContractWrite(t, it, cw, bindings, batchCallEntry) + + ctx := tests.Context(t) + require.NoError(t, cr.Bind(ctx, bindings)) + + result, err := cr.BatchGetLatestValues(ctx, batchGetLatestValueRequest) + require.NoError(t, err) + + for idx := 0; idx < 10; idx++ { + fmt.Printf("expected: %+v\n", batchCallEntry[bound1][idx].ReturnValue) + if val, err := result[bound1][idx].GetResult(); err == nil { + fmt.Printf("result: %+v\n", val) + } + } + + for i := 0; i < 10; i++ { + testDataAnyContract, testDataAnySecondContract := batchCallEntry[bound1], batchCallEntry[bound2] + resultAnyContract, resultAnySecondContract := result[bound1], result[bound2] + returnValueAnyContract, errAnyContract := resultAnyContract[i].GetResult() + returnValueAnySecondContract, errAnySecondContract := resultAnySecondContract[i].GetResult() + assert.NoError(t, errAnyContract) + assert.NoError(t, errAnySecondContract) + assert.Contains(t, resultAnyContract[i].ReadName, MethodTakingLatestParamsReturningTestStruct) + assert.Contains(t, resultAnySecondContract[i].ReadName, MethodTakingLatestParamsReturningTestStruct) + assert.Equal(t, testDataAnyContract[i].ReturnValue, returnValueAnyContract) + assert.Equal(t, testDataAnySecondContract[i].ReturnValue, returnValueAnySecondContract) + } + }, + }, + } RunTests(t, it, testCases) } @@ -137,7 +383,7 @@ type SolanaChainComponentsInterfaceTesterHelper[T WrappedTestingT[T]] interface Logger(t T) logger.Logger GetPrimaryIDL(t T) []byte GetSecondaryIDL(t T) []byte - CreateAccount(t T, it SolanaChainComponentsInterfaceTester[T], contractName string, value uint64) solana.PublicKey + CreateAccount(t T, it SolanaChainComponentsInterfaceTester[T], contractName string, value uint64, testStruct TestStruct) solana.PublicKey TXM() *txm.TxManager SolanaClient() *client.Client } @@ -166,11 +412,15 @@ func (it *SolanaChainComponentsInterfaceTester[T]) Name() string { } func (it *SolanaChainComponentsInterfaceTester[T]) GetAccountBytes(i int) []byte { - return nil + pubKeyBytes := make([]byte, 32) + binary.LittleEndian.PutUint64(pubKeyBytes, uint64(i)) + return solana.PublicKeyFromBytes(pubKeyBytes).Bytes() } func (it *SolanaChainComponentsInterfaceTester[T]) GetAccountString(i int) string { - return "" + pubKeyBytes := make([]byte, 32) + binary.LittleEndian.PutUint64(pubKeyBytes, uint64(i)) + return solana.PublicKeyFromBytes(pubKeyBytes).String() } func (it *SolanaChainComponentsInterfaceTester[T]) GetContractReader(t T) types.ContractReader { @@ -211,9 +461,10 @@ func (it *SolanaChainComponentsInterfaceTester[T]) getTestIdx(name string) uint6 func (it *SolanaChainComponentsInterfaceTester[T]) GetBindings(t T) []types.BoundContract { // Create a new account with fresh state for each test + testStruct := CreateTestStruct(0, it) return []types.BoundContract{ - {Name: AnyContractName, Address: it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument).String()}, - {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument).String()}, + {Name: AnyContractName, Address: it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument, testStruct).String()}, + // {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument, testStruct).String()}, } } @@ -348,7 +599,7 @@ func (h *helper) GetJSONEncodedIDL(t *testing.T, fileName string) []byte { return bts } -func (h *helper) CreateAccount(t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], contractName string, value uint64) solana.PublicKey { +func (h *helper) CreateAccount(t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], contractName string, value uint64, testStruct TestStruct) solana.PublicKey { t.Helper() var programID solana.PublicKey @@ -359,7 +610,7 @@ func (h *helper) CreateAccount(t *testing.T, it SolanaChainComponentsInterfaceTe programID = h.secondaryProgramID } - h.runInitialize(t, it, contractName, programID, value) + h.runInitialize(t, it, contractName, programID, value, testStruct) return programID } @@ -368,12 +619,18 @@ type InitializeArgs struct { Value uint64 } +type StoreStructArgs struct { + TestIdx uint64 + Data TestStruct +} + func (h *helper) runInitialize( t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], contractName string, programID solana.PublicKey, value uint64, + testStruct TestStruct, ) { t.Helper() @@ -387,18 +644,26 @@ func (h *helper) runInitialize( return } - args := InitializeArgs{ + initArgs := InitializeArgs{ TestIdx: testIdx, Value: value, } + SubmitTransactionToCW(t, &it, cw, "initialize", initArgs, types.BoundContract{Name: contractName, Address: programID.String()}, types.Finalized) - SubmitTransactionToCW(t, &it, cw, "initialize", args, types.BoundContract{Name: contractName, Address: programID.String()}, types.Finalized) + storeStructArgs := StoreStructArgs{ + TestIdx: testIdx, + Data: testStruct, + } + SubmitTransactionToCW(t, &it, cw, MethodSettingStruct, storeStructArgs, types.BoundContract{Name: contractName, Address: programID.String()}, types.Finalized) } func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T) config.ContractReader { idx := it.getTestIdx(t.Name()) - pdaPrefix := []byte("data") - pdaPrefix = binary.LittleEndian.AppendUint64(pdaPrefix, idx) + pdaDataPrefix := []byte("data") + pdaDataPrefix = binary.LittleEndian.AppendUint64(pdaDataPrefix, idx) + pdaStructDataPrefix := []byte("struct_data") + pdaStructDataPrefix = binary.LittleEndian.AppendUint64(pdaStructDataPrefix, idx) + testStruct := CreateTestStruct(0, it) return config.ContractReader{ Namespaces: map[string]config.ChainContractReader{ AnyContractName: { @@ -408,7 +673,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T ChainSpecificName: "DataAccount", ReadType: config.Account, PDADefiniton: codec.PDATypeDef{ - Prefix: pdaPrefix, + Prefix: pdaDataPrefix, }, OutputModifications: commoncodec.ModifiersConfig{ &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, @@ -416,8 +681,9 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T }, MethodReturningUint64Slice: { ChainSpecificName: "DataAccount", + ReadType: config.Account, PDADefiniton: codec.PDATypeDef{ - Prefix: pdaPrefix, + Prefix: pdaDataPrefix, }, OutputModifications: commoncodec.ModifiersConfig{ &commoncodec.PropertyExtractorConfig{FieldName: "U64Slice"}, @@ -425,13 +691,59 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T }, MethodSettingUint64: { ChainSpecificName: "DataAccount", + ReadType: config.Account, PDADefiniton: codec.PDATypeDef{ - Prefix: pdaPrefix, + Prefix: pdaDataPrefix, }, OutputModifications: commoncodec.ModifiersConfig{ &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, }, }, + MethodReturningSeenStruct: { + ChainSpecificName: "TestStruct", + ReadType: config.Account, + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaStructDataPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.HardCodeModifierConfig{ + OnChainValues: map[string]any{ + "DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), + }, + OffChainValues: map[string]any{ + "ExtraField": AnyExtraValue, + "DifferentField": testStruct.DifferentField, + "NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, + }, + }, + &commoncodec.AddressBytesToStringModifierConfig{ + Fields: []string{"AccountStruct.AccountStr"}, + }, + }, + }, + MethodTakingLatestParamsReturningTestStruct: { + ChainSpecificName: "TestStruct", + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaStructDataPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.HardCodeModifierConfig{ + OnChainValues: map[string]any{ + "DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), + }, + OffChainValues: map[string]any{ + "ExtraField": AnyExtraValue, + "DifferentField": testStruct.DifferentField, + "NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, + }, + }, + &commoncodec.AddressBytesToStringModifierConfig{ + Fields: []string{"AccountStruct.AccountStr"}, + }, + }, + }, }, }, AnySecondContractName: { @@ -440,7 +752,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T MethodReturningUint64: { ChainSpecificName: "Data", PDADefiniton: codec.PDATypeDef{ - Prefix: pdaPrefix, + Prefix: pdaDataPrefix, }, OutputModifications: commoncodec.ModifiersConfig{ &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, @@ -456,6 +768,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T idx := it.getTestIdx(t.Name()) testIdx := binary.LittleEndian.AppendUint64([]byte{}, idx) fromAddress := solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String() + testStruct := CreateTestStruct(0, it) return chainwriter.ChainWriterConfig{ Programs: map[string]chainwriter.ProgramConfig{ AnyContractName: { @@ -476,7 +789,6 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T chainwriter.PDALookups{ Name: "Account", PublicKey: chainwriter.AccountConstant{ - Name: "ProgramID", Address: primaryProgramPubKey, }, Seeds: []chainwriter.Seed{ @@ -489,8 +801,61 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T chainwriter.AccountConstant{ Name: "SystemProgram", Address: solana.SystemProgramID.String(), + IsWritable: false, IsSigner: false, + }, + }, + DebugIDLocation: "", + }, + MethodSettingStruct: { + FromAddress: fromAddress, + InputModifications: []commoncodec.ModifierConfig{ + &commoncodec.AddressBytesToStringModifierConfig{ + Fields: []string{"Data.AccountStruct.AccountStr"}, + }, + &commoncodec.HardCodeModifierConfig{ + OnChainValues: map[string]any{ + "Data.Padding0": []byte{}, + "Data.Padding1": []byte{}, + "Data.Padding2": []byte{}, + "Data.NestedDynamicStruct.Padding": []byte{}, + "Data.NestedStaticStruct.Padding": []byte{}, + "Data.DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "Data.NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), + }, + OffChainValues: map[string]any{ + "Data.DifferentField": testStruct.DifferentField, + "Data.NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, + }, + }, + }, + ChainSpecificName: "store", + LookupTables: chainwriter.LookupTables{}, + Accounts: []chainwriter.Lookup{ + chainwriter.AccountConstant{ + Name: "Signer", + Address: fromAddress, + IsSigner: true, + IsWritable: true, + }, + chainwriter.PDALookups{ + Name: "Account", + PublicKey: chainwriter.AccountConstant{ + Name: "ProgramID", + Address: primaryProgramPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("struct_data")}, + {Static: testIdx}, + }, + IsWritable: true, + IsSigner: false, + }, + chainwriter.AccountConstant{ + Name: "SystemProgram", + Address: solana.SystemProgramID.String(), IsWritable: false, + IsSigner: false, }, }, DebugIDLocation: "", @@ -526,10 +891,10 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IsSigner: false, }, chainwriter.AccountConstant{ - Name: "SystemProgram", + Name: "SystemAccount", Address: solana.SystemProgramID.String(), - IsSigner: false, IsWritable: false, + IsSigner: false, }, }, DebugIDLocation: "", @@ -550,6 +915,26 @@ func mustUnmarshalIDL[T WrappedTestingT[T]](t T, rawIDL string) codec.IDL { return idl } +// Copied from chainlink-common since this method is not public: https://github.com/smartcontractkit/chainlink-common/blob/aea9294a7d555844336a92c9ffe41219dfb26c68/pkg/types/interfacetests/utils.go#L88 +func batchContractWrite[T TestingT[T]](t T, tester ChainComponentsInterfaceTester[T], cw types.ContractWriter, boundContracts []types.BoundContract, batchCallEntry BatchCallEntry) { + nameToAddress := make(map[string]string) + for _, bc := range boundContracts { + nameToAddress[bc.Name] = bc.Address + } + + // For each contract in the batch call entry, submit the read entries to the chain + for contract, contractBatch := range batchCallEntry { + require.Contains(t, nameToAddress, contract.Name) + for _, readEntry := range contractBatch { + val, isOk := readEntry.ReturnValue.(*TestStruct) + if !isOk { + require.Fail(t, "expected *TestStruct for contract: %s read: %s, but received %T", contract.Name, readEntry.Name, readEntry.ReturnValue) + } + SubmitTransactionToCW(t, tester, cw, MethodSettingStruct, val, types.BoundContract{Name: contract.Name, Address: nameToAddress[contract.Name]}, types.Unconfirmed) + } + } +} + const ( primaryProgramPubKey = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" secondaryProgramPubKey = "9SFyk8NmGYh5D612mJwUYhguCRY9cFgaS2vksrigepjf" diff --git a/pkg/solana/codec/byte_string_modifier.go b/pkg/solana/codec/byte_string_modifier.go index f33080ed4..89a620e09 100644 --- a/pkg/solana/codec/byte_string_modifier.go +++ b/pkg/solana/codec/byte_string_modifier.go @@ -27,9 +27,9 @@ func (s SolanaAddressModifier) DecodeAddress(str string) ([]byte, error) { return nil, fmt.Errorf("%w: failed to decode Base58 address: %s", commontypes.ErrInvalidType, err) } - if pubkey.IsZero() { - return nil, fmt.Errorf("%w: zero-value address", commontypes.ErrInvalidType) - } + // if pubkey.IsZero() { + // return nil, fmt.Errorf("%w: zero-value address", commontypes.ErrInvalidType) + // } if !pubkey.IsOnCurve() { return nil, fmt.Errorf("%w: address %q with length of %d is not on the ed25519 curve", commontypes.ErrInvalidType, str, len(str)) From 4ae6d68735c6fa3ba42da94f503ed80394a8279a Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 00:10:53 -0600 Subject: [PATCH 07/15] Added store test struct method to secondary contract --- contracts/Cargo.lock | 2 + .../Store.go | 188 ++++++++++ .../Store_test.go | 32 ++ .../accounts.go | 196 ++++++++++ .../instructions.go | 7 + .../types.go | 335 ++++++++++++++++++ .../Cargo.toml | 4 +- .../src/lib.rs | 117 ++++++ .../relayinterface/chain_components_test.go | 55 ++- 9 files changed, 934 insertions(+), 2 deletions(-) create mode 100644 contracts/generated/contract_reader_interface_secondary/Store.go create mode 100644 contracts/generated/contract_reader_interface_secondary/Store_test.go diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 01c246ad8..5a8abfaef 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -733,6 +733,8 @@ name = "contract-reader-interface-secondary" version = "0.1.0" dependencies = [ "anchor-lang", + "bytemuck", + "solana-program", ] [[package]] diff --git a/contracts/generated/contract_reader_interface_secondary/Store.go b/contracts/generated/contract_reader_interface_secondary/Store.go new file mode 100644 index 000000000..2c026c92d --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/Store.go @@ -0,0 +1,188 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary + +import ( + "errors" + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +// Store is the `store` instruction. +type Store struct { + TestIdx *uint64 + Data *TestStructData + + // [0] = [WRITE, SIGNER] signer + // + // [1] = [WRITE] testStruct + // + // [2] = [] systemProgram + ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` +} + +// NewStoreInstructionBuilder creates a new `Store` instruction builder. +func NewStoreInstructionBuilder() *Store { + nd := &Store{ + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), + } + return nd +} + +// SetTestIdx sets the "testIdx" parameter. +func (inst *Store) SetTestIdx(testIdx uint64) *Store { + inst.TestIdx = &testIdx + return inst +} + +// SetData sets the "data" parameter. +func (inst *Store) SetData(data TestStructData) *Store { + inst.Data = &data + return inst +} + +// SetSignerAccount sets the "signer" account. +func (inst *Store) SetSignerAccount(signer ag_solanago.PublicKey) *Store { + inst.AccountMetaSlice[0] = ag_solanago.Meta(signer).WRITE().SIGNER() + return inst +} + +// GetSignerAccount gets the "signer" account. +func (inst *Store) GetSignerAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[0] +} + +// SetTestStructAccount sets the "testStruct" account. +func (inst *Store) SetTestStructAccount(testStruct ag_solanago.PublicKey) *Store { + inst.AccountMetaSlice[1] = ag_solanago.Meta(testStruct).WRITE() + return inst +} + +// GetTestStructAccount gets the "testStruct" account. +func (inst *Store) GetTestStructAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + +// SetSystemProgramAccount sets the "systemProgram" account. +func (inst *Store) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *Store { + inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) + return inst +} + +// GetSystemProgramAccount gets the "systemProgram" account. +func (inst *Store) GetSystemProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +func (inst Store) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: Instruction_Store, + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst Store) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *Store) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.TestIdx == nil { + return errors.New("TestIdx parameter is not set") + } + if inst.Data == nil { + return errors.New("Data parameter is not set") + } + } + + // Check whether all (required) accounts are set: + { + if inst.AccountMetaSlice[0] == nil { + return errors.New("accounts.Signer is not set") + } + if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.TestStruct is not set") + } + if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.SystemProgram is not set") + } + } + return nil +} + +func (inst *Store) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("Store")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("TestIdx", *inst.TestIdx)) + paramsBranch.Child(ag_format.Param(" Data", *inst.Data)) + }) + + // Accounts of the instruction: + instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" signer", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta(" testStruct", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) + }) + }) + }) +} + +func (obj Store) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `TestIdx` param: + err = encoder.Encode(obj.TestIdx) + if err != nil { + return err + } + // Serialize `Data` param: + err = encoder.Encode(obj.Data) + if err != nil { + return err + } + return nil +} +func (obj *Store) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `TestIdx`: + err = decoder.Decode(&obj.TestIdx) + if err != nil { + return err + } + // Deserialize `Data`: + err = decoder.Decode(&obj.Data) + if err != nil { + return err + } + return nil +} + +// NewStoreInstruction declares a new Store instruction with the provided parameters and accounts. +func NewStoreInstruction( + // Parameters: + testIdx uint64, + data TestStructData, + // Accounts: + signer ag_solanago.PublicKey, + testStruct ag_solanago.PublicKey, + systemProgram ag_solanago.PublicKey) *Store { + return NewStoreInstructionBuilder(). + SetTestIdx(testIdx). + SetData(data). + SetSignerAccount(signer). + SetTestStructAccount(testStruct). + SetSystemProgramAccount(systemProgram) +} diff --git a/contracts/generated/contract_reader_interface_secondary/Store_test.go b/contracts/generated/contract_reader_interface_secondary/Store_test.go new file mode 100644 index 000000000..9b728e9c6 --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/Store_test.go @@ -0,0 +1,32 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package contract_reader_interface_secondary + +import ( + "bytes" + ag_gofuzz "github.com/gagliardetto/gofuzz" + ag_require "github.com/stretchr/testify/require" + "strconv" + "testing" +) + +func TestEncodeDecode_Store(t *testing.T) { + fu := ag_gofuzz.New().NilChance(0) + for i := 0; i < 1; i++ { + t.Run("Store"+strconv.Itoa(i), func(t *testing.T) { + { + params := new(Store) + fu.Fuzz(params) + params.AccountMetaSlice = nil + buf := new(bytes.Buffer) + err := encodeT(*params, buf) + ag_require.NoError(t, err) + got := new(Store) + err = decodeT(got, buf.Bytes()) + got.AccountMetaSlice = nil + ag_require.NoError(t, err) + ag_require.Equal(t, params, got) + } + }) + } +} diff --git a/contracts/generated/contract_reader_interface_secondary/accounts.go b/contracts/generated/contract_reader_interface_secondary/accounts.go index 2ddebc9d9..78b89fdd0 100644 --- a/contracts/generated/contract_reader_interface_secondary/accounts.go +++ b/contracts/generated/contract_reader_interface_secondary/accounts.go @@ -70,3 +70,199 @@ func (obj *Data) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { } return nil } + +type TestStruct struct { + Idx uint64 + Bump uint8 + Padding0 [7]uint8 + Field int32 + Padding1 [4]uint8 + OracleId uint8 + Padding2 [15]uint8 + OracleIds [32]uint8 + Accounts [2][32]uint8 + DifferentField [32]uint8 + Padding3 [8]uint8 + BigField ag_binary.Int128 + AccountStruct AccountStruct + NestedDynamicStruct MidLevelDynamicTestStruct + NestedStaticStruct MidLevelStaticTestStruct +} + +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 `Idx` param: + err = encoder.Encode(obj.Idx) + if err != nil { + return err + } + // Serialize `Bump` param: + err = encoder.Encode(obj.Bump) + if err != nil { + return err + } + // Serialize `Padding0` param: + err = encoder.Encode(obj.Padding0) + if err != nil { + return err + } + // Serialize `Field` param: + err = encoder.Encode(obj.Field) + if err != nil { + return err + } + // Serialize `Padding1` param: + err = encoder.Encode(obj.Padding1) + if err != nil { + return err + } + // Serialize `OracleId` param: + err = encoder.Encode(obj.OracleId) + if err != nil { + return err + } + // Serialize `Padding2` param: + err = encoder.Encode(obj.Padding2) + if err != nil { + return err + } + // Serialize `OracleIds` param: + err = encoder.Encode(obj.OracleIds) + 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 `Padding3` param: + err = encoder.Encode(obj.Padding3) + if err != nil { + return err + } + // Serialize `BigField` param: + err = encoder.Encode(obj.BigField) + if err != nil { + return err + } + // Serialize `AccountStruct` param: + err = encoder.Encode(obj.AccountStruct) + 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 `Idx`: + err = decoder.Decode(&obj.Idx) + if err != nil { + return err + } + // Deserialize `Bump`: + err = decoder.Decode(&obj.Bump) + if err != nil { + return err + } + // Deserialize `Padding0`: + err = decoder.Decode(&obj.Padding0) + if err != nil { + return err + } + // Deserialize `Field`: + err = decoder.Decode(&obj.Field) + if err != nil { + return err + } + // Deserialize `Padding1`: + err = decoder.Decode(&obj.Padding1) + if err != nil { + return err + } + // Deserialize `OracleId`: + err = decoder.Decode(&obj.OracleId) + if err != nil { + return err + } + // Deserialize `Padding2`: + err = decoder.Decode(&obj.Padding2) + if err != nil { + return err + } + // Deserialize `OracleIds`: + err = decoder.Decode(&obj.OracleIds) + 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 `Padding3`: + err = decoder.Decode(&obj.Padding3) + if err != nil { + return err + } + // Deserialize `BigField`: + err = decoder.Decode(&obj.BigField) + if err != nil { + return err + } + // Deserialize `AccountStruct`: + err = decoder.Decode(&obj.AccountStruct) + 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/contracts/generated/contract_reader_interface_secondary/instructions.go b/contracts/generated/contract_reader_interface_secondary/instructions.go index 9398b7a0f..457c2556d 100644 --- a/contracts/generated/contract_reader_interface_secondary/instructions.go +++ b/contracts/generated/contract_reader_interface_secondary/instructions.go @@ -29,6 +29,8 @@ func init() { var ( Instruction_Initialize = ag_binary.TypeID([8]byte{175, 175, 109, 31, 13, 152, 155, 237}) + + Instruction_Store = ag_binary.TypeID([8]byte{220, 28, 207, 235, 0, 234, 193, 246}) ) // InstructionIDToName returns the name of the instruction given its ID. @@ -36,6 +38,8 @@ func InstructionIDToName(id ag_binary.TypeID) string { switch id { case Instruction_Initialize: return "Initialize" + case Instruction_Store: + return "Store" default: return "" } @@ -59,6 +63,9 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( { "initialize", (*Initialize)(nil), }, + { + "store", (*Store)(nil), + }, }, ) diff --git a/contracts/generated/contract_reader_interface_secondary/types.go b/contracts/generated/contract_reader_interface_secondary/types.go index 1a669df33..17195e965 100644 --- a/contracts/generated/contract_reader_interface_secondary/types.go +++ b/contracts/generated/contract_reader_interface_secondary/types.go @@ -1,3 +1,338 @@ // Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. package contract_reader_interface_secondary + +import ( + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" +) + +type TestStructData struct { + Field int32 + Padding0 [4]uint8 + OracleId uint8 + Padding1 [15]uint8 + OracleIds [32]uint8 + Accounts [2][32]uint8 + DifferentField [32]uint8 + Padding2 [8]uint8 + BigField ag_binary.Int128 + AccountStruct AccountStruct + NestedDynamicStruct MidLevelDynamicTestStruct + NestedStaticStruct MidLevelStaticTestStruct +} + +func (obj TestStructData) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Field` param: + err = encoder.Encode(obj.Field) + if err != nil { + return err + } + // Serialize `Padding0` param: + err = encoder.Encode(obj.Padding0) + if err != nil { + return err + } + // Serialize `OracleId` param: + err = encoder.Encode(obj.OracleId) + if err != nil { + return err + } + // Serialize `Padding1` param: + err = encoder.Encode(obj.Padding1) + if err != nil { + return err + } + // Serialize `OracleIds` param: + err = encoder.Encode(obj.OracleIds) + 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 `Padding2` param: + err = encoder.Encode(obj.Padding2) + if err != nil { + return err + } + // Serialize `BigField` param: + err = encoder.Encode(obj.BigField) + if err != nil { + return err + } + // Serialize `AccountStruct` param: + err = encoder.Encode(obj.AccountStruct) + 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 *TestStructData) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Field`: + err = decoder.Decode(&obj.Field) + if err != nil { + return err + } + // Deserialize `Padding0`: + err = decoder.Decode(&obj.Padding0) + if err != nil { + return err + } + // Deserialize `OracleId`: + err = decoder.Decode(&obj.OracleId) + if err != nil { + return err + } + // Deserialize `Padding1`: + err = decoder.Decode(&obj.Padding1) + if err != nil { + return err + } + // Deserialize `OracleIds`: + err = decoder.Decode(&obj.OracleIds) + 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 `Padding2`: + err = decoder.Decode(&obj.Padding2) + if err != nil { + return err + } + // Deserialize `BigField`: + err = decoder.Decode(&obj.BigField) + if err != nil { + return err + } + // Deserialize `AccountStruct`: + err = decoder.Decode(&obj.AccountStruct) + 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 MidLevelDynamicTestStruct struct { + FixedBytes [2]uint8 + Padding [6]uint8 + Inner InnerDynamicTestStruct +} + +func (obj MidLevelDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Padding` param: + err = encoder.Encode(obj.Padding) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *MidLevelDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Padding`: + err = decoder.Decode(&obj.Padding) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} + +type InnerDynamicTestStruct struct { + I int64 + S [32]uint8 +} + +func (obj InnerDynamicTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `I` param: + err = encoder.Encode(obj.I) + if err != nil { + return err + } + // Serialize `S` param: + err = encoder.Encode(obj.S) + if err != nil { + return err + } + return nil +} + +func (obj *InnerDynamicTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `I`: + err = decoder.Decode(&obj.I) + if err != nil { + return err + } + // Deserialize `S`: + err = decoder.Decode(&obj.S) + if err != nil { + return err + } + return nil +} + +type MidLevelStaticTestStruct struct { + FixedBytes [2]uint8 + Padding [6]uint8 + Inner InnerStaticTestStruct +} + +func (obj MidLevelStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `FixedBytes` param: + err = encoder.Encode(obj.FixedBytes) + if err != nil { + return err + } + // Serialize `Padding` param: + err = encoder.Encode(obj.Padding) + if err != nil { + return err + } + // Serialize `Inner` param: + err = encoder.Encode(obj.Inner) + if err != nil { + return err + } + return nil +} + +func (obj *MidLevelStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `FixedBytes`: + err = decoder.Decode(&obj.FixedBytes) + if err != nil { + return err + } + // Deserialize `Padding`: + err = decoder.Decode(&obj.Padding) + if err != nil { + return err + } + // Deserialize `Inner`: + err = decoder.Decode(&obj.Inner) + if err != nil { + return err + } + return nil +} + +type InnerStaticTestStruct struct { + I int64 + A ag_solanago.PublicKey +} + +func (obj InnerStaticTestStruct) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `I` param: + err = encoder.Encode(obj.I) + if err != nil { + return err + } + // Serialize `A` param: + err = encoder.Encode(obj.A) + if err != nil { + return err + } + return nil +} + +func (obj *InnerStaticTestStruct) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `I`: + err = decoder.Decode(&obj.I) + if err != nil { + return err + } + // Deserialize `A`: + err = decoder.Decode(&obj.A) + if err != nil { + return err + } + return nil +} diff --git a/contracts/programs/contract-reader-interface-secondary/Cargo.toml b/contracts/programs/contract-reader-interface-secondary/Cargo.toml index 494865214..7be4fbb4f 100644 --- a/contracts/programs/contract-reader-interface-secondary/Cargo.toml +++ b/contracts/programs/contract-reader-interface-secondary/Cargo.toml @@ -16,4 +16,6 @@ cpi = ["no-entrypoint"] default = [] [dependencies] -anchor-lang = "0.29.0" +solana-program = "1.17.25" # pin solana to 1.17 +anchor-lang = { version = "0.29.0", features = ["init-if-needed"] } +bytemuck = "1.7" diff --git a/contracts/programs/contract-reader-interface-secondary/src/lib.rs b/contracts/programs/contract-reader-interface-secondary/src/lib.rs index bc43f4df7..bc3b16561 100644 --- a/contracts/programs/contract-reader-interface-secondary/src/lib.rs +++ b/contracts/programs/contract-reader-interface-secondary/src/lib.rs @@ -14,6 +14,29 @@ pub mod contract_reader_interface_secondary { account.bump = ctx.bumps.data; Ok(()) } + + pub fn store( + ctx: Context, + test_idx: u64, + data: TestStructData, + ) -> Result<()> { + let test_struct_account = &mut ctx.accounts.test_struct.load_init()?; + + test_struct_account.idx = test_idx; + test_struct_account.bump = ctx.bumps.test_struct; + + test_struct_account.field = data.field; + test_struct_account.oracle_id = data.oracle_id; + test_struct_account.oracle_ids = data.oracle_ids; + test_struct_account.accounts = data.accounts; + test_struct_account.different_field = data.different_field; + test_struct_account.big_field = data.big_field; + test_struct_account.account_struct = data.account_struct.clone(); + test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct.clone(); + test_struct_account.nested_static_struct = data.nested_static_struct.clone(); + + Ok(()) + } } #[derive(Accounts)] @@ -34,9 +57,103 @@ pub struct Initialize<'info> { pub system_program: Program<'info, System>, } +#[derive(Accounts)] +#[instruction(test_idx: u64)] +pub struct StoreTestStruct<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + #[account( + init_if_needed, + payer = signer, + space = size_of::() + 8, + seeds=[b"struct_data".as_ref(), test_idx.to_le_bytes().as_ref()], + bump + )] + pub test_struct: AccountLoader<'info, TestStruct>, + + pub system_program: Program<'info, System>, +} + #[account] pub struct Data { pub u64_value: u64, pub idx: u64, pub bump: u8 } + +#[account(zero_copy)] +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct TestStruct { + pub idx: u64, + pub bump: u8, + _padding0: [u8; 7], + pub field: i32, + _padding1: [u8; 4], + pub oracle_id: u8, + _padding2: [u8; 15], + pub oracle_ids: [u8; 32], + pub accounts: [[u8;32]; 2], + pub different_field: [u8; 32], // hiding field since string does not play well with zero copy + _padding3: [u8; 8], + pub big_field: i128, + + pub account_struct: AccountStruct, + pub nested_dynamic_struct: MidLevelDynamicTestStruct, + pub nested_static_struct: MidLevelStaticTestStruct, +} + +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct TestStructData { + pub field: i32, + _padding0: [u8; 4], + pub oracle_id: u8, + _padding1: [u8; 15], + pub oracle_ids: [u8; 32], + pub accounts: [[u8;32]; 2], + pub different_field: [u8; 32], + _padding2: [u8; 8], + pub big_field: i128, + + pub account_struct: AccountStruct, + pub nested_dynamic_struct: MidLevelDynamicTestStruct, + pub nested_static_struct: MidLevelStaticTestStruct, +} + +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct AccountStruct { + pub account: Pubkey, + pub account_str: Pubkey, +} + +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct MidLevelDynamicTestStruct { + pub fixed_bytes: [u8; 2], + pub _padding: [u8; 6], // explicit padding to avoid uninitialized bytes for zero_copy + pub inner: InnerDynamicTestStruct, +} + +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct InnerDynamicTestStruct { + pub i: i64, + pub s: [u8; 32], +} + +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct MidLevelStaticTestStruct { + pub fixed_bytes: [u8; 2], + pub _padding: [u8; 6], // explicit padding to avoid uninitialized bytes for zero_copy + pub inner: InnerStaticTestStruct, +} + +#[zero_copy] +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct InnerStaticTestStruct { + pub i: i64, + pub a: Pubkey, +} diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 40f72be5e..3375ed843 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -464,7 +464,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) GetBindings(t T) []types.Boun testStruct := CreateTestStruct(0, it) return []types.BoundContract{ {Name: AnyContractName, Address: it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument, testStruct).String()}, - // {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument, testStruct).String()}, + {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument, testStruct).String()}, } } @@ -899,6 +899,59 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T }, DebugIDLocation: "", }, + MethodSettingStruct: { + FromAddress: fromAddress, + InputModifications: []commoncodec.ModifierConfig{ + &commoncodec.AddressBytesToStringModifierConfig{ + Fields: []string{"Data.AccountStruct.AccountStr"}, + }, + &commoncodec.HardCodeModifierConfig{ + OnChainValues: map[string]any{ + "Data.Padding0": []byte{}, + "Data.Padding1": []byte{}, + "Data.Padding2": []byte{}, + "Data.NestedDynamicStruct.Padding": []byte{}, + "Data.NestedStaticStruct.Padding": []byte{}, + "Data.DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "Data.NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), + }, + OffChainValues: map[string]any{ + "Data.DifferentField": testStruct.DifferentField, + "Data.NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, + }, + }, + }, + ChainSpecificName: "store", + LookupTables: chainwriter.LookupTables{}, + Accounts: []chainwriter.Lookup{ + chainwriter.AccountConstant{ + Name: "Signer", + Address: fromAddress, + IsSigner: true, + IsWritable: true, + }, + chainwriter.PDALookups{ + Name: "Account", + PublicKey: chainwriter.AccountConstant{ + Name: "ProgramID", + Address: secondaryProgramPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("struct_data")}, + {Static: testIdx}, + }, + IsWritable: true, + IsSigner: false, + }, + chainwriter.AccountConstant{ + Name: "SystemProgram", + Address: solana.SystemProgramID.String(), + IsWritable: false, + IsSigner: false, + }, + }, + DebugIDLocation: "", + }, }, }, }, From d2c3ab08908c6206d338aa9b90f627db9610b3f6 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 00:15:56 -0600 Subject: [PATCH 08/15] Enabled test struct batch interface test --- integration-tests/relayinterface/chain_components_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 3375ed843..92cf1b0e0 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -86,7 +86,6 @@ func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { ContractReaderGetLatestValue, ContractReaderGetLatestValueAsValuesDotValue, ContractReaderBatchGetLatestValue, - ContractReaderBatchGetLatestValueWithModifiersOwnMapstructureOverride, ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, From 794182a699f9472a3115dedc6172862171a07c55 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 00:30:55 -0600 Subject: [PATCH 09/15] Enabled multi contract interface tests --- integration-tests/relayinterface/chain_components_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 92cf1b0e0..1452ecd37 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -90,8 +90,6 @@ func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, // disable failing test - ContractReaderGetLatestValueFromMultipleContractsNamesSameFunction, - ContractReaderBatchGetLatestValueMultipleContractNamesSameFunction, ContractReaderBatchGetLatestValueSetsErrorsProperly, // disable failing tests requiring solana specific implementation SolanaContractReaderGetLatestValue, From 6539f289414cdab4c96935e3cc8d1ffd190cdb67 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 11:13:22 -0600 Subject: [PATCH 10/15] Removed failing Solana specific tests --- .../relayinterface/chain_components_test.go | 246 +----------------- 1 file changed, 3 insertions(+), 243 deletions(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 1452ecd37..749d77705 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -7,7 +7,6 @@ import ( "context" "encoding/binary" "encoding/json" - "fmt" "os" "path/filepath" "sync" @@ -18,7 +17,6 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/gagliardetto/solana-go/rpc/ws" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -31,7 +29,6 @@ import ( . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-common/pkg/values" contractprimary "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" contractsecondary "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface_secondary" @@ -82,22 +79,14 @@ func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { it.DisableTests([]string{ // solana is a no-op on confidence level ContractReaderGetLatestValueBasedOnConfidenceLevel, - // disabling tests that required Solana specific logic. Covered in the Solana specific tests + // disable failing tests + ContractReaderBatchGetLatestValueSetsErrorsProperly, ContractReaderGetLatestValue, ContractReaderGetLatestValueAsValuesDotValue, ContractReaderBatchGetLatestValue, ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, - // disable failing test - ContractReaderBatchGetLatestValueSetsErrorsProperly, - // disable failing tests requiring solana specific implementation - SolanaContractReaderGetLatestValue, - SolanaContractReaderGetLatestValueAsValuesDotValue, - SolanaContractReaderBatchGetLatestValue, - SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, - SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, - // events not yet supported ContractReaderGetLatestValueGetsLatestForEvent, ContractReaderGetLatestValueBasedOnConfidenceLevelForEvent, @@ -131,236 +120,7 @@ func RunChainComponentsInLoopSolanaTests[T WrappedTestingT[T]](t T, it ChainComp func RunContractReaderSolanaTests[T WrappedTestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) { RunContractReaderInterfaceTests(t, it, false, true) - testCases := []Testcase[T] { - { - Name: SolanaContractReaderGetLatestValue, - Test: func(t T) { - cr := it.GetContractReader(t) - cw := it.GetContractWriter(t) - contracts := it.GetBindings(t) - ctx := tests.Context(t) - firstItem := CreateTestStruct(0, it) - testIdx := it.testContext[t.Name()] - - args1 := StoreStructArgs{ - TestIdx: testIdx, - Data: firstItem, - } - _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args1, contracts[0], types.Unconfirmed) - - secondItem := CreateTestStruct(1, it) - args2 := StoreStructArgs{ - TestIdx: testIdx, - Data: secondItem, - } - _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args2, contracts[0], types.Unconfirmed) - - bound := BindingsByName(contracts, AnyContractName)[0] // minimum of one bound contract expected, otherwise panics - - require.NoError(t, cr.Bind(ctx, contracts)) - - actual := &TestStruct{} - params := &LatestParams{I: 1} - require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, actual)) - assert.Equal(t, &firstItem, actual) - - params.I = 2 - actual = &TestStruct{} - require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, actual)) - assert.Equal(t, &secondItem, actual) - }, - }, - { - Name: SolanaContractReaderGetLatestValueAsValuesDotValue, - Test: func(t T) { - cr := it.GetContractReader(t) - cw := it.GetContractWriter(t) - contracts := it.GetBindings(t) - ctx := tests.Context(t) - firstItem := CreateTestStruct(0, it) - testIdx := it.testContext[t.Name()] - args1 := StoreStructArgs{ - TestIdx: testIdx, - Data: firstItem, - } - _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args1, contracts[0], types.Unconfirmed) - - secondItem := CreateTestStruct(1, it) - args2 := StoreStructArgs{ - TestIdx: testIdx, - Data: secondItem, - } - _ = SubmitTransactionToCW(t, it, cw, MethodSettingStruct, args2, contracts[0], types.Unconfirmed) - - bound := BindingsByName(contracts, AnyContractName)[0] // minimum of one bound contract expected, otherwise panics - - require.NoError(t, cr.Bind(ctx, contracts)) - - params := &LatestParams{I: 1} - var value values.Value - - err := cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, &value) - require.NoError(t, err) - - actual := TestStruct{} - err = value.UnwrapTo(&actual) - require.NoError(t, err) - assert.Equal(t, &firstItem, &actual) - - params = &LatestParams{I: 2} - err = cr.GetLatestValue(ctx, bound.ReadIdentifier(MethodTakingLatestParamsReturningTestStruct), primitives.Unconfirmed, params, &value) - require.NoError(t, err) - - actual = TestStruct{} - err = value.UnwrapTo(&actual) - require.NoError(t, err) - assert.Equal(t, &secondItem, &actual) - }, - }, - { - Name: SolanaContractReaderBatchGetLatestValue, - Test: func(t T) { - cr := it.GetContractReader(t) - cw := it.GetContractWriter(t) - bindings := it.GetBindings(t) - // setup test data - firstItem := CreateTestStruct(1, it) - testIdx := it.testContext[t.Name()] - args := StoreStructArgs{ - TestIdx: testIdx, - Data: firstItem, - } - bound := BindingsByName(bindings, AnyContractName)[0] - - batchCallEntry := make(BatchCallEntry) - batchCallEntry[bound] = ContractBatchEntry{{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args}} - batchContractWrite(t, it, cw, bindings, batchCallEntry) - - // setup call data - params, actual := &LatestParams{I: 1}, &TestStruct{} - batchGetLatestValueRequest := make(types.BatchGetLatestValuesRequest) - batchGetLatestValueRequest[bound] = []types.BatchRead{ - { - ReadName: MethodTakingLatestParamsReturningTestStruct, - Params: params, - ReturnVal: actual, - }, - } - - ctx := tests.Context(t) - - require.NoError(t, cr.Bind(ctx, bindings)) - result, err := cr.BatchGetLatestValues(ctx, batchGetLatestValueRequest) - require.NoError(t, err) - - anyContractBatch := result[bound] - returnValue, err := anyContractBatch[0].GetResult() - assert.NoError(t, err) - assert.Contains(t, anyContractBatch[0].ReadName, MethodTakingLatestParamsReturningTestStruct) - assert.Equal(t, &firstItem, returnValue) - }, - }, - { - Name: SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, - Test: func(t T) { - cr := it.GetContractReader(t) - cw := it.GetContractWriter(t) - bindings := it.GetBindings(t) - batchCallEntry := make(BatchCallEntry) - batchGetLatestValueRequest := make(types.BatchGetLatestValuesRequest) - bound := BindingsByName(bindings, AnyContractName)[0] - testIdx := it.testContext[t.Name()] - - for i := 0; i < 10; i++ { - // setup test data - ts := CreateTestStruct(i, it) - args := StoreStructArgs{ - TestIdx: testIdx, - Data: ts, - } - batchCallEntry[bound] = append(batchCallEntry[bound], ReadEntry{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args}) - // setup call data - batchGetLatestValueRequest[bound] = append( - batchGetLatestValueRequest[bound], - types.BatchRead{ReadName: MethodTakingLatestParamsReturningTestStruct, Params: &LatestParams{I: 1 + i}, ReturnVal: &TestStruct{}}, - ) - } - batchContractWrite(t, it, cw, bindings, batchCallEntry) - - ctx := tests.Context(t) - require.NoError(t, cr.Bind(ctx, bindings)) - - result, err := cr.BatchGetLatestValues(ctx, batchGetLatestValueRequest) - require.NoError(t, err) - - for i := 0; i < 10; i++ { - resultAnyContract, testDataAnyContract := result[bound], batchCallEntry[bound] - returnValue, err := resultAnyContract[i].GetResult() - assert.NoError(t, err) - assert.Contains(t, resultAnyContract[i].ReadName, MethodTakingLatestParamsReturningTestStruct) - assert.Equal(t, testDataAnyContract[i].ReturnValue, returnValue) - } - }, - }, - { - Name: SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, - Test: func(t T) { - cr := it.GetContractReader(t) - cw := it.GetContractWriter(t) - bindings := it.GetBindings(t) - batchCallEntry := make(BatchCallEntry) - batchGetLatestValueRequest := make(types.BatchGetLatestValuesRequest) - bound1 := BindingsByName(bindings, AnyContractName)[0] - bound2 := BindingsByName(bindings, AnySecondContractName)[0] - testIdx := it.testContext[t.Name()] - - for i := 0; i < 10; i++ { - // setup test data - ts1, ts2 := CreateTestStruct(i, it), CreateTestStruct(i+10, it) - args1 := StoreStructArgs{ - TestIdx: testIdx, - Data: ts1, - } - args2 := StoreStructArgs{ - TestIdx: testIdx, - Data: ts2, - } - batchCallEntry[bound1] = append(batchCallEntry[bound1], ReadEntry{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args1}) - batchCallEntry[bound2] = append(batchCallEntry[bound2], ReadEntry{Name: MethodTakingLatestParamsReturningTestStruct, ReturnValue: &args2}) - // setup call data - batchGetLatestValueRequest[bound1] = append(batchGetLatestValueRequest[bound1], types.BatchRead{ReadName: MethodTakingLatestParamsReturningTestStruct, Params: &LatestParams{I: 1 + i}, ReturnVal: &TestStruct{}}) - batchGetLatestValueRequest[bound2] = append(batchGetLatestValueRequest[bound2], types.BatchRead{ReadName: MethodTakingLatestParamsReturningTestStruct, Params: &LatestParams{I: 1 + i}, ReturnVal: &TestStruct{}}) - } - batchContractWrite(t, it, cw, bindings, batchCallEntry) - - ctx := tests.Context(t) - require.NoError(t, cr.Bind(ctx, bindings)) - - result, err := cr.BatchGetLatestValues(ctx, batchGetLatestValueRequest) - require.NoError(t, err) - - for idx := 0; idx < 10; idx++ { - fmt.Printf("expected: %+v\n", batchCallEntry[bound1][idx].ReturnValue) - if val, err := result[bound1][idx].GetResult(); err == nil { - fmt.Printf("result: %+v\n", val) - } - } - - for i := 0; i < 10; i++ { - testDataAnyContract, testDataAnySecondContract := batchCallEntry[bound1], batchCallEntry[bound2] - resultAnyContract, resultAnySecondContract := result[bound1], result[bound2] - returnValueAnyContract, errAnyContract := resultAnyContract[i].GetResult() - returnValueAnySecondContract, errAnySecondContract := resultAnySecondContract[i].GetResult() - assert.NoError(t, errAnyContract) - assert.NoError(t, errAnySecondContract) - assert.Contains(t, resultAnyContract[i].ReadName, MethodTakingLatestParamsReturningTestStruct) - assert.Contains(t, resultAnySecondContract[i].ReadName, MethodTakingLatestParamsReturningTestStruct) - assert.Equal(t, testDataAnyContract[i].ReturnValue, returnValueAnyContract) - assert.Equal(t, testDataAnySecondContract[i].ReturnValue, returnValueAnySecondContract) - } - }, - }, - } + var testCases []Testcase[T] RunTests(t, it, testCases) } From be4e9dc29590e82ce67b32a2d29096ebecccc3ca Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 11:14:43 -0600 Subject: [PATCH 11/15] Removed Solana byte string modifier zero-value check --- .../relayinterface/chain_components_test.go | 126 ++++++++---------- pkg/solana/codec/byte_string_modifier.go | 4 - 2 files changed, 53 insertions(+), 77 deletions(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 749d77705..c859c231c 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -269,7 +269,7 @@ func (h *helper) Init(t *testing.T) { h.sc = solanaClient - loader := solanautils.NewLoader[client.ReaderWriter](func(ctx context.Context) (client.ReaderWriter, error) { return solanaClient, nil}) + loader := solanautils.NewLoader[client.ReaderWriter](func(ctx context.Context) (client.ReaderWriter, error) { return solanaClient, nil }) mkey := keyMocks.NewSimpleKeystore(t) mkey.On("Sign", mock.Anything, privateKey.PublicKey().String(), mock.Anything).Return(func(_ context.Context, _ string, data []byte) []byte { sig, _ := privateKey.Sign(data) @@ -342,7 +342,7 @@ func (h *helper) GetSecondaryIDL(t *testing.T) []byte { func (h *helper) GetJSONEncodedIDL(t *testing.T, fileName string) []byte { t.Helper() - soPath := filepath.Join(utils.IDLDir, fileName) + soPath := filepath.Join(utils.IDLDir, fileName) _, err := os.Stat(soPath) if err != nil { @@ -378,7 +378,7 @@ type InitializeArgs struct { type StoreStructArgs struct { TestIdx uint64 - Data TestStruct + Data TestStruct } func (h *helper) runInitialize( @@ -393,7 +393,7 @@ func (h *helper) runInitialize( cw := it.GetContractWriter(t) - // Fetch test index from map + // Fetch test index from map it.testContextMu.RLock() defer it.testContextMu.RUnlock() testIdx, exists := it.testContext[t.Name()] @@ -403,13 +403,13 @@ func (h *helper) runInitialize( initArgs := InitializeArgs{ TestIdx: testIdx, - Value: value, + Value: value, } SubmitTransactionToCW(t, &it, cw, "initialize", initArgs, types.BoundContract{Name: contractName, Address: programID.String()}, types.Finalized) storeStructArgs := StoreStructArgs{ TestIdx: testIdx, - Data: testStruct, + Data: testStruct, } SubmitTransactionToCW(t, &it, cw, MethodSettingStruct, storeStructArgs, types.BoundContract{Name: contractName, Address: programID.String()}, types.Finalized) } @@ -465,12 +465,12 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T OutputModifications: commoncodec.ModifiersConfig{ &commoncodec.HardCodeModifierConfig{ OnChainValues: map[string]any{ - "DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), "NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), }, OffChainValues: map[string]any{ - "ExtraField": AnyExtraValue, - "DifferentField": testStruct.DifferentField, + "ExtraField": AnyExtraValue, + "DifferentField": testStruct.DifferentField, "NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, }, }, @@ -487,12 +487,12 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T OutputModifications: commoncodec.ModifiersConfig{ &commoncodec.HardCodeModifierConfig{ OnChainValues: map[string]any{ - "DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), "NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), }, OffChainValues: map[string]any{ - "ExtraField": AnyExtraValue, - "DifferentField": testStruct.DifferentField, + "ExtraField": AnyExtraValue, + "DifferentField": testStruct.DifferentField, "NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, }, }, @@ -538,9 +538,9 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T LookupTables: chainwriter.LookupTables{}, Accounts: []chainwriter.Lookup{ chainwriter.AccountConstant{ - Name: "Signer", - Address: fromAddress, - IsSigner: true, + Name: "Signer", + Address: fromAddress, + IsSigner: true, IsWritable: true, }, chainwriter.PDALookups{ @@ -556,43 +556,43 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IsSigner: false, }, chainwriter.AccountConstant{ - Name: "SystemProgram", - Address: solana.SystemProgramID.String(), + Name: "SystemProgram", + Address: solana.SystemProgramID.String(), IsWritable: false, - IsSigner: false, + IsSigner: false, }, }, DebugIDLocation: "", }, MethodSettingStruct: { - FromAddress: fromAddress, + FromAddress: fromAddress, InputModifications: []commoncodec.ModifierConfig{ &commoncodec.AddressBytesToStringModifierConfig{ Fields: []string{"Data.AccountStruct.AccountStr"}, }, &commoncodec.HardCodeModifierConfig{ OnChainValues: map[string]any{ - "Data.Padding0": []byte{}, - "Data.Padding1": []byte{}, - "Data.Padding2": []byte{}, + "Data.Padding0": []byte{}, + "Data.Padding1": []byte{}, + "Data.Padding2": []byte{}, "Data.NestedDynamicStruct.Padding": []byte{}, - "Data.NestedStaticStruct.Padding": []byte{}, - "Data.DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "Data.NestedStaticStruct.Padding": []byte{}, + "Data.DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), "Data.NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), }, OffChainValues: map[string]any{ - "Data.DifferentField": testStruct.DifferentField, + "Data.DifferentField": testStruct.DifferentField, "Data.NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, }, }, }, ChainSpecificName: "store", - LookupTables: chainwriter.LookupTables{}, + LookupTables: chainwriter.LookupTables{}, Accounts: []chainwriter.Lookup{ chainwriter.AccountConstant{ - Name: "Signer", - Address: fromAddress, - IsSigner: true, + Name: "Signer", + Address: fromAddress, + IsSigner: true, IsWritable: true, }, chainwriter.PDALookups{ @@ -609,10 +609,10 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IsSigner: false, }, chainwriter.AccountConstant{ - Name: "SystemProgram", - Address: solana.SystemProgramID.String(), + Name: "SystemProgram", + Address: solana.SystemProgramID.String(), IsWritable: false, - IsSigner: false, + IsSigner: false, }, }, DebugIDLocation: "", @@ -629,9 +629,9 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T LookupTables: chainwriter.LookupTables{}, Accounts: []chainwriter.Lookup{ chainwriter.AccountConstant{ - Name: "Signer", - Address: fromAddress, - IsSigner: true, + Name: "Signer", + Address: fromAddress, + IsSigner: true, IsWritable: true, }, chainwriter.PDALookups{ @@ -648,43 +648,43 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IsSigner: false, }, chainwriter.AccountConstant{ - Name: "SystemAccount", - Address: solana.SystemProgramID.String(), + Name: "SystemAccount", + Address: solana.SystemProgramID.String(), IsWritable: false, - IsSigner: false, + IsSigner: false, }, }, DebugIDLocation: "", }, MethodSettingStruct: { - FromAddress: fromAddress, + FromAddress: fromAddress, InputModifications: []commoncodec.ModifierConfig{ &commoncodec.AddressBytesToStringModifierConfig{ Fields: []string{"Data.AccountStruct.AccountStr"}, }, &commoncodec.HardCodeModifierConfig{ OnChainValues: map[string]any{ - "Data.Padding0": []byte{}, - "Data.Padding1": []byte{}, - "Data.Padding2": []byte{}, + "Data.Padding0": []byte{}, + "Data.Padding1": []byte{}, + "Data.Padding2": []byte{}, "Data.NestedDynamicStruct.Padding": []byte{}, - "Data.NestedStaticStruct.Padding": []byte{}, - "Data.DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), + "Data.NestedStaticStruct.Padding": []byte{}, + "Data.DifferentField": copy(make([]byte, 32), []byte(testStruct.DifferentField)), "Data.NestedDynamicStruct.Inner.S": copy(make([]byte, 32), []byte(testStruct.NestedDynamicStruct.Inner.S)), }, OffChainValues: map[string]any{ - "Data.DifferentField": testStruct.DifferentField, + "Data.DifferentField": testStruct.DifferentField, "Data.NestedDynamicStruct.Inner.S": testStruct.NestedDynamicStruct.Inner.S, }, }, }, ChainSpecificName: "store", - LookupTables: chainwriter.LookupTables{}, + LookupTables: chainwriter.LookupTables{}, Accounts: []chainwriter.Lookup{ chainwriter.AccountConstant{ - Name: "Signer", - Address: fromAddress, - IsSigner: true, + Name: "Signer", + Address: fromAddress, + IsSigner: true, IsWritable: true, }, chainwriter.PDALookups{ @@ -701,10 +701,10 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractWriterConfig(t T IsSigner: false, }, chainwriter.AccountConstant{ - Name: "SystemProgram", - Address: solana.SystemProgramID.String(), + Name: "SystemProgram", + Address: solana.SystemProgramID.String(), IsWritable: false, - IsSigner: false, + IsSigner: false, }, }, DebugIDLocation: "", @@ -725,27 +725,7 @@ func mustUnmarshalIDL[T WrappedTestingT[T]](t T, rawIDL string) codec.IDL { return idl } -// Copied from chainlink-common since this method is not public: https://github.com/smartcontractkit/chainlink-common/blob/aea9294a7d555844336a92c9ffe41219dfb26c68/pkg/types/interfacetests/utils.go#L88 -func batchContractWrite[T TestingT[T]](t T, tester ChainComponentsInterfaceTester[T], cw types.ContractWriter, boundContracts []types.BoundContract, batchCallEntry BatchCallEntry) { - nameToAddress := make(map[string]string) - for _, bc := range boundContracts { - nameToAddress[bc.Name] = bc.Address - } - - // For each contract in the batch call entry, submit the read entries to the chain - for contract, contractBatch := range batchCallEntry { - require.Contains(t, nameToAddress, contract.Name) - for _, readEntry := range contractBatch { - val, isOk := readEntry.ReturnValue.(*TestStruct) - if !isOk { - require.Fail(t, "expected *TestStruct for contract: %s read: %s, but received %T", contract.Name, readEntry.Name, readEntry.ReturnValue) - } - SubmitTransactionToCW(t, tester, cw, MethodSettingStruct, val, types.BoundContract{Name: contract.Name, Address: nameToAddress[contract.Name]}, types.Unconfirmed) - } - } -} - const ( - primaryProgramPubKey = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" + primaryProgramPubKey = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" secondaryProgramPubKey = "9SFyk8NmGYh5D612mJwUYhguCRY9cFgaS2vksrigepjf" ) diff --git a/pkg/solana/codec/byte_string_modifier.go b/pkg/solana/codec/byte_string_modifier.go index 89a620e09..8b93994a8 100644 --- a/pkg/solana/codec/byte_string_modifier.go +++ b/pkg/solana/codec/byte_string_modifier.go @@ -27,10 +27,6 @@ func (s SolanaAddressModifier) DecodeAddress(str string) ([]byte, error) { return nil, fmt.Errorf("%w: failed to decode Base58 address: %s", commontypes.ErrInvalidType, err) } - // if pubkey.IsZero() { - // return nil, fmt.Errorf("%w: zero-value address", commontypes.ErrInvalidType) - // } - if !pubkey.IsOnCurve() { return nil, fmt.Errorf("%w: address %q with length of %d is not on the ed25519 curve", commontypes.ErrInvalidType, str, len(str)) } From df42e5fda695ca49abf31c4efdb9c62a72412c53 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 11:41:10 -0600 Subject: [PATCH 12/15] Fixed rust linting --- .../src/lib.rs | 20 ++++++++----------- .../contract-reader-interface/src/lib.rs | 18 +++++++---------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/contracts/programs/contract-reader-interface-secondary/src/lib.rs b/contracts/programs/contract-reader-interface-secondary/src/lib.rs index bc3b16561..6ba47af70 100644 --- a/contracts/programs/contract-reader-interface-secondary/src/lib.rs +++ b/contracts/programs/contract-reader-interface-secondary/src/lib.rs @@ -15,16 +15,12 @@ pub mod contract_reader_interface_secondary { Ok(()) } - pub fn store( - ctx: Context, - test_idx: u64, - data: TestStructData, - ) -> Result<()> { + pub fn store(ctx: Context, test_idx: u64, data: TestStructData) -> Result<()> { let test_struct_account = &mut ctx.accounts.test_struct.load_init()?; - + test_struct_account.idx = test_idx; test_struct_account.bump = ctx.bumps.test_struct; - + test_struct_account.field = data.field; test_struct_account.oracle_id = data.oracle_id; test_struct_account.oracle_ids = data.oracle_ids; @@ -34,7 +30,7 @@ pub mod contract_reader_interface_secondary { test_struct_account.account_struct = data.account_struct.clone(); test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct.clone(); test_struct_account.nested_static_struct = data.nested_static_struct.clone(); - + Ok(()) } } @@ -63,7 +59,7 @@ pub struct StoreTestStruct<'info> { #[account(mut)] pub signer: Signer<'info>, - #[account( + #[account( init_if_needed, payer = signer, space = size_of::() + 8, @@ -79,7 +75,7 @@ pub struct StoreTestStruct<'info> { pub struct Data { pub u64_value: u64, pub idx: u64, - pub bump: u8 + pub bump: u8, } #[account(zero_copy)] @@ -93,7 +89,7 @@ pub struct TestStruct { pub oracle_id: u8, _padding2: [u8; 15], pub oracle_ids: [u8; 32], - pub accounts: [[u8;32]; 2], + pub accounts: [[u8; 32]; 2], pub different_field: [u8; 32], // hiding field since string does not play well with zero copy _padding3: [u8; 8], pub big_field: i128, @@ -111,7 +107,7 @@ pub struct TestStructData { pub oracle_id: u8, _padding1: [u8; 15], pub oracle_ids: [u8; 32], - pub accounts: [[u8;32]; 2], + pub accounts: [[u8; 32]; 2], pub different_field: [u8; 32], _padding2: [u8; 8], pub big_field: i128, diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 60c002eb5..435fe4ea4 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -30,16 +30,12 @@ pub mod contract_reader_interface { Ok(()) } - pub fn store( - ctx: Context, - test_idx: u64, - data: TestStructData, - ) -> Result<()> { + pub fn store(ctx: Context, test_idx: u64, data: TestStructData) -> Result<()> { let test_struct_account = &mut ctx.accounts.test_struct.load_init()?; - + test_struct_account.idx = test_idx; test_struct_account.bump = ctx.bumps.test_struct; - + test_struct_account.field = data.field; test_struct_account.oracle_id = data.oracle_id; test_struct_account.oracle_ids = data.oracle_ids; @@ -49,7 +45,7 @@ pub mod contract_reader_interface { test_struct_account.account_struct = data.account_struct.clone(); test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct.clone(); test_struct_account.nested_static_struct = data.nested_static_struct.clone(); - + Ok(()) } } @@ -98,7 +94,7 @@ pub struct StoreTestStruct<'info> { #[account(mut)] pub signer: Signer<'info>, - #[account( + #[account( init_if_needed, payer = signer, space = size_of::() + 8, @@ -137,7 +133,7 @@ pub struct TestStruct { pub oracle_id: u8, _padding2: [u8; 15], pub oracle_ids: [u8; 32], - pub accounts: [[u8;32]; 2], + pub accounts: [[u8; 32]; 2], pub different_field: [u8; 32], // hiding field since string does not play well with zero copy _padding3: [u8; 8], pub big_field: i128, @@ -155,7 +151,7 @@ pub struct TestStructData { pub oracle_id: u8, _padding1: [u8; 15], pub oracle_ids: [u8; 32], - pub accounts: [[u8;32]; 2], + pub accounts: [[u8; 32]; 2], pub different_field: [u8; 32], _padding2: [u8; 8], pub big_field: i128, From ea161171d38ad7d26faa192057a1f3c291406fec Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 12:02:26 -0600 Subject: [PATCH 13/15] Fixed Solana address modifier test --- pkg/solana/codec/byte_string_modifier_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/solana/codec/byte_string_modifier_test.go b/pkg/solana/codec/byte_string_modifier_test.go index e0be1ce2c..9483e4a5d 100644 --- a/pkg/solana/codec/byte_string_modifier_test.go +++ b/pkg/solana/codec/byte_string_modifier_test.go @@ -45,10 +45,10 @@ func TestSolanaAddressModifier(t *testing.T) { assert.Contains(t, err.Error(), commontypes.ErrInvalidType.Error()) }) - t.Run("DecodeAddress returns error for zero-value address", func(t *testing.T) { - _, err := modifier.DecodeAddress(solana.PublicKey{}.String()) - assert.Error(t, err) - assert.Contains(t, err.Error(), commontypes.ErrInvalidType.Error()) + t.Run("DecodeAddress decodes zero-value address", func(t *testing.T) { + decodedBytes, err := modifier.DecodeAddress(solana.PublicKey{}.String()) + require.NoError(t, err) + assert.Equal(t, solana.PublicKey{}.Bytes(), decodedBytes) }) t.Run("DecodeAddress returns error for address under 32 chars", func(t *testing.T) { From afc047e72ef57d219452378b3460a147d5957968 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 12:19:26 -0600 Subject: [PATCH 14/15] Fixed rust linting --- .../contract-reader-interface-secondary/src/lib.rs | 6 +++--- contracts/programs/contract-reader-interface/src/lib.rs | 6 +++--- integration-tests/relayinterface/chain_components_test.go | 8 -------- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/contracts/programs/contract-reader-interface-secondary/src/lib.rs b/contracts/programs/contract-reader-interface-secondary/src/lib.rs index 6ba47af70..590d75643 100644 --- a/contracts/programs/contract-reader-interface-secondary/src/lib.rs +++ b/contracts/programs/contract-reader-interface-secondary/src/lib.rs @@ -27,9 +27,9 @@ pub mod contract_reader_interface_secondary { test_struct_account.accounts = data.accounts; test_struct_account.different_field = data.different_field; test_struct_account.big_field = data.big_field; - test_struct_account.account_struct = data.account_struct.clone(); - test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct.clone(); - test_struct_account.nested_static_struct = data.nested_static_struct.clone(); + test_struct_account.account_struct = data.account_struct; + test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct; + test_struct_account.nested_static_struct = data.nested_static_struct; Ok(()) } diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 435fe4ea4..691e6b5b9 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -42,9 +42,9 @@ pub mod contract_reader_interface { test_struct_account.accounts = data.accounts; test_struct_account.different_field = data.different_field; test_struct_account.big_field = data.big_field; - test_struct_account.account_struct = data.account_struct.clone(); - test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct.clone(); - test_struct_account.nested_static_struct = data.nested_static_struct.clone(); + test_struct_account.account_struct = data.account_struct; + test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct; + test_struct_account.nested_static_struct = data.nested_static_struct; Ok(()) } diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index c859c231c..71eb0c43e 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -44,14 +44,6 @@ import ( solanautils "github.com/smartcontractkit/chainlink-solana/pkg/solana/utils" ) -const ( - SolanaContractReaderGetLatestValueAsValuesDotValue = "Gets the latest value as a values.Value for Solana" - SolanaContractReaderGetLatestValue = "Gets the latest value for Solana" - SolanaContractReaderBatchGetLatestValue = "BatchGetLatestValues works for Solana" - SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder = "BatchGetLatestValues supports same read with different params and results retain order from request for Solana" - SolanaContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts = "BatchGetLatestValues supports same read with different params and results retain order from request even with multiple contracts for Solana" -) - func TestChainComponents(t *testing.T) { t.Parallel() helper := &helper{} From b079914c22a0f81da3e155de6a7bc3a16b1e1c65 Mon Sep 17 00:00:00 2001 From: amit-momin Date: Fri, 7 Feb 2025 13:11:46 -0600 Subject: [PATCH 15/15] Fixed interface tests on loop --- .../programs/contract-reader-interface-secondary/src/lib.rs | 2 +- contracts/programs/contract-reader-interface/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/programs/contract-reader-interface-secondary/src/lib.rs b/contracts/programs/contract-reader-interface-secondary/src/lib.rs index 590d75643..b0bd69bbf 100644 --- a/contracts/programs/contract-reader-interface-secondary/src/lib.rs +++ b/contracts/programs/contract-reader-interface-secondary/src/lib.rs @@ -43,7 +43,7 @@ pub struct Initialize<'info> { // derived test PDA #[account( - init, + init_if_needed, payer = signer, space = size_of::() + 8, seeds=[b"data".as_ref(), test_idx.to_le_bytes().as_ref()], diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 691e6b5b9..8d7bc3dc0 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -58,7 +58,7 @@ pub struct Initialize<'info> { // derived test PDA #[account( - init, + init_if_needed, payer = signer, space = size_of::() + 8, seeds=[b"data".as_ref(), test_idx.to_le_bytes().as_ref()], @@ -72,7 +72,7 @@ pub struct Initialize<'info> { pub struct InitializeLookupTableData<'info> { /// PDA for LookupTableDataAccount, derived from seeds and created by the System Program #[account( - init, + init_if_needed, payer = admin, space = size_of::() + 8, seeds = [b"data"],