From 2b49a7f1c8e1339d911fab3acdb8e8a2e2392b18 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Mon, 23 May 2022 22:27:46 +0100 Subject: [PATCH 01/18] feat: setup system program coder structure --- tests/custom-coder/Anchor.toml | 1 + tests/custom-coder/package.json | 4 + .../programs/spl-token/src/lib.rs | 43 ++ tests/custom-coder/programs/system/Cargo.toml | 20 + tests/custom-coder/programs/system/Xargo.toml | 2 + tests/custom-coder/programs/system/src/lib.rs | 178 ++++++ tests/custom-coder/tests/system-coder.ts | 36 ++ ts/src/coder/index.ts | 1 + ts/src/coder/system/accounts.ts | 29 + ts/src/coder/system/events.ts | 14 + ts/src/coder/system/index.ts | 23 + ts/src/coder/system/instruction.ts | 268 ++++++++ ts/src/coder/system/state.ts | 14 + ts/src/spl/index.ts | 5 + ts/src/spl/system.ts | 580 ++++++++++++++++++ 15 files changed, 1218 insertions(+) create mode 100644 tests/custom-coder/programs/system/Cargo.toml create mode 100644 tests/custom-coder/programs/system/Xargo.toml create mode 100644 tests/custom-coder/programs/system/src/lib.rs create mode 100644 tests/custom-coder/tests/system-coder.ts create mode 100644 ts/src/coder/system/accounts.ts create mode 100644 ts/src/coder/system/events.ts create mode 100644 ts/src/coder/system/index.ts create mode 100644 ts/src/coder/system/instruction.ts create mode 100644 ts/src/coder/system/state.ts create mode 100644 ts/src/spl/system.ts diff --git a/tests/custom-coder/Anchor.toml b/tests/custom-coder/Anchor.toml index 6ea244cafb..75faf62408 100644 --- a/tests/custom-coder/Anchor.toml +++ b/tests/custom-coder/Anchor.toml @@ -1,6 +1,7 @@ [programs.localnet] custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w" +system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB" [registry] url = "https://anchor.projectserum.com" diff --git a/tests/custom-coder/package.json b/tests/custom-coder/package.json index 802bc74d8c..f14edc3a1e 100644 --- a/tests/custom-coder/package.json +++ b/tests/custom-coder/package.json @@ -15,5 +15,9 @@ }, "scripts": { "test": "anchor test" + }, + "dependencies": { + "mocha": "^10.0.0", + "ts-mocha": "^10.0.0" } } diff --git a/tests/custom-coder/programs/spl-token/src/lib.rs b/tests/custom-coder/programs/spl-token/src/lib.rs index 15f222e4ee..07583dd4c2 100644 --- a/tests/custom-coder/programs/spl-token/src/lib.rs +++ b/tests/custom-coder/programs/spl-token/src/lib.rs @@ -131,31 +131,41 @@ pub mod spl_token { #[derive(Accounts)] pub struct InitializeMint<'info> { #[account(mut)] + /// CHECK: mint: AccountInfo<'info>, + /// CHECK: rent: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeAccount<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, + /// CHECK: mint: AccountInfo<'info>, + /// CHECK: authority: AccountInfo<'info>, + /// CHECK: rent: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeMultisig<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, + /// CHECK: rent: AccountInfo<'info>, } #[derive(Accounts)] pub struct Transfer<'info> { #[account(mut)] + /// CHECK: source: AccountInfo<'info>, #[account(mut)] + /// CHECK: destination: AccountInfo<'info>, authority: Signer<'info>, } @@ -163,7 +173,9 @@ pub struct Transfer<'info> { #[derive(Accounts)] pub struct Approve<'info> { #[account(mut)] + /// CHECK: source: AccountInfo<'info>, + /// CHECK: delegate: AccountInfo<'info>, authority: Signer<'info>, } @@ -171,6 +183,7 @@ pub struct Approve<'info> { #[derive(Accounts)] pub struct Revoke<'info> { #[account(mut)] + /// CHECK: source: AccountInfo<'info>, authority: Signer<'info>, } @@ -178,6 +191,7 @@ pub struct Revoke<'info> { #[derive(Accounts)] pub struct SetAuthority<'info> { #[account(mut)] + /// CHECK: pub mint: AccountInfo<'info>, pub authority: Signer<'info>, } @@ -185,8 +199,10 @@ pub struct SetAuthority<'info> { #[derive(Accounts)] pub struct MintTo<'info> { #[account(mut)] + /// CHECK: pub mint: AccountInfo<'info>, #[account(mut)] + /// CHECK: pub to: AccountInfo<'info>, pub authority: Signer<'info>, } @@ -194,8 +210,10 @@ pub struct MintTo<'info> { #[derive(Accounts)] pub struct Burn<'info> { #[account(mut)] + /// CHECK: source: AccountInfo<'info>, #[account(mut)] + /// CHECK: mint: AccountInfo<'info>, authority: Signer<'info>, } @@ -203,16 +221,21 @@ pub struct Burn<'info> { #[derive(Accounts)] pub struct CloseAccount<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, #[account(mut)] + /// CHECK: destination: AccountInfo<'info>, + /// CHECK: authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct FreezeAccount<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, + /// CHECK: mint: AccountInfo<'info>, authority: Signer<'info>, } @@ -220,7 +243,9 @@ pub struct FreezeAccount<'info> { #[derive(Accounts)] pub struct ThawAccount<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, + /// CHECK: mint: AccountInfo<'info>, authority: Signer<'info>, } @@ -228,9 +253,12 @@ pub struct ThawAccount<'info> { #[derive(Accounts)] pub struct TransferChecked<'info> { #[account(mut)] + /// CHECK: source: AccountInfo<'info>, + /// CHECK: mint: AccountInfo<'info>, #[account(mut)] + /// CHECK: destination: AccountInfo<'info>, authority: Signer<'info>, } @@ -238,8 +266,11 @@ pub struct TransferChecked<'info> { #[derive(Accounts)] pub struct ApproveChecked<'info> { #[account(mut)] + /// CHECK: source: AccountInfo<'info>, + /// CHECK: mint: AccountInfo<'info>, + /// CHECK: delegate: AccountInfo<'info>, authority: Signer<'info>, } @@ -247,8 +278,10 @@ pub struct ApproveChecked<'info> { #[derive(Accounts)] pub struct MintToChecked<'info> { #[account(mut)] + /// CHECK: mint: AccountInfo<'info>, #[account(mut)] + /// CHECK: to: AccountInfo<'info>, authority: Signer<'info>, } @@ -256,8 +289,10 @@ pub struct MintToChecked<'info> { #[derive(Accounts)] pub struct BurnChecked<'info> { #[account(mut)] + /// CHECK: source: AccountInfo<'info>, #[account(mut)] + /// CHECK: mint: AccountInfo<'info>, authority: Signer<'info>, } @@ -265,32 +300,40 @@ pub struct BurnChecked<'info> { #[derive(Accounts)] pub struct InitializeAccount2<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, + /// CHECK: mint: AccountInfo<'info>, + /// CHECK: rent: AccountInfo<'info>, } #[derive(Accounts)] pub struct SyncNative<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeAccount3<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, + /// CHECK: mint: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeMultisig2<'info> { #[account(mut)] + /// CHECK: account: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeMint2<'info> { #[account(mut)] + /// CHECK: mint: AccountInfo<'info>, } diff --git a/tests/custom-coder/programs/system/Cargo.toml b/tests/custom-coder/programs/system/Cargo.toml new file mode 100644 index 0000000000..def0d8f77c --- /dev/null +++ b/tests/custom-coder/programs/system/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "system" +version = "0.1.0" +description = "Created with Anchor" +rust-version = "1.56" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "system" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.24.2" diff --git a/tests/custom-coder/programs/system/Xargo.toml b/tests/custom-coder/programs/system/Xargo.toml new file mode 100644 index 0000000000..475fb71ed1 --- /dev/null +++ b/tests/custom-coder/programs/system/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/tests/custom-coder/programs/system/src/lib.rs b/tests/custom-coder/programs/system/src/lib.rs new file mode 100644 index 0000000000..69db3b58cf --- /dev/null +++ b/tests/custom-coder/programs/system/src/lib.rs @@ -0,0 +1,178 @@ +// This file is autogenerated with https://github.com/acheroncrypto/native-to-anchor + +use anchor_lang::prelude::*; + +declare_id!("9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"); + +#[program] +pub mod system_program { + use super::*; + + pub fn create_account( + ctx: Context, + lamports: u64, + space: u64, + owner: Pubkey, + ) -> Result<()> { + Ok(()) + } + + pub fn assign(ctx: Context, owner: Pubkey) -> Result<()> { + Ok(()) + } + + pub fn transfer(ctx: Context, lamports: u64) -> Result<()> { + Ok(()) + } + + pub fn create_account_with_seed( + ctx: Context, + base: Pubkey, + seed: String, + lamports: u64, + space: u64, + owner: Pubkey, + ) -> Result<()> { + Ok(()) + } + + pub fn advance_nonce_account(ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn withdraw_nonce_account(ctx: Context, arg: u64) -> Result<()> { + Ok(()) + } + + pub fn authorize_nonce_account(ctx: Context, arg: Pubkey) -> Result<()> { + Ok(()) + } + + pub fn allocate(ctx: Context, space: u64) -> Result<()> { + Ok(()) + } + + pub fn allocate_with_seed( + ctx: Context, + base: Pubkey, + seed: String, + space: u64, + owner: Pubkey, + ) -> Result<()> { + Ok(()) + } + + pub fn assign_with_seed( + ctx: Context, + base: Pubkey, + seed: String, + owner: Pubkey, + ) -> Result<()> { + Ok(()) + } + + pub fn transfer_with_seed( + ctx: Context, + lamports: u64, + from_seed: String, + from_owner: Pubkey, + ) -> Result<()> { + Ok(()) + } +} + +#[derive(Accounts)] +pub struct CreateAccount<'info> { + #[account(mut)] + from: Signer<'info>, + #[account(mut)] + to: Signer<'info>, +} + +#[derive(Accounts)] +pub struct Assign<'info> { + #[account(mut)] + pubkey: Signer<'info>, +} + +#[derive(Accounts)] +pub struct Transfer<'info> { + #[account(mut)] + from: Signer<'info>, + #[account(mut)] + /// CHECK: + to: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct CreateAccountWithSeed<'info> { + #[account(mut)] + from: Signer<'info>, + #[account(mut)] + /// CHECK: + to: AccountInfo<'info>, + base: Signer<'info>, +} + +#[derive(Accounts)] +pub struct AdvanceNonceAccount<'info> { + #[account(mut)] + /// CHECK: + nonce: AccountInfo<'info>, + //recent_blockhashes::id): AccountInfo<'info>, + //authorized: Signer<'info>, +} + +#[derive(Accounts)] +pub struct WithdrawNonceAccount<'info> { + #[account(mut)] + /// CHECK: + nonce: AccountInfo<'info>, + #[account(mut)] + /// CHECK: + to: AccountInfo<'info>, + //recent_blockhashes::id): AccountInfo<'info>, + //rent: Sysvar<'info, Rent>, + //authorized: Signer<'info>, +} + +#[derive(Accounts)] +pub struct AuthorizeNonceAccount<'info> { + #[account(mut)] + /// CHECK: + nonce: AccountInfo<'info>, + authorized: Signer<'info>, +} + +#[derive(Accounts)] +pub struct Allocate<'info> { + #[account(mut)] + pubkey: Signer<'info>, +} + +#[derive(Accounts)] +pub struct AllocateWithSeed<'info> { + #[account(mut)] + /// CHECK: + address: AccountInfo<'info>, + base: Signer<'info>, +} + +#[derive(Accounts)] +pub struct AssignWithSeed<'info> { + #[account(mut)] + /// CHECK: + address: AccountInfo<'info>, + base: Signer<'info>, +} + +#[derive(Accounts)] +pub struct TransferWithSeed<'info> { + #[account(mut)] + /// CHECK: + from: AccountInfo<'info>, + from_base: Signer<'info>, + #[account(mut)] + /// CHECK: + to: AccountInfo<'info>, +} diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts new file mode 100644 index 0000000000..1421442cad --- /dev/null +++ b/tests/custom-coder/tests/system-coder.ts @@ -0,0 +1,36 @@ +import * as anchor from "@project-serum/anchor"; +import { Spl } from "@project-serum/anchor"; +import * as assert from "assert"; +import BN from "bn.js"; +import { Keypair, SystemProgram, SYSVAR_RENT_PUBKEY } from "@solana/web3.js"; + +describe("system-coder", () => { + // Configure the client to use the local cluster. + const provider = anchor.AnchorProvider.env(); + anchor.setProvider(provider); + + // Client. + const program = Spl.system(); + + // Constants. + const mintKeypair = Keypair.generate(); + const aliceTokenKeypair = Keypair.generate(); + const bobTokenKeypair = Keypair.generate(); + const rent = SYSVAR_RENT_PUBKEY; + + it("Creates an account", async () => { + // arrange + const space = 100; + const lamports = await program.provider.connection.getMinimumBalanceForRentExemption(space); + // act + await program.methods + .createAccount(new BN(lamports), new BN(space), provider.wallet.publicKey) + .accounts({ + from: provider.wallet.publicKey, + to: aliceTokenKeypair.publicKey, + }).signers([aliceTokenKeypair]).rpc(); + // assert + const aliceAccount = await program.provider.connection.getAccountInfo(aliceTokenKeypair.publicKey); + console.log(aliceAccount) + }); +}); diff --git a/ts/src/coder/index.ts b/ts/src/coder/index.ts index 6256103a03..29a15ce164 100644 --- a/ts/src/coder/index.ts +++ b/ts/src/coder/index.ts @@ -3,6 +3,7 @@ import { Event } from "../program/event.js"; export * from "./borsh/index.js"; export * from "./spl-token/index.js"; +export * from "./system/index.js"; /** * Coder provides a facade for encoding and decoding all IDL related objects. diff --git a/ts/src/coder/system/accounts.ts b/ts/src/coder/system/accounts.ts new file mode 100644 index 0000000000..d8ab9ec9a0 --- /dev/null +++ b/ts/src/coder/system/accounts.ts @@ -0,0 +1,29 @@ +import { AccountsCoder } from "../index.js"; +import { Idl, IdlTypeDef } from "../../idl.js"; + +export class SystemAccountsCoder + implements AccountsCoder +{ + constructor(private idl: Idl) {} + + public async encode(accountName: A, account: T): Promise { + throw new Error("System program does not have accounts"); + } + + public decode(accountName: A, ix: Buffer): T { + throw new Error("System program does not have accounts"); + } + + public decodeUnchecked(accountName: A, ix: Buffer): T { + throw new Error("System program does not have accounts"); + } + + // TODO: this won't use the appendData. + public memcmp(accountName: A, _appendData?: Buffer): any { + throw new Error("System program does not have accounts"); + } + + public size(idlAccount: IdlTypeDef): number { + throw new Error("System program does not have accounts"); + } +} diff --git a/ts/src/coder/system/events.ts b/ts/src/coder/system/events.ts new file mode 100644 index 0000000000..9a9f5faac9 --- /dev/null +++ b/ts/src/coder/system/events.ts @@ -0,0 +1,14 @@ +import { EventCoder } from "../index.js"; +import { Idl } from "../../idl.js"; +import { Event } from "../../program/event"; +import { IdlEvent } from "../../idl"; + +export class SystemEventsCoder implements EventCoder { + constructor(_idl: Idl) {} + + decode>( + _log: string + ): Event | null { + throw new Error("System program does not have events"); + } +} diff --git a/ts/src/coder/system/index.ts b/ts/src/coder/system/index.ts new file mode 100644 index 0000000000..be8db86ade --- /dev/null +++ b/ts/src/coder/system/index.ts @@ -0,0 +1,23 @@ +import { Idl } from "../../idl.js"; +import { Coder } from "../index.js"; +import { SystemInstructionCoder } from "./instruction.js"; +import { SystemStateCoder } from "./state.js"; +import { SystemAccountsCoder } from "./accounts.js"; +import { SystemEventsCoder } from "./events.js"; + +/** + * Coder for the System program. + */ +export class SystemCoder implements Coder { + readonly instruction: SystemInstructionCoder; + readonly accounts: SystemAccountsCoder; + readonly state: SystemStateCoder; + readonly events: SystemEventsCoder; + + constructor(idl: Idl) { + this.instruction = new SystemInstructionCoder(idl); + this.accounts = new SystemAccountsCoder(idl); + this.events = new SystemEventsCoder(idl); + this.state = new SystemStateCoder(idl); + } +} diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts new file mode 100644 index 0000000000..cf0b5af5d5 --- /dev/null +++ b/ts/src/coder/system/instruction.ts @@ -0,0 +1,268 @@ +import * as BufferLayout from "buffer-layout"; +import camelCase from "camelcase"; +import { InstructionCoder } from "../index.js"; +import { Idl } from "../../idl.js"; +import { PublicKey } from "@solana/web3.js"; + +export class SystemInstructionCoder implements InstructionCoder { + // eslint-disable-next-line @typescript-eslint/no-empty-function + constructor(_: Idl) {} + + encode(ixName: string, ix: any): Buffer { + switch (camelCase(ixName)) { + case 'createAccount': { + return encodeCreateAccount(ix); + } + case 'assign': { + return encodeAssign(ix); + } + case 'transfer': { + return encodeTransfer(ix); + } + case 'createAccountWithSeed': { + return encodeCreateAccountWithSeed(ix); + } + case 'advanceNonceAccount': { + return encodeAdvanceNonceAccount(ix); + } + case 'withdrawNonceAccount': { + return encodeWithdrawNonceAccount(ix); + } + case 'authorizeNonceAccount': { + return encodeAuthorizeNonceAccount(ix); + } + case 'allocate': { + return encodeAllocate(ix); + } + case 'allocateWithSeed': { + return encodeAllocateWithSeed(ix); + } + case 'assignWithSeed': { + return encodeAssignWithSeed(ix); + } + case 'transferWithSeed': { + return encodeTransferWithSeed(ix); + } + default: { + throw new Error(`Invalid instruction: ${ixName}`); + } + } + } + + encodeState(_ixName: string, _ix: any): Buffer { + throw new Error('System does not have state'); + } +} + +function encodeCreateAccount({ lamports, space, owner }: any): Buffer { + return encodeData({ + createAccount: { lamports, space, owner }, + }); +} + +function encodeAssign({ owner }: any): Buffer { + return encodeData({ + assign: { owner }, + }); +} + +function encodeTransfer({ lamports }: any): Buffer { + return encodeData({ + transfer: { lamports }, + }); +} + +function encodeCreateAccountWithSeed({ + base, + seed, + lamports, + space, + owner, +}: any): Buffer { + return encodeData({ + createAccountWithSeed: { base, seed, lamports, space, owner }, + }); +} + +function encodeAdvanceNonceAccount(_ix: any): Buffer { + return encodeData({ + advanceNonceAccount: {}, + }); +} + +function encodeWithdrawNonceAccount({ arg }: any): Buffer { + return encodeData({ + withdrawNonceAccount: { arg }, + }); +} + +function encodeAuthorizeNonceAccount({ arg }: any): Buffer { + return encodeData({ + authorizeNonceAccount: { arg }, + }); +} + +function encodeAllocate({ space }: any): Buffer { + return encodeData({ + allocate: { space }, + }); +} + +function encodeAllocateWithSeed({ base, seed, space, owner }: any): Buffer { + return encodeData({ + allocateWithSeed: { base, seed, space, owner }, + }); +} + +function encodeAssignWithSeed({ base, seed, owner }: any): Buffer { + return encodeData({ + assignWithSeed: { base, seed, owner }, + }); +} + +function encodeTransferWithSeed({ + lamports, + fromSeed, + fromOwner, +}: any): Buffer { + return encodeData({ + transferWithSeed: { lamports, fromSeed, fromOwner }, + }); +} + + +class WrappedLayout extends BufferLayout.Layout { + layout: BufferLayout.Layout; + decoder: (data: T) => U; + encoder: (src: U) => T; + + constructor( + layout: BufferLayout.Layout, + decoder: (data: T) => U, + encoder: (src: U) => T, + property?: string + ) { + super(layout.span, property); + this.layout = layout; + this.decoder = decoder; + this.encoder = encoder; + } + + decode(b: Buffer, offset?: number): U { + return this.decoder(this.layout.decode(b, offset)); + } + + encode(src: U, b: Buffer, offset?: number): number { + return this.layout.encode(this.encoder(src), b, offset); + } + + getSpan(b: Buffer, offset?: number): number { + return this.layout.getSpan(b, offset); + } +} + +function publicKey(property?: string): BufferLayout.Layout { + return new WrappedLayout( + BufferLayout.blob(32), + (b: Buffer) => new PublicKey(b), + (key: PublicKey) => key.toBuffer(), + property + ); +} + +const LAYOUT = BufferLayout.union(BufferLayout.u8('instruction')); +LAYOUT.addVariant( + 0, + BufferLayout.struct([ + BufferLayout.nu64('lamports'), + BufferLayout.nu64('space'), + publicKey('owner'), + ]), + 'createAccount' +); +LAYOUT.addVariant( + 1, + BufferLayout.struct([publicKey('owner')]), + 'assign' +); +LAYOUT.addVariant( + 2, + BufferLayout.struct([BufferLayout.nu64('lamports')]), + 'transfer' +); +LAYOUT.addVariant( + 3, + BufferLayout.struct([ + publicKey('base'), + BufferLayout.cstr('seed'), // I'm not sure if this would work + BufferLayout.nu64('lamports'), + BufferLayout.nu64('space'), + publicKey('owner') + ]), + 'createAccountWithSeed' +); +LAYOUT.addVariant( + 4, + BufferLayout.struct([]), + 'advanceNonceAccount' +); +LAYOUT.addVariant( + 5, + BufferLayout.struct([BufferLayout.nu64('arg')]), + 'withdrawNonceAccount' +); +LAYOUT.addVariant( + 6, + BufferLayout.struct([ + publicKey('arg'), + ]), + 'authorizeNonceAccount' +); +LAYOUT.addVariant( + 7, + BufferLayout.struct([BufferLayout.nu64('space')]), + 'allocate' +); +LAYOUT.addVariant( + 8, + BufferLayout.struct([ + publicKey('base'), + BufferLayout.cstr('seed'), + BufferLayout.nu64('space'), + publicKey('owner'), + ]), + 'allocateWithSeed' +); +LAYOUT.addVariant( + 9, + BufferLayout.struct([ + publicKey('base'), + BufferLayout.cstr('seed'), + publicKey('owner') + ]), + 'assignWithSeed' +); +LAYOUT.addVariant( + 10, + BufferLayout.struct([ + BufferLayout.nu64('space'), + BufferLayout.cstr('fromSeed'), + publicKey('fromOwner'), + ]), + 'transferWithSeed' +); + +/* function publicKey(property: string): any { + return BufferLayout.blob(32, property); +} + */ + +function encodeData(instruction: any): Buffer { + const b = Buffer.alloc(instructionMaxSpan); + const span = LAYOUT.encode(instruction, b); + return b.slice(0, span); +} + +const instructionMaxSpan = Math.max( + ...Object.values(LAYOUT.registry).map((r: any) => r.span) +); diff --git a/ts/src/coder/system/state.ts b/ts/src/coder/system/state.ts new file mode 100644 index 0000000000..9a7d51a852 --- /dev/null +++ b/ts/src/coder/system/state.ts @@ -0,0 +1,14 @@ +import { StateCoder } from "../index.js"; +import { Idl } from "../../idl"; + +export class SystemStateCoder implements StateCoder { + // eslint-disable-next-line @typescript-eslint/no-empty-function + constructor(_idl: Idl) {} + + encode(_name: string, _account: T): Promise { + throw new Error('System does not have state'); + } + decode(_ix: Buffer): T { + throw new Error('System does not have state'); + } +} diff --git a/ts/src/spl/index.ts b/ts/src/spl/index.ts index 8929f775f2..941c63aede 100644 --- a/ts/src/spl/index.ts +++ b/ts/src/spl/index.ts @@ -1,5 +1,6 @@ import { Program, Provider } from "../index.js"; import { program as tokenProgram, SplToken } from "./token.js"; +import { program as systemProgram, SystemProgram } from "./system.js"; export { SplToken } from "./token.js"; @@ -7,4 +8,8 @@ export class Spl { public static token(provider?: Provider): Program { return tokenProgram(provider); } + + public static system(provider?: Provider): Program { + return systemProgram(provider); + } } diff --git a/ts/src/spl/system.ts b/ts/src/spl/system.ts new file mode 100644 index 0000000000..848e1532da --- /dev/null +++ b/ts/src/spl/system.ts @@ -0,0 +1,580 @@ +import { PublicKey } from "@solana/web3.js"; +import { Program } from "../program/index.js"; +import Provider from "../provider.js"; +import { SystemCoder } from "../coder/system/index.js"; + + +const SYSTEM_PROGRAM_ID = new PublicKey( + "11111111111111111111111111111111" +); + +export function program(provider?: Provider): Program { + return new Program(IDL, SYSTEM_PROGRAM_ID, provider, coder()); +} + +export function coder(): SystemCoder { + return new SystemCoder(IDL); +} + +/** + * System IDL. + */ + export type SystemProgram = { + "version": "0.1.0", + "name": "system_program", + "instructions": [ + { + "name": "createAccount", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "lamports", + "type": "u64" + }, + { + "name": "space", + "type": "u64" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "assign", + "accounts": [ + { + "name": "pubkey", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "transfer", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "lamports", + "type": "u64" + } + ] + }, + { + "name": "createAccountWithSeed", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + }, + { + "name": "base", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "seed", + "type": "string" + }, + { + "name": "lamports", + "type": "u64" + }, + { + "name": "space", + "type": "u64" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "advanceNonceAccount", + "accounts": [ + { + "name": "nonce", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "withdrawNonceAccount", + "accounts": [ + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "to", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "arg", + "type": "u64" + } + ] + }, + { + "name": "authorizeNonceAccount", + "accounts": [ + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "authorized", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "arg", + "type": "publicKey" + } + ] + }, + { + "name": "allocate", + "accounts": [ + { + "name": "pubkey", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "space", + "type": "u64" + } + ] + }, + { + "name": "allocateWithSeed", + "accounts": [ + { + "name": "address", + "isMut": true, + "isSigner": false + }, + { + "name": "base", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "seed", + "type": "string" + }, + { + "name": "space", + "type": "u64" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "assignWithSeed", + "accounts": [ + { + "name": "address", + "isMut": true, + "isSigner": false + }, + { + "name": "base", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "seed", + "type": "string" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "transferWithSeed", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": false + }, + { + "name": "fromBase", + "isMut": false, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "lamports", + "type": "u64" + }, + { + "name": "fromSeed", + "type": "string" + }, + { + "name": "fromOwner", + "type": "publicKey" + } + ] + } + ] +}; + +export const IDL: SystemProgram = { + "version": "0.1.0", + "name": "system_program", + "instructions": [ + { + "name": "createAccount", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "lamports", + "type": "u64" + }, + { + "name": "space", + "type": "u64" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "assign", + "accounts": [ + { + "name": "pubkey", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "transfer", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "lamports", + "type": "u64" + } + ] + }, + { + "name": "createAccountWithSeed", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + }, + { + "name": "base", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "seed", + "type": "string" + }, + { + "name": "lamports", + "type": "u64" + }, + { + "name": "space", + "type": "u64" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "advanceNonceAccount", + "accounts": [ + { + "name": "nonce", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "withdrawNonceAccount", + "accounts": [ + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "to", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "arg", + "type": "u64" + } + ] + }, + { + "name": "authorizeNonceAccount", + "accounts": [ + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "authorized", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "arg", + "type": "publicKey" + } + ] + }, + { + "name": "allocate", + "accounts": [ + { + "name": "pubkey", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "space", + "type": "u64" + } + ] + }, + { + "name": "allocateWithSeed", + "accounts": [ + { + "name": "address", + "isMut": true, + "isSigner": false + }, + { + "name": "base", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "seed", + "type": "string" + }, + { + "name": "space", + "type": "u64" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "assignWithSeed", + "accounts": [ + { + "name": "address", + "isMut": true, + "isSigner": false + }, + { + "name": "base", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "seed", + "type": "string" + }, + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "transferWithSeed", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": false + }, + { + "name": "fromBase", + "isMut": false, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "lamports", + "type": "u64" + }, + { + "name": "fromSeed", + "type": "string" + }, + { + "name": "fromOwner", + "type": "publicKey" + } + ] + } + ] +}; From 711ae8802cfd65e3a0dcc2489bfe683c6ee7a4d4 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 13:46:10 +0100 Subject: [PATCH 02/18] fix: properly encode public keys --- ts/src/coder/system/instruction.ts | 191 ++++++++++++----------------- 1 file changed, 80 insertions(+), 111 deletions(-) diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts index cf0b5af5d5..48bf93a69d 100644 --- a/ts/src/coder/system/instruction.ts +++ b/ts/src/coder/system/instruction.ts @@ -2,7 +2,6 @@ import * as BufferLayout from "buffer-layout"; import camelCase from "camelcase"; import { InstructionCoder } from "../index.js"; import { Idl } from "../../idl.js"; -import { PublicKey } from "@solana/web3.js"; export class SystemInstructionCoder implements InstructionCoder { // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -10,37 +9,37 @@ export class SystemInstructionCoder implements InstructionCoder { encode(ixName: string, ix: any): Buffer { switch (camelCase(ixName)) { - case 'createAccount': { + case "createAccount": { return encodeCreateAccount(ix); } - case 'assign': { + case "assign": { return encodeAssign(ix); } - case 'transfer': { + case "transfer": { return encodeTransfer(ix); } - case 'createAccountWithSeed': { + case "createAccountWithSeed": { return encodeCreateAccountWithSeed(ix); } - case 'advanceNonceAccount': { + case "advanceNonceAccount": { return encodeAdvanceNonceAccount(ix); } - case 'withdrawNonceAccount': { + case "withdrawNonceAccount": { return encodeWithdrawNonceAccount(ix); } - case 'authorizeNonceAccount': { + case "authorizeNonceAccount": { return encodeAuthorizeNonceAccount(ix); } - case 'allocate': { + case "allocate": { return encodeAllocate(ix); } - case 'allocateWithSeed': { + case "allocateWithSeed": { return encodeAllocateWithSeed(ix); } - case 'assignWithSeed': { + case "assignWithSeed": { return encodeAssignWithSeed(ix); } - case 'transferWithSeed': { + case "transferWithSeed": { return encodeTransferWithSeed(ix); } default: { @@ -50,19 +49,19 @@ export class SystemInstructionCoder implements InstructionCoder { } encodeState(_ixName: string, _ix: any): Buffer { - throw new Error('System does not have state'); + throw new Error("System does not have state"); } } function encodeCreateAccount({ lamports, space, owner }: any): Buffer { return encodeData({ - createAccount: { lamports, space, owner }, + createAccount: { lamports, space, owner: owner.toBuffer() }, }); } function encodeAssign({ owner }: any): Buffer { return encodeData({ - assign: { owner }, + assign: { owner: owner.toBuffer() }, }); } @@ -80,7 +79,13 @@ function encodeCreateAccountWithSeed({ owner, }: any): Buffer { return encodeData({ - createAccountWithSeed: { base, seed, lamports, space, owner }, + createAccountWithSeed: { + base: base.toBuffer(), + seed, + lamports, + space, + owner: owner.toBuffer(), + }, }); } @@ -98,7 +103,7 @@ function encodeWithdrawNonceAccount({ arg }: any): Buffer { function encodeAuthorizeNonceAccount({ arg }: any): Buffer { return encodeData({ - authorizeNonceAccount: { arg }, + authorizeNonceAccount: { arg: arg.toBuffer() }, }); } @@ -110,13 +115,22 @@ function encodeAllocate({ space }: any): Buffer { function encodeAllocateWithSeed({ base, seed, space, owner }: any): Buffer { return encodeData({ - allocateWithSeed: { base, seed, space, owner }, + allocateWithSeed: { + base: base.toBuffer(), + seed, + space, + owner: owner.toBuffer(), + }, }); } function encodeAssignWithSeed({ base, seed, owner }: any): Buffer { return encodeData({ - assignWithSeed: { base, seed, owner }, + assignWithSeed: { + base: base.toBuffer(), + seed, + owner: owner.toBuffer(), + }, }); } @@ -126,140 +140,95 @@ function encodeTransferWithSeed({ fromOwner, }: any): Buffer { return encodeData({ - transferWithSeed: { lamports, fromSeed, fromOwner }, + transferWithSeed: { + lamports, + fromSeed, + fromOwner: fromOwner.toBuffer(), + }, }); } - -class WrappedLayout extends BufferLayout.Layout { - layout: BufferLayout.Layout; - decoder: (data: T) => U; - encoder: (src: U) => T; - - constructor( - layout: BufferLayout.Layout, - decoder: (data: T) => U, - encoder: (src: U) => T, - property?: string - ) { - super(layout.span, property); - this.layout = layout; - this.decoder = decoder; - this.encoder = encoder; - } - - decode(b: Buffer, offset?: number): U { - return this.decoder(this.layout.decode(b, offset)); - } - - encode(src: U, b: Buffer, offset?: number): number { - return this.layout.encode(this.encoder(src), b, offset); - } - - getSpan(b: Buffer, offset?: number): number { - return this.layout.getSpan(b, offset); - } -} - -function publicKey(property?: string): BufferLayout.Layout { - return new WrappedLayout( - BufferLayout.blob(32), - (b: Buffer) => new PublicKey(b), - (key: PublicKey) => key.toBuffer(), - property - ); -} - -const LAYOUT = BufferLayout.union(BufferLayout.u8('instruction')); +const LAYOUT = BufferLayout.union(BufferLayout.u32("instruction")); LAYOUT.addVariant( 0, BufferLayout.struct([ - BufferLayout.nu64('lamports'), - BufferLayout.nu64('space'), - publicKey('owner'), + BufferLayout.nu64("lamports"), + BufferLayout.nu64("space"), + publicKey("owner"), ]), - 'createAccount' -); -LAYOUT.addVariant( - 1, - BufferLayout.struct([publicKey('owner')]), - 'assign' + "createAccount" ); +LAYOUT.addVariant(1, BufferLayout.struct([publicKey("owner")]), "assign"); LAYOUT.addVariant( 2, - BufferLayout.struct([BufferLayout.nu64('lamports')]), - 'transfer' + BufferLayout.struct([BufferLayout.nu64("lamports")]), + "transfer" ); LAYOUT.addVariant( 3, BufferLayout.struct([ - publicKey('base'), - BufferLayout.cstr('seed'), // I'm not sure if this would work - BufferLayout.nu64('lamports'), - BufferLayout.nu64('space'), - publicKey('owner') + publicKey("base"), + BufferLayout.cstr("seed"), // I'm not sure if this would work + BufferLayout.nu64("lamports"), + BufferLayout.nu64("space"), + publicKey("owner"), ]), - 'createAccountWithSeed' + "createAccountWithSeed" ); +LAYOUT.addVariant(4, BufferLayout.struct([]), "advanceNonceAccount"); LAYOUT.addVariant( - 4, - BufferLayout.struct([]), - 'advanceNonceAccount' -); -LAYOUT.addVariant( - 5, - BufferLayout.struct([BufferLayout.nu64('arg')]), - 'withdrawNonceAccount' + 5, + BufferLayout.struct([BufferLayout.nu64("arg")]), + "withdrawNonceAccount" ); LAYOUT.addVariant( 6, - BufferLayout.struct([ - publicKey('arg'), - ]), - 'authorizeNonceAccount' + BufferLayout.struct([publicKey("arg")]), + "authorizeNonceAccount" ); LAYOUT.addVariant( 7, - BufferLayout.struct([BufferLayout.nu64('space')]), - 'allocate' + BufferLayout.struct([BufferLayout.nu64("space")]), + "allocate" ); LAYOUT.addVariant( 8, BufferLayout.struct([ - publicKey('base'), - BufferLayout.cstr('seed'), - BufferLayout.nu64('space'), - publicKey('owner'), + publicKey("base"), + BufferLayout.cstr("seed"), + BufferLayout.nu64("space"), + publicKey("owner"), ]), - 'allocateWithSeed' + "allocateWithSeed" ); LAYOUT.addVariant( - 9, + 9, BufferLayout.struct([ - publicKey('base'), - BufferLayout.cstr('seed'), - publicKey('owner') - ]), - 'assignWithSeed' + publicKey("base"), + BufferLayout.cstr("seed"), + publicKey("owner"), + ]), + "assignWithSeed" ); LAYOUT.addVariant( - 10, + 10, BufferLayout.struct([ - BufferLayout.nu64('space'), - BufferLayout.cstr('fromSeed'), - publicKey('fromOwner'), - ]), - 'transferWithSeed' + BufferLayout.nu64("space"), + BufferLayout.cstr("fromSeed"), + publicKey("fromOwner"), + ]), + "transferWithSeed" ); -/* function publicKey(property: string): any { +function publicKey(property: string): any { return BufferLayout.blob(32, property); } - */ function encodeData(instruction: any): Buffer { const b = Buffer.alloc(instructionMaxSpan); + const span = LAYOUT.encode(instruction, b); + return b.slice(0, span); } From cf1e2beaaadb18d14081ae15e02dbc4837919802 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 13:46:24 +0100 Subject: [PATCH 03/18] test: add a few more asserts --- tests/custom-coder/tests/system-coder.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index 1421442cad..b7f8292947 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -2,7 +2,7 @@ import * as anchor from "@project-serum/anchor"; import { Spl } from "@project-serum/anchor"; import * as assert from "assert"; import BN from "bn.js"; -import { Keypair, SystemProgram, SYSVAR_RENT_PUBKEY } from "@solana/web3.js"; +import { Keypair, SystemProgram } from "@solana/web3.js"; describe("system-coder", () => { // Configure the client to use the local cluster. @@ -13,24 +13,24 @@ describe("system-coder", () => { const program = Spl.system(); // Constants. - const mintKeypair = Keypair.generate(); const aliceTokenKeypair = Keypair.generate(); - const bobTokenKeypair = Keypair.generate(); - const rent = SYSVAR_RENT_PUBKEY; it("Creates an account", async () => { // arrange const space = 100; const lamports = await program.provider.connection.getMinimumBalanceForRentExemption(space); + const owner = SystemProgram.programId; // act await program.methods - .createAccount(new BN(lamports), new BN(space), provider.wallet.publicKey) + .createAccount(new BN(lamports), new BN(space), owner) .accounts({ from: provider.wallet.publicKey, to: aliceTokenKeypair.publicKey, }).signers([aliceTokenKeypair]).rpc(); // assert const aliceAccount = await program.provider.connection.getAccountInfo(aliceTokenKeypair.publicKey); - console.log(aliceAccount) + assert.notEqual(aliceAccount, null); + assert.ok(owner.equals(aliceAccount.owner)); + assert.equal(lamports, aliceAccount.lamports); }); }); From 4db47db2e8d2c5a050fcc9266b40ef13853daad7 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 14:02:46 +0100 Subject: [PATCH 04/18] test: add assign test case --- tests/custom-coder/tests/system-coder.ts | 33 +++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index b7f8292947..2bc9e6e828 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -3,6 +3,7 @@ import { Spl } from "@project-serum/anchor"; import * as assert from "assert"; import BN from "bn.js"; import { Keypair, SystemProgram } from "@solana/web3.js"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; describe("system-coder", () => { // Configure the client to use the local cluster. @@ -18,7 +19,10 @@ describe("system-coder", () => { it("Creates an account", async () => { // arrange const space = 100; - const lamports = await program.provider.connection.getMinimumBalanceForRentExemption(space); + const lamports = + await program.provider.connection.getMinimumBalanceForRentExemption( + space + ); const owner = SystemProgram.programId; // act await program.methods @@ -26,11 +30,34 @@ describe("system-coder", () => { .accounts({ from: provider.wallet.publicKey, to: aliceTokenKeypair.publicKey, - }).signers([aliceTokenKeypair]).rpc(); + }) + .signers([aliceTokenKeypair]) + .rpc(); // assert - const aliceAccount = await program.provider.connection.getAccountInfo(aliceTokenKeypair.publicKey); + const aliceAccount = await program.provider.connection.getAccountInfo( + aliceTokenKeypair.publicKey + ); assert.notEqual(aliceAccount, null); assert.ok(owner.equals(aliceAccount.owner)); assert.equal(lamports, aliceAccount.lamports); }); + + it("Assign an account to a program", async () => { + // arrange + const owner = TOKEN_PROGRAM_ID; + // act + await program.methods + .assign(owner) + .accounts({ + pubkey: aliceTokenKeypair.publicKey, + }) + .signers([aliceTokenKeypair]) + .rpc(); + // assert + const aliceAccount = await program.provider.connection.getAccountInfo( + aliceTokenKeypair.publicKey + ); + assert.notEqual(aliceAccount, null); + assert.ok(owner.equals(aliceAccount.owner)); + }); }); From 0c06ef49433917c157543c59e9e839cbfcd0a843 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 20:54:19 +0100 Subject: [PATCH 05/18] feat: setup create account with seed ix --- tests/custom-coder/tests/system-coder.ts | 45 +++++++++++++++++-- ts/src/coder/system/instruction.ts | 55 +++++++++++++----------- ts/src/coder/system/layout.ts | 49 +++++++++++++++++++++ 3 files changed, 122 insertions(+), 27 deletions(-) create mode 100644 ts/src/coder/system/layout.ts diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index 2bc9e6e828..5a46bef6fc 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -1,9 +1,12 @@ import * as anchor from "@project-serum/anchor"; import { Spl } from "@project-serum/anchor"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { + Keypair, PublicKey, + SystemProgram +} from "@solana/web3.js"; import * as assert from "assert"; import BN from "bn.js"; -import { Keypair, SystemProgram } from "@solana/web3.js"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; describe("system-coder", () => { // Configure the client to use the local cluster. @@ -42,7 +45,7 @@ describe("system-coder", () => { assert.equal(lamports, aliceAccount.lamports); }); - it("Assign an account to a program", async () => { + it("Assigns an account to a program", async () => { // arrange const owner = TOKEN_PROGRAM_ID; // act @@ -60,4 +63,40 @@ describe("system-coder", () => { assert.notEqual(aliceAccount, null); assert.ok(owner.equals(aliceAccount.owner)); }); + + it("Creates an account with seed", async () => { + const space = 100; + const lamports = + await program.provider.connection.getMinimumBalanceForRentExemption( + space + ); + const owner = SystemProgram.programId; + const seed = "seeds"; + const bobPublicKey = await PublicKey.createWithSeed( + aliceTokenKeypair.publicKey, + seed, + owner + ); + // act + await program.methods + .createAccountWithSeed( + aliceTokenKeypair.publicKey, + seed, + new BN(lamports), + new BN(space), + owner + ) + .accounts({ + base: aliceTokenKeypair.publicKey, + from: provider.wallet.publicKey, + to: bobPublicKey, + }) + .signers([aliceTokenKeypair]) + .rpc(); + // assert + const bobAccount = await program.provider.connection.getAccountInfo( + bobPublicKey + ); + assert.notEqual(bobAccount, null); + }); }); diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts index 48bf93a69d..18ada4be18 100644 --- a/ts/src/coder/system/instruction.ts +++ b/ts/src/coder/system/instruction.ts @@ -1,7 +1,8 @@ import * as BufferLayout from "buffer-layout"; import camelCase from "camelcase"; -import { InstructionCoder } from "../index.js"; import { Idl } from "../../idl.js"; +import { InstructionCoder } from "../index.js"; +import { RustStringLayout } from "./layout.js"; export class SystemInstructionCoder implements InstructionCoder { // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -78,15 +79,18 @@ function encodeCreateAccountWithSeed({ space, owner, }: any): Buffer { - return encodeData({ - createAccountWithSeed: { - base: base.toBuffer(), - seed, - lamports, - space, - owner: owner.toBuffer(), + return encodeData( + { + createAccountWithSeed: { + base: base.toBuffer(), + seed, + lamports, + space, + owner: owner.toBuffer(), + }, }, - }); + CREATE_ACCOUNT_WITH_SEED_LAYOUT.span + seed.length + ); } function encodeAdvanceNonceAccount(_ix: any): Buffer { @@ -152,8 +156,8 @@ const LAYOUT = BufferLayout.union(BufferLayout.u32("instruction")); LAYOUT.addVariant( 0, BufferLayout.struct([ - BufferLayout.nu64("lamports"), - BufferLayout.nu64("space"), + BufferLayout.ns64("lamports"), + BufferLayout.ns64("space"), publicKey("owner"), ]), "createAccount" @@ -161,16 +165,16 @@ LAYOUT.addVariant( LAYOUT.addVariant(1, BufferLayout.struct([publicKey("owner")]), "assign"); LAYOUT.addVariant( 2, - BufferLayout.struct([BufferLayout.nu64("lamports")]), + BufferLayout.struct([BufferLayout.ns64("lamports")]), "transfer" ); -LAYOUT.addVariant( +const CREATE_ACCOUNT_WITH_SEED_LAYOUT = LAYOUT.addVariant( 3, BufferLayout.struct([ publicKey("base"), - BufferLayout.cstr("seed"), // I'm not sure if this would work - BufferLayout.nu64("lamports"), - BufferLayout.nu64("space"), + new RustStringLayout("seed"), + BufferLayout.ns64("lamports"), + BufferLayout.ns64("space"), publicKey("owner"), ]), "createAccountWithSeed" @@ -178,7 +182,7 @@ LAYOUT.addVariant( LAYOUT.addVariant(4, BufferLayout.struct([]), "advanceNonceAccount"); LAYOUT.addVariant( 5, - BufferLayout.struct([BufferLayout.nu64("arg")]), + BufferLayout.struct([BufferLayout.ns64("arg")]), "withdrawNonceAccount" ); LAYOUT.addVariant( @@ -188,7 +192,7 @@ LAYOUT.addVariant( ); LAYOUT.addVariant( 7, - BufferLayout.struct([BufferLayout.nu64("space")]), + BufferLayout.struct([BufferLayout.ns64("space")]), "allocate" ); LAYOUT.addVariant( @@ -196,7 +200,7 @@ LAYOUT.addVariant( BufferLayout.struct([ publicKey("base"), BufferLayout.cstr("seed"), - BufferLayout.nu64("space"), + BufferLayout.ns64("space"), publicKey("owner"), ]), "allocateWithSeed" @@ -213,7 +217,7 @@ LAYOUT.addVariant( LAYOUT.addVariant( 10, BufferLayout.struct([ - BufferLayout.nu64("space"), + BufferLayout.ns64("space"), BufferLayout.cstr("fromSeed"), publicKey("fromOwner"), ]), @@ -224,12 +228,15 @@ function publicKey(property: string): any { return BufferLayout.blob(32, property); } -function encodeData(instruction: any): Buffer { - const b = Buffer.alloc(instructionMaxSpan); - +function encodeData(instruction: any, maxSpan?: number): Buffer { + const b = Buffer.alloc(maxSpan ?? instructionMaxSpan); const span = LAYOUT.encode(instruction, b); - return b.slice(0, span); + if (maxSpan === undefined) { + return b.slice(0, span); + } + + return b; } const instructionMaxSpan = Math.max( diff --git a/ts/src/coder/system/layout.ts b/ts/src/coder/system/layout.ts new file mode 100644 index 0000000000..4226dd9705 --- /dev/null +++ b/ts/src/coder/system/layout.ts @@ -0,0 +1,49 @@ +import BN from "bn.js"; +import * as BufferLayout from "buffer-layout"; +import { Layout } from "buffer-layout"; + +export class RustStringLayout extends Layout { + layout = BufferLayout.struct< + Readonly<{ + length?: number; + lengthPadding?: number; + chars: Buffer; + }> + >( + [ + BufferLayout.u32("length"), + BufferLayout.u32("lengthPadding"), + BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), "chars"), + ], + this.property + ); + + constructor(public property?: string) { + super(-1, property); + } + + encode(src: string | null, b: Buffer, offset = 0): number { + if (src === null || src === undefined) { + return this.layout.span; + } + + const data = { + chars: Buffer.from(src, "utf8"), + }; + + return this.layout.encode(data, b, offset); + } + + decode(b: Buffer, offset = 0): string | null { + const data = this.layout.decode(b, offset); + return data["chars"].toString(); + } + + getSpan(b: Buffer, offset = 0): number { + return ( + BufferLayout.u32().span + + BufferLayout.u32().span + + new BN(new Uint8Array(b).slice(offset, offset + 4), 10, "le").toNumber() + ); + } +} From e9dea42094d8b6f6cfe26007c5625eeabc927870 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 22:42:39 +0100 Subject: [PATCH 06/18] feat: setup init nonce account ix --- tests/custom-coder/programs/system/src/lib.rs | 17 + tests/custom-coder/tests/system-coder.ts | 39 +- ts/src/coder/system/instruction.ts | 22 +- ts/src/spl/system.ts | 749 ++++++++++-------- 4 files changed, 471 insertions(+), 356 deletions(-) diff --git a/tests/custom-coder/programs/system/src/lib.rs b/tests/custom-coder/programs/system/src/lib.rs index 69db3b58cf..8aead0cdd5 100644 --- a/tests/custom-coder/programs/system/src/lib.rs +++ b/tests/custom-coder/programs/system/src/lib.rs @@ -44,6 +44,13 @@ pub mod system_program { Ok(()) } + pub fn initialize_nonce_account( + ctx: Context, + authorized: Pubkey, + ) -> Result<()> { + Ok(()) + } + pub fn authorize_nonce_account(ctx: Context, arg: Pubkey) -> Result<()> { Ok(()) } @@ -136,6 +143,16 @@ pub struct WithdrawNonceAccount<'info> { //authorized: Signer<'info>, } +#[derive(Accounts)] +pub struct InitializeNonceAccount<'info> { + #[account(mut)] + /// CHECK: + nonce: AccountInfo<'info>, + /// CHECK: + recent_blockhashes: AccountInfo<'info>, + rent: Sysvar<'info, Rent>, +} + #[derive(Accounts)] pub struct AuthorizeNonceAccount<'info> { #[account(mut)] diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index 5a46bef6fc..5189f1999e 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -2,8 +2,11 @@ import * as anchor from "@project-serum/anchor"; import { Spl } from "@project-serum/anchor"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { - Keypair, PublicKey, - SystemProgram + Keypair, + NONCE_ACCOUNT_LENGTH, + PublicKey, + SystemProgram, + SYSVAR_RECENT_BLOCKHASHES_PUBKEY } from "@solana/web3.js"; import * as assert from "assert"; import BN from "bn.js"; @@ -99,4 +102,36 @@ describe("system-coder", () => { ); assert.notEqual(bobAccount, null); }); + + it("Initializes nonce account", async () => { + // arrange + const nonceKeypair = Keypair.generate(); + const owner = SystemProgram.programId; + const space = NONCE_ACCOUNT_LENGTH; + const lamports = + await provider.connection.getMinimumBalanceForRentExemption(space); + // act + await program.methods + .initializeNonceAccount(provider.wallet.publicKey) + .accounts({ + nonce: nonceKeypair.publicKey, + recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, + }) + .preInstructions([ + await program.methods + .createAccount(new BN(lamports), new BN(space), owner) + .accounts({ + from: provider.wallet.publicKey, + to: nonceKeypair.publicKey, + }) + .instruction(), + ]) + .signers([nonceKeypair]) + .rpc(); + // assert + const nonceAccount = await program.provider.connection.getAccountInfo( + nonceKeypair.publicKey + ); + assert.notEqual(nonceAccount, null); + }); }); diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts index 18ada4be18..da9e6b3028 100644 --- a/ts/src/coder/system/instruction.ts +++ b/ts/src/coder/system/instruction.ts @@ -28,6 +28,9 @@ export class SystemInstructionCoder implements InstructionCoder { case "withdrawNonceAccount": { return encodeWithdrawNonceAccount(ix); } + case "initializeNonceAccount": { + return encodeInitializeNonceAccount(ix); + } case "authorizeNonceAccount": { return encodeAuthorizeNonceAccount(ix); } @@ -93,6 +96,12 @@ function encodeCreateAccountWithSeed({ ); } +function encodeInitializeNonceAccount({ authorized }: any): Buffer { + return encodeData({ + initializeNonceAccount: { authorized: authorized.toBuffer() }, + }); +} + function encodeAdvanceNonceAccount(_ix: any): Buffer { return encodeData({ advanceNonceAccount: {}, @@ -187,16 +196,21 @@ LAYOUT.addVariant( ); LAYOUT.addVariant( 6, + BufferLayout.struct([publicKey("authorized")]), + "initializeNonceAccount" +); +LAYOUT.addVariant( + 7, BufferLayout.struct([publicKey("arg")]), "authorizeNonceAccount" ); LAYOUT.addVariant( - 7, + 8, BufferLayout.struct([BufferLayout.ns64("space")]), "allocate" ); LAYOUT.addVariant( - 8, + 9, BufferLayout.struct([ publicKey("base"), BufferLayout.cstr("seed"), @@ -206,7 +220,7 @@ LAYOUT.addVariant( "allocateWithSeed" ); LAYOUT.addVariant( - 9, + 10, BufferLayout.struct([ publicKey("base"), BufferLayout.cstr("seed"), @@ -215,7 +229,7 @@ LAYOUT.addVariant( "assignWithSeed" ); LAYOUT.addVariant( - 10, + 11, BufferLayout.struct([ BufferLayout.ns64("space"), BufferLayout.cstr("fromSeed"), diff --git a/ts/src/spl/system.ts b/ts/src/spl/system.ts index 848e1532da..5c06116553 100644 --- a/ts/src/spl/system.ts +++ b/ts/src/spl/system.ts @@ -3,10 +3,7 @@ import { Program } from "../program/index.js"; import Provider from "../provider.js"; import { SystemCoder } from "../coder/system/index.js"; - -const SYSTEM_PROGRAM_ID = new PublicKey( - "11111111111111111111111111111111" -); +const SYSTEM_PROGRAM_ID = new PublicKey("11111111111111111111111111111111"); export function program(provider?: Provider): Program { return new Program(IDL, SYSTEM_PROGRAM_ID, provider, coder()); @@ -19,562 +16,614 @@ export function coder(): SystemCoder { /** * System IDL. */ - export type SystemProgram = { - "version": "0.1.0", - "name": "system_program", - "instructions": [ +export type SystemProgram = { + version: "0.1.0"; + name: "system_program"; + instructions: [ { - "name": "createAccount", - "accounts": [ + name: "createAccount"; + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": true + name: "from"; + isMut: true; + isSigner: true; }, { - "name": "to", - "isMut": true, - "isSigner": true + name: "to"; + isMut: true; + isSigner: true; } - ], - "args": [ + ]; + args: [ { - "name": "lamports", - "type": "u64" + name: "lamports"; + type: "u64"; }, { - "name": "space", - "type": "u64" + name: "space"; + type: "u64"; }, { - "name": "owner", - "type": "publicKey" + name: "owner"; + type: "publicKey"; } - ] + ]; }, { - "name": "assign", - "accounts": [ + name: "assign"; + accounts: [ { - "name": "pubkey", - "isMut": true, - "isSigner": true + name: "pubkey"; + isMut: true; + isSigner: true; } - ], - "args": [ + ]; + args: [ { - "name": "owner", - "type": "publicKey" + name: "owner"; + type: "publicKey"; } - ] + ]; }, { - "name": "transfer", - "accounts": [ + name: "transfer"; + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": true + name: "from"; + isMut: true; + isSigner: true; }, { - "name": "to", - "isMut": true, - "isSigner": false + name: "to"; + isMut: true; + isSigner: false; } - ], - "args": [ + ]; + args: [ { - "name": "lamports", - "type": "u64" + name: "lamports"; + type: "u64"; } - ] + ]; }, { - "name": "createAccountWithSeed", - "accounts": [ + name: "createAccountWithSeed"; + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": true + name: "from"; + isMut: true; + isSigner: true; }, { - "name": "to", - "isMut": true, - "isSigner": false + name: "to"; + isMut: true; + isSigner: false; }, { - "name": "base", - "isMut": false, - "isSigner": true + name: "base"; + isMut: false; + isSigner: true; } - ], - "args": [ + ]; + args: [ { - "name": "base", - "type": "publicKey" + name: "base"; + type: "publicKey"; }, { - "name": "seed", - "type": "string" + name: "seed"; + type: "string"; }, { - "name": "lamports", - "type": "u64" + name: "lamports"; + type: "u64"; }, { - "name": "space", - "type": "u64" + name: "space"; + type: "u64"; }, { - "name": "owner", - "type": "publicKey" + name: "owner"; + type: "publicKey"; } - ] + ]; }, { - "name": "advanceNonceAccount", - "accounts": [ + name: "advanceNonceAccount"; + accounts: [ { - "name": "nonce", - "isMut": true, - "isSigner": false + name: "nonce"; + isMut: true; + isSigner: false; } - ], - "args": [] + ]; + args: []; }, { - "name": "withdrawNonceAccount", - "accounts": [ + name: "withdrawNonceAccount"; + accounts: [ { - "name": "nonce", - "isMut": true, - "isSigner": false + name: "nonce"; + isMut: true; + isSigner: false; }, { - "name": "to", - "isMut": true, - "isSigner": false + name: "to"; + isMut: true; + isSigner: false; } - ], - "args": [ + ]; + args: [ { - "name": "arg", - "type": "u64" + name: "arg"; + type: "u64"; } - ] + ]; }, { - "name": "authorizeNonceAccount", - "accounts": [ + name: "initializeNonceAccount"; + accounts: [ + { + name: "nonce"; + isMut: true; + isSigner: false; + }, { - "name": "nonce", - "isMut": true, - "isSigner": false + name: "recentBlockhashes"; + isMut: false; + isSigner: false; }, { - "name": "authorized", - "isMut": false, - "isSigner": true + name: "rent"; + isMut: false; + isSigner: false; } - ], - "args": [ + ]; + args: [ + { + name: "authorized"; + type: "publicKey"; + } + ]; + }, + { + name: "authorizeNonceAccount"; + accounts: [ { - "name": "arg", - "type": "publicKey" + name: "nonce"; + isMut: true; + isSigner: false; + }, + { + name: "authorized"; + isMut: false; + isSigner: true; } - ] + ]; + args: [ + { + name: "arg"; + type: "publicKey"; + } + ]; }, { - "name": "allocate", - "accounts": [ + name: "allocate"; + accounts: [ { - "name": "pubkey", - "isMut": true, - "isSigner": true + name: "pubkey"; + isMut: true; + isSigner: true; } - ], - "args": [ + ]; + args: [ { - "name": "space", - "type": "u64" + name: "space"; + type: "u64"; } - ] + ]; }, { - "name": "allocateWithSeed", - "accounts": [ + name: "allocateWithSeed"; + accounts: [ { - "name": "address", - "isMut": true, - "isSigner": false + name: "address"; + isMut: true; + isSigner: false; }, { - "name": "base", - "isMut": false, - "isSigner": true + name: "base"; + isMut: false; + isSigner: true; } - ], - "args": [ + ]; + args: [ { - "name": "base", - "type": "publicKey" + name: "base"; + type: "publicKey"; }, { - "name": "seed", - "type": "string" + name: "seed"; + type: "string"; }, { - "name": "space", - "type": "u64" + name: "space"; + type: "u64"; }, { - "name": "owner", - "type": "publicKey" + name: "owner"; + type: "publicKey"; } - ] + ]; }, { - "name": "assignWithSeed", - "accounts": [ + name: "assignWithSeed"; + accounts: [ { - "name": "address", - "isMut": true, - "isSigner": false + name: "address"; + isMut: true; + isSigner: false; }, { - "name": "base", - "isMut": false, - "isSigner": true + name: "base"; + isMut: false; + isSigner: true; } - ], - "args": [ + ]; + args: [ { - "name": "base", - "type": "publicKey" + name: "base"; + type: "publicKey"; }, { - "name": "seed", - "type": "string" + name: "seed"; + type: "string"; }, { - "name": "owner", - "type": "publicKey" + name: "owner"; + type: "publicKey"; } - ] + ]; }, { - "name": "transferWithSeed", - "accounts": [ + name: "transferWithSeed"; + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": false + name: "from"; + isMut: true; + isSigner: false; }, { - "name": "fromBase", - "isMut": false, - "isSigner": true + name: "fromBase"; + isMut: false; + isSigner: true; }, { - "name": "to", - "isMut": true, - "isSigner": false + name: "to"; + isMut: true; + isSigner: false; } - ], - "args": [ + ]; + args: [ { - "name": "lamports", - "type": "u64" + name: "lamports"; + type: "u64"; }, { - "name": "fromSeed", - "type": "string" + name: "fromSeed"; + type: "string"; }, { - "name": "fromOwner", - "type": "publicKey" + name: "fromOwner"; + type: "publicKey"; } - ] + ]; } - ] + ]; }; export const IDL: SystemProgram = { - "version": "0.1.0", - "name": "system_program", - "instructions": [ + version: "0.1.0", + name: "system_program", + instructions: [ { - "name": "createAccount", - "accounts": [ + name: "createAccount", + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": true + name: "from", + isMut: true, + isSigner: true, }, { - "name": "to", - "isMut": true, - "isSigner": true - } + name: "to", + isMut: true, + isSigner: true, + }, ], - "args": [ + args: [ { - "name": "lamports", - "type": "u64" + name: "lamports", + type: "u64", }, { - "name": "space", - "type": "u64" + name: "space", + type: "u64", }, { - "name": "owner", - "type": "publicKey" - } - ] + name: "owner", + type: "publicKey", + }, + ], }, { - "name": "assign", - "accounts": [ + name: "assign", + accounts: [ { - "name": "pubkey", - "isMut": true, - "isSigner": true - } + name: "pubkey", + isMut: true, + isSigner: true, + }, ], - "args": [ + args: [ { - "name": "owner", - "type": "publicKey" - } - ] + name: "owner", + type: "publicKey", + }, + ], }, { - "name": "transfer", - "accounts": [ + name: "transfer", + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": true + name: "from", + isMut: true, + isSigner: true, }, { - "name": "to", - "isMut": true, - "isSigner": false - } + name: "to", + isMut: true, + isSigner: false, + }, ], - "args": [ + args: [ { - "name": "lamports", - "type": "u64" - } - ] + name: "lamports", + type: "u64", + }, + ], }, { - "name": "createAccountWithSeed", - "accounts": [ + name: "createAccountWithSeed", + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": true + name: "from", + isMut: true, + isSigner: true, }, { - "name": "to", - "isMut": true, - "isSigner": false + name: "to", + isMut: true, + isSigner: false, }, { - "name": "base", - "isMut": false, - "isSigner": true - } + name: "base", + isMut: false, + isSigner: true, + }, ], - "args": [ + args: [ { - "name": "base", - "type": "publicKey" + name: "base", + type: "publicKey", }, { - "name": "seed", - "type": "string" + name: "seed", + type: "string", }, { - "name": "lamports", - "type": "u64" + name: "lamports", + type: "u64", }, { - "name": "space", - "type": "u64" + name: "space", + type: "u64", }, { - "name": "owner", - "type": "publicKey" - } - ] + name: "owner", + type: "publicKey", + }, + ], }, { - "name": "advanceNonceAccount", - "accounts": [ + name: "advanceNonceAccount", + accounts: [ { - "name": "nonce", - "isMut": true, - "isSigner": false - } + name: "nonce", + isMut: true, + isSigner: false, + }, ], - "args": [] + args: [], }, { - "name": "withdrawNonceAccount", - "accounts": [ + name: "withdrawNonceAccount", + accounts: [ { - "name": "nonce", - "isMut": true, - "isSigner": false + name: "nonce", + isMut: true, + isSigner: false, }, { - "name": "to", - "isMut": true, - "isSigner": false - } + name: "to", + isMut: true, + isSigner: false, + }, ], - "args": [ + args: [ { - "name": "arg", - "type": "u64" - } - ] + name: "arg", + type: "u64", + }, + ], }, { - "name": "authorizeNonceAccount", - "accounts": [ + name: "initializeNonceAccount", + accounts: [ { - "name": "nonce", - "isMut": true, - "isSigner": false + name: "nonce", + isMut: true, + isSigner: false, }, { - "name": "authorized", - "isMut": false, - "isSigner": true - } + name: "recentBlockhashes", + isMut: false, + isSigner: false, + }, + { + name: "rent", + isMut: false, + isSigner: false, + }, ], - "args": [ + args: [ { - "name": "arg", - "type": "publicKey" - } - ] + name: "authorized", + type: "publicKey", + }, + ], }, { - "name": "allocate", - "accounts": [ + name: "authorizeNonceAccount", + accounts: [ { - "name": "pubkey", - "isMut": true, - "isSigner": true - } + name: "nonce", + isMut: true, + isSigner: false, + }, + { + name: "authorized", + isMut: false, + isSigner: true, + }, ], - "args": [ + args: [ { - "name": "space", - "type": "u64" - } - ] + name: "arg", + type: "publicKey", + }, + ], }, { - "name": "allocateWithSeed", - "accounts": [ + name: "allocate", + accounts: [ { - "name": "address", - "isMut": true, - "isSigner": false + name: "pubkey", + isMut: true, + isSigner: true, }, + ], + args: [ { - "name": "base", - "isMut": false, - "isSigner": true - } + name: "space", + type: "u64", + }, ], - "args": [ + }, + { + name: "allocateWithSeed", + accounts: [ + { + name: "address", + isMut: true, + isSigner: false, + }, { - "name": "base", - "type": "publicKey" + name: "base", + isMut: false, + isSigner: true, }, + ], + args: [ { - "name": "seed", - "type": "string" + name: "base", + type: "publicKey", }, { - "name": "space", - "type": "u64" + name: "seed", + type: "string", }, { - "name": "owner", - "type": "publicKey" - } - ] + name: "space", + type: "u64", + }, + { + name: "owner", + type: "publicKey", + }, + ], }, { - "name": "assignWithSeed", - "accounts": [ + name: "assignWithSeed", + accounts: [ { - "name": "address", - "isMut": true, - "isSigner": false + name: "address", + isMut: true, + isSigner: false, }, { - "name": "base", - "isMut": false, - "isSigner": true - } + name: "base", + isMut: false, + isSigner: true, + }, ], - "args": [ + args: [ { - "name": "base", - "type": "publicKey" + name: "base", + type: "publicKey", }, { - "name": "seed", - "type": "string" + name: "seed", + type: "string", }, { - "name": "owner", - "type": "publicKey" - } - ] + name: "owner", + type: "publicKey", + }, + ], }, { - "name": "transferWithSeed", - "accounts": [ + name: "transferWithSeed", + accounts: [ { - "name": "from", - "isMut": true, - "isSigner": false + name: "from", + isMut: true, + isSigner: false, }, { - "name": "fromBase", - "isMut": false, - "isSigner": true + name: "fromBase", + isMut: false, + isSigner: true, }, { - "name": "to", - "isMut": true, - "isSigner": false - } + name: "to", + isMut: true, + isSigner: false, + }, ], - "args": [ + args: [ { - "name": "lamports", - "type": "u64" + name: "lamports", + type: "u64", }, { - "name": "fromSeed", - "type": "string" + name: "fromSeed", + type: "string", }, { - "name": "fromOwner", - "type": "publicKey" - } - ] - } - ] + name: "fromOwner", + type: "publicKey", + }, + ], + }, + ], }; From 00ad0d443f10acfe484fcf1130526cb9a61e6f64 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 22:55:35 +0100 Subject: [PATCH 07/18] test: add transfer test case --- tests/custom-coder/tests/system-coder.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index 5189f1999e..fb541fd0c5 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -3,6 +3,7 @@ import { Spl } from "@project-serum/anchor"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { Keypair, + LAMPORTS_PER_SOL, NONCE_ACCOUNT_LENGTH, PublicKey, SystemProgram, @@ -134,4 +135,24 @@ describe("system-coder", () => { ); assert.notEqual(nonceAccount, null); }); + + it("Transfers lamports", async () => { + // arrange + const receiverKeypair = Keypair.generate(); + const lamports = 0.1* LAMPORTS_PER_SOL; + // act + await program.methods + .transfer(new BN(lamports)) + .accounts({ + from: provider.wallet.publicKey, + to: receiverKeypair.publicKey, + }) + .rpc(); + // assert + const receiverAccount = await program.provider.connection.getAccountInfo( + receiverKeypair.publicKey + ); + assert.notEqual(receiverAccount, null); + assert.equal(lamports, receiverAccount.lamports); + }); }); From 33b324bd52a53406f064c0a192a90872616df1d8 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 23:33:15 +0100 Subject: [PATCH 08/18] feat: setup advance nonce ix --- tests/custom-coder/programs/system/src/lib.rs | 29 ++++--- tests/custom-coder/tests/system-coder.ts | 62 ++++++++++++--- ts/src/spl/system.ts | 76 +++++++++++++++++-- 3 files changed, 137 insertions(+), 30 deletions(-) diff --git a/tests/custom-coder/programs/system/src/lib.rs b/tests/custom-coder/programs/system/src/lib.rs index 8aead0cdd5..41a7eebe7d 100644 --- a/tests/custom-coder/programs/system/src/lib.rs +++ b/tests/custom-coder/programs/system/src/lib.rs @@ -2,7 +2,7 @@ use anchor_lang::prelude::*; -declare_id!("9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"); +declare_id!("11111111111111111111111111111111"); #[program] pub mod system_program { @@ -36,11 +36,14 @@ pub mod system_program { Ok(()) } - pub fn advance_nonce_account(ctx: Context) -> Result<()> { + pub fn advance_nonce_account( + ctx: Context, + authorized: Pubkey, + ) -> Result<()> { Ok(()) } - pub fn withdraw_nonce_account(ctx: Context, arg: u64) -> Result<()> { + pub fn withdraw_nonce_account(ctx: Context, lamports: u64) -> Result<()> { Ok(()) } @@ -51,7 +54,10 @@ pub mod system_program { Ok(()) } - pub fn authorize_nonce_account(ctx: Context, arg: Pubkey) -> Result<()> { + pub fn authorize_nonce_account( + ctx: Context, + authorized: Pubkey, + ) -> Result<()> { Ok(()) } @@ -126,8 +132,9 @@ pub struct AdvanceNonceAccount<'info> { #[account(mut)] /// CHECK: nonce: AccountInfo<'info>, - //recent_blockhashes::id): AccountInfo<'info>, - //authorized: Signer<'info>, + /// CHECK: + recent_blockhashes: AccountInfo<'info>, + authorized: Signer<'info>, } #[derive(Accounts)] @@ -138,16 +145,16 @@ pub struct WithdrawNonceAccount<'info> { #[account(mut)] /// CHECK: to: AccountInfo<'info>, - //recent_blockhashes::id): AccountInfo<'info>, - //rent: Sysvar<'info, Rent>, - //authorized: Signer<'info>, + /// CHECK: + recent_blockhashes: AccountInfo<'info>, + rent: Sysvar<'info, Rent>, + authorized: Signer<'info>, } #[derive(Accounts)] pub struct InitializeNonceAccount<'info> { #[account(mut)] - /// CHECK: - nonce: AccountInfo<'info>, + nonce: Signer<'info>, /// CHECK: recent_blockhashes: AccountInfo<'info>, rent: Sysvar<'info, Rent>, diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index fb541fd0c5..d2f1ba7af7 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -7,7 +7,7 @@ import { NONCE_ACCOUNT_LENGTH, PublicKey, SystemProgram, - SYSVAR_RECENT_BLOCKHASHES_PUBKEY + SYSVAR_RECENT_BLOCKHASHES_PUBKEY, } from "@solana/web3.js"; import * as assert from "assert"; import BN from "bn.js"; @@ -104,6 +104,26 @@ describe("system-coder", () => { assert.notEqual(bobAccount, null); }); + it("Transfers lamports", async () => { + // arrange + const receiverKeypair = Keypair.generate(); + const lamports = 0.1 * LAMPORTS_PER_SOL; + // act + await program.methods + .transfer(new BN(lamports)) + .accounts({ + from: provider.wallet.publicKey, + to: receiverKeypair.publicKey, + }) + .rpc(); + // assert + const receiverAccount = await program.provider.connection.getAccountInfo( + receiverKeypair.publicKey + ); + assert.notEqual(receiverAccount, null); + assert.equal(lamports, receiverAccount.lamports); + }); + it("Initializes nonce account", async () => { // arrange const nonceKeypair = Keypair.generate(); @@ -136,23 +156,43 @@ describe("system-coder", () => { assert.notEqual(nonceAccount, null); }); - it("Transfers lamports", async () => { + it("Advances a nonce", async () => { // arrange - const receiverKeypair = Keypair.generate(); - const lamports = 0.1* LAMPORTS_PER_SOL; + const nonceKeypair = Keypair.generate(); + const owner = SystemProgram.programId; + const space = NONCE_ACCOUNT_LENGTH; + const lamports = + await provider.connection.getMinimumBalanceForRentExemption(space); // act await program.methods - .transfer(new BN(lamports)) + .initializeNonceAccount(provider.wallet.publicKey) .accounts({ - from: provider.wallet.publicKey, - to: receiverKeypair.publicKey, + nonce: nonceKeypair.publicKey, + recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, + }) + .preInstructions([ + await program.methods + .createAccount(new BN(lamports), new BN(space), owner) + .accounts({ + from: provider.wallet.publicKey, + to: nonceKeypair.publicKey, + }) + .instruction(), + ]) + .signers([nonceKeypair]) + .rpc(); + await new Promise((r) => setTimeout(r, 500)); // Wait for next slot + await program.methods + .advanceNonceAccount(provider.wallet.publicKey) + .accounts({ + nonce: nonceKeypair.publicKey, + recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, }) .rpc(); // assert - const receiverAccount = await program.provider.connection.getAccountInfo( - receiverKeypair.publicKey + const nonceAccount = await program.provider.connection.getAccountInfo( + nonceKeypair.publicKey ); - assert.notEqual(receiverAccount, null); - assert.equal(lamports, receiverAccount.lamports); + assert.notEqual(nonceAccount, null); }); }); diff --git a/ts/src/spl/system.ts b/ts/src/spl/system.ts index 5c06116553..aa0d84acd1 100644 --- a/ts/src/spl/system.ts +++ b/ts/src/spl/system.ts @@ -135,9 +135,24 @@ export type SystemProgram = { name: "nonce"; isMut: true; isSigner: false; + }, + { + name: "recentBlockhashes"; + isMut: false; + isSigner: false; + }, + { + name: "authorized"; + isMut: false; + isSigner: true; + } + ]; + args: [ + { + name: "authorized"; + type: "publicKey"; } ]; - args: []; }, { name: "withdrawNonceAccount"; @@ -151,11 +166,26 @@ export type SystemProgram = { name: "to"; isMut: true; isSigner: false; + }, + { + name: "recentBlockhashes"; + isMut: false; + isSigner: false; + }, + { + name: "rent"; + isMut: false; + isSigner: false; + }, + { + name: "authorized"; + isMut: false; + isSigner: true; } ]; args: [ { - name: "arg"; + name: "lamports"; type: "u64"; } ]; @@ -166,7 +196,7 @@ export type SystemProgram = { { name: "nonce"; isMut: true; - isSigner: false; + isSigner: true; }, { name: "recentBlockhashes"; @@ -202,7 +232,7 @@ export type SystemProgram = { ]; args: [ { - name: "arg"; + name: "authorized"; type: "publicKey"; } ]; @@ -442,8 +472,23 @@ export const IDL: SystemProgram = { isMut: true, isSigner: false, }, + { + name: "recentBlockhashes", + isMut: false, + isSigner: false, + }, + { + name: "authorized", + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: "authorized", + type: "publicKey", + }, ], - args: [], }, { name: "withdrawNonceAccount", @@ -458,10 +503,25 @@ export const IDL: SystemProgram = { isMut: true, isSigner: false, }, + { + name: "recentBlockhashes", + isMut: false, + isSigner: false, + }, + { + name: "rent", + isMut: false, + isSigner: false, + }, + { + name: "authorized", + isMut: false, + isSigner: true, + }, ], args: [ { - name: "arg", + name: "lamports", type: "u64", }, ], @@ -472,7 +532,7 @@ export const IDL: SystemProgram = { { name: "nonce", isMut: true, - isSigner: false, + isSigner: true, }, { name: "recentBlockhashes", @@ -508,7 +568,7 @@ export const IDL: SystemProgram = { ], args: [ { - name: "arg", + name: "authorized", type: "publicKey", }, ], From a40c34e2b8fbbeed16e2e96dd3dca1ec23139a9f Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 23:51:15 +0100 Subject: [PATCH 09/18] fix: rename properties accordingly --- ts/src/coder/system/instruction.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts index da9e6b3028..f3d4f08ba8 100644 --- a/ts/src/coder/system/instruction.ts +++ b/ts/src/coder/system/instruction.ts @@ -102,21 +102,21 @@ function encodeInitializeNonceAccount({ authorized }: any): Buffer { }); } -function encodeAdvanceNonceAccount(_ix: any): Buffer { +function encodeAdvanceNonceAccount({ authorized }: any): Buffer { return encodeData({ - advanceNonceAccount: {}, + advanceNonceAccount: { authorized: authorized.toBuffer() }, }); } -function encodeWithdrawNonceAccount({ arg }: any): Buffer { +function encodeWithdrawNonceAccount({ lamports }: any): Buffer { return encodeData({ - withdrawNonceAccount: { arg }, + withdrawNonceAccount: { lamports }, }); } -function encodeAuthorizeNonceAccount({ arg }: any): Buffer { +function encodeAuthorizeNonceAccount({ authorized }: any): Buffer { return encodeData({ - authorizeNonceAccount: { arg: arg.toBuffer() }, + authorizeNonceAccount: { authorized: authorized.toBuffer() }, }); } @@ -188,10 +188,14 @@ const CREATE_ACCOUNT_WITH_SEED_LAYOUT = LAYOUT.addVariant( ]), "createAccountWithSeed" ); -LAYOUT.addVariant(4, BufferLayout.struct([]), "advanceNonceAccount"); +LAYOUT.addVariant( + 4, + BufferLayout.struct([publicKey("authorized")]), + "advanceNonceAccount" +); LAYOUT.addVariant( 5, - BufferLayout.struct([BufferLayout.ns64("arg")]), + BufferLayout.struct([BufferLayout.ns64("lamports")]), "withdrawNonceAccount" ); LAYOUT.addVariant( @@ -201,7 +205,7 @@ LAYOUT.addVariant( ); LAYOUT.addVariant( 7, - BufferLayout.struct([publicKey("arg")]), + BufferLayout.struct([publicKey("authorized")]), "authorizeNonceAccount" ); LAYOUT.addVariant( From 6ccf7e242c71fa847c5bd3a3ca3fdd090eacbf04 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Tue, 24 May 2022 23:52:16 +0100 Subject: [PATCH 10/18] test: add test case for withdraw from nonce --- tests/custom-coder/tests/system-coder.ts | 135 ++++++++++++++++++++--- 1 file changed, 122 insertions(+), 13 deletions(-) diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index d2f1ba7af7..02c087ccf2 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -8,6 +8,7 @@ import { PublicKey, SystemProgram, SYSVAR_RECENT_BLOCKHASHES_PUBKEY, + SYSVAR_RENT_PUBKEY, } from "@solana/web3.js"; import * as assert from "assert"; import BN from "bn.js"; @@ -21,7 +22,7 @@ describe("system-coder", () => { const program = Spl.system(); // Constants. - const aliceTokenKeypair = Keypair.generate(); + const aliceKeypair = Keypair.generate(); it("Creates an account", async () => { // arrange @@ -36,13 +37,13 @@ describe("system-coder", () => { .createAccount(new BN(lamports), new BN(space), owner) .accounts({ from: provider.wallet.publicKey, - to: aliceTokenKeypair.publicKey, + to: aliceKeypair.publicKey, }) - .signers([aliceTokenKeypair]) + .signers([aliceKeypair]) .rpc(); // assert const aliceAccount = await program.provider.connection.getAccountInfo( - aliceTokenKeypair.publicKey + aliceKeypair.publicKey ); assert.notEqual(aliceAccount, null); assert.ok(owner.equals(aliceAccount.owner)); @@ -56,13 +57,13 @@ describe("system-coder", () => { await program.methods .assign(owner) .accounts({ - pubkey: aliceTokenKeypair.publicKey, + pubkey: aliceKeypair.publicKey, }) - .signers([aliceTokenKeypair]) + .signers([aliceKeypair]) .rpc(); // assert const aliceAccount = await program.provider.connection.getAccountInfo( - aliceTokenKeypair.publicKey + aliceKeypair.publicKey ); assert.notEqual(aliceAccount, null); assert.ok(owner.equals(aliceAccount.owner)); @@ -77,25 +78,25 @@ describe("system-coder", () => { const owner = SystemProgram.programId; const seed = "seeds"; const bobPublicKey = await PublicKey.createWithSeed( - aliceTokenKeypair.publicKey, + aliceKeypair.publicKey, seed, owner ); // act await program.methods .createAccountWithSeed( - aliceTokenKeypair.publicKey, + aliceKeypair.publicKey, seed, new BN(lamports), new BN(space), owner ) .accounts({ - base: aliceTokenKeypair.publicKey, + base: aliceKeypair.publicKey, from: provider.wallet.publicKey, to: bobPublicKey, }) - .signers([aliceTokenKeypair]) + .signers([aliceKeypair]) .rpc(); // assert const bobAccount = await program.provider.connection.getAccountInfo( @@ -156,7 +157,7 @@ describe("system-coder", () => { assert.notEqual(nonceAccount, null); }); - it("Advances a nonce", async () => { + it("Advances a nonce account", async () => { // arrange const nonceKeypair = Keypair.generate(); const owner = SystemProgram.programId; @@ -181,7 +182,7 @@ describe("system-coder", () => { ]) .signers([nonceKeypair]) .rpc(); - await new Promise((r) => setTimeout(r, 500)); // Wait for next slot + // These have to be separate to make sure advance is in another slot. await program.methods .advanceNonceAccount(provider.wallet.publicKey) .accounts({ @@ -195,4 +196,112 @@ describe("system-coder", () => { ); assert.notEqual(nonceAccount, null); }); + + it("Authorizes a nonce account", async () => { + // arrange + const nonceKeypair = Keypair.generate(); + const owner = SystemProgram.programId; + const space = NONCE_ACCOUNT_LENGTH; + const lamports = + await provider.connection.getMinimumBalanceForRentExemption(space); + // act + await program.methods + .initializeNonceAccount(provider.wallet.publicKey) + .accounts({ + nonce: nonceKeypair.publicKey, + recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, + }) + .preInstructions([ + await program.methods + .createAccount(new BN(lamports), new BN(space), owner) + .accounts({ + from: provider.wallet.publicKey, + to: nonceKeypair.publicKey, + }) + .instruction(), + ]) + .signers([nonceKeypair]) + .rpc(); + await program.methods + .authorizeNonceAccount(aliceKeypair.publicKey) + .accounts({ + nonce: nonceKeypair.publicKey, + authorized: provider.wallet.publicKey, + }) + .rpc(); + // assert + const nonceAccount = await program.provider.connection.getAccountInfo( + nonceKeypair.publicKey + ); + assert.notEqual(nonceAccount, null); + }); + + it("Withdraws from nonce account", async () => { + // arrange + const nonceKeypair = Keypair.generate(); + const owner = SystemProgram.programId; + const space = NONCE_ACCOUNT_LENGTH; + const lamports = + await provider.connection.getMinimumBalanceForRentExemption(space); + const amount = 0.1 * LAMPORTS_PER_SOL; + const aliceBalanceBefore = ( + await program.provider.connection.getAccountInfo(aliceKeypair.publicKey) + ).lamports; + // act + await program.methods + .initializeNonceAccount(provider.wallet.publicKey) + .accounts({ + nonce: nonceKeypair.publicKey, + recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, + }) + .preInstructions([ + await program.methods + .createAccount(new BN(lamports), new BN(space), owner) + .accounts({ + from: provider.wallet.publicKey, + to: nonceKeypair.publicKey, + }) + .instruction(), + ]) + .signers([nonceKeypair]) + .rpc(); + await program.methods + .advanceNonceAccount(provider.wallet.publicKey) + .accounts({ + nonce: nonceKeypair.publicKey, + recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, + }) + .postInstructions([ + await program.methods + .transfer(new BN(amount)) + .accounts({ + from: provider.wallet.publicKey, + to: nonceKeypair.publicKey, + }) + .instruction(), + ]) + .rpc(); + await program.methods + .authorizeNonceAccount(aliceKeypair.publicKey) + .accounts({ + nonce: nonceKeypair.publicKey, + authorized: provider.wallet.publicKey, + }) + .rpc(); + await program.methods + .withdrawNonceAccount(new BN(amount)) + .accounts({ + authorized: aliceKeypair.publicKey, + nonce: nonceKeypair.publicKey, + recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, + to: aliceKeypair.publicKey, + }) + .signers([aliceKeypair]) + .rpc(); + // assert + const aliceBalanceAfter = ( + await program.provider.connection.getAccountInfo(aliceKeypair.publicKey) + ).lamports; + assert.equal(aliceBalanceAfter - aliceBalanceBefore, amount); + }); }); From 1d4d9065d1c283e830d5a7949396111d677d2de2 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 18:13:10 +0100 Subject: [PATCH 11/18] test: setup allocate test case --- tests/custom-coder/tests/system-coder.ts | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index 02c087ccf2..f9cf91f9f8 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -8,7 +8,6 @@ import { PublicKey, SystemProgram, SYSVAR_RECENT_BLOCKHASHES_PUBKEY, - SYSVAR_RENT_PUBKEY, } from "@solana/web3.js"; import * as assert from "assert"; import BN from "bn.js"; @@ -69,6 +68,38 @@ describe("system-coder", () => { assert.ok(owner.equals(aliceAccount.owner)); }); + it("Allocates space to an account", async () => { + // arrange + const newKeypair = Keypair.generate(); + const space = 100; + const lamports = + await program.provider.connection.getMinimumBalanceForRentExemption( + space + ); + // act + await program.methods + .allocate(new BN(space)) + .accounts({ + pubkey: newKeypair.publicKey, + }) + .postInstructions([ + await program.methods + .transfer(new BN(lamports)) + .accounts({ + from: provider.wallet.publicKey, + to: newKeypair.publicKey, + }) + .instruction(), + ]) + .signers([newKeypair]) + .rpc(); + // assert + const newAccountAfter = await program.provider.connection.getAccountInfo( + newKeypair.publicKey + ); + assert.equal(space, newAccountAfter.data.byteLength); + }); + it("Creates an account with seed", async () => { const space = 100; const lamports = From 16157ef9af5e8d9c51bbd10fb94f6f4b43692fe8 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 19:42:20 +0100 Subject: [PATCH 12/18] feat: setup with seed methods --- tests/custom-coder/programs/system/src/lib.rs | 10 +-- tests/custom-coder/tests/system-coder.ts | 86 +++++++++++++++++++ ts/src/coder/system/instruction.ts | 69 ++++++++------- ts/src/spl/system.ts | 20 ++--- 4 files changed, 140 insertions(+), 45 deletions(-) diff --git a/tests/custom-coder/programs/system/src/lib.rs b/tests/custom-coder/programs/system/src/lib.rs index 41a7eebe7d..f9020c3933 100644 --- a/tests/custom-coder/programs/system/src/lib.rs +++ b/tests/custom-coder/programs/system/src/lib.rs @@ -87,8 +87,8 @@ pub mod system_program { pub fn transfer_with_seed( ctx: Context, lamports: u64, - from_seed: String, - from_owner: Pubkey, + seed: String, + owner: Pubkey, ) -> Result<()> { Ok(()) } @@ -178,7 +178,7 @@ pub struct Allocate<'info> { pub struct AllocateWithSeed<'info> { #[account(mut)] /// CHECK: - address: AccountInfo<'info>, + account: AccountInfo<'info>, base: Signer<'info>, } @@ -186,7 +186,7 @@ pub struct AllocateWithSeed<'info> { pub struct AssignWithSeed<'info> { #[account(mut)] /// CHECK: - address: AccountInfo<'info>, + account: AccountInfo<'info>, base: Signer<'info>, } @@ -195,7 +195,7 @@ pub struct TransferWithSeed<'info> { #[account(mut)] /// CHECK: from: AccountInfo<'info>, - from_base: Signer<'info>, + base: Signer<'info>, #[account(mut)] /// CHECK: to: AccountInfo<'info>, diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index f9cf91f9f8..48dadb7807 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -136,6 +136,92 @@ describe("system-coder", () => { assert.notEqual(bobAccount, null); }); + + it("Allocates and assigns an account with seed", async () => { + const owner = TOKEN_PROGRAM_ID; + const seed = "seeds2"; + const space = 100; + const lamports = + await program.provider.connection.getMinimumBalanceForRentExemption( + space + ); + const bobPublicKey = await PublicKey.createWithSeed( + aliceKeypair.publicKey, + seed, + owner + ); + // act + await program.methods + .allocateWithSeed(aliceKeypair.publicKey, seed, new BN(space), owner) + .accounts({ + base: aliceKeypair.publicKey, + account: bobPublicKey, + }) + .postInstructions([ + await program.methods + .transfer(new BN(lamports)) + .accounts({ + from: provider.wallet.publicKey, + to: bobPublicKey, + }) + .instruction(), + await program.methods + .assignWithSeed(aliceKeypair.publicKey, seed, owner) + .accounts({ + base: aliceKeypair.publicKey, + account: bobPublicKey, + }) + .instruction(), + ]) + .signers([aliceKeypair]) + .rpc(); + // assert + const bobAccount = await program.provider.connection.getAccountInfo( + bobPublicKey + ); + assert.notEqual(bobAccount, null); + assert.ok(owner.equals(bobAccount.owner)); + }); + + it("Transfers from account with seed", async () => { + const lamports = 1 * LAMPORTS_PER_SOL; + const owner = SystemProgram.programId; + const seed = "seeds3"; + const bobPublicKey = await PublicKey.createWithSeed( + aliceKeypair.publicKey, + seed, + owner + ); + const aliceAccountBefore = await program.provider.connection.getAccountInfo( + aliceKeypair.publicKey + ); + // act + await program.methods + .transfer(new BN(lamports)) + .accounts({ + from: provider.wallet.publicKey, + to: bobPublicKey, + }) + .rpc(); + await program.methods + .transferWithSeed(new BN(lamports), seed, owner) + .accounts({ + from: bobPublicKey, + base: aliceKeypair.publicKey, + to: aliceKeypair.publicKey, + }) + .signers([aliceKeypair]) + .rpc(); + // assert + const aliceAccountAfter = await program.provider.connection.getAccountInfo( + aliceKeypair.publicKey + ); + assert.equal( + aliceAccountBefore.lamports + lamports, + aliceAccountAfter.lamports + ); + }); + it("Transfers lamports", async () => { // arrange const receiverKeypair = Keypair.generate(); diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts index f3d4f08ba8..ab70076eaa 100644 --- a/ts/src/coder/system/instruction.ts +++ b/ts/src/coder/system/instruction.ts @@ -92,7 +92,7 @@ function encodeCreateAccountWithSeed({ owner: owner.toBuffer(), }, }, - CREATE_ACCOUNT_WITH_SEED_LAYOUT.span + seed.length + LAYOUT.getVariant(3).span + seed.length ); } @@ -127,38 +127,47 @@ function encodeAllocate({ space }: any): Buffer { } function encodeAllocateWithSeed({ base, seed, space, owner }: any): Buffer { - return encodeData({ - allocateWithSeed: { - base: base.toBuffer(), - seed, - space, - owner: owner.toBuffer(), + return encodeData( + { + allocateWithSeed: { + base: base.toBuffer(), + seed, + space, + owner: owner.toBuffer(), + }, }, - }); + LAYOUT.getVariant(9).span + seed.length + ); } function encodeAssignWithSeed({ base, seed, owner }: any): Buffer { - return encodeData({ - assignWithSeed: { - base: base.toBuffer(), - seed, - owner: owner.toBuffer(), + return encodeData( + { + assignWithSeed: { + base: base.toBuffer(), + seed, + owner: owner.toBuffer(), + }, }, - }); + LAYOUT.getVariant(10).span + seed.length + ); } function encodeTransferWithSeed({ lamports, - fromSeed, - fromOwner, + seed, + owner, }: any): Buffer { - return encodeData({ - transferWithSeed: { - lamports, - fromSeed, - fromOwner: fromOwner.toBuffer(), + return encodeData( + { + transferWithSeed: { + lamports, + seed, + owner: owner.toBuffer(), + }, }, - }); + LAYOUT.getVariant(11).span + seed.length + ); } const LAYOUT = BufferLayout.union(BufferLayout.u32("instruction")); @@ -177,7 +186,7 @@ LAYOUT.addVariant( BufferLayout.struct([BufferLayout.ns64("lamports")]), "transfer" ); -const CREATE_ACCOUNT_WITH_SEED_LAYOUT = LAYOUT.addVariant( +LAYOUT.addVariant( 3, BufferLayout.struct([ publicKey("base"), @@ -189,8 +198,8 @@ const CREATE_ACCOUNT_WITH_SEED_LAYOUT = LAYOUT.addVariant( "createAccountWithSeed" ); LAYOUT.addVariant( - 4, - BufferLayout.struct([publicKey("authorized")]), + 4, + BufferLayout.struct([publicKey("authorized")]), "advanceNonceAccount" ); LAYOUT.addVariant( @@ -217,7 +226,7 @@ LAYOUT.addVariant( 9, BufferLayout.struct([ publicKey("base"), - BufferLayout.cstr("seed"), + new RustStringLayout("seed"), BufferLayout.ns64("space"), publicKey("owner"), ]), @@ -227,7 +236,7 @@ LAYOUT.addVariant( 10, BufferLayout.struct([ publicKey("base"), - BufferLayout.cstr("seed"), + new RustStringLayout("seed"), publicKey("owner"), ]), "assignWithSeed" @@ -235,9 +244,9 @@ LAYOUT.addVariant( LAYOUT.addVariant( 11, BufferLayout.struct([ - BufferLayout.ns64("space"), - BufferLayout.cstr("fromSeed"), - publicKey("fromOwner"), + BufferLayout.ns64("lamports"), + new RustStringLayout("seed"), + publicKey("owner"), ]), "transferWithSeed" ); diff --git a/ts/src/spl/system.ts b/ts/src/spl/system.ts index aa0d84acd1..daacad473f 100644 --- a/ts/src/spl/system.ts +++ b/ts/src/spl/system.ts @@ -257,7 +257,7 @@ export type SystemProgram = { name: "allocateWithSeed"; accounts: [ { - name: "address"; + name: "account"; isMut: true; isSigner: false; }, @@ -290,7 +290,7 @@ export type SystemProgram = { name: "assignWithSeed"; accounts: [ { - name: "address"; + name: "account"; isMut: true; isSigner: false; }, @@ -324,7 +324,7 @@ export type SystemProgram = { isSigner: false; }, { - name: "fromBase"; + name: "base"; isMut: false; isSigner: true; }, @@ -340,11 +340,11 @@ export type SystemProgram = { type: "u64"; }, { - name: "fromSeed"; + name: "seed"; type: "string"; }, { - name: "fromOwner"; + name: "owner"; type: "publicKey"; } ]; @@ -593,7 +593,7 @@ export const IDL: SystemProgram = { name: "allocateWithSeed", accounts: [ { - name: "address", + name: "account", isMut: true, isSigner: false, }, @@ -626,7 +626,7 @@ export const IDL: SystemProgram = { name: "assignWithSeed", accounts: [ { - name: "address", + name: "account", isMut: true, isSigner: false, }, @@ -660,7 +660,7 @@ export const IDL: SystemProgram = { isSigner: false, }, { - name: "fromBase", + name: "base", isMut: false, isSigner: true, }, @@ -676,11 +676,11 @@ export const IDL: SystemProgram = { type: "u64", }, { - name: "fromSeed", + name: "seed", type: "string", }, { - name: "fromOwner", + name: "owner", type: "publicKey", }, ], From 72a2bbd674594558785b4d56f7572b7106799f7a Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 20:47:55 +0100 Subject: [PATCH 13/18] feat: setup nonce account coder --- tests/custom-coder/programs/system/src/lib.rs | 14 +++ tests/custom-coder/tests/system-coder.ts | 9 +- ts/src/coder/system/accounts.ts | 91 +++++++++++++++++- ts/src/coder/system/instruction.ts | 69 ++++++++++++-- ts/src/coder/system/layout.ts | 49 ---------- ts/src/spl/system.ts | 92 +++++++++++++++++++ 6 files changed, 257 insertions(+), 67 deletions(-) delete mode 100644 ts/src/coder/system/layout.ts diff --git a/tests/custom-coder/programs/system/src/lib.rs b/tests/custom-coder/programs/system/src/lib.rs index f9020c3933..b3daea249f 100644 --- a/tests/custom-coder/programs/system/src/lib.rs +++ b/tests/custom-coder/programs/system/src/lib.rs @@ -200,3 +200,17 @@ pub struct TransferWithSeed<'info> { /// CHECK: to: AccountInfo<'info>, } + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct FeeCalculator { + pub lamports_per_signature: u64, +} + +#[account] +pub struct Nonce { + pub version: u32, + pub state: u32, + pub authorized_pubkey: Pubkey, + pub nonce: Pubkey, + pub fee_calculator: FeeCalculator, +} diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index 48dadb7807..b0e8570464 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -136,7 +136,6 @@ describe("system-coder", () => { assert.notEqual(bobAccount, null); }); - it("Allocates and assigns an account with seed", async () => { const owner = TOKEN_PROGRAM_ID; const seed = "seeds2"; @@ -268,10 +267,11 @@ describe("system-coder", () => { .signers([nonceKeypair]) .rpc(); // assert - const nonceAccount = await program.provider.connection.getAccountInfo( + const nonceAccount = await program.account.nonce.fetch( nonceKeypair.publicKey ); assert.notEqual(nonceAccount, null); + assert.ok(nonceAccount.authorizedPubkey.equals(provider.wallet.publicKey)); }); it("Advances a nonce account", async () => { @@ -308,7 +308,7 @@ describe("system-coder", () => { }) .rpc(); // assert - const nonceAccount = await program.provider.connection.getAccountInfo( + const nonceAccount = await program.account.nonce.fetch( nonceKeypair.publicKey ); assert.notEqual(nonceAccount, null); @@ -347,10 +347,11 @@ describe("system-coder", () => { }) .rpc(); // assert - const nonceAccount = await program.provider.connection.getAccountInfo( + const nonceAccount = await program.account.nonce.fetch( nonceKeypair.publicKey ); assert.notEqual(nonceAccount, null); + assert.ok(nonceAccount.authorizedPubkey.equals(aliceKeypair.publicKey)); }); it("Withdraws from nonce account", async () => { diff --git a/ts/src/coder/system/accounts.ts b/ts/src/coder/system/accounts.ts index d8ab9ec9a0..ba5347eac1 100644 --- a/ts/src/coder/system/accounts.ts +++ b/ts/src/coder/system/accounts.ts @@ -1,5 +1,8 @@ import { AccountsCoder } from "../index.js"; import { Idl, IdlTypeDef } from "../../idl.js"; +import * as BufferLayout from "buffer-layout"; +import { NONCE_ACCOUNT_LENGTH, PublicKey } from "@solana/web3.js"; +import { accountSize } from "../common.js"; export class SystemAccountsCoder implements AccountsCoder @@ -7,23 +10,101 @@ export class SystemAccountsCoder constructor(private idl: Idl) {} public async encode(accountName: A, account: T): Promise { - throw new Error("System program does not have accounts"); + switch (accountName) { + case "nonce": { + const buffer = Buffer.alloc(NONCE_ACCOUNT_LENGTH); + const len = NONCE_ACCOUNT_LAYOUT.encode(account, buffer); + return buffer.slice(0, len); + } + default: { + throw new Error(`Invalid account name: ${accountName}`); + } + } } public decode(accountName: A, ix: Buffer): T { - throw new Error("System program does not have accounts"); + return this.decodeUnchecked(accountName, ix); } public decodeUnchecked(accountName: A, ix: Buffer): T { - throw new Error("System program does not have accounts"); + switch (accountName) { + case "nonce": { + return decodeNonceAccount(ix); + } + default: { + throw new Error(`Invalid account name: ${accountName}`); + } + } } // TODO: this won't use the appendData. public memcmp(accountName: A, _appendData?: Buffer): any { - throw new Error("System program does not have accounts"); + switch (accountName) { + case "nonce": { + return { + dataSize: NONCE_ACCOUNT_LENGTH, + }; + } + default: { + throw new Error(`Invalid account name: ${accountName}`); + } + } } public size(idlAccount: IdlTypeDef): number { - throw new Error("System program does not have accounts"); + return accountSize(this.idl, idlAccount) ?? 0; } } + +function decodeNonceAccount(ix: Buffer): T { + return NONCE_ACCOUNT_LAYOUT.decode(ix) as T; +} + +class WrappedLayout extends BufferLayout.Layout { + layout: BufferLayout.Layout; + decoder: (data: T) => U; + encoder: (src: U) => T; + + constructor( + layout: BufferLayout.Layout, + decoder: (data: T) => U, + encoder: (src: U) => T, + property?: string + ) { + super(layout.span, property); + this.layout = layout; + this.decoder = decoder; + this.encoder = encoder; + } + + decode(b: Buffer, offset?: number): U { + return this.decoder(this.layout.decode(b, offset)); + } + + encode(src: U, b: Buffer, offset?: number): number { + return this.layout.encode(this.encoder(src), b, offset); + } + + getSpan(b: Buffer, offset?: number): number { + return this.layout.getSpan(b, offset); + } +} + +function publicKey(property?: string): BufferLayout.Layout { + return new WrappedLayout( + BufferLayout.blob(32), + (b: Buffer) => new PublicKey(b), + (key: PublicKey) => key.toBuffer(), + property + ); +} + +const NONCE_ACCOUNT_LAYOUT = BufferLayout.struct([ + BufferLayout.u32("version"), + BufferLayout.u32("state"), + publicKey("authorizedPubkey"), + publicKey("nonce"), + BufferLayout.struct([ + BufferLayout.nu64("lamportsPerSignature") + ], "feeCalculator"), +]); diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts index ab70076eaa..2993703574 100644 --- a/ts/src/coder/system/instruction.ts +++ b/ts/src/coder/system/instruction.ts @@ -1,8 +1,8 @@ +import BN from "bn.js"; import * as BufferLayout from "buffer-layout"; import camelCase from "camelcase"; import { Idl } from "../../idl.js"; import { InstructionCoder } from "../index.js"; -import { RustStringLayout } from "./layout.js"; export class SystemInstructionCoder implements InstructionCoder { // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -57,6 +57,61 @@ export class SystemInstructionCoder implements InstructionCoder { } } + +class RustStringLayout extends BufferLayout.Layout { + layout = BufferLayout.struct< + Readonly<{ + length?: number; + lengthPadding?: number; + chars: Buffer; + }> + >( + [ + BufferLayout.u32("length"), + BufferLayout.u32("lengthPadding"), + BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), "chars"), + ], + this.property + ); + + constructor(public property?: string) { + super(-1, property); + } + + encode(src: string | null, b: Buffer, offset = 0): number { + if (src === null || src === undefined) { + return this.layout.span; + } + + const data = { + chars: Buffer.from(src, "utf8"), + }; + + return this.layout.encode(data, b, offset); + } + + decode(b: Buffer, offset = 0): string | null { + const data = this.layout.decode(b, offset); + return data["chars"].toString(); + } + + getSpan(b: Buffer, offset = 0): number { + return ( + BufferLayout.u32().span + + BufferLayout.u32().span + + new BN(new Uint8Array(b).slice(offset, offset + 4), 10, "le").toNumber() + ); + } +} + +function rustStringLayout(property: string) { + return new RustStringLayout(property) +} + +function publicKey(property: string): any { + return BufferLayout.blob(32, property); +} + function encodeCreateAccount({ lamports, space, owner }: any): Buffer { return encodeData({ createAccount: { lamports, space, owner: owner.toBuffer() }, @@ -190,7 +245,7 @@ LAYOUT.addVariant( 3, BufferLayout.struct([ publicKey("base"), - new RustStringLayout("seed"), + rustStringLayout("seed"), BufferLayout.ns64("lamports"), BufferLayout.ns64("space"), publicKey("owner"), @@ -226,7 +281,7 @@ LAYOUT.addVariant( 9, BufferLayout.struct([ publicKey("base"), - new RustStringLayout("seed"), + rustStringLayout("seed"), BufferLayout.ns64("space"), publicKey("owner"), ]), @@ -236,7 +291,7 @@ LAYOUT.addVariant( 10, BufferLayout.struct([ publicKey("base"), - new RustStringLayout("seed"), + rustStringLayout("seed"), publicKey("owner"), ]), "assignWithSeed" @@ -245,16 +300,12 @@ LAYOUT.addVariant( 11, BufferLayout.struct([ BufferLayout.ns64("lamports"), - new RustStringLayout("seed"), + rustStringLayout("seed"), publicKey("owner"), ]), "transferWithSeed" ); -function publicKey(property: string): any { - return BufferLayout.blob(32, property); -} - function encodeData(instruction: any, maxSpan?: number): Buffer { const b = Buffer.alloc(maxSpan ?? instructionMaxSpan); const span = LAYOUT.encode(instruction, b); diff --git a/ts/src/coder/system/layout.ts b/ts/src/coder/system/layout.ts deleted file mode 100644 index 4226dd9705..0000000000 --- a/ts/src/coder/system/layout.ts +++ /dev/null @@ -1,49 +0,0 @@ -import BN from "bn.js"; -import * as BufferLayout from "buffer-layout"; -import { Layout } from "buffer-layout"; - -export class RustStringLayout extends Layout { - layout = BufferLayout.struct< - Readonly<{ - length?: number; - lengthPadding?: number; - chars: Buffer; - }> - >( - [ - BufferLayout.u32("length"), - BufferLayout.u32("lengthPadding"), - BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), "chars"), - ], - this.property - ); - - constructor(public property?: string) { - super(-1, property); - } - - encode(src: string | null, b: Buffer, offset = 0): number { - if (src === null || src === undefined) { - return this.layout.span; - } - - const data = { - chars: Buffer.from(src, "utf8"), - }; - - return this.layout.encode(data, b, offset); - } - - decode(b: Buffer, offset = 0): string | null { - const data = this.layout.decode(b, offset); - return data["chars"].toString(); - } - - getSpan(b: Buffer, offset = 0): number { - return ( - BufferLayout.u32().span + - BufferLayout.u32().span + - new BN(new Uint8Array(b).slice(offset, offset + 4), 10, "le").toNumber() - ); - } -} diff --git a/ts/src/spl/system.ts b/ts/src/spl/system.ts index daacad473f..e42ead4dc9 100644 --- a/ts/src/spl/system.ts +++ b/ts/src/spl/system.ts @@ -350,6 +350,52 @@ export type SystemProgram = { ]; } ]; + accounts: [ + { + name: "nonce"; + type: { + kind: "struct"; + fields: [ + { + name: "version"; + type: "u32"; + }, + { + name: "state"; + type: "u32"; + }, + { + name: "authorizedPubkey"; + type: "publicKey"; + }, + { + name: "nonce"; + type: "publicKey"; + }, + { + name: "feeCalculator"; + type: { + defined: "FeeCalculator"; + }; + } + ]; + }; + } + ]; + types: [ + { + name: "FeeCalculator"; + type: { + kind: "struct"; + fields: [ + { + name: "lamportsPerSignature"; + type: "u64"; + } + ]; + }; + } + ]; }; export const IDL: SystemProgram = { @@ -686,4 +732,50 @@ export const IDL: SystemProgram = { ], }, ], + accounts: [ + { + name: "nonce", + type: { + kind: "struct", + fields: [ + { + name: "version", + type: "u32", + }, + { + name: "state", + type: "u32", + }, + { + name: "authorizedPubkey", + type: "publicKey", + }, + { + name: "nonce", + type: "publicKey", + }, + { + name: "feeCalculator", + type: { + defined: "FeeCalculator", + }, + }, + ], + }, + }, + ], + types: [ + { + name: "FeeCalculator", + type: { + kind: "struct", + fields: [ + { + name: "lamportsPerSignature", + type: "u64", + }, + ], + }, + }, + ], }; From e82be7863d6a6dd83128c4a306003e111bf55b54 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 20:56:33 +0100 Subject: [PATCH 14/18] refactor: move system to a native namespace --- tests/custom-coder/tests/system-coder.ts | 4 ++-- ts/src/index.ts | 1 + ts/src/native/index.ts | 10 ++++++++++ ts/src/{spl => native}/system.ts | 0 ts/src/spl/index.ts | 5 ----- 5 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 ts/src/native/index.ts rename ts/src/{spl => native}/system.ts (100%) diff --git a/tests/custom-coder/tests/system-coder.ts b/tests/custom-coder/tests/system-coder.ts index b0e8570464..766fd3a4dd 100644 --- a/tests/custom-coder/tests/system-coder.ts +++ b/tests/custom-coder/tests/system-coder.ts @@ -1,5 +1,5 @@ import * as anchor from "@project-serum/anchor"; -import { Spl } from "@project-serum/anchor"; +import { Native } from "@project-serum/anchor"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { Keypair, @@ -18,7 +18,7 @@ describe("system-coder", () => { anchor.setProvider(provider); // Client. - const program = Spl.system(); + const program = Native.system(); // Constants. const aliceKeypair = Keypair.generate(); diff --git a/ts/src/index.ts b/ts/src/index.ts index 30ecb76112..95dc4120eb 100644 --- a/ts/src/index.ts +++ b/ts/src/index.ts @@ -16,6 +16,7 @@ export * from "./coder/index.js"; export * as utils from "./utils/index.js"; export * from "./program/index.js"; export * from "./spl/index.js"; +export * from "./native/index.js"; export declare const workspace: any; export declare class Wallet extends NodeWallet {} diff --git a/ts/src/native/index.ts b/ts/src/native/index.ts new file mode 100644 index 0000000000..905c2a0afd --- /dev/null +++ b/ts/src/native/index.ts @@ -0,0 +1,10 @@ +import { Program, Provider } from "../index.js"; +import { program as systemProgram, SystemProgram } from "./system.js"; + +export { SystemProgram } from "./system.js"; + +export class Native { + public static system(provider?: Provider): Program { + return systemProgram(provider); + } +} diff --git a/ts/src/spl/system.ts b/ts/src/native/system.ts similarity index 100% rename from ts/src/spl/system.ts rename to ts/src/native/system.ts diff --git a/ts/src/spl/index.ts b/ts/src/spl/index.ts index 941c63aede..8929f775f2 100644 --- a/ts/src/spl/index.ts +++ b/ts/src/spl/index.ts @@ -1,6 +1,5 @@ import { Program, Provider } from "../index.js"; import { program as tokenProgram, SplToken } from "./token.js"; -import { program as systemProgram, SystemProgram } from "./system.js"; export { SplToken } from "./token.js"; @@ -8,8 +7,4 @@ export class Spl { public static token(provider?: Provider): Program { return tokenProgram(provider); } - - public static system(provider?: Provider): Program { - return systemProgram(provider); - } } From 6f2ad9691918b8ee2bed88d988030dd3917ce355 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 21:01:37 +0100 Subject: [PATCH 15/18] docs: add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d2f9fdbf6..7f3a189da7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ com/project-serum/anchor/pull/1841)). * lang: Add `PartialEq` and `Eq` for `anchor_lang::Error` ([#1544](https://github.com/project-serum/anchor/pull/1544)). * cli: Add `b` and `t` aliases for `build` and `test` respectively ([#1823](https://github.com/project-serum/anchor/pull/1823)). * spl: Add `sync_native` token program CPI wrapper function ([#1833](https://github.com/project-serum/anchor/pull/1833)). +* ts: Implement a coder for system program ([#1920](https://github.com/project-serum/anchor/pull/1920)). ### Fixes From 18254adff539c56a7352b1ea8e27dd313bcfa51b Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 21:08:28 +0100 Subject: [PATCH 16/18] fix: run prettier to clean lint errors --- ts/src/coder/system/accounts.ts | 7 ++++--- ts/src/coder/system/instruction.ts | 9 ++------- ts/src/coder/system/state.ts | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ts/src/coder/system/accounts.ts b/ts/src/coder/system/accounts.ts index ba5347eac1..c5749dc3c0 100644 --- a/ts/src/coder/system/accounts.ts +++ b/ts/src/coder/system/accounts.ts @@ -104,7 +104,8 @@ const NONCE_ACCOUNT_LAYOUT = BufferLayout.struct([ BufferLayout.u32("state"), publicKey("authorizedPubkey"), publicKey("nonce"), - BufferLayout.struct([ - BufferLayout.nu64("lamportsPerSignature") - ], "feeCalculator"), + BufferLayout.struct( + [BufferLayout.nu64("lamportsPerSignature")], + "feeCalculator" + ), ]); diff --git a/ts/src/coder/system/instruction.ts b/ts/src/coder/system/instruction.ts index 2993703574..894cdf12c6 100644 --- a/ts/src/coder/system/instruction.ts +++ b/ts/src/coder/system/instruction.ts @@ -57,7 +57,6 @@ export class SystemInstructionCoder implements InstructionCoder { } } - class RustStringLayout extends BufferLayout.Layout { layout = BufferLayout.struct< Readonly<{ @@ -105,7 +104,7 @@ class RustStringLayout extends BufferLayout.Layout { } function rustStringLayout(property: string) { - return new RustStringLayout(property) + return new RustStringLayout(property); } function publicKey(property: string): any { @@ -208,11 +207,7 @@ function encodeAssignWithSeed({ base, seed, owner }: any): Buffer { ); } -function encodeTransferWithSeed({ - lamports, - seed, - owner, -}: any): Buffer { +function encodeTransferWithSeed({ lamports, seed, owner }: any): Buffer { return encodeData( { transferWithSeed: { diff --git a/ts/src/coder/system/state.ts b/ts/src/coder/system/state.ts index 9a7d51a852..2d7f1a2ef8 100644 --- a/ts/src/coder/system/state.ts +++ b/ts/src/coder/system/state.ts @@ -6,9 +6,9 @@ export class SystemStateCoder implements StateCoder { constructor(_idl: Idl) {} encode(_name: string, _account: T): Promise { - throw new Error('System does not have state'); + throw new Error("System does not have state"); } decode(_ix: Buffer): T { - throw new Error('System does not have state'); + throw new Error("System does not have state"); } } From d6eed1795cccf936759e39fd3cb61e16925767e8 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 21:47:48 +0100 Subject: [PATCH 17/18] fix: use path to get anchor-lang dep --- tests/custom-coder/programs/system/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/custom-coder/programs/system/Cargo.toml b/tests/custom-coder/programs/system/Cargo.toml index def0d8f77c..a32504afa7 100644 --- a/tests/custom-coder/programs/system/Cargo.toml +++ b/tests/custom-coder/programs/system/Cargo.toml @@ -17,4 +17,4 @@ cpi = ["no-entrypoint"] default = [] [dependencies] -anchor-lang = "0.24.2" +anchor-lang = { path = "../../../../lang" } From 76486c8d07a6dde3cbc3e24c953a1ff2afef4181 Mon Sep 17 00:00:00 2001 From: Daniel Marin Date: Wed, 25 May 2022 22:10:15 +0100 Subject: [PATCH 18/18] fix: rename system program to prevent errors --- tests/custom-coder/Anchor.toml | 2 +- .../programs/{system => native-system}/Cargo.toml | 4 ++-- .../programs/{system => native-system}/Xargo.toml | 0 .../programs/{system => native-system}/src/lib.rs | 6 ++---- 4 files changed, 5 insertions(+), 7 deletions(-) rename tests/custom-coder/programs/{system => native-system}/Cargo.toml (86%) rename tests/custom-coder/programs/{system => native-system}/Xargo.toml (100%) rename tests/custom-coder/programs/{system => native-system}/src/lib.rs (96%) diff --git a/tests/custom-coder/Anchor.toml b/tests/custom-coder/Anchor.toml index 75faf62408..ec4e7c95bd 100644 --- a/tests/custom-coder/Anchor.toml +++ b/tests/custom-coder/Anchor.toml @@ -1,7 +1,7 @@ [programs.localnet] custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w" -system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB" +native_system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB" [registry] url = "https://anchor.projectserum.com" diff --git a/tests/custom-coder/programs/system/Cargo.toml b/tests/custom-coder/programs/native-system/Cargo.toml similarity index 86% rename from tests/custom-coder/programs/system/Cargo.toml rename to tests/custom-coder/programs/native-system/Cargo.toml index a32504afa7..b805cdb25f 100644 --- a/tests/custom-coder/programs/system/Cargo.toml +++ b/tests/custom-coder/programs/native-system/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "system" +name = "native-system" version = "0.1.0" description = "Created with Anchor" rust-version = "1.56" @@ -7,7 +7,7 @@ edition = "2021" [lib] crate-type = ["cdylib", "lib"] -name = "system" +name = "native_system" [features] no-entrypoint = [] diff --git a/tests/custom-coder/programs/system/Xargo.toml b/tests/custom-coder/programs/native-system/Xargo.toml similarity index 100% rename from tests/custom-coder/programs/system/Xargo.toml rename to tests/custom-coder/programs/native-system/Xargo.toml diff --git a/tests/custom-coder/programs/system/src/lib.rs b/tests/custom-coder/programs/native-system/src/lib.rs similarity index 96% rename from tests/custom-coder/programs/system/src/lib.rs rename to tests/custom-coder/programs/native-system/src/lib.rs index b3daea249f..8ac2b71574 100644 --- a/tests/custom-coder/programs/system/src/lib.rs +++ b/tests/custom-coder/programs/native-system/src/lib.rs @@ -1,11 +1,9 @@ -// This file is autogenerated with https://github.com/acheroncrypto/native-to-anchor - use anchor_lang::prelude::*; -declare_id!("11111111111111111111111111111111"); +declare_id!("9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"); #[program] -pub mod system_program { +pub mod native_system { use super::*; pub fn create_account(