feat: Setup a coder for SystemProgram (#1920)

This commit is contained in:
Daniel Marin 2022-05-26 11:03:44 +00:00 committed by GitHub
parent f866d073db
commit 707ebf26e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1982 additions and 0 deletions

View File

@ -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

View File

@ -1,6 +1,7 @@
[programs.localnet]
custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w"
native_system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"
[registry]
url = "https://anchor.projectserum.com"

View File

@ -15,5 +15,9 @@
},
"scripts": {
"test": "anchor test"
},
"dependencies": {
"mocha": "^10.0.0",
"ts-mocha": "^10.0.0"
}
}

View File

@ -0,0 +1,20 @@
[package]
name = "native-system"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "native_system"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -0,0 +1,214 @@
use anchor_lang::prelude::*;
declare_id!("9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB");
#[program]
pub mod native_system {
use super::*;
pub fn create_account(
ctx: Context<CreateAccount>,
lamports: u64,
space: u64,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn assign(ctx: Context<Assign>, owner: Pubkey) -> Result<()> {
Ok(())
}
pub fn transfer(ctx: Context<Transfer>, lamports: u64) -> Result<()> {
Ok(())
}
pub fn create_account_with_seed(
ctx: Context<CreateAccountWithSeed>,
base: Pubkey,
seed: String,
lamports: u64,
space: u64,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn advance_nonce_account(
ctx: Context<AdvanceNonceAccount>,
authorized: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn withdraw_nonce_account(ctx: Context<WithdrawNonceAccount>, lamports: u64) -> Result<()> {
Ok(())
}
pub fn initialize_nonce_account(
ctx: Context<InitializeNonceAccount>,
authorized: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn authorize_nonce_account(
ctx: Context<AuthorizeNonceAccount>,
authorized: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn allocate(ctx: Context<Allocate>, space: u64) -> Result<()> {
Ok(())
}
pub fn allocate_with_seed(
ctx: Context<AllocateWithSeed>,
base: Pubkey,
seed: String,
space: u64,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn assign_with_seed(
ctx: Context<AssignWithSeed>,
base: Pubkey,
seed: String,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn transfer_with_seed(
ctx: Context<TransferWithSeed>,
lamports: u64,
seed: String,
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>,
/// CHECK:
recent_blockhashes: AccountInfo<'info>,
authorized: Signer<'info>,
}
#[derive(Accounts)]
pub struct WithdrawNonceAccount<'info> {
#[account(mut)]
/// CHECK:
nonce: AccountInfo<'info>,
#[account(mut)]
/// CHECK:
to: AccountInfo<'info>,
/// CHECK:
recent_blockhashes: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
authorized: Signer<'info>,
}
#[derive(Accounts)]
pub struct InitializeNonceAccount<'info> {
#[account(mut)]
nonce: Signer<'info>,
/// CHECK:
recent_blockhashes: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
}
#[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:
account: AccountInfo<'info>,
base: Signer<'info>,
}
#[derive(Accounts)]
pub struct AssignWithSeed<'info> {
#[account(mut)]
/// CHECK:
account: AccountInfo<'info>,
base: Signer<'info>,
}
#[derive(Accounts)]
pub struct TransferWithSeed<'info> {
#[account(mut)]
/// CHECK:
from: AccountInfo<'info>,
base: Signer<'info>,
#[account(mut)]
/// 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,
}

View File

@ -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>,
}

View File

@ -0,0 +1,425 @@
import * as anchor from "@project-serum/anchor";
import { Native } from "@project-serum/anchor";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
import {
Keypair,
LAMPORTS_PER_SOL,
NONCE_ACCOUNT_LENGTH,
PublicKey,
SystemProgram,
SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
} from "@solana/web3.js";
import * as assert from "assert";
import BN from "bn.js";
describe("system-coder", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
// Client.
const program = Native.system();
// Constants.
const aliceKeypair = Keypair.generate();
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), owner)
.accounts({
from: provider.wallet.publicKey,
to: aliceKeypair.publicKey,
})
.signers([aliceKeypair])
.rpc();
// assert
const aliceAccount = await program.provider.connection.getAccountInfo(
aliceKeypair.publicKey
);
assert.notEqual(aliceAccount, null);
assert.ok(owner.equals(aliceAccount.owner));
assert.equal(lamports, aliceAccount.lamports);
});
it("Assigns an account to a program", async () => {
// arrange
const owner = TOKEN_PROGRAM_ID;
// act
await program.methods
.assign(owner)
.accounts({
pubkey: aliceKeypair.publicKey,
})
.signers([aliceKeypair])
.rpc();
// assert
const aliceAccount = await program.provider.connection.getAccountInfo(
aliceKeypair.publicKey
);
assert.notEqual(aliceAccount, null);
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 =
await program.provider.connection.getMinimumBalanceForRentExemption(
space
);
const owner = SystemProgram.programId;
const seed = "seeds";
const bobPublicKey = await PublicKey.createWithSeed(
aliceKeypair.publicKey,
seed,
owner
);
// act
await program.methods
.createAccountWithSeed(
aliceKeypair.publicKey,
seed,
new BN(lamports),
new BN(space),
owner
)
.accounts({
base: aliceKeypair.publicKey,
from: provider.wallet.publicKey,
to: bobPublicKey,
})
.signers([aliceKeypair])
.rpc();
// assert
const bobAccount = await program.provider.connection.getAccountInfo(
bobPublicKey
);
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();
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();
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.account.nonce.fetch(
nonceKeypair.publicKey
);
assert.notEqual(nonceAccount, null);
assert.ok(nonceAccount.authorizedPubkey.equals(provider.wallet.publicKey));
});
it("Advances 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();
// These have to be separate to make sure advance is in another slot.
await program.methods
.advanceNonceAccount(provider.wallet.publicKey)
.accounts({
nonce: nonceKeypair.publicKey,
recentBlockhashes: SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
})
.rpc();
// assert
const nonceAccount = await program.account.nonce.fetch(
nonceKeypair.publicKey
);
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.account.nonce.fetch(
nonceKeypair.publicKey
);
assert.notEqual(nonceAccount, null);
assert.ok(nonceAccount.authorizedPubkey.equals(aliceKeypair.publicKey));
});
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);
});
});

