Explorar o código

feat: Setup a coder for SystemProgram (#1920)

Daniel Marin %!s(int64=3) %!d(string=hai) anos
pai
achega
707ebf26e7

+ 1 - 0
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
 

+ 1 - 0
tests/custom-coder/Anchor.toml

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

+ 4 - 0
tests/custom-coder/package.json

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

+ 20 - 0
tests/custom-coder/programs/native-system/Cargo.toml

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

+ 2 - 0
tests/custom-coder/programs/native-system/Xargo.toml

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

+ 214 - 0
tests/custom-coder/programs/native-system/src/lib.rs

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

+ 43 - 0
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>,
 }

+ 425 - 0
tests/custom-coder/tests/system-coder.ts

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

+ 1 - 0
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.

+ 111 - 0
ts/src/coder/system/accounts.ts

@@ -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"
+  ),
+]);

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

+ 23 - 0
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);
+  }
+}

+ 317 - 0
ts/src/coder/system/instruction.ts

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

+ 14 - 0
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<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");
+  }
+}

+ 1 - 0
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 {}

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

+ 781 - 0
ts/src/native/system.ts

@@ -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",
+          },
+        ],
+      },
+    },
+  ],
+};