Browse Source

close account example

jpcaulfi 2 years ago
parent
commit
1780636d2e
39 changed files with 540 additions and 134 deletions
  1. 1 0
      .gitignore
  2. 1 1
      basics/close-account/anchor/Anchor.toml
  3. 0 0
      basics/close-account/anchor/Cargo.toml
  4. 0 0
      basics/close-account/anchor/README.md
  5. 0 0
      basics/close-account/anchor/migrations/deploy.ts
  6. 0 0
      basics/close-account/anchor/package.json
  7. 2 2
      basics/close-account/anchor/programs/close-account/Cargo.toml
  8. 0 0
      basics/close-account/anchor/programs/close-account/Xargo.toml
  9. 14 10
      basics/close-account/anchor/programs/close-account/src/instructions/close_user.rs
  10. 18 12
      basics/close-account/anchor/programs/close-account/src/instructions/create_user.rs
  11. 5 0
      basics/close-account/anchor/programs/close-account/src/instructions/mod.rs
  12. 4 4
      basics/close-account/anchor/programs/close-account/src/lib.rs
  13. 3 0
      basics/close-account/anchor/programs/close-account/src/state/mod.rs
  14. 0 0
      basics/close-account/anchor/programs/close-account/src/state/user.rs
  15. 82 0
      basics/close-account/anchor/tests/close-account.ts
  16. 9 0
      basics/close-account/anchor/tsconfig.json
  17. 8 0
      basics/close-account/native/cicd.sh
  18. 18 0
      basics/close-account/native/package.json
  19. 12 0
      basics/close-account/native/program/Cargo.toml
  20. 30 0
      basics/close-account/native/program/src/instructions/close_user.rs
  21. 46 0
      basics/close-account/native/program/src/instructions/create_user.rs
  22. 2 0
      basics/close-account/native/program/src/instructions/mod.rs
  23. 11 0
      basics/close-account/native/program/src/lib.rs
  24. 23 0
      basics/close-account/native/program/src/processor.rs
  25. 0 0
      basics/close-account/native/program/src/state/mod.rs
  26. 10 0
      basics/close-account/native/program/src/state/user.rs
  27. 60 0
      basics/close-account/native/tests/close-account.test.ts
  28. 0 0
      basics/close-account/native/tests/tsconfig.test.json
  29. 3 0
      basics/close-account/native/ts/index.ts
  30. 60 0
      basics/close-account/native/ts/instructions/close.ts
  31. 66 0
      basics/close-account/native/ts/instructions/create.ts
  32. 7 0
      basics/close-account/native/ts/instructions/index.ts
  33. 35 0
      basics/close-account/native/ts/state/index.ts
  34. 1 0
      basics/close-account/native/ts/util/index.ts
  35. 9 0
      basics/close-account/native/ts/util/util.ts
  36. 0 7
      basics/destroy-an-account/anchor/destroy-an-account/.gitignore
  37. 0 8
      basics/destroy-an-account/anchor/destroy-an-account/.prettierignore
  38. 0 5
      basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/src/instructions/mod.rs
  39. 0 85
      basics/destroy-an-account/anchor/destroy-an-account/tests/destroy-an-account.ts

+ 1 - 0
.gitignore

@@ -1,5 +1,6 @@
 .template
 .idea
+.DS_Store
 
 test-ledger/
 

+ 1 - 1
basics/destroy-an-account/anchor/destroy-an-account/Anchor.toml → basics/close-account/anchor/Anchor.toml

@@ -2,7 +2,7 @@
 seeds = false
 skip-lint = false
 [programs.localnet]
-destroy_an_account = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+close_account_program = "9SWqhEABWnKXTPvSLc4aQAJyESVxtRvYBvwF2WuBy7jf"
 
 [registry]
 url = "https://api.apr.dev"

+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/Cargo.toml → basics/close-account/anchor/Cargo.toml


+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/README.md → basics/close-account/anchor/README.md


+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/migrations/deploy.ts → basics/close-account/anchor/migrations/deploy.ts


+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/package.json → basics/close-account/anchor/package.json


+ 2 - 2
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/Cargo.toml → basics/close-account/anchor/programs/close-account/Cargo.toml

@@ -1,12 +1,12 @@
 [package]