View File

@ -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.

View File

@ -0,0 +1,111 @@
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<A extends string = string>
implements AccountsCoder
{
constructor(private idl: Idl) {}
public async encode<T = any>(accountName: A, account: T): Promise<Buffer> {
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<T = any>(accountName: A, ix: Buffer): T {
return this.decodeUnchecked(accountName, ix);
}
public decodeUnchecked<T = any>(accountName: A, ix: Buffer): T {
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 {
switch (accountName) {
case "nonce": {
return {
dataSize: NONCE_ACCOUNT_LENGTH,
};
}
default: {
throw new Error(`Invalid account name: ${accountName}`);
}
}
}
public size(idlAccount: IdlTypeDef): number {
return accountSize(this.idl, idlAccount) ?? 0;
}
}
function decodeNonceAccount<T = any>(ix: Buffer): T {
return NONCE_ACCOUNT_LAYOUT.decode(ix) as T;
}
class WrappedLayout<T, U> extends BufferLayout.Layout<U> {
layout: BufferLayout.Layout<T>;
decoder: (data: T) => U;
encoder: (src: U) => T;
constructor(
layout: BufferLayout.Layout<T>,
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<PublicKey> {
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"
),
]);

View File

@ -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<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
_log: string
): Event<E, T> | null {
throw new Error("System program does not have events");
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,317 @@
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";
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 "initializeNonceAccount": {
return encodeInitializeNonceAccount(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");
}
}
class RustStringLayout extends BufferLayout.Layout<string | null> {
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() },
});
}
function encodeAssign({ owner }: any): Buffer {
return encodeData({
assign: { owner: owner.toBuffer() },
});
}
function encodeTransfer({ lamports }: any): Buffer {
return encodeData({
transfer: { lamports },
});
}
function encodeCreateAccountWithSeed({
base,
seed,
lamports,
space,
owner,
}: any): Buffer {
return encodeData(
{
createAccountWithSeed: {
base: base.toBuffer(),
seed,
lamports,
space,
owner: owner.toBuffer(),
},
},
LAYOUT.getVariant(3).span + seed.length
);
}
function encodeInitializeNonceAccount({ authorized }: any): Buffer {
return encodeData({
initializeNonceAccount: { authorized: authorized.toBuffer() },
});
}
function encodeAdvanceNonceAccount({ authorized }: any): Buffer {
return encodeData({
advanceNonceAccount: { authorized: authorized.toBuffer() },
});
}
function encodeWithdrawNonceAccount({ lamports }: any): Buffer {
return encodeData({
withdrawNonceAccount: { lamports },
});
}
function encodeAuthorizeNonceAccount({ authorized }: any): Buffer {
return encodeData({
authorizeNonceAccount: { authorized: authorized.toBuffer() },
});
}
function encodeAllocate({ space }: any): Buffer {
return encodeData({
allocate: { space },
});
}
function encodeAllocateWithSeed({ base, seed, space, owner }: any): Buffer {
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(),
},
},
LAYOUT.getVariant(10).span + seed.length
);
}
function encodeTransferWithSeed({ lamports, seed, owner }: any): Buffer {
return encodeData(
{
transferWithSeed: {
lamports,
seed,
owner: owner.toBuffer(),
},
},
LAYOUT.getVariant(11).span + seed.length
);
}
const LAYOUT = BufferLayout.union(BufferLayout.u32("instruction"));
LAYOUT.addVariant(
0,
BufferLayout.struct([
BufferLayout.ns64("lamports"),
BufferLayout.ns64("space"),
publicKey("owner"),
]),
"createAccount"
);
LAYOUT.addVariant(1, BufferLayout.struct([publicKey("owner")]), "assign");
LAYOUT.addVariant(
2,
BufferLayout.struct([BufferLayout.ns64("lamports")]),
"transfer"
);
LAYOUT.addVariant(
3,
BufferLayout.struct([
publicKey("base"),
rustStringLayout("seed"),
BufferLayout.ns64("lamports"),
BufferLayout.ns64("space"),
publicKey("owner"),
]),
"createAccountWithSeed"
);
LAYOUT.addVariant(
4,
BufferLayout.struct([publicKey("authorized")]),
"advanceNonceAccount"
);
LAYOUT.addVariant(
5,
BufferLayout.struct([BufferLayout.ns64("lamports")]),
"withdrawNonceAccount"
);
LAYOUT.addVariant(
6,
BufferLayout.struct([publicKey("authorized")]),
"initializeNonceAccount"
);
LAYOUT.addVariant(
7,
BufferLayout.struct([publicKey("authorized")]),
"authorizeNonceAccount"
);
LAYOUT.addVariant(
8,
BufferLayout.struct([BufferLayout.ns64("space")]),
"allocate"
);
LAYOUT.addVariant(
9,
BufferLayout.struct([
publicKey("base"),
rustStringLayout("seed"),
BufferLayout.ns64("space"),
publicKey("owner"),
]),
"allocateWithSeed"
);
LAYOUT.addVariant(
10,
BufferLayout.struct([
publicKey("base"),
rustStringLayout("seed"),
publicKey("owner"),
]),
"assignWithSeed"
);
LAYOUT.addVariant(
11,
BufferLayout.struct([
BufferLayout.ns64("lamports"),
rustStringLayout("seed"),
publicKey("owner"),
]),
"transferWithSeed"
);
function encodeData(instruction: any, maxSpan?: number): Buffer {
const b = Buffer.alloc(maxSpan ?? instructionMaxSpan);
const span = LAYOUT.encode(instruction, b);
if (maxSpan === undefined) {
return b.slice(0, span);
}
return b;
}
const instructionMaxSpan = Math.max(
...Object.values(LAYOUT.registry).map((r: any) => r.span)
);

