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..5a8abfaef 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -724,6 +724,17 @@ name = "contract-reader-interface" version = "0.1.0" dependencies = [ "anchor-lang", + "bytemuck", + "solana-program", +] + +[[package]] +name = "contract-reader-interface-secondary" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "bytemuck", + "solana-program", ] [[package]] diff --git a/contracts/generated/contract_reader_interface/Store.go b/contracts/generated/contract_reader_interface/Store.go new file mode 100644 index 000000000..76414519e --- /dev/null +++ b/contracts/generated/contract_reader_interface/Store.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" +) + +// 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/Store_test.go b/contracts/generated/contract_reader_interface/Store_test.go new file mode 100644 index 000000000..6a0f8e181 --- /dev/null +++ b/contracts/generated/contract_reader_interface/Store_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_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/accounts.go b/contracts/generated/contract_reader_interface/accounts.go index d02ccb874..3a7197749 100644 --- a/contracts/generated/contract_reader_interface/accounts.go +++ b/contracts/generated/contract_reader_interface/accounts.go @@ -157,3 +157,199 @@ func (obj *DataAccount) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err er } 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/instructions.go b/contracts/generated/contract_reader_interface/instructions.go index c86960a41..f4b7f964c 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_Store = ag_binary.TypeID([8]byte{220, 28, 207, 235, 0, 234, 193, 246}) ) // 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_Store: + return "Store" default: return "" } @@ -66,6 +70,9 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( { "initialize_lookup_table", (*InitializeLookupTable)(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/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/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 new file mode 100644 index 000000000..78b89fdd0 --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/accounts.go @@ -0,0 +1,268 @@ +// 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 +} + +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 new file mode 100644 index 000000000..457c2556d --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/instructions.go @@ -0,0 +1,124 @@ +// 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}) + + 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. +func InstructionIDToName(id ag_binary.TypeID) string { + switch id { + case Instruction_Initialize: + return "Initialize" + case Instruction_Store: + return "Store" + 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), + }, + { + "store", (*Store)(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..17195e965 --- /dev/null +++ b/contracts/generated/contract_reader_interface_secondary/types.go @@ -0,0 +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 new file mode 100644 index 000000000..7be4fbb4f --- /dev/null +++ b/contracts/programs/contract-reader-interface-secondary/Cargo.toml @@ -0,0 +1,21 @@ +[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] +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/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..b0bd69bbf --- /dev/null +++ b/contracts/programs/contract-reader-interface-secondary/src/lib.rs @@ -0,0 +1,155 @@ +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(()) + } + + 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; + test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct; + test_struct_account.nested_static_struct = data.nested_static_struct; + + Ok(()) + } +} + +#[derive(Accounts)] +#[instruction(test_idx: u64)] +pub struct Initialize<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + // derived test PDA + #[account( + init_if_needed, + 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>, +} + +#[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/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 51c06a33e..8d7bc3dc0 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; @@ -30,6 +29,25 @@ pub mod contract_reader_interface { 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; + test_struct_account.nested_dynamic_struct = data.nested_dynamic_struct; + test_struct_account.nested_static_struct = data.nested_static_struct; + + Ok(()) + } } #[derive(Accounts)] @@ -40,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()], @@ -54,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"], @@ -70,6 +88,24 @@ pub struct InitializeLookupTableData<'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 LookupTableDataAccount { pub version: u8, // Version of the data account @@ -85,3 +121,79 @@ pub struct DataAccount { pub u64_value: u64, pub u64_slice: Vec, } + +#[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 68102ef66..71eb0c43e 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" @@ -29,14 +30,14 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - contract "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" - + 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" "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" @@ -50,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) @@ -58,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) @@ -68,23 +69,21 @@ func TestChainComponents(t *testing.T) { func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) { it.DisableTests([]string{ - // disable tests that set values + // solana is a no-op on confidence level ContractReaderGetLatestValueBasedOnConfidenceLevel, - // disable anything returning a struct or requiring input params for now - ContractReaderGetLatestValueAsValuesDotValue, + // disable failing tests + ContractReaderBatchGetLatestValueSetsErrorsProperly, ContractReaderGetLatestValue, - ContractReaderGetLatestValueWithModifiersUsingOwnMapstrctureOverrides, + ContractReaderGetLatestValueAsValuesDotValue, + ContractReaderBatchGetLatestValue, + ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrder, + ContractReaderBatchGetLatestValueDifferentParamsResultsRetainOrderMultipleContracts, + // 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, @@ -100,17 +99,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] @@ -118,7 +117,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] @@ -126,96 +125,35 @@ 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 + GetPrimaryIDL(t T) []byte + GetSecondaryIDL(t T) []byte + CreateAccount(t T, it SolanaChainComponentsInterfaceTester[T], contractName string, value uint64, testStruct TestStruct) 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.ContractReaderService - 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 { @@ -223,48 +161,59 @@ 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 { - ctx := it.Helper.Context(t) - if it.cr != nil { - return it.cr - } - + contractReaderConfig := it.buildContractReaderConfig(t) var events chainreader.EventsReader svc, err := chainreader.NewContractReaderService( it.Helper.Logger(t), it.Helper.RPCClient(), - it.contractReaderConfig, + contractReaderConfig, events) 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 + testStruct := CreateTestStruct(0, it) 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, testStruct).String()}, + {Name: AnySecondContractName, Address: it.Helper.CreateAccount(t, *it, AnySecondContractName, AnyDifferentValueToReadWithoutAnArgument, testStruct).String()}, } } @@ -279,16 +228,16 @@ func (it *SolanaChainComponentsInterfaceTester[T]) GenerateBlocksTillConfidenceL } type helper struct { - programID solana.PublicKey - rpcURL string - wsURL string - rpcClient *rpc.Client - wsClient *ws.Client - idlBts []byte - nonce uint64 - nonceMu sync.Mutex - 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) { @@ -297,7 +246,7 @@ func (h *helper) Init(t *testing.T) { privateKey, err := solana.PrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]) require.NoError(t, err) - h.rpcURL, h.wsURL = utils.SetupTestValidatorWithAnchorPrograms(t, privateKey.PublicKey().String(), []string{"contract-reader-interface"}) + h.rpcURL, h.wsURL = utils.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) @@ -312,7 +261,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) @@ -326,11 +275,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) + + secondaryPubkey, err := solana.PublicKeyFromBase58(secondaryProgramPubKey) require.NoError(t, err) + contractsecondary.SetProgramID(secondaryPubkey) - contract.SetProgramID(pubkey) - h.programID = pubkey + h.primaryProgramID = primaryPubkey + h.secondaryProgramID = secondaryPubkey } func (h *helper) RPCClient() *chainreader.RPCClientWrapper { @@ -353,14 +307,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.primaryIdlBts != nil { + return h.primaryIdlBts + } + + 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.idlBts != nil { - return h.idlBts + if h.secondaryIdlBts != nil { + return h.secondaryIdlBts } - soPath := filepath.Join(utils.IDLDir, "contract_reader_interface.json") + 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 { @@ -371,29 +345,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], 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() - // 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) + var programID solana.PublicKey + switch contractName { + case AnyContractName: + programID = h.primaryProgramID + case AnySecondContractName: + programID = h.secondaryProgramID + } - return pubKey + h.runInitialize(t, it, contractName, programID, value, testStruct) + return programID } type InitializeArgs struct { @@ -401,28 +368,346 @@ type InitializeArgs struct { Value uint64 } +type StoreStructArgs struct { + TestIdx uint64 + Data TestStruct +} + func (h *helper) runInitialize( t *testing.T, it SolanaChainComponentsInterfaceTester[*testing.T], - nonce uint64, + contractName string, + programID solana.PublicKey, value uint64, + testStruct TestStruct, ) { t.Helper() cw := it.GetContractWriter(t) - args := InitializeArgs{ - TestIdx: nonce * value, + // Fetch test index from map + it.testContextMu.RLock() + defer it.testContextMu.RUnlock() + testIdx, exists := it.testContext[t.Name()] + if !exists { + return + } + + initArgs := InitializeArgs{ + TestIdx: testIdx, Value: value, } + SubmitTransactionToCW(t, &it, cw, "initialize", initArgs, types.BoundContract{Name: contractName, Address: programID.String()}, types.Finalized) - buf := make([]byte, 8) - binary.LittleEndian.PutUint64(buf, nonce*value) + 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()) + 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: { + IDL: mustUnmarshalIDL(t, string(it.Helper.GetPrimaryIDL(t))), + Reads: map[string]config.ReadDefinition{ + MethodReturningUint64: { + ChainSpecificName: "DataAccount", + ReadType: config.Account, + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaDataPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, + }, + }, + MethodReturningUint64Slice: { + ChainSpecificName: "DataAccount", + ReadType: config.Account, + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaDataPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Slice"}, + }, + }, + MethodSettingUint64: { + ChainSpecificName: "DataAccount", + ReadType: config.Account, + PDADefiniton: codec.PDATypeDef{ + 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: { + IDL: mustUnmarshalIDL(t, string(it.Helper.GetSecondaryIDL(t))), + Reads: map[string]config.ReadDefinition{ + MethodReturningUint64: { + ChainSpecificName: "Data", + PDADefiniton: codec.PDATypeDef{ + Prefix: pdaDataPrefix, + }, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, + }, + }, + }, + }, + }, + } +} - 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) + fromAddress := solana.MustPrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]).PublicKey().String() + testStruct := CreateTestStruct(0, it) + return chainwriter.ChainWriterConfig{ + Programs: map[string]chainwriter.ProgramConfig{ + AnyContractName: { + IDL: string(it.Helper.GetPrimaryIDL(t)), + Methods: map[string]chainwriter.MethodConfig{ + "initialize": { + 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{ + Address: primaryProgramPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("data")}, + {Static: testIdx}, + }, + IsWritable: true, + IsSigner: false, + }, + 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: "", + }, + }, + }, + AnySecondContractName: { + IDL: string(it.Helper.GetSecondaryIDL(t)), + Methods: map[string]chainwriter.MethodConfig{ + "initialize": { + 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{ + Name: "ProgramID", + Address: secondaryProgramPubKey, + }, + Seeds: []chainwriter.Seed{ + {Static: []byte("data")}, + {Static: testIdx}, + }, + IsWritable: true, + IsSigner: false, + }, + chainwriter.AccountConstant{ + Name: "SystemAccount", + 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: 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: "", + }, + }, + }, + }, + } } -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) @@ -432,4 +717,7 @@ func mustUnmarshalIDL[T TestingT[T]](t T, rawIDL string) codec.IDL { return idl } -const programPubKey = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" +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..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)) } 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) {