-name = "destroy-an-account"
+name = "close-account-program"
 version = "0.1.0"
 description = "Created with Anchor"
 edition = "2021"
 
 [lib]
 crate-type = ["cdylib", "lib"]
-name = "destroy_an_account"
+name = "close_account_program"
 
 [features]
 no-entrypoint = []

+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/Xargo.toml → basics/close-account/anchor/programs/close-account/Xargo.toml


+ 14 - 10
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/src/instructions/destroy_user.rs → basics/close-account/anchor/programs/close-account/src/instructions/close_user.rs

@@ -1,23 +1,27 @@
-use crate::state::user::User;
 use anchor_lang::prelude::*;
 use anchor_lang::AccountsClose;
 
+use crate::state::*;
+
 #[derive(Accounts)]
-pub struct DestroyUserContext<'info> {
-    #[account(mut)]
-    pub user: Signer<'info>,
+pub struct CloseUserContext<'info> {
     #[account(
-    mut,
-    seeds=[User::PREFIX.as_bytes(), user_account.user.key().as_ref()],
-    has_one=user,
-    bump=user_account.bump
+        mut,
+        seeds = [
+            User::PREFIX.as_bytes().as_ref(), 
+            user.key().as_ref(),
+        ],
+        has_one = user,
+        bump = user_account.bump
     )]
-    pub user_account: Box<Account<'info, User>>,
+    pub user_account: Account<'info, User>,
+    #[account(mut)]
+    pub user: Signer<'info>,
     pub system_program: Program<'info, System>,
     pub rent: Sysvar<'info, Rent>,
 }
 