View File

@ -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<T = any>(_name: string, _account: T): Promise<Buffer> {
throw new Error("System does not have state");
}
decode<T = any>(_ix: Buffer): T {
throw new Error("System does not have state");
}
}

View File

@ -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 {}

10
ts/src/native/index.ts Normal file
View File

@ -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<SystemProgram> {
return systemProgram(provider);
}
}

781
ts/src/native/system.ts Normal file
View File

@ -0,0 +1,781 @@
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<SystemProgram> {
return new Program<SystemProgram>(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;
},
{
name: "recentBlockhashes";
isMut: false;
isSigner: false;
},
{
name: "authorized";
isMut: false;
isSigner: true;
}
];
args: [
{
name: "authorized";
type: "publicKey";
}
];
},
{
name: "withdrawNonceAccount";
accounts: [
{
name: "nonce";
isMut: true;
isSigner: false;
},
{
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: "lamports";
type: "u64";
}
];
},
{
name: "initializeNonceAccount";
accounts: [
{
name: "nonce";
isMut: true;
isSigner: true;
},
{
name: "recentBlockhashes";
isMut: false;
isSigner: false;
},
{
name: "rent";
isMut: false;
isSigner: false;
}
];
args: [
{
name: "authorized";
type: "publicKey";
}
];
},
{
name: "authorizeNonceAccount";
accounts: [
{
name: "nonce";
isMut: true;
isSigner: false;
},
{
name: "authorized";
isMut: false;
isSigner: true;
}
];
args: [
{
name: "authorized";
type: "publicKey";
}
];
},
{
name: "allocate";
accounts: [
{
name: "pubkey";
isMut: true;
isSigner: true;
}
];
args: [
{
name: "space";
type: "u64";
}
];
},
{
name: "allocateWithSeed";
accounts: [
{
name: "account";
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: "account";
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: "base";
isMut: false;
isSigner: true;
},
{
name: "to";
isMut: true;
isSigner: false;
}
];
args: [
{
name: "lamports";
type: "u64";
},
{
name: "seed";
type: "string";
},
{
name: "owner";
type: "publicKey";
}
];
}
];
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 = {
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,
},
{
name: "recentBlockhashes",
isMut: false,
isSigner: false,
},
{
name: "authorized",
isMut: false,
isSigner: true,
},
],
args: [
{
name: "authorized",
type: "publicKey",
},
],
},
{
name: "withdrawNonceAccount",
accounts: [
{
name: "nonce",
isMut: true,
isSigner: false,
},
{
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: "lamports",
type: "u64",
},
],
},
{
name: "initializeNonceAccount",
accounts: [
{
name: "nonce",
isMut: true,
isSigner: true,
},
{
name: "recentBlockhashes",
isMut: false,
isSigner: false,
},
{
name: "rent",
isMut: false,
isSigner: false,
},
],
args: [
{
name: "authorized",
type: "publicKey",
},
],
},
{
name: "authorizeNonceAccount",
accounts: [
{
name: "nonce",
isMut: true,
isSigner: false,
},
{
name: "authorized",
isMut: false,
isSigner: true,
},
],
args: [
{
name: "authorized",
type: "publicKey",
},
],
},
{
name: "allocate",
accounts: [
{
name: "pubkey",
isMut: true,
isSigner: true,
},
],
args: [
{
name: "space",
type: "u64",
},
],
},
{
name: "allocateWithSeed",
accounts: [
{
name: "account",
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: "account",
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: "base",
isMut: false,
isSigner: true,
},
{
name: "to",
isMut: true,
isSigner: false,
},
],
args: [
{
name: "lamports",
type: "u64",
},
{
name: "seed",
type: "string",
},
{
name: "owner",
type: "publicKey",
},
],
},
],
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",
},
],
},
},
],
};