diff --git a/tests/misc/Anchor.toml b/tests/misc/Anchor.toml index e4990e1ca4..d5eace4c72 100644 --- a/tests/misc/Anchor.toml +++ b/tests/misc/Anchor.toml @@ -7,6 +7,7 @@ init_if_needed = "BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2" lamports = "Lamports11111111111111111111111111111111111" misc = "3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh" misc_optional = "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG" +remaining_accounts = "RemainingAccounts11111111111111111111111111" [workspace] exclude = ["programs/shared"] diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml index 2461dc2684..366cc8d1c9 100644 --- a/tests/misc/programs/misc-optional/Cargo.toml +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -18,5 +18,5 @@ default = [] [dependencies] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } -spl-associated-token-account = "1.1.1" -bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"]} +spl-associated-token-account = { version = "1.1.1", features = ["no-entrypoint"] } +bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"] } diff --git a/tests/misc/programs/misc/Cargo.toml b/tests/misc/programs/misc/Cargo.toml index 530b77f38b..caa7507bac 100644 --- a/tests/misc/programs/misc/Cargo.toml +++ b/tests/misc/programs/misc/Cargo.toml @@ -18,5 +18,5 @@ default = [] [dependencies] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } -spl-associated-token-account = "1.1.1" -bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"]} +spl-associated-token-account = { version = "1.1.1", features = ["no-entrypoint"] } +bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"] } diff --git a/tests/misc/programs/remaining-accounts/Cargo.toml b/tests/misc/programs/remaining-accounts/Cargo.toml new file mode 100644 index 0000000000..19f2010d5f --- /dev/null +++ b/tests/misc/programs/remaining-accounts/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "remaining-accounts" +version = "0.1.0" +description = "Created with Anchor" +rust-version = "1.60" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "remaining_accounts" + +[features] +no-entrypoint = [] +no-idl = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } +anchor-spl = { path = "../../../../spl" } diff --git a/tests/misc/programs/remaining-accounts/Xargo.toml b/tests/misc/programs/remaining-accounts/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/tests/misc/programs/remaining-accounts/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/tests/misc/programs/remaining-accounts/src/account.rs b/tests/misc/programs/remaining-accounts/src/account.rs new file mode 100644 index 0000000000..7ab4eb298c --- /dev/null +++ b/tests/misc/programs/remaining-accounts/src/account.rs @@ -0,0 +1,11 @@ +use anchor_lang::prelude::*; + +#[account] +#[derive(InitSpace)] +pub struct Data { + pub someone: Pubkey, +} + +#[account] +#[derive(InitSpace)] +pub struct Another {} diff --git a/tests/misc/programs/remaining-accounts/src/context.rs b/tests/misc/programs/remaining-accounts/src/context.rs new file mode 100644 index 0000000000..a17f3ed610 --- /dev/null +++ b/tests/misc/programs/remaining-accounts/src/context.rs @@ -0,0 +1,26 @@ +use crate::account::*; +use anchor_lang::prelude::*; +use anchor_spl::token::Token; + +#[derive(Accounts)] +pub struct TestInit<'info> { + #[account(init, payer = payer, space = Data::INIT_SPACE + 8)] + pub data: Account<'info, Data>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestInitAnother<'info> { + #[account(init, payer = payer, space = Data::INIT_SPACE + 8)] + pub another: Account<'info, Another>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestRemainingAccounts<'info> { + pub token_program: Program<'info, Token>, +} diff --git a/tests/misc/programs/remaining-accounts/src/lib.rs b/tests/misc/programs/remaining-accounts/src/lib.rs new file mode 100644 index 0000000000..b93b91fd43 --- /dev/null +++ b/tests/misc/programs/remaining-accounts/src/lib.rs @@ -0,0 +1,42 @@ +//! Testing of handling of remaining accounts with anchor Account structs + +use account::*; +use anchor_lang::prelude::*; +use anchor_spl::token::TokenAccount; +use context::*; + +mod account; +mod context; + +declare_id!("RemainingAccounts11111111111111111111111111"); + +#[program] +pub mod remaining_accounts { + use super::*; + + pub fn test_init(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_init_another(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_remaining_accounts<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, TestRemainingAccounts>, + ) -> Result<()> { + let remaining_accounts_iter = &mut ctx.remaining_accounts.iter(); + + let token_account = + Account::::try_from(next_account_info(remaining_accounts_iter)?)?; + + let data_account_info = next_account_info(remaining_accounts_iter)?; + require_eq!(data_account_info.is_writable, true); + let mut data = Account::::try_from(data_account_info)?; + + data.someone = token_account.owner; + data.exit(ctx.program_id)?; + + Ok(()) + } +} diff --git a/tests/misc/tests/remaining-accounts/Test.toml b/tests/misc/tests/remaining-accounts/Test.toml new file mode 100644 index 0000000000..34c1c9052d --- /dev/null +++ b/tests/misc/tests/remaining-accounts/Test.toml @@ -0,0 +1,2 @@ +[scripts] +test = "yarn run ts-mocha -t 1000000 ./tests/remaining-accounts/*.ts" diff --git a/tests/misc/tests/remaining-accounts/remaining-accounts.ts b/tests/misc/tests/remaining-accounts/remaining-accounts.ts new file mode 100644 index 0000000000..7a9fc9009e --- /dev/null +++ b/tests/misc/tests/remaining-accounts/remaining-accounts.ts @@ -0,0 +1,111 @@ +import * as anchor from "@coral-xyz/anchor"; + +import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token"; +import { assert } from "chai"; +import { RemainingAccounts, IDL } from "../../target/types/remaining_accounts"; +import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; + +describe(IDL.name, () => { + // Configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); + const payer = NodeWallet.local().payer; + + const program = anchor.workspace + .RemainingAccounts as anchor.Program; + + it("Account can be used with remaining accounts - read token account and write someone to Data", async () => { + const data = anchor.web3.Keypair.generate(); + await program.methods + .testInit() + .accounts({ data: data.publicKey }) + .signers([data]) + .rpc(); + const ata = await Token.createWrappedNativeAccount( + program.provider.connection, + TOKEN_PROGRAM_ID, + payer.publicKey, + payer, + 0 + ); + + // Data is not initialized + try { + await program.methods + .testRemainingAccounts() + .accounts({ + tokenProgram: TOKEN_PROGRAM_ID, + }) + .remainingAccounts([ + { pubkey: ata, isSigner: false, isWritable: false }, + { + pubkey: anchor.web3.Keypair.generate().publicKey, + isSigner: false, + isWritable: true, + }, + ]) + .rpc(); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof anchor.AnchorError); + const err: anchor.AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 3012); + assert.strictEqual(err.error.errorCode.code, "AccountNotInitialized"); + } + + // Can read and write from account infos from remaining_accounts + await program.methods + .testRemainingAccounts() + .accounts({ + tokenProgram: TOKEN_PROGRAM_ID, + }) + .remainingAccounts([ + { pubkey: ata, isSigner: false, isWritable: false }, + { + pubkey: data.publicKey, + isSigner: false, + isWritable: true, + }, + ]) + .rpc(); + + const dataAccount = await program.account.data.fetch(data.publicKey); + assert.strictEqual( + dataAccount.someone.toString(), + payer.publicKey.toString() + ); + + // Another account + const another = anchor.web3.Keypair.generate(); + await program.methods + .testInitAnother() + .accounts({ another: another.publicKey }) + .signers([another]) + .rpc(); + + try { + await program.methods + .testRemainingAccounts() + .accounts({ + tokenProgram: TOKEN_PROGRAM_ID, + }) + .remainingAccounts([ + { pubkey: ata, isSigner: false, isWritable: false }, + { + pubkey: another.publicKey, + isSigner: false, + isWritable: true, + }, + ]) + .rpc(); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof anchor.AnchorError); + const err: anchor.AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 3002); + assert.strictEqual( + err.error.errorCode.code, + "AccountDiscriminatorMismatch" + ); + } + }); +}); diff --git a/ts/yarn.lock b/ts/yarn.lock index 15b4dd5b92..b8fb4ce366 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -440,6 +440,35 @@ resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-11.0.0.tgz#719cf05fcc1abb6533610a2e0f5dd1e61eac14fe" integrity sha512-VoNqai1vR5anRF5Tuh/+SWDFk7xi7oMwHrHrbm1BprYXjB2RJsWLhUrStMssDxEl5lW/z3EUdg8RvH/IUBccSQ== +"@coral-xyz/anchor@=0.28.1-beta.2": + version "0.28.1-beta.2" + resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.28.1-beta.2.tgz#4ddd4b2b66af04407be47cf9524147793ec514a0" + integrity sha512-xreUcOFF8+IQKWOBUrDKJbIw2ftpRVybFlEPVrbSlOBCbreCWrQ5754Gt9cHIcuBDAzearCDiBqzsGQdNgPJiw== + dependencies: + "@coral-xyz/borsh" "^0.28.0" + "@noble/hashes" "^1.3.1" + "@solana/web3.js" "^1.68.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.2" + camelcase "^6.3.0" + cross-fetch "^3.1.5" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + pako "^2.0.3" + snake-case "^3.0.4" + superstruct "^0.15.4" + toml "^3.0.0" + +"@coral-xyz/borsh@^0.28.0": + version "0.28.0" + resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.28.0.tgz#fa368a2f2475bbf6f828f4657f40a52102e02b6d" + integrity sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ== + dependencies: + bn.js "^5.1.2" + buffer-layout "^1.2.0" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1417,7 +1446,7 @@ base-x@^3.0.2, base-x@^3.0.6: dependencies: safe-buffer "^5.0.1" -base64-js@^1.3.1: +base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==