-pub fn destroy_user(ctx: Context<DestroyUserContext>) -> Result<()> {
+pub fn close_user(ctx: Context<CloseUserContext>) -> Result<()> {
     let user = &mut ctx.accounts.user;
     let user_account = &mut ctx.accounts.user_account;
     user_account.close(user.to_account_info())?;

+ 18 - 12
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/src/instructions/create_user.rs → basics/close-account/anchor/programs/close-account/src/instructions/create_user.rs

@@ -1,6 +1,7 @@
-use crate::state::user::User;
 use anchor_lang::prelude::*;
 
+use crate::state::*;
+
 #[derive(AnchorSerialize, AnchorDeserialize)]
 pub struct CreateUserArgs {
     pub name: String,
@@ -9,26 +10,31 @@ pub struct CreateUserArgs {
 #[derive(Accounts)]
 #[instruction(args: CreateUserArgs)]
 pub struct CreateUserContext<'info> {
-    #[account(mut)]
-    pub user: Signer<'info>,
     #[account(
-    init,
-    seeds=[User::PREFIX.as_bytes(), user.key().as_ref()],
-    payer=user,
-    space=User::SIZE,
-    bump
+        init,
+        space = User::SIZE,
+        payer = payer,
+        seeds = [
+            User::PREFIX.as_bytes().as_ref(), 
+            payer.key().as_ref(),
+        ],
+        bump
     )]
-    pub user_account: Box<Account<'info, User>>,
+    pub user_account: Account<'info, User>,
+    #[account(mut)]
+    pub payer: Signer<'info>,
     pub system_program: Program<'info, System>,
     pub rent: Sysvar<'info, Rent>,
 }
 
 pub fn create_user(ctx: Context<CreateUserContext>, args: CreateUserArgs) -> Result<()> {
-    let user = &ctx.accounts.user;
+    let payer = &ctx.accounts.payer;
     let user_account = &mut ctx.accounts.user_account;
 
-    user_account.bump = *ctx.bumps.get("user_account").unwrap();
-    user_account.user = user.key();
+    msg!("{:#?}", ctx.bumps);
+
+    user_account.bump = *ctx.bumps.get("user_account").expect("Bump not found.");
+    user_account.user = payer.key();
     user_account.name = args.name;
 
     Ok(())

+ 5 - 0
basics/close-account/anchor/programs/close-account/src/instructions/mod.rs

@@ -0,0 +1,5 @@
+pub mod close_user;
+pub mod create_user;
+
+pub use close_user::*;
+pub use create_user::*;

+ 4 - 4
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/src/lib.rs → basics/close-account/anchor/programs/close-account/src/lib.rs

@@ -4,17 +4,17 @@ mod instructions;
 mod state;
 use instructions::*;
 
-declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+declare_id!("9SWqhEABWnKXTPvSLc4aQAJyESVxtRvYBvwF2WuBy7jf");
 
 #[program]
-pub mod destroy_an_account {
+pub mod close_account_program {
     use super::*;
 
     pub fn create_user(ctx: Context<CreateUserContext>, args: CreateUserArgs) -> Result<()> {
         create_user::create_user(ctx, args)
     }
 
-    pub fn destroy_user(ctx: Context<DestroyUserContext>) -> Result<()> {
-        destroy_user::destroy_user(ctx)
+    pub fn close_user(ctx: Context<CloseUserContext>) -> Result<()> {
+        close_user::close_user(ctx)
     }
 }

+ 3 - 0
basics/close-account/anchor/programs/close-account/src/state/mod.rs

@@ -0,0 +1,3 @@
+pub mod user;
+
+pub use user::*;

+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/src/state/user.rs → basics/close-account/anchor/programs/close-account/src/state/user.rs


+ 82 - 0
basics/close-account/anchor/tests/close-account.ts

@@ -0,0 +1,82 @@
+import * as anchor from "@project-serum/anchor";
+import { Program } from "@project-serum/anchor";
+import {Keypair, LAMPORTS_PER_SOL} from "@solana/web3.js";
+import { CloseAccountProgram } from "../target/types/close_account_program";
+import {BlockheightBasedTransactionConfirmationStrategy, PublicKey} from "@solana/web3.js";
+import assert from "assert";
+
+describe("close-an-account", () => {
+    // Configure the client to use the local cluster.
+    anchor.setProvider(anchor.AnchorProvider.env());
+
+    const program = anchor.workspace.CloseAccountProgram as Program<CloseAccountProgram>;
+    const connection = program.provider.connection;
+    const payer = Keypair.generate();
+
+    async function airdrop(receiver: PublicKey, amount: number) {
+        const sig = await program.provider.connection.requestAirdrop(receiver, amount);
+        const blockStats = await program.provider.connection.getLatestBlockhash();
+        const strategy: BlockheightBasedTransactionConfirmationStrategy = {
+            signature: sig,
+            blockhash: blockStats.blockhash,
+            lastValidBlockHeight: blockStats.lastValidBlockHeight
+        }
+        await program.provider.connection.confirmTransaction(strategy, "confirmed");
+    }
+
+    function getUserAccount(user: PublicKey): [PublicKey, number] {
+        return PublicKey.findProgramAddressSync(
+            [
+                Buffer.from("USER"),
+                user.toBuffer()
+            ], program.programId)
+    }
+
+    it("Airdrop", async () => {
+        const balanceBefore = await connection.getBalance(payer.publicKey);
+        await airdrop(payer.publicKey, LAMPORTS_PER_SOL);
+        const balanceAfter = await connection.getBalance(payer.publicKey);
+        assert.equal(balanceAfter, balanceBefore + LAMPORTS_PER_SOL);
+    });
+
+
+    it("Create Account", async () => {
+        const [userAccountAddress] = getUserAccount(payer.publicKey);
+        const userAccountBefore = await program.account.user.fetchNullable(userAccountAddress, "confirmed");
+        assert.equal(userAccountBefore, null);
+
+        await program.methods
+            .createUser({
+                name: "John Doe"
+            })
+            .accounts({
+                payer: payer.publicKey,
+                userAccount: userAccountAddress
+            })
+            .signers([payer])
+            .rpc({commitment: "confirmed", skipPreflight: true});
+
+        const userAccountAfter = await program.account.user.fetchNullable(userAccountAddress, "confirmed");
+        assert.notEqual(userAccountAfter, null);
+        assert.equal(userAccountAfter.name, "John Doe");
+        assert.equal(userAccountAfter.user.toBase58(), payer.publicKey.toBase58());
+    })
+
+    it("Close Account", async () => {
+        const [userAccountAddress] = getUserAccount(payer.publicKey);
+        const userAccountBefore = await program.account.user.fetchNullable(userAccountAddress, "confirmed");
+        assert.notEqual(userAccountBefore, null);
+
+        await program.methods
+            .closeUser()
+            .accounts({
+                user: payer.publicKey,
+                userAccount: userAccountAddress
+            })
+            .signers([payer])
+            .rpc({commitment: "confirmed"});
+
+        const userAccountAfter = await program.account.user.fetchNullable(userAccountAddress, "processed");
+        assert.equal(userAccountAfter, null);
+    })
+});

+ 9 - 0
basics/close-account/anchor/tsconfig.json

@@ -0,0 +1,9 @@
+{
+  "compilerOptions": {
+    "typeRoots": ["./node_modules/@types"],
+    "lib": ["es2015"],
+    "module": "commonjs",
+    "target": "es6",
+    "esModuleInterop": true
+  }
+}

+ 8 - 0
basics/close-account/native/cicd.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# This script is for quick building & deploying of the program.
+# It also serves as a reference for the commands used for building & deploying Solana programs.
+# Run this bad boy with "bash cicd.sh" or "./cicd.sh"
+
+cargo build-bpf --manifest-path=./program/Cargo.toml --bpf-out-dir=./program/target/so
+solana program deploy ./program/target/so/program.so

+ 18 - 0
basics/close-account/native/package.json

@@ -0,0 +1,18 @@
+{
+  "scripts": {
+    "test": "yarn run ts-mocha -p ./tests/tsconfig.test.json -t 1000000 ./tests/close-account.test.ts"
+  },
+  "dependencies": {
+    "@solana/web3.js": "^1.35",
+    "fs": "^0.0.1-security"
+  },
+  "devDependencies": {
+    "@types/bn.js": "^5.1.0",
+    "@types/chai": "^4.3.1",
+    "@types/mocha": "^9.1.1",
+    "chai": "^4.3.4",
+    "mocha": "^9.0.3",
+    "ts-mocha": "^10.0.0",
+    "typescript": "^4.3.5"
+  }
+}

+ 12 - 0
basics/close-account/native/program/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "program"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+borsh = "0.9.3"
+borsh-derive = "0.9.1"
+solana-program = "1.10.12"
+
+[lib]
+crate-type = ["cdylib", "lib"]

+ 30 - 0
basics/close-account/native/program/src/instructions/close_user.rs

@@ -0,0 +1,30 @@
+use solana_program::{
+    account_info::{next_account_info, AccountInfo},
+    entrypoint::ProgramResult,
+    rent::Rent,
+    sysvar::Sysvar,
+};
+
+pub fn close_user(accounts: &[AccountInfo]) -> ProgramResult {
+    let accounts_iter = &mut accounts.iter();
+    let target_account = next_account_info(accounts_iter)?;
+    let payer = next_account_info(accounts_iter)?;
+    let system_program = next_account_info(accounts_iter)?;
+
+    let account_span = 0usize;
+    let lamports_required = (Rent::get()?).minimum_balance(account_span);
+
+    let diff = target_account.lamports() - lamports_required;
+
+    // Send the rent back to the payer
+    **target_account.lamports.borrow_mut() -= diff;
+    **payer.lamports.borrow_mut() += diff;
+
+    // Realloc the account to zero
+    target_account.realloc(account_span, true)?;
+
+    // Assign the account to the System Program
+    target_account.assign(system_program.key);
+
+    Ok(())
+}

+ 46 - 0
basics/close-account/native/program/src/instructions/create_user.rs

@@ -0,0 +1,46 @@
+use borsh::BorshSerialize;
+use solana_program::{
+    account_info::{next_account_info, AccountInfo},
+    entrypoint::ProgramResult,
+    program::invoke_signed,
+    pubkey::Pubkey,
+    rent::Rent,
+    system_instruction,
+    sysvar::Sysvar,
+};
+
+use crate::state::user::User;
+
+pub fn create_user(program_id: &Pubkey, accounts: &[AccountInfo], data: User) -> ProgramResult {
+    let accounts_iter = &mut accounts.iter();
+    let target_account = next_account_info(accounts_iter)?;
+    let payer = next_account_info(accounts_iter)?;
+    let system_program = next_account_info(accounts_iter)?;
+
+    let account_span = (data.try_to_vec()?).len();
+    let lamports_required = (Rent::get()?).minimum_balance(account_span);
+
+    let (_, bump) = Pubkey::find_program_address(
+        &[User::SEED_PREFIX.as_bytes(), payer.key.as_ref()],
+        program_id,
+    );
+
+    invoke_signed(
+        &system_instruction::create_account(
+            &payer.key,
+            &target_account.key,
+            lamports_required,
+            account_span as u64,
+            program_id,
+        ),
+        &[
+            payer.clone(),
+            target_account.clone(),
+            system_program.clone(),
+        ],
+        &[&[User::SEED_PREFIX.as_bytes(), payer.key.as_ref(), &[bump]]],
+    )?;
+
+    data.serialize(&mut &mut target_account.data.borrow_mut()[..])?;
+    Ok(())
+}

+ 2 - 0
basics/close-account/native/program/src/instructions/mod.rs

@@ -0,0 +1,2 @@
+pub mod close_user;
+pub mod create_user;

+ 11 - 0
basics/close-account/native/program/src/lib.rs

@@ -0,0 +1,11 @@
+
+pub mod instructions;
+pub mod processor;
+pub mod state;
+
+use {
+    solana_program::entrypoint,
+    crate::processor::process_instruction,
+};
+
+entrypoint!(process_instruction);

+ 23 - 0
basics/close-account/native/program/src/processor.rs

@@ -0,0 +1,23 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
+
+use crate::instructions::{close_user::close_user, create_user::create_user};
+use crate::state::user::User;
+
+#[derive(BorshSerialize, BorshDeserialize)]
+pub enum MyInstruction {
+    CreateUser(User),
+    CloseUser,
+}
+
+pub fn process_instruction(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    input: &[u8],
+) -> ProgramResult {
+    let instruction = MyInstruction::try_from_slice(&input)?;
+    match instruction {
+        MyInstruction::CreateUser(data) => create_user(program_id, accounts, data),
+        MyInstruction::CloseUser => close_user(accounts),
+    }
+}

+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/src/state/mod.rs → basics/close-account/native/program/src/state/mod.rs


+ 10 - 0
basics/close-account/native/program/src/state/user.rs

@@ -0,0 +1,10 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+
+#[derive(BorshDeserialize, BorshSerialize)]
+pub struct User {
+    pub name: String,
+}
+
+impl User {
+    pub const SEED_PREFIX: &'static str = "USER";
+}

+ 60 - 0
basics/close-account/native/tests/close-account.test.ts

@@ -0,0 +1,60 @@
+import {
+    Connection,
+    Keypair,
+    PublicKey,
+    sendAndConfirmTransaction,
+    Transaction,
+} from '@solana/web3.js';
+import {
+    describe,
+    it,
+} from 'mocha';
+import {
+    createCreateUserInstruction,
+    createCloseUserInstruction,
+    createKeypairFromFile,
+} from '../ts';
+
+
+
+describe("Close Account!", async () => {
+
+    const connection = new Connection(`http://localhost:8899`, 'confirmed');
+    const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
+    const program = createKeypairFromFile('./program/target/deploy/program-keypair.json');
+
+    const testAccountPublicKey = PublicKey.findProgramAddressSync(
+        [Buffer.from("USER"), payer.publicKey.toBuffer()],
+        program.publicKey,
+    )[0];
+
+    it("Create the account", async () => {
+        console.log(`${testAccountPublicKey}`);
+        const ix = createCreateUserInstruction(
+            testAccountPublicKey,
+            payer.publicKey, 
+            program.publicKey,
+            "Jacob",
+        );
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer]
+        );
+    });
+    
+    it("Close the account", async () => {
+        const ix = createCloseUserInstruction(
+            testAccountPublicKey,
+            payer.publicKey, 
+            program.publicKey,
+        );
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer],
+            { skipPreflight: true }
+        );
+    });
+  });
+  

+ 0 - 0
basics/destroy-an-account/anchor/destroy-an-account/tsconfig.json → basics/close-account/native/tests/tsconfig.test.json


+ 3 - 0
basics/close-account/native/ts/index.ts

@@ -0,0 +1,3 @@
+export * from './instructions';
+export * from './state';
+export * from './util/util';

+ 60 - 0
basics/close-account/native/ts/instructions/close.ts

@@ -0,0 +1,60 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+import { 
+    PublicKey, 
+    SystemProgram,
+    TransactionInstruction 
+} from '@solana/web3.js';
+import { MyInstruction } from ".";
+
+
+export class Close {
+
+    instruction: MyInstruction;
+
+    constructor(props: {
+        instruction: MyInstruction,
+    }) {
+        this.instruction = props.instruction;
+    }
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(CloseSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(CloseSchema, Close, buffer);
+    };
+};
+
+export const CloseSchema = new Map([
+    [ Close, { 
+        kind: 'struct', 
+        fields: [ 
+            ['instruction', 'u8'],
+        ],
+    }]
+]);
+
+export function createCloseUserInstruction(
+    target: PublicKey,
+    payer: PublicKey,
+    programId: PublicKey,
+): TransactionInstruction {
+
+    const instructionObject = new Close({
+        instruction: MyInstruction.CloseUser,
+    });
+
+    const ix = new TransactionInstruction({
+        keys: [
+            {pubkey: target, isSigner: false, isWritable: true},
+            {pubkey: payer, isSigner: true, isWritable: true},
+            {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
+        ],
+        programId: programId,
+        data: instructionObject.toBuffer(),
+    });
+
+    return ix;
+}

+ 66 - 0
basics/close-account/native/ts/instructions/create.ts

@@ -0,0 +1,66 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+import { 
+    PublicKey, 
+    SystemProgram,
+    TransactionInstruction 
+} from '@solana/web3.js';
+import { MyInstruction } from ".";
+
+
+export class Create {
+
+    instruction: MyInstruction;
+    name: string;
+
+    constructor(props: {
+        instruction: MyInstruction,
+        name: string,
+    }) {
+        this.instruction = props.instruction;
+        this.name = props.name;
+    }
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(CreateSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(CreateSchema, Create, buffer);
+    };
+};
+
+export const CreateSchema = new Map([
+    [ Create, { 
+        kind: 'struct', 
+        fields: [ 
+            ['instruction', 'u8'],
+            ['name', 'string'], 
+        ],
+    }]
+]);
+
+export function createCreateUserInstruction(
+    target: PublicKey,
+    payer: PublicKey,
+    programId: PublicKey,
+    name: string,
+): TransactionInstruction {
+
+    const instructionObject = new Create({
+        instruction: MyInstruction.CreateUser,
+        name,
+    });
+
+    const ix = new TransactionInstruction({
+        keys: [
+            {pubkey: target, isSigner: false, isWritable: true},
+            {pubkey: payer, isSigner: true, isWritable: true},
+            {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
+        ],
+        programId: programId,
+        data: instructionObject.toBuffer(),
+    });
+
+    return ix;
+}

+ 7 - 0
basics/close-account/native/ts/instructions/index.ts

@@ -0,0 +1,7 @@
+export * from './create';
+export * from './close';
+
+export enum MyInstruction {
+    CreateUser,
+    CloseUser,
+}

+ 35 - 0
basics/close-account/native/ts/state/index.ts

@@ -0,0 +1,35 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+
+
+export class User {
+
+    name: string;
+
+    constructor(props: {
+        name: string,
+    }) {
+        this.name = props.name;
+    }
+
+    toBase58() {
+        return borsh.serialize(UserSchema, this).toString()
+    };
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(UserSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(UserSchema, User, buffer);
+    };
+};
+
+export const UserSchema = new Map([
+    [ User, { 
+        kind: 'struct', 
+        fields: [ 
+            ['name', 'string'],
+        ],
+    }]
+]);

+ 1 - 0
basics/close-account/native/ts/util/index.ts

@@ -0,0 +1 @@
+export * from './util';

+ 9 - 0
basics/close-account/native/ts/util/util.ts

@@ -0,0 +1,9 @@
+import { Keypair } from '@solana/web3.js';
+
+
+export function createKeypairFromFile(path: string): Keypair {
+    return Keypair.fromSecretKey(
+        Buffer.from(JSON.parse(require('fs').readFileSync(path, "utf-8")))
+    )
+};
+

+ 0 - 7
basics/destroy-an-account/anchor/destroy-an-account/.gitignore

@@ -1,7 +0,0 @@
-
-.anchor
-.DS_Store
-target
-**/*.rs.bk
-node_modules
-test-ledger

+ 0 - 8
basics/destroy-an-account/anchor/destroy-an-account/.prettierignore

@@ -1,8 +0,0 @@
-
-.anchor
-.DS_Store
-target
-node_modules
-dist
-build
-test-ledger

+ 0 - 5
basics/destroy-an-account/anchor/destroy-an-account/programs/destroy-an-account/src/instructions/mod.rs

@@ -1,5 +0,0 @@
-pub mod create_user;
-pub mod destroy_user;
-
-pub use create_user::*;
-pub use destroy_user::*;

+ 0 - 85
basics/destroy-an-account/anchor/destroy-an-account/tests/destroy-an-account.ts

@@ -1,85 +0,0 @@
-import * as anchor from "@project-serum/anchor";
-import { Program } from "@project-serum/anchor";
-import {Keypair, LAMPORTS_PER_SOL} from "@solana/web3.js";
-import { DestroyAnAccount } from "../target/types/destroy_an_account";
-import {BlockheightBasedTransactionConfirmationStrategy, PublicKey} from "@solana/web3.js";
-import assert from "assert";
-
-const PROGRAM_ID = new PublicKey("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
-
-export async function airdrop(program: Program<any>, receiver: PublicKey, amount: number) {
-    const sig = await program.provider.connection.requestAirdrop(receiver, amount);
-    const blockStats = await program.provider.connection.getLatestBlockhash();
-    const strategy: BlockheightBasedTransactionConfirmationStrategy = {
-        signature: sig,
-        blockhash: blockStats.blockhash,
-        lastValidBlockHeight: blockStats.lastValidBlockHeight
-    }
-    await program.provider.connection.confirmTransaction(strategy, "confirmed");
-}
-
-export async function getUserAccount(user: PublicKey): Promise<[PublicKey, number]> {
-    return await PublicKey.findProgramAddress(
-        [
-            Buffer.from(anchor.utils.bytes.utf8.encode("USER")),
-            user.toBuffer()
-        ], PROGRAM_ID)
-}
-
-
-describe("destroy-an-account", () => {
-    // Configure the client to use the local cluster.
-    anchor.setProvider(anchor.AnchorProvider.env());
-
-    const program = anchor.workspace.DestroyAnAccount as Program<DestroyAnAccount>;
-    const connection = program.provider.connection;
-    const user = Keypair.generate();
-
-    it("Airdrop", async () => {
-        const balanceBefore = await connection.getBalance(user.publicKey);
-        await airdrop(program, user.publicKey, LAMPORTS_PER_SOL);
-        const balanceAfter = await connection.getBalance(user.publicKey);
-        assert.equal(balanceAfter, balanceBefore + LAMPORTS_PER_SOL);
-    });
-
-
-    it("Create Account", async () => {
-        const [userAccountAddress] = await getUserAccount(user.publicKey);
-        const userAccountBefore = await program.account.user.fetchNullable(userAccountAddress, "processed");
-        assert.equal(userAccountBefore, null);
-
-        await program.methods
-            .createUser({
-                name: "John Doe"
-            })
-            .accounts({
-                user: user.publicKey,
-                userAccount: userAccountAddress
-            })
-            .signers([user])
-            .rpc({commitment: "confirmed"});
-
-        const userAccountAfter = await program.account.user.fetchNullable(userAccountAddress, "processed");
-        assert.notEqual(userAccountAfter, null);
-        assert.equal(userAccountAfter.name, "John Doe");
-        assert.equal(userAccountAfter.user.toBase58(), user.publicKey.toBase58());
-    })
-
-    it("Destroy Account", async () => {
-        const [userAccountAddress] = await getUserAccount(user.publicKey);
-        const userAccountBefore = await program.account.user.fetchNullable(userAccountAddress, "processed");
-        assert.notEqual(userAccountBefore, null);
-
-        await program.methods
-            .destroyUser()
-            .accounts({
-                user: user.publicKey,
-                userAccount: userAccountAddress
-            })
-            .signers([user])
-            .rpc({commitment: "confirmed"});
-
-        const userAccountAfter = await program.account.user.fetchNullable(userAccountAddress, "processed");
-        assert.equal(userAccountAfter, null);
-    })
-});