Browse Source

merged upstream

Dadepo Aderemi 3 years ago
parent
commit
4954a5bad3
100 changed files with 2532 additions and 50 deletions
  1. BIN
      .DS_Store
  2. 2 0
      .gitignore
  3. 5 6
      README.md
  4. 14 0
      basics/account-data/anchor/Anchor.toml
  5. 13 0
      basics/account-data/anchor/Cargo.toml
  6. 14 0
      basics/account-data/anchor/package.json
  7. 19 0
      basics/account-data/anchor/programs/anchor-program-example/Cargo.toml
  8. 2 0
      basics/account-data/anchor/programs/anchor-program-example/Xargo.toml
  9. 49 0
      basics/account-data/anchor/programs/anchor-program-example/src/instructions/create.rs
  10. 3 0
      basics/account-data/anchor/programs/anchor-program-example/src/instructions/mod.rs
  11. 32 0
      basics/account-data/anchor/programs/anchor-program-example/src/lib.rs
  12. 27 0
      basics/account-data/anchor/programs/anchor-program-example/src/state/address_info.rs
  13. 3 0
      basics/account-data/anchor/programs/anchor-program-example/src/state/mod.rs
  14. 40 0
      basics/account-data/anchor/tests/test.ts
  15. 10 0
      basics/account-data/anchor/tsconfig.json
  16. 8 0
      basics/account-data/native/cicd.sh
  17. 18 0
      basics/account-data/native/package.json
  18. 12 0
      basics/account-data/native/program/Cargo.toml
  19. 46 0
      basics/account-data/native/program/src/instructions/create.rs
  20. 3 0
      basics/account-data/native/program/src/instructions/mod.rs
  21. 10 0
      basics/account-data/native/program/src/lib.rs
  22. 27 0
      basics/account-data/native/program/src/processor.rs
  23. 27 0
      basics/account-data/native/program/src/state/address_info.rs
  24. 3 0
      basics/account-data/native/program/src/state/mod.rs
  25. 92 0
      basics/account-data/native/tests/test.ts
  26. 10 0
      basics/account-data/native/tsconfig.json
  27. 14 0
      basics/counter/README.md
  28. 7 0
      basics/counter/anchor/.gitignore
  29. 8 0
      basics/counter/anchor/.prettierignore
  30. 15 0
      basics/counter/anchor/Anchor.toml
  31. 13 0
      basics/counter/anchor/Cargo.toml
  32. 5 0
      basics/counter/anchor/README.md
  33. 12 0
      basics/counter/anchor/migrations/deploy.ts
  34. 19 0
      basics/counter/anchor/package.json
  35. 19 0
      basics/counter/anchor/programs/counter_anchor/Cargo.toml
  36. 2 0
      basics/counter/anchor/programs/counter_anchor/Xargo.toml
  37. 37 0
      basics/counter/anchor/programs/counter_anchor/src/lib.rs
  38. 44 0
      basics/counter/anchor/tests/counter_anchor.ts
  39. 10 0
      basics/counter/anchor/tsconfig.json
  40. 16 0
      basics/counter/mpl-stack/.solitarc.js
  41. 17 0
      basics/counter/mpl-stack/Cargo.toml
  42. 13 0
      basics/counter/mpl-stack/README.md
  43. 42 0
      basics/counter/mpl-stack/idl/counter_mpl_stack.json
  44. 33 0
      basics/counter/mpl-stack/idl/counter_solana_native.json
  45. 6 0
      basics/counter/mpl-stack/jest.config.js
  46. 30 0
      basics/counter/mpl-stack/package.json
  47. 67 0
      basics/counter/mpl-stack/src/lib.rs
  48. 7 0
      basics/counter/mpl-stack/src/state.rs
  49. 119 0
      basics/counter/mpl-stack/tests/counter.test.ts
  50. 156 0
      basics/counter/mpl-stack/ts/generated/accounts/Counter.ts
  51. 5 0
      basics/counter/mpl-stack/ts/generated/accounts/index.ts
  52. 19 0
      basics/counter/mpl-stack/ts/generated/index.ts
  53. 62 0
      basics/counter/mpl-stack/ts/generated/instructions/Increment.ts
  54. 1 0
      basics/counter/mpl-stack/ts/generated/instructions/index.ts
  55. 1 0
      basics/counter/mpl-stack/ts/index.ts
  56. 16 0
      basics/counter/native/Cargo.toml
  57. 14 0
      basics/counter/native/README.md
  58. 6 0
      basics/counter/native/jest.config.js
  59. 28 0
      basics/counter/native/package.json
  60. 60 0
      basics/counter/native/src/lib.rs
  61. 6 0
      basics/counter/native/src/state.rs
  62. 123 0
      basics/counter/native/tests/counter.test.ts
  63. 17 0
      basics/counter/native/ts/accounts/counter.ts
  64. 1 0
      basics/counter/native/ts/accounts/index.ts
  65. 5 0
      basics/counter/native/ts/index.ts
  66. 25 0
      basics/counter/native/ts/instructions/createIncrementInstruction.ts
  67. 1 0
      basics/counter/native/ts/instructions/index.ts
  68. 7 0
      basics/counter/seahorse/.gitignore
  69. 8 0
      basics/counter/seahorse/.prettierignore
  70. 15 0
      basics/counter/seahorse/Anchor.toml
  71. 13 0
      basics/counter/seahorse/Cargo.toml
  72. 5 0
      basics/counter/seahorse/README.md
  73. 12 0
      basics/counter/seahorse/migrations/deploy.ts
  74. 19 0
      basics/counter/seahorse/package.json
  75. 20 0
      basics/counter/seahorse/programs/counter_seahorse/Cargo.toml
  76. 2 0
      basics/counter/seahorse/programs/counter_seahorse/Xargo.toml
  77. 63 0
      basics/counter/seahorse/programs/counter_seahorse/src/lib.rs
  78. 23 0
      basics/counter/seahorse/programs_py/counter_seahorse.py
  79. 0 0
      basics/counter/seahorse/programs_py/seahorse/__init__.py
  80. 330 0
      basics/counter/seahorse/programs_py/seahorse/prelude.py
  81. 43 0
      basics/counter/seahorse/tests/counter_seahorse.ts
  82. 10 0
      basics/counter/seahorse/tsconfig.json
  83. 6 6
      basics/create-account/native/program/src/lib.rs
  84. 8 22
      basics/create-account/native/tests/test.ts
  85. 48 11
      basics/hello-solana/seahorse/hello_solana/programs/hello_solana/src/lib.rs
  86. 24 5
      basics/hello-solana/seahorse/hello_solana/programs_py/hello_solana.py
  87. 14 0
      basics/program-derived-addresses/anchor/Anchor.toml
  88. 13 0
      basics/program-derived-addresses/anchor/Cargo.toml
  89. 14 0
      basics/program-derived-addresses/anchor/package.json
  90. 19 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/Cargo.toml
  91. 2 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/Xargo.toml
  92. 36 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/create.rs
  93. 30 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/increment.rs
  94. 5 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/mod.rs
  95. 29 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/lib.rs
  96. 3 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/state/mod.rs
  97. 26 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/state/page_visits.rs
  98. 77 0
      basics/program-derived-addresses/anchor/tests/test.ts
  99. 10 0
      basics/program-derived-addresses/anchor/tsconfig.json
  100. 8 0
      basics/program-derived-addresses/native/cicd.sh

BIN
.DS_Store


+ 2 - 0
.gitignore

@@ -1,5 +1,7 @@
 .template
 
+test-ledger/
+
 **/*/node_modules
 **/*/package-lock.json
 **/*/Cargo.lock

+ 5 - 6
README.md

@@ -38,21 +38,20 @@ Regardless of what you may want to add on top of existing Solana programs, the n
 :mag: *Got something you want to see here? Add it to the list. Or better yet, write one & create a PR!*
 * ### [ ] Basics
     * [x] Hello Solana
-    * [x] Processing Instructions
-    * [x] Repository Layout
     * [x] Checking Accounts
     * [x] Create an Account
-    * [ ] Assign Data to an Account
+    * [x] Assign Data to an Account
     * [x] Rent
     * [ ] Reallocate an Account's Data
     * [ ] Transfer an Account's Ownership
     * [ ] Destroy an Account
+    * [x] Processing Instructions
+    * [x] Create a Simple PDA
+    * [ ] PDAs Expanded
+    * [x] Repository Layout
     * [x] Transfer SOL
     * [ ] Stake SOL with a Validator
     * [x] Cross-program Invocation
-    * [ ] Create a Simple PDA
-    * [ ] PDAs Expanded
-    * [ ] PDAs as a Database
 * ### [ ] Tokens
     * [ ] Create an SPL Token
     * [ ] Token Metadata

+ 14 - 0
basics/account-data/anchor/Anchor.toml

@@ -0,0 +1,14 @@
+[features]
+seeds = false
+[programs.localnet]
+anchor_program_example = "FFKtnYFyzPj1qFjE9epkrfYHJwZMdh8CvJrB6XsKeFVz"
+
+[registry]
+url = "https://anchor.projectserum.com"
+
+[provider]
+cluster = "localnet"
+wallet = "~/.config/solana/id.json"
+
+[scripts]
+test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

+ 13 - 0
basics/account-data/anchor/Cargo.toml

@@ -0,0 +1,13 @@
+[workspace]
+members = [
+    "programs/*"
+]
+
+[profile.release]
+overflow-checks = true
+lto = "fat"
+codegen-units = 1
+[profile.release.build-override]
+opt-level = 3
+incremental = false
+codegen-units = 1

+ 14 - 0
basics/account-data/anchor/package.json

@@ -0,0 +1,14 @@
+{
+    "dependencies": {
+        "@project-serum/anchor": "^0.24.2"
+    },
+    "devDependencies": {
+        "@types/bn.js": "^5.1.0",
+        "@types/chai": "^4.3.0",
+        "@types/mocha": "^9.0.0",
+        "chai": "^4.3.4",
+        "mocha": "^9.0.3",
+        "ts-mocha": "^10.0.0",
+        "typescript": "^4.3.5"
+    }
+}

+ 19 - 0
basics/account-data/anchor/programs/anchor-program-example/Cargo.toml

@@ -0,0 +1,19 @@
+[package]
+name = "anchor-program-example"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "anchor_program_example"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang = "0.24.2"

+ 2 - 0
basics/account-data/anchor/programs/anchor-program-example/Xargo.toml

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

+ 49 - 0
basics/account-data/anchor/programs/anchor-program-example/src/instructions/create.rs

@@ -0,0 +1,49 @@
+use anchor_lang::prelude::*;
+
+use crate::state::AddressInfo;
+
+
+pub fn create_address_info(
+    ctx: Context<CreateAddressInfo>,
+    name: String,
+    house_number: u8,
+    street: String,
+    city: String,
+) -> Result<()> {
+
+    let address_info = AddressInfo::new(
+        name,
+        house_number,
+        street,
+        city,
+    );
+
+    let account_span = (address_info.try_to_vec()?).len();
+    let lamports_required = (Rent::get()?).minimum_balance(account_span);
+
+    system_program::create_account(
+        CpiContext::new(
+            ctx.accounts.system_program.to_account_info(),
+            system_program::CreateAccount {
+                from: ctx.accounts.payer.to_account_info(),
+                to: ctx.accounts.address_info.to_account_info(),
+            },
+        ),
+        lamports_required,
+        account_span as u64,
+        &ctx.accounts.system_program.key(),
+    )?;
+
+    let address_info_account = &mut ctx.accounts.address_info;
+    address_info_account.set_inner(address_info);
+    Ok(())
+}
+
+#[derive(Accounts)]
+pub struct CreateAddressInfo<'info> {
+    #[account(mut)]
+    address_info: Account<'info, AddressInfo>,
+    #[account(mut)]
+    payer: Signer<'info>,
+    system_program: Program<'info, System>,
+}

+ 3 - 0
basics/account-data/anchor/programs/anchor-program-example/src/instructions/mod.rs

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

+ 32 - 0
basics/account-data/anchor/programs/anchor-program-example/src/lib.rs

@@ -0,0 +1,32 @@
+use anchor_lang::prelude::*;
+
+use instructions::*;
+
+pub mod instructions;
+pub mod state;
+
+
+declare_id!("FFKtnYFyzPj1qFjE9epkrfYHJwZMdh8CvJrB6XsKeFVz");
+
+
+#[program]
+pub mod anchor_program_example {
+    use super::*;
+
+    pub fn create_address_info(
+        ctx: Context<CreateAddressInfo>,
+        name: String,
+        house_number: u8,
+        street: String,
+        city: String,
+    ) -> Result<()> {
+        
+        instructions::create::create_address_info(
+            ctx,
+            name,
+            house_number,
+            street,
+            city,
+        )
+    }
+}

+ 27 - 0
basics/account-data/anchor/programs/anchor-program-example/src/state/address_info.rs

@@ -0,0 +1,27 @@
+use anchor_lang::prelude::*;
+
+
+#[account]
+pub struct AddressInfo {
+    pub name: String,
+    pub house_number: u8,
+    pub street: String,
+    pub city: String,
+}
+
+impl AddressInfo {
+
+    pub fn new(
+        name: String,
+        house_number: u8,
+        street: String,
+        city: String,
+    ) -> Self {
+        AddressInfo {
+            name,
+            house_number,
+            street,
+            city,
+        }
+    }
+}

+ 3 - 0
basics/account-data/anchor/programs/anchor-program-example/src/state/mod.rs

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

+ 40 - 0
basics/account-data/anchor/tests/test.ts

@@ -0,0 +1,40 @@
+import * as anchor from "@project-serum/anchor";
+import { AnchorProgramExample } from "../target/types/anchor_program_example";
+
+describe("Account Data!", () => {
+
+  const provider = anchor.AnchorProvider.env();
+  anchor.setProvider(provider);
+  const payer = provider.wallet as anchor.Wallet;
+  const program = anchor.workspace.AnchorProgramExample as anchor.Program<AnchorProgramExample>;
+
+  const addressInfoAccount = anchor.web3.Keypair.generate();
+
+  it("Create the address info account", async () => {
+    console.log(`Payer Address      : ${payer.publicKey}`);
+    console.log(`Address Info Acct  : ${addressInfoAccount.publicKey}`);
+    await program.methods.createAddressInfo(
+      "Joe C",
+      136,
+      "Mile High Dr.",
+      "Solana Beach",
+    )
+      .accounts({
+        addressInfo: addressInfoAccount.publicKey,
+        payer: payer.publicKey,
+        systemProgram: anchor.web3.SystemProgram.programId,
+      })
+      .signers([payer.payer])
+      .rpc();
+  });
+
+  it("Read the new account's data", async () => {
+    const addressInfo = await program.account.addressInfo.fetch(
+      addressInfoAccount.publicKey
+    );
+    console.log(`Name     : ${addressInfo.name}`);
+    console.log(`House Num: ${addressInfo.houseNumber}`);
+    console.log(`Street   : ${addressInfo.street}`);
+    console.log(`City     : ${addressInfo.city}`);
+  });
+});

+ 10 - 0
basics/account-data/anchor/tsconfig.json

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

+ 8 - 0
basics/account-data/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/account-data/native/package.json

@@ -0,0 +1,18 @@
+{
+  "scripts": {
+    "test": "yarn run ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts"
+  },
+  "dependencies": {
+    "@solana/web3.js": "^1.47.3",
+    "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/account-data/native/program/Cargo.toml

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

+ 46 - 0
basics/account-data/native/program/src/instructions/create.rs

@@ -0,0 +1,46 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+use solana_program::{
+    account_info::{ AccountInfo, next_account_info },
+    entrypoint::ProgramResult, 
+    program::invoke,
+    pubkey::Pubkey,
+    rent::Rent,
+    system_instruction,
+    system_program,
+    sysvar::Sysvar,
+};
+
+use crate::state::AddressInfo;
+
+
+pub fn create_address_info(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    address_info: AddressInfo,
+) -> ProgramResult {
+
+    let accounts_iter = &mut accounts.iter();
+    let address_info_account = next_account_info(accounts_iter)?;
+    let payer = next_account_info(accounts_iter)?;
+    let system_program = next_account_info(accounts_iter)?;
+
+    let account_span = (address_info.try_to_vec()?).len();
+    let lamports_required = (Rent::get()?).minimum_balance(account_span);
+
+    invoke(
+        &system_instruction::create_account(
+            &payer.key,
+            &address_info_account.key,
+            lamports_required,
+            account_span as u64,
+            program_id,
+        ),
+        &[
+            payer.clone(), address_info_account.clone(), system_program.clone()
+        ]
+    )?;
+    
+    address_info.serialize(&mut &mut address_info_account.data.borrow_mut()[..])?;
+    Ok(())
+}
+

+ 3 - 0
basics/account-data/native/program/src/instructions/mod.rs

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

+ 10 - 0
basics/account-data/native/program/src/lib.rs

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

+ 27 - 0
basics/account-data/native/program/src/processor.rs

@@ -0,0 +1,27 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+use solana_program::{
+    account_info::AccountInfo, 
+    entrypoint::ProgramResult, 
+    program_error::ProgramError,
+    pubkey::Pubkey,
+};
+
+use crate::instructions;
+use crate::state::AddressInfo;
+
+
+pub fn process_instruction(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+
+    match AddressInfo::try_from_slice(&instruction_data) {
+        Ok(address_info) => return instructions::create::create_address_info(
+            program_id, accounts, address_info
+        ),
+        Err(_) => {},
+    };
+
+    Err(ProgramError::InvalidInstructionData)
+}

+ 27 - 0
basics/account-data/native/program/src/state/address_info.rs

@@ -0,0 +1,27 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+
+
+#[derive(BorshDeserialize, BorshSerialize, Debug)]
+pub struct AddressInfo {
+    pub name: String,
+    pub house_number: u8,
+    pub street: String,
+    pub city: String,
+}
+
+impl AddressInfo {
+
+    pub fn new(
+        name: String,
+        house_number: u8,
+        street: String,
+        city: String,
+    ) -> Self {
+        AddressInfo {
+            name,
+            house_number,
+            street,
+            city,
+        }
+    }
+}

+ 3 - 0
basics/account-data/native/program/src/state/mod.rs

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

+ 92 - 0
basics/account-data/native/tests/test.ts

@@ -0,0 +1,92 @@
+import {
+    Connection,
+    Keypair,
+    PublicKey,
+    sendAndConfirmTransaction,
+    SystemProgram,
+    Transaction,
+    TransactionInstruction,
+} from '@solana/web3.js';
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+
+
+function createKeypairFromFile(path: string): Keypair {
+    return Keypair.fromSecretKey(
+        Buffer.from(JSON.parse(require('fs').readFileSync(path, "utf-8")))
+    )
+};
+
+
+describe("Account Data!", () => {
+
+    const connection = new Connection(`http://localhost:8899`, 'confirmed');
+    const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
+    const PROGRAM_ID: PublicKey = new PublicKey(
+        "BCw7MQWBugruuYgno5crGUGFNufqGJbPpzZevhRRRQAu"
+    );
+
+    class Assignable {
+        constructor(properties) {
+            Object.keys(properties).map((key) => {
+                return (this[key] = properties[key]);
+            });
+        };
+    };
+
+    class AddressInfo extends Assignable {
+        toBuffer() { return Buffer.from(borsh.serialize(AddressInfoSchema, this)) }
+        
+        static fromBuffer(buffer: Buffer) {
+            return borsh.deserialize(AddressInfoSchema, AddressInfo, buffer);
+        };
+    };
+    const AddressInfoSchema = new Map([
+        [ AddressInfo, { 
+            kind: 'struct', 
+            fields: [ 
+                ['name', 'string'], 
+                ['house_number', 'u8'], 
+                ['street', 'string'], 
+                ['city', 'string'], 
+            ],
+        }]
+    ]);
+
+    const addressInfoAccount = Keypair.generate();
+
+    it("Create the address info account", async () => {
+        console.log(`Payer Address      : ${payer.publicKey}`);
+        console.log(`Address Info Acct  : ${addressInfoAccount.publicKey}`);
+        let ix = new TransactionInstruction({
+            keys: [
+                {pubkey: addressInfoAccount.publicKey, isSigner: true, isWritable: true},
+                {pubkey: payer.publicKey, isSigner: true, isWritable: true},
+                {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
+            ],
+            programId: PROGRAM_ID,
+            data: (
+                new AddressInfo({
+                    name: "Joe C",
+                    house_number: 136,
+                    street: "Mile High Dr.",
+                    city: "Solana Beach",
+                })
+            ).toBuffer(),
+        });
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer, addressInfoAccount]
+        );
+    });
+
+    it("Read the new account's data", async () => {
+        const accountInfo = await connection.getAccountInfo(addressInfoAccount.publicKey);
+        const readAddressInfo = AddressInfo.fromBuffer(accountInfo.data);
+        console.log(`Name     : ${readAddressInfo.name}`);
+        console.log(`House Num: ${readAddressInfo.house_number}`);
+        console.log(`Street   : ${readAddressInfo.street}`);
+        console.log(`City     : ${readAddressInfo.city}`);
+    });
+});

+ 10 - 0
basics/account-data/native/tsconfig.json

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

+ 14 - 0
basics/counter/README.md

@@ -0,0 +1,14 @@
+# Counter
+
+This example program allows anyone to create a counter and increment it.
+
+Any counter can be incremented by any key.
+
+## Note: Seahorse
+
+Seahorse currently does not allow the program to initialize anchor 
+accounts unless they are PDAs. 
+
+Seahorse example only allows users to increment the counter that corresponds to their public key.
+
+

+ 7 - 0
basics/counter/anchor/.gitignore

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

+ 8 - 0
basics/counter/anchor/.prettierignore

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

+ 15 - 0
basics/counter/anchor/Anchor.toml

@@ -0,0 +1,15 @@
+[features]
+seeds = false
+skip-lint = false
+[programs.localnet]
+counter_anchor = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
+[registry]
+url = "https://api.apr.dev"
+
+[provider]
+cluster = "localnet"
+wallet = "/Users/noahgundotra/.config/solana/id.json"
+
+[scripts]
+test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

+ 13 - 0
basics/counter/anchor/Cargo.toml

@@ -0,0 +1,13 @@
+[workspace]
+members = [
+    "programs/*"
+]
+
+[profile.release]
+overflow-checks = true
+lto = "fat"
+codegen-units = 1
+[profile.release.build-override]
+opt-level = 3
+incremental = false
+codegen-units = 1

+ 5 - 0
basics/counter/anchor/README.md

@@ -0,0 +1,5 @@
+# Anchor Counter
+
+Anchor enforces init constraints that enforces good programming paradigms.
+
+This means this program has an additional initialization instruction for `Counter`s that the Solana native program does not.

+ 12 - 0
basics/counter/anchor/migrations/deploy.ts

@@ -0,0 +1,12 @@
+// Migrations are an early feature. Currently, they're nothing more than this
+// single deploy script that's invoked from the CLI, injecting a provider
+// configured from the workspace's Anchor.toml.
+
+const anchor = require("@project-serum/anchor");
+
+module.exports = async function (provider) {
+  // Configure client to use the provider.
+  anchor.setProvider(provider);
+
+  // Add your deploy script here.
+};

+ 19 - 0
basics/counter/anchor/package.json

@@ -0,0 +1,19 @@
+{
+    "scripts": {
+        "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
+        "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
+    },
+    "dependencies": {
+        "@project-serum/anchor": "^0.25.0"
+    },
+    "devDependencies": {
+        "chai": "^4.3.4",
+        "mocha": "^9.0.3",
+        "ts-mocha": "^10.0.0",
+        "@types/bn.js": "^5.1.0",
+        "@types/chai": "^4.3.0",
+        "@types/mocha": "^9.0.0",
+        "typescript": "^4.3.5",
+        "prettier": "^2.6.2"
+    }
+}

+ 19 - 0
basics/counter/anchor/programs/counter_anchor/Cargo.toml

@@ -0,0 +1,19 @@
+[package]
+name = "counter_anchor"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "counter_anchor"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang =  "0.25.0"

+ 2 - 0
basics/counter/anchor/programs/counter_anchor/Xargo.toml

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

+ 37 - 0
basics/counter/anchor/programs/counter_anchor/src/lib.rs

@@ -0,0 +1,37 @@
+use anchor_lang::prelude::*;
+
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
+#[program]
+pub mod counter_anchor {
+    use super::*;
+
+    pub fn initialize_counter(ctx: Context<InitializeCounter>) -> Result<()> {
+        Ok(())
+    }
+
+    pub fn increment(ctx: Context<Increment>) -> Result<()> {
+        ctx.accounts.counter.count += 1;
+        Ok(())
+    }
+}
+
+#[account]
+pub struct Counter {
+    count: u64,
+}
+
+#[derive(Accounts)]
+pub struct InitializeCounter<'info> {
+    #[account(init, space=8+8, payer=payer)]
+    pub counter: Account<'info, Counter>,
+    #[account(mut)]
+    pub payer: Signer<'info>,
+    pub system_program: Program<'info, System>,
+}
+
+#[derive(Accounts)]
+pub struct Increment<'info> {
+    #[account(mut)]
+    pub counter: Account<'info, Counter>,
+}

+ 44 - 0
basics/counter/anchor/tests/counter_anchor.ts

@@ -0,0 +1,44 @@
+import * as anchor from "@project-serum/anchor";
+import { Program } from "@project-serum/anchor";
+import {
+  Keypair
+} from '@solana/web3.js'
+import { assert } from "chai";
+import { CounterAnchor } from "../target/types/counter_anchor";
+
+describe("counter_anchor", () => {
+  // Configure the client to use the local cluster.
+  anchor.setProvider(anchor.AnchorProvider.env());
+
+  const program = anchor.workspace.CounterAnchor as Program<CounterAnchor>;
+
+  it("Test increment", async () => {
+    const counterKeypair = Keypair.generate();
+    const counter = counterKeypair.publicKey;
+
+    // Initialize counter
+    await program.methods
+      .initializeCounter()
+      .accounts({ counter, payer: program.provider.publicKey })
+      .signers([counterKeypair])
+      .rpc({ skipPreflight: true, commitment: "confirmed" });
+    let currentCount = (await program.account.counter.fetch(counter, "confirmed")).count.toNumber();
+    assert(currentCount === 0, "Expected initialized count to be 0");
+
+    // Increment counter
+    await program.methods
+      .increment()
+      .accounts({ counter })
+      .rpc({ skipPreflight: true, commitment: "confirmed" });
+    currentCount = (await program.account.counter.fetch(counter, "confirmed")).count.toNumber();
+    assert(currentCount === 1, "Expected count to be 1");
+
+    // Increment counter
+    await program.methods
+      .increment()
+      .accounts({ counter })
+      .rpc({ skipPreflight: true, commitment: "confirmed" });
+    currentCount = (await program.account.counter.fetch(counter, "confirmed")).count.toNumber();
+    assert(currentCount === 2, "Expected count to be 2");
+  });
+});

+ 10 - 0
basics/counter/anchor/tsconfig.json

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

+ 16 - 0
basics/counter/mpl-stack/.solitarc.js

@@ -0,0 +1,16 @@
+// @ts-check
+const path = require('path');
+const programDir = path.join(__dirname);
+const idlDir = path.join(__dirname, 'idl');
+const sdkDir = path.join(__dirname, 'ts', 'generated');
+const binaryInstallDir = path.join(__dirname, 'target', 'solita');
+
+module.exports = {
+    idlGenerator: 'shank',
+    programName: 'counter_mpl_stack',
+    programId: 'Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS',
+    idlDir,
+    sdkDir,
+    binaryInstallDir,
+    programDir,
+};

+ 17 - 0
basics/counter/mpl-stack/Cargo.toml

@@ -0,0 +1,17 @@
+[package]
+name = "counter-mpl-stack"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+
+[features]
+no-entrypoint = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+borsh = "0.9"
+shank = "0.0.8"
+solana-program = "1.10.38"

+ 13 - 0
basics/counter/mpl-stack/README.md

@@ -0,0 +1,13 @@
+# Counter: MPL Stack
+
+This example program is written using Solana native using MPL stack.
+
+
+## Setup
+
+1. Build the program with `cargo build-bpf`
+2. Compile the idl with `shank build`
+3. Build the typescript SDK with `yarn solita`
+ - Temporarily, we have to modify line 58 in ts/generated/accounts/Counter.ts 
+ to `const accountInfo = await connection.getAccountInfo(address, { commitment: "confirmed" });` in order to allow the tests to pass. In the future versions of Solita, this will be fixed.
+4. Run tests with `yarn test`

+ 42 - 0
basics/counter/mpl-stack/idl/counter_mpl_stack.json

@@ -0,0 +1,42 @@
+{
+  "version": "0.1.0",
+  "name": "counter_mpl_stack",
+  "instructions": [
+    {
+      "name": "Increment",
+      "accounts": [
+        {
+          "name": "counter",
+          "isMut": true,
+          "isSigner": false,
+          "desc": "Counter account to increment"
+        }
+      ],
+      "args": [],
+      "discriminant": {
+        "type": "u8",
+        "value": 0
+      }
+    }
+  ],
+  "accounts": [
+    {
+      "name": "Counter",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "count",
+            "type": "u64"
+          }
+        ]
+      }
+    }
+  ],
+  "metadata": {
+    "origin": "shank",
+    "address": "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
+    "binaryVersion": "0.0.8",
+    "libVersion": "0.0.8"
+  }
+}

+ 33 - 0
basics/counter/mpl-stack/idl/counter_solana_native.json

@@ -0,0 +1,33 @@
+{
+  "version": "0.1.0",
+  "name": "counter_solana_native",
+  "instructions": [
+    {
+      "name": "Increment",
+      "accounts": [],
+      "args": [],
+      "discriminant": {
+        "type": "u8",
+        "value": 0
+      }
+    }
+  ],
+  "accounts": [
+    {
+      "name": "Counter",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "count",
+            "type": "u64"
+          }
+        ]
+      }
+    }
+  ],
+  "metadata": {
+    "origin": "shank",
+    "address": "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+  }
+}

+ 6 - 0
basics/counter/mpl-stack/jest.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+    preset: 'ts-jest/presets/default',
+    testEnvironment: 'node',
+    testTimeout: 100000,
+    resolver: "ts-jest-resolver",
+};

+ 30 - 0
basics/counter/mpl-stack/package.json

@@ -0,0 +1,30 @@
+{
+  "name": "counter-mpl-stack",
+  "version": "0.1.0",
+  "description": "Counter program written using MPL tooling",
+  "main": "index.js",
+  "author": "ngundotra",
+  "license": "Apache-2.0",
+  "private": false,
+  "scripts": {
+    "start-validator": "solana-test-validator --reset --quiet --bpf-program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS ./target/deploy/counter_solana_native.so",
+    "run-tests": "jest tests --detectOpenHandles",
+    "test": "start-server-and-test start-validator http://localhost:8899/health run-tests"
+  },
+  "devDependencies": {
+    "@types/bn.js": "^5.1.1",
+    "@types/jest": "^29.0.0",
+    "chai": "^4.3.6",
+    "jest": "^29.0.2",
+    "start-server-and-test": "^1.14.0",
+    "ts-jest": "^28.0.8",
+    "ts-jest-resolver": "^2.0.0",
+    "ts-node": "^10.9.1",
+    "typescript": "^4.8.2"
+  },
+  "dependencies": {
+    "@metaplex-foundation/beet": "^0.6.1",
+    "@metaplex-foundation/solita": "^0.15.2",
+    "@solana/web3.js": "^1.56.2"
+  }
+}

+ 67 - 0
basics/counter/mpl-stack/src/lib.rs

@@ -0,0 +1,67 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use shank::ShankInstruction;
+use solana_program::{
+    account_info::{next_account_info, AccountInfo},
+    declare_id,
+    entrypoint::ProgramResult,
+    msg,
+    program::{invoke, invoke_signed},
+    program_error::ProgramError,
+    pubkey::Pubkey,
+    system_instruction,
+};
+
+mod state;
+use state::*;
+
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
+#[cfg(not(feature = "no-entrypoint"))]
+use solana_program::entrypoint;
+
+#[cfg(not(feature = "no-entrypoint"))]
+entrypoint!(process_instruction);
+
+#[derive(ShankInstruction, BorshDeserialize, BorshSerialize)]
+pub enum Instruction {
+    #[account(0, writable, name = "counter", desc = "Counter account to increment")]
+    Increment,
+}
+
+pub fn process_instruction(
+    _program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    let (instruction_discriminant, instruction_data_inner) = instruction_data.split_at(1);
+    match instruction_discriminant[0] {
+        0 => {
+            msg!("Instruction: Increment");
+            process_increment_counter(accounts, instruction_data_inner)?;
+        }
+        _ => {
+            msg!("Error: unknown instruction")
+        }
+    }
+    Ok(())
+}
+
+pub fn process_increment_counter(
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> Result<(), ProgramError> {
+    let account_info_iter = &mut accounts.iter();
+
+    let counter_account = next_account_info(account_info_iter)?;
+    assert!(
+        counter_account.is_writable,
+        "Counter account must be writable"
+    );
+
+    let mut counter = Counter::try_from_slice(&counter_account.try_borrow_mut_data()?)?;
+    counter.count += 1;
+    counter.serialize(&mut *counter_account.data.borrow_mut())?;
+
+    msg!("Counter state incremented to {:?}", counter.count);
+    Ok(())
+}

+ 7 - 0
basics/counter/mpl-stack/src/state.rs

@@ -0,0 +1,7 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use shank::ShankAccount;
+
+#[derive(ShankAccount, BorshSerialize, BorshDeserialize, Debug, Clone)]
+pub struct Counter {
+    pub count: u64,
+}

+ 119 - 0
basics/counter/mpl-stack/tests/counter.test.ts

@@ -0,0 +1,119 @@
+import {
+    Connection,
+    Keypair,
+    LAMPORTS_PER_SOL,
+    Transaction,
+    TransactionInstruction,
+    sendAndConfirmTransaction,
+    SystemProgram
+} from '@solana/web3.js';
+import {
+    bignum
+} from '@metaplex-foundation/beet';
+import { assert } from 'chai';
+import { BN } from 'bn.js';
+
+import {
+    createIncrementInstruction,
+    Counter,
+    PROGRAM_ID,
+} from '../ts';
+
+function convertBignumToNumber(bignum: bignum): number {
+    return new BN(bignum).toNumber();
+}
+
+describe("Counter Solana Native", () => {
+    const connection = new Connection("http://localhost:8899");
+
+    it("Test allocate counter + increment tx", async () => {
+        // Randomly generate our wallet
+        const payerKeypair = Keypair.generate();
+        const payer = payerKeypair.publicKey;
+
+        // Randomly generate the account key 
+        // to sign for setting up the Counter state
+        const counterKeypair = Keypair.generate();
+        const counter = counterKeypair.publicKey;
+
+        // Airdrop our wallet 1 Sol
+        await connection.requestAirdrop(payer, LAMPORTS_PER_SOL);
+
+        // Create a TransactionInstruction to interact with our counter program
+        const allocIx: TransactionInstruction = SystemProgram.createAccount({
+            fromPubkey: payer,
+            newAccountPubkey: counter,
+            lamports: await connection.getMinimumBalanceForRentExemption(Counter.byteSize),
+            space: Counter.byteSize,
+            programId: PROGRAM_ID
+        })
+        const incrementIx: TransactionInstruction = createIncrementInstruction({ counter });
+        let tx = new Transaction().add(allocIx).add(incrementIx);
+
+        // Explicitly set the feePayer to be our wallet (this is set to first signer by default)
+        tx.feePayer = payer;
+
+        // Fetch a "timestamp" so validators know this is a recent transaction
+        tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash;
+
+        // Send transaction to network (local network)
+        await sendAndConfirmTransaction(
+            connection,
+            tx,
+            [payerKeypair, counterKeypair],
+            { skipPreflight: true, commitment: 'confirmed' }
+        );
+
+        // Get the counter account info from network
+        let count = (await Counter.fromAccountAddress(connection, counter)).count;
+        assert((new BN(count)).toNumber() === 1, "Expected count to have been 1");
+        console.log(`[alloc+increment] count is: ${count}`);
+    });
+    it("Test allocate tx and increment tx", async () => {
+        const payerKeypair = Keypair.generate();
+        const payer = payerKeypair.publicKey;
+
+        const counterKeypair = Keypair.generate();
+        const counter = counterKeypair.publicKey;
+
+        await connection.requestAirdrop(payer, LAMPORTS_PER_SOL);
+
+        // Check allocate tx
+        const allocIx: TransactionInstruction = SystemProgram.createAccount({
+            fromPubkey: payer,
+            newAccountPubkey: counter,
+            lamports: await connection.getMinimumBalanceForRentExemption(Counter.byteSize),
+            space: Counter.byteSize,
+            programId: PROGRAM_ID
+        })
+        let tx = new Transaction().add(allocIx);
+        tx.feePayer = payer;
+        tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash;
+        await sendAndConfirmTransaction(
+            connection,
+            tx,
+            [payerKeypair, counterKeypair],
+            { skipPreflight: true, commitment: 'confirmed' }
+        );
+
+        let count = (await Counter.fromAccountAddress(connection, counter)).count;
+        assert(convertBignumToNumber(count) === 0, "Expected count to have been 0");
+        console.log(`[allocate] count is: ${count}`);
+
+        // Check increment tx
+        const incrementIx: TransactionInstruction = createIncrementInstruction({ counter });
+        tx = new Transaction().add(incrementIx);
+        tx.feePayer = payer;
+        tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash;
+        await sendAndConfirmTransaction(
+            connection,
+            tx,
+            [payerKeypair],
+            { skipPreflight: true, commitment: 'confirmed' }
+        );
+
+        count = (await Counter.fromAccountAddress(connection, counter)).count;
+        assert(convertBignumToNumber(count) === 1, "Expected count to have been 1");
+        console.log(`[increment] count is: ${count}`);
+    })
+})

+ 156 - 0
basics/counter/mpl-stack/ts/generated/accounts/Counter.ts

@@ -0,0 +1,156 @@
+/**
+ * This code was GENERATED using the solita package.
+ * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality.
+ *
+ * See: https://github.com/metaplex-foundation/solita
+ */
+
+import * as beet from '@metaplex-foundation/beet'
+import * as web3 from '@solana/web3.js'
+import * as beetSolana from '@metaplex-foundation/beet-solana'
+
+/**
+ * Arguments used to create {@link Counter}
+ * @category Accounts
+ * @category generated
+ */
+export type CounterArgs = {
+  count: beet.bignum
+}
+/**
+ * Holds the data for the {@link Counter} Account and provides de/serialization
+ * functionality for that data
+ *
+ * @category Accounts
+ * @category generated
+ */
+export class Counter implements CounterArgs {
+  private constructor(readonly count: beet.bignum) { }
+
+  /**
+   * Creates a {@link Counter} instance from the provided args.
+   */
+  static fromArgs(args: CounterArgs) {
+    return new Counter(args.count)
+  }
+
+  /**
+   * Deserializes the {@link Counter} from the data of the provided {@link web3.AccountInfo}.
+   * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it.
+   */
+  static fromAccountInfo(
+    accountInfo: web3.AccountInfo<Buffer>,
+    offset = 0
+  ): [Counter, number] {
+    return Counter.deserialize(accountInfo.data, offset)
+  }
+
+  /**
+   * Retrieves the account info from the provided address and deserializes
+   * the {@link Counter} from its data.
+   *
+   * @throws Error if no account info is found at the address or if deserialization fails
+   */
+  static async fromAccountAddress(
+    connection: web3.Connection,
+    address: web3.PublicKey
+  ): Promise<Counter> {
+    const accountInfo = await connection.getAccountInfo(address, { commitment: "confirmed" });
+    if (accountInfo == null) {
+      throw new Error(`Unable to find Counter account at ${address}`)
+    }
+    return Counter.fromAccountInfo(accountInfo, 0)[0]
+  }
+
+  /**
+   * Provides a {@link web3.Connection.getProgramAccounts} config builder,
+   * to fetch accounts matching filters that can be specified via that builder.
+   *
+   * @param programId - the program that owns the accounts we are filtering
+   */
+  static gpaBuilder(
+    programId: web3.PublicKey = new web3.PublicKey(
+      'Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS'
+    )
+  ) {
+    return beetSolana.GpaBuilder.fromStruct(programId, counterBeet)
+  }
+
+  /**
+   * Deserializes the {@link Counter} from the provided data Buffer.
+   * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it.
+   */
+  static deserialize(buf: Buffer, offset = 0): [Counter, number] {
+    return counterBeet.deserialize(buf, offset)
+  }
+
+  /**
+   * Serializes the {@link Counter} into a Buffer.
+   * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it.
+   */
+  serialize(): [Buffer, number] {
+    return counterBeet.serialize(this)
+  }
+
+  /**
+   * Returns the byteSize of a {@link Buffer} holding the serialized data of
+   * {@link Counter}
+   */
+  static get byteSize() {
+    return counterBeet.byteSize
+  }
+
+  /**
+   * Fetches the minimum balance needed to exempt an account holding
+   * {@link Counter} data from rent
+   *
+   * @param connection used to retrieve the rent exemption information
+   */
+  static async getMinimumBalanceForRentExemption(
+    connection: web3.Connection,
+    commitment?: web3.Commitment
+  ): Promise<number> {
+    return connection.getMinimumBalanceForRentExemption(
+      Counter.byteSize,
+      commitment
+    )
+  }
+
+  /**
+   * Determines if the provided {@link Buffer} has the correct byte size to
+   * hold {@link Counter} data.
+   */
+  static hasCorrectByteSize(buf: Buffer, offset = 0) {
+    return buf.byteLength - offset === Counter.byteSize
+  }
+
+  /**
+   * Returns a readable version of {@link Counter} properties
+   * and can be used to convert to JSON and/or logging
+   */
+  pretty() {
+    return {
+      count: (() => {
+        const x = <{ toNumber: () => number }>this.count
+        if (typeof x.toNumber === 'function') {
+          try {
+            return x.toNumber()
+          } catch (_) {
+            return x
+          }
+        }
+        return x
+      })(),
+    }
+  }
+}
+
+/**
+ * @category Accounts
+ * @category generated
+ */
+export const counterBeet = new beet.BeetStruct<Counter, CounterArgs>(
+  [['count', beet.u64]],
+  Counter.fromArgs,
+  'Counter'
+)

+ 5 - 0
basics/counter/mpl-stack/ts/generated/accounts/index.ts

@@ -0,0 +1,5 @@
+export * from './Counter'
+
+import { Counter } from './Counter'
+
+export const accountProviders = { Counter }

+ 19 - 0
basics/counter/mpl-stack/ts/generated/index.ts

@@ -0,0 +1,19 @@
+import { PublicKey } from '@solana/web3.js'
+export * from './accounts'
+export * from './instructions'
+
+/**
+ * Program address
+ *
+ * @category constants
+ * @category generated
+ */
+export const PROGRAM_ADDRESS = 'Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS'
+
+/**
+ * Program public key
+ *
+ * @category constants
+ * @category generated
+ */
+export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS)

+ 62 - 0
basics/counter/mpl-stack/ts/generated/instructions/Increment.ts

@@ -0,0 +1,62 @@
+/**
+ * This code was GENERATED using the solita package.
+ * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality.
+ *
+ * See: https://github.com/metaplex-foundation/solita
+ */
+
+import * as beet from '@metaplex-foundation/beet'
+import * as web3 from '@solana/web3.js'
+
+/**
+ * @category Instructions
+ * @category Increment
+ * @category generated
+ */
+export const IncrementStruct = new beet.BeetArgsStruct<{
+  instructionDiscriminator: number
+}>([['instructionDiscriminator', beet.u8]], 'IncrementInstructionArgs')
+/**
+ * Accounts required by the _Increment_ instruction
+ *
+ * @property [_writable_] counter Counter account to increment
+ * @category Instructions
+ * @category Increment
+ * @category generated
+ */
+export type IncrementInstructionAccounts = {
+  counter: web3.PublicKey
+}
+
+export const incrementInstructionDiscriminator = 0
+
+/**
+ * Creates a _Increment_ instruction.
+ *
+ * @param accounts that will be accessed while the instruction is processed
+ * @category Instructions
+ * @category Increment
+ * @category generated
+ */
+export function createIncrementInstruction(
+  accounts: IncrementInstructionAccounts,
+  programId = new web3.PublicKey('Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS')
+) {
+  const [data] = IncrementStruct.serialize({
+    instructionDiscriminator: incrementInstructionDiscriminator,
+  })
+  const keys: web3.AccountMeta[] = [
+    {
+      pubkey: accounts.counter,
+      isWritable: true,
+      isSigner: false,
+    },
+  ]
+
+  const ix = new web3.TransactionInstruction({
+    programId,
+    keys,
+    data,
+  })
+  return ix
+}

+ 1 - 0
basics/counter/mpl-stack/ts/generated/instructions/index.ts

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

+ 1 - 0
basics/counter/mpl-stack/ts/index.ts

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

+ 16 - 0
basics/counter/native/Cargo.toml

@@ -0,0 +1,16 @@
+[package]
+name = "counter-solana-native"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+
+[features]
+no-entrypoint = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+borsh = "0.9"
+solana-program = "1.10.38"

+ 14 - 0
basics/counter/native/README.md

@@ -0,0 +1,14 @@
+# Counter: Solana Native
+
+This example program is written in Solana using only the Solana toolsuite.
+
+## Setup
+
+1. Build the program with `cargo build-bpf`
+2. Run tests + local validator with `yarn test`
+
+## Debugging
+
+1. Start test validator with `yarn start-validator`
+2. Start listening to program logs with `solana config set -ul && solana logs`
+3. Run tests with `yarn run-tests`

+ 6 - 0
basics/counter/native/jest.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+    preset: 'ts-jest/presets/default',
+    testEnvironment: 'node',
+    testTimeout: 100000,
+    resolver: "ts-jest-resolver",
+};

+ 28 - 0
basics/counter/native/package.json

@@ -0,0 +1,28 @@
+{
+  "name": "counter-solana-native",
+  "version": "0.1.0",
+  "description": "Counter program written using only Solana tooling",
+  "main": "index.js",
+  "author": "ngundotra",
+  "license": "Apache-2.0",
+  "private": false,
+  "scripts": {
+    "start-validator": "solana-test-validator --reset --quiet --bpf-program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS ./target/deploy/counter_solana_native.so",
+    "run-tests": "jest tests --detectOpenHandles",
+    "test": "start-server-and-test start-validator http://localhost:8899/health run-tests"
+  },
+  "devDependencies": {
+    "@types/bn.js": "^5.1.1",
+    "@types/jest": "^29.0.0",
+    "chai": "^4.3.6",
+    "jest": "^29.0.2",
+    "start-server-and-test": "^1.14.0",
+    "ts-jest": "^28.0.8",
+    "ts-jest-resolver": "^2.0.0",
+    "ts-node": "^10.9.1",
+    "typescript": "^4.8.2"
+  },
+  "dependencies": {
+    "@solana/web3.js": "^1.56.2"
+  }
+}

+ 60 - 0
basics/counter/native/src/lib.rs

@@ -0,0 +1,60 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use solana_program::{
+    account_info::{next_account_info, AccountInfo},
+    declare_id,
+    entrypoint::ProgramResult,
+    msg,
+    program::{invoke, invoke_signed},
+    program_error::ProgramError,
+    pubkey::Pubkey,
+    system_instruction,
+};
+
+mod state;
+use state::*;
+
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
+#[cfg(not(feature = "no-entrypoint"))]
+use solana_program::entrypoint;
+
+#[cfg(not(feature = "no-entrypoint"))]
+entrypoint!(process_instruction);
+
+pub fn process_instruction(
+    _program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    let (instruction_discriminant, instruction_data_inner) = instruction_data.split_at(1);
+    match instruction_discriminant[0] {
+        0 => {
+            msg!("Instruction: Increment");
+            process_increment_counter(accounts, instruction_data_inner)?;
+        }
+        _ => {
+            msg!("Error: unknown instruction")
+        }
+    }
+    Ok(())
+}
+
+pub fn process_increment_counter(
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> Result<(), ProgramError> {
+    let account_info_iter = &mut accounts.iter();
+
+    let counter_account = next_account_info(account_info_iter)?;
+    assert!(
+        counter_account.is_writable,
+        "Counter account must be writable"
+    );
+
+    let mut counter = Counter::try_from_slice(&counter_account.try_borrow_mut_data()?)?;
+    counter.count += 1;
+    counter.serialize(&mut *counter_account.data.borrow_mut())?;
+
+    msg!("Counter state incremented to {:?}", counter.count);
+    Ok(())
+}

+ 6 - 0
basics/counter/native/src/state.rs

@@ -0,0 +1,6 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+
+#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
+pub struct Counter {
+    pub count: u64,
+}

+ 123 - 0
basics/counter/native/tests/counter.test.ts

@@ -0,0 +1,123 @@
+import {
+    Connection,
+    Keypair,
+    LAMPORTS_PER_SOL,
+    Transaction,
+    TransactionInstruction,
+    sendAndConfirmTransaction,
+    SystemProgram
+} from '@solana/web3.js';
+import { assert } from 'chai';
+
+import {
+    createIncrementInstruction,
+    deserializeCounterAccount,
+    Counter,
+    COUNTER_ACCOUNT_SIZE,
+    PROGRAM_ID,
+} from '../ts';
+
+describe("Counter Solana Native", () => {
+    const connection = new Connection("http://localhost:8899");
+
+    it("Test allocate counter + increment tx", async () => {
+        // Randomly generate our wallet
+        const payerKeypair = Keypair.generate();
+        const payer = payerKeypair.publicKey;
+
+        // Randomly generate the account key 
+        // to sign for setting up the Counter state
+        const counterKeypair = Keypair.generate();
+        const counter = counterKeypair.publicKey;
+
+        // Airdrop our wallet 1 Sol
+        await connection.requestAirdrop(payer, LAMPORTS_PER_SOL);
+
+        // Create a TransactionInstruction to interact with our counter program
+        const allocIx: TransactionInstruction = SystemProgram.createAccount({
+            fromPubkey: payer,
+            newAccountPubkey: counter,
+            lamports: await connection.getMinimumBalanceForRentExemption(COUNTER_ACCOUNT_SIZE),
+            space: COUNTER_ACCOUNT_SIZE,
+            programId: PROGRAM_ID
+        })
+        const incrementIx: TransactionInstruction = createIncrementInstruction({ counter }, {});
+        let tx = new Transaction().add(allocIx).add(incrementIx);
+
+        // Explicitly set the feePayer to be our wallet (this is set to first signer by default)
+        tx.feePayer = payer;
+
+        // Fetch a "timestamp" so validators know this is a recent transaction
+        tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash;
+
+        // Send transaction to network (local network)
+        await sendAndConfirmTransaction(
+            connection,
+            tx,
+            [payerKeypair, counterKeypair],
+            { skipPreflight: true, commitment: 'confirmed' }
+        );
+
+        // Get the counter account info from network
+        const counterAccountInfo = await connection.getAccountInfo(counter, { commitment: "confirmed" });
+        assert(counterAccountInfo, "Expected counter account to have been created");
+
+        // Deserialize the counter & check count has been incremented
+        const counterAccount = deserializeCounterAccount(counterAccountInfo.data);
+        assert(counterAccount.count.toNumber() === 1, "Expected count to have been 1");
+        console.log(`[alloc+increment] count is: ${counterAccount.count.toNumber()}`);
+    });
+    it("Test allocate tx and increment tx", async () => {
+        const payerKeypair = Keypair.generate();
+        const payer = payerKeypair.publicKey;
+
+        const counterKeypair = Keypair.generate();
+        const counter = counterKeypair.publicKey;
+
+        await connection.requestAirdrop(payer, LAMPORTS_PER_SOL);
+
+        // Check allocate tx
+        const allocIx: TransactionInstruction = SystemProgram.createAccount({
+            fromPubkey: payer,
+            newAccountPubkey: counter,
+            lamports: await connection.getMinimumBalanceForRentExemption(COUNTER_ACCOUNT_SIZE),
+            space: COUNTER_ACCOUNT_SIZE,
+            programId: PROGRAM_ID
+        })
+        let tx = new Transaction().add(allocIx);
+        tx.feePayer = payer;
+        tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash;
+        await sendAndConfirmTransaction(
+            connection,
+            tx,
+            [payerKeypair, counterKeypair],
+            { skipPreflight: true, commitment: 'confirmed' }
+        );
+
+        let counterAccountInfo = await connection.getAccountInfo(counter, { commitment: "confirmed" });
+        assert(counterAccountInfo, "Expected counter account to have been created");
+
+        let counterAccount = deserializeCounterAccount(counterAccountInfo.data);
+        assert(counterAccount.count.toNumber() === 0, "Expected count to have been 0");
+        console.log(`[allocate] count is: ${counterAccount.count.toNumber()}`);
+
+        // Check increment tx
+        const incrementIx: TransactionInstruction = createIncrementInstruction({ counter }, {});
+        tx = new Transaction().add(incrementIx);
+        tx.feePayer = payer;
+        tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash;
+        await sendAndConfirmTransaction(
+            connection,
+            tx,
+            [payerKeypair],
+            { skipPreflight: true, commitment: 'confirmed' }
+        );
+
+        counterAccountInfo = await connection.getAccountInfo(counter, { commitment: "confirmed" });
+        assert(counterAccountInfo, "Expected counter account to have been created");
+
+        counterAccount = deserializeCounterAccount(counterAccountInfo.data);
+        assert(counterAccount.count.toNumber() === 1, "Expected count to have been 1");
+        console.log(`[increment] count is: ${counterAccount.count.toNumber()}`);
+    })
+})

+ 17 - 0
basics/counter/native/ts/accounts/counter.ts

@@ -0,0 +1,17 @@
+import * as BN from 'bn.js';
+
+export type Counter = {
+    count: BN
+}
+
+export const COUNTER_ACCOUNT_SIZE = 8;
+
+export function deserializeCounterAccount(data: Buffer): Counter {
+    if (data.byteLength !== 8) {
+        throw Error("Need exactly 8 bytes to deserialize counter")
+    }
+
+    return {
+        count: new BN(data, 'le')
+    }
+}

+ 1 - 0
basics/counter/native/ts/accounts/index.ts

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

+ 5 - 0
basics/counter/native/ts/index.ts

@@ -0,0 +1,5 @@
+import { PublicKey } from '@solana/web3.js';
+export * from './instructions';
+export * from './accounts';
+
+export const PROGRAM_ID = new PublicKey("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

+ 25 - 0
basics/counter/native/ts/instructions/createIncrementInstruction.ts

@@ -0,0 +1,25 @@
+import { PublicKey, TransactionInstruction } from '@solana/web3.js';
+import { PROGRAM_ID } from '../';
+
+export type IncrementInstructionAccounts = {
+    counter: PublicKey,
+}
+export type IncrementInstructionArgs = {
+}
+
+export function createIncrementInstruction(
+    accounts: IncrementInstructionAccounts,
+    args: IncrementInstructionArgs
+): TransactionInstruction {
+    return new TransactionInstruction({
+        programId: PROGRAM_ID,
+        keys: [
+            {
+                pubkey: accounts.counter,
+                isSigner: false,
+                isWritable: true
+            }
+        ],
+        data: Buffer.from([0x0])
+    })
+}

+ 1 - 0
basics/counter/native/ts/instructions/index.ts

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

+ 7 - 0
basics/counter/seahorse/.gitignore

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

+ 8 - 0
basics/counter/seahorse/.prettierignore

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

+ 15 - 0
basics/counter/seahorse/Anchor.toml

@@ -0,0 +1,15 @@
+[features]
+seeds = true
+skip-lint = false
+[programs.localnet]
+counter_seahorse = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
+[registry]
+url = "https://api.apr.dev"
+
+[provider]
+cluster = "localnet"
+wallet = "/Users/noahgundotra/.config/solana/id.json"
+
+[scripts]
+test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

+ 13 - 0
basics/counter/seahorse/Cargo.toml

@@ -0,0 +1,13 @@
+[workspace]
+members = [
+    "programs/*"
+]
+
+[profile.release]
+overflow-checks = true
+lto = "fat"
+codegen-units = 1
+[profile.release.build-override]
+opt-level = 3
+incremental = false
+codegen-units = 1

+ 5 - 0
basics/counter/seahorse/README.md

@@ -0,0 +1,5 @@
+# counter_seahorse
+
+This project was created by Seahorse 0.1.6.
+
+To get started, just add your code to **programs_py/counter_seahorse.py** and run `seahorse build`.

+ 12 - 0
basics/counter/seahorse/migrations/deploy.ts

@@ -0,0 +1,12 @@
+// Migrations are an early feature. Currently, they're nothing more than this
+// single deploy script that's invoked from the CLI, injecting a provider
+// configured from the workspace's Anchor.toml.
+
+const anchor = require("@project-serum/anchor");
+
+module.exports = async function (provider) {
+  // Configure client to use the provider.
+  anchor.setProvider(provider);
+
+  // Add your deploy script here.
+};

+ 19 - 0
basics/counter/seahorse/package.json

@@ -0,0 +1,19 @@
+{
+    "scripts": {
+        "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
+        "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
+    },
+    "dependencies": {
+        "@project-serum/anchor": "^0.25.0"
+    },
+    "devDependencies": {
+        "chai": "^4.3.4",
+        "mocha": "^9.0.3",
+        "ts-mocha": "^10.0.0",
+        "@types/bn.js": "^5.1.0",
+        "@types/chai": "^4.3.0",
+        "@types/mocha": "^9.0.0",
+        "typescript": "^4.3.5",
+        "prettier": "^2.6.2"
+    }
+}

+ 20 - 0
basics/counter/seahorse/programs/counter_seahorse/Cargo.toml

@@ -0,0 +1,20 @@
+[package]
+name = "counter_seahorse"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "counter_seahorse"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang = "0.25.0"
+anchor-spl = "0.25.0"

+ 2 - 0
basics/counter/seahorse/programs/counter_seahorse/Xargo.toml

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

+ 63 - 0
basics/counter/seahorse/programs/counter_seahorse/src/lib.rs

@@ -0,0 +1,63 @@
+use anchor_lang::prelude::*;
+use anchor_lang::solana_program;
+use anchor_spl::associated_token;
+use anchor_spl::token;
+use std::convert::TryFrom;
+
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
+#[derive(Debug)]
+#[account]
+pub struct Counter {
+    count: u64,
+}
+
+pub fn initialize_counter_handler(mut ctx: Context<InitializeCounter>, mut seed: u8) -> Result<()> {
+    let mut counter = &mut ctx.accounts.counter;
+    let mut payer = &mut ctx.accounts.payer;
+
+    Ok(())
+}
+
+pub fn increment_handler(mut ctx: Context<Increment>) -> Result<()> {
+    let mut counter = &mut ctx.accounts.counter;
+
+    counter.count += 1;
+
+    Ok(())
+}
+
+#[derive(Accounts)]
+# [instruction (seed : u8)]
+pub struct InitializeCounter<'info> {
+    #[account(
+        init,
+        payer = payer,
+        seeds = [seed.to_le_bytes().as_ref()],
+        bump,
+        space = 8 + std::mem::size_of::<Counter>()
+    )]
+    pub counter: Box<Account<'info, Counter>>,
+    #[account(mut)]
+    pub payer: Signer<'info>,
+    pub system_program: Program<'info, System>,
+}
+
+#[derive(Accounts)]
+pub struct Increment<'info> {
+    #[account(mut)]
+    pub counter: Box<Account<'info, Counter>>,
+}
+
+#[program]
+pub mod counter_seahorse {
+    use super::*;
+
+    pub fn initialize_counter(ctx: Context<InitializeCounter>, seed: u8) -> Result<()> {
+        initialize_counter_handler(ctx, seed)
+    }
+
+    pub fn increment(ctx: Context<Increment>) -> Result<()> {
+        increment_handler(ctx)
+    }
+}

+ 23 - 0
basics/counter/seahorse/programs_py/counter_seahorse.py

@@ -0,0 +1,23 @@
+# counter_seahorse
+# Built with Seahorse v0.1.6
+
+from seahorse.prelude import *
+
+declare_id('Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS')
+
+
+class Counter(Account):
+    count: u64
+
+
+@instruction
+def initialize_counter(counter: Empty[Counter], payer: Signer, seed: u8):
+    counter.init(
+        payer=payer,
+        seeds=[seed]
+    )
+
+
+@instruction
+def increment(counter: Counter):
+    counter.count += 1

+ 0 - 0
basics/counter/seahorse/programs_py/seahorse/__init__.py


+ 330 - 0
basics/counter/seahorse/programs_py/seahorse/prelude.py

@@ -0,0 +1,330 @@
+# seahorse.prelude: the basis for writing Seahorse programs.
+#
+# NOTE: this file just contains types and documentation for your editor. This
+# is NOT executable code, and you won't be able to change the behavior of your
+# Seahorse programs by editing this file.
+
+from typing import *
+from math import floor, ceil
+
+T = TypeVar('T')
+N = TypeVar('N')
+
+
+# ===========================================================
+# Internal types - here for completeness, but not really used
+# ===========================================================
+    
+class ProgramResult:
+    """Result from executing an instruction - either a success, or a failure with an error message."""
+
+
+# ==========
+# Rust types
+# ==========
+
+class u8:
+    """Single-byte unsigned integer."""
+
+    def __init__(self, _: Any):
+        return self
+
+    def __add__(self, _: Any):
+        return self
+
+    def __radd__(self, _: Any):
+        return self
+
+    def __iadd__(self, _: Any):
+        return self
+
+    def __sub__(self, _: Any):
+        return self
+
+    def __rsub__(self, _: Any):
+        return self
+
+    def __isub__(self, _: Any):
+        return self
+
+    def __mul__(self, _: Any):
+        return self
+
+    def __rmul__(self, _: Any):
+        return self
+
+    def __imul__(self, _: Any):
+        return self
+
+    def __div__(self, _: Any):
+        return self
+
+    def __rdiv__(self, _: Any):
+        return self
+
+    def __idiv__(self, _: Any):
+        return self
+
+class u64:
+    """64-bit unsigned integer."""
+
+    def __init__(self, _: Any):
+        return self
+
+    def __add__(self, _: Any):
+        return self
+
+    def __radd__(self, _: Any):
+        return self
+
+    def __iadd__(self, _: Any):
+        return self
+
+    def __sub__(self, _: Any):
+        return self
+
+    def __rsub__(self, _: Any):
+        return self
+
+    def __isub__(self, _: Any):
+        return self
+
+    def __mul__(self, _: Any):
+        return self
+
+    def __rmul__(self, _: Any):
+        return self
+
+    def __imul__(self, _: Any):
+        return self
+
+    def __div__(self, _: Any):
+        return self
+
+    def __rdiv__(self, _: Any):
+        return self
+
+    def __idiv__(self, _: Any):
+        return self
+
+class i64:
+    """64-bit signed integer."""
+
+    def __init__(self, _: Any):
+        return self
+
+    def __add__(self, _: Any):
+        return self
+
+    def __radd__(self, _: Any):
+        return self
+
+    def __iadd__(self, _: Any):
+        return self
+
+    def __sub__(self, _: Any):
+        return self
+
+    def __rsub__(self, _: Any):
+        return self
+
+    def __isub__(self, _: Any):
+        return self
+
+    def __mul__(self, _: Any):
+        return self
+
+    def __rmul__(self, _: Any):
+        return self
+
+    def __imul__(self, _: Any):
+        return self
+
+    def __div__(self, _: Any):
+        return self
+
+    def __rdiv__(self, _: Any):
+        return self
+
+    def __idiv__(self, _: Any):
+        return self
+
+class f64:
+    """64-bit floating point number."""
+
+    def __add__(self, _: Any):
+        return self
+
+    def __radd__(self, _: Any):
+        return self
+
+    def __iadd__(self, _: Any):
+        return self
+
+    def __sub__(self, _: Any):
+        return self
+
+    def __rsub__(self, _: Any):
+        return self
+
+    def __isub__(self, _: Any):
+        return self
+
+    def __mul__(self, _: Any):
+        return self
+
+    def __rmul__(self, _: Any):
+        return self
+
+    def __imul__(self, _: Any):
+        return self
+
+    def __div__(self, _: Any):
+        return self
+
+    def __rdiv__(self, _: Any):
+        return self
+
+    def __idiv__(self, _: Any):
+        return self
+
+
+class Array(Generic[T, N]):
+    """A fixed-length array: contains type T and has size N.
+
+    Lists (Python builtin type) can coerce to this type. Example:
+
+    ```
+    class MyData(Account):
+        data: Array[u64, 4]
+
+    @instruction
+    def set_data(my_data: MyData):
+        # Will successfully set `data` to [0, 1, 2, 3]
+        my_data.data = [i for i in range(0, 4)]
+        # Will attempt (and fail, crashing the instruction at runtime!) to set `data` to [0, 1, 2, 3, 4]
+        my_data.data = [i for i in range(0, 5)]
+    ```
+    """
+
+class Enum:
+    """A type that can have one of multiple named values.
+
+    Note that unlike Rust enums, these cannot contain any data (other than the variant itself). Example:
+
+    ```
+    class MyEnum(Enum):
+        ONE = 1
+        TWO = 2
+        THREE = 3
+
+    @instruction
+    def use_enum(code: MyEnum):
+        if code == MyEnum.ONE:
+            print(1)
+        # ...
+    ```
+    """
+
+# ============
+# Solana types
+# ============
+
+class Pubkey:
+    """32-byte account identifier."""
+
+class SolanaAccount:
+    """Generic Solana account."""
+
+    def key(self) -> Pubkey:
+        """Get this account's key."""
+
+    def transfer_lamports(self, to: SolanaAccount, amount: u64):
+        """Transfer some SOL (as an amount of lamports) to another account.
+
+        Note: this will successfully transfer from a program-owned account without needing to
+        provide the seeds for a PDA, so no signer field is required (unlike the SPL methods).
+        """
+
+class Account(SolanaAccount):
+    """User-defined Solana account."""
+
+class Signer(SolanaAccount):
+    """Instruction signer."""
+
+class Empty(Generic[T]):
+    """An account that needs to be initialized."""
+
+    def bump(self) -> u8:
+        """Get this account's bump, needed if you want to use this account to sign CPI calls."""
+
+    def init(self, payer: Signer, seeds: List[Union[str, Account, u8]], mint: TokenMint, authority: Account) -> T:
+        """
+        Initialize the account.
+        
+        @param payer: The account that will pay for the rent cost of the initialized account. Must be an instruction signer.
+        @param seeds: A list of parameters to uniquely identify this account among all accounts created by your program. These may be string literals or other accounts.
+        @param mint: If initializing a TokenAccount, this is the mint that the account belongs to.
+        @param decimals: If initializing a TokenMint, this is the number of decimals the new token has.
+        @param authority: If initializing a TokenAccount/TokenMint, this is the account that has authority over the account.
+        @returns: The new, initialized account. All of the data in this account will be set to 0.
+        """
+
+class TokenAccount(SolanaAccount):
+    """SPL token account."""
+
+    def authority(self) -> Pubkey:
+        """Get the owner of this token account."""
+
+    def amount(self) -> u64:
+        """Get the amount of token stored in this account."""
+
+    def transfer(self, authority: SolanaAccount, to: TokenAccount, amount: u64, signer: List[Union[str, Account, u8]] = None):
+        """
+        Transfer funds from this SPL token account to another.
+        
+        @param authority: The account that owns this TokenAccount. Must be an instruction signer or the account given by the `signer` param.
+        @param to: The recipient TokenAccount.
+        @param amount: How much (in *native* token units) to transfer.
+        @param signer: (Optional) seeds for the signature of a PDA.
+        """
+
+class TokenMint(SolanaAccount):
+    """SPL token mint."""
+
+    def authority(self) -> Pubkey:
+        """Get the owner of this token account."""
+
+    def mint(self, authority: SolanaAccount, to: TokenAccount, amount: u64, signer: List[Union[str, Account, u8]] = None):
+        """
+        Mint new tokens to a token account.
+
+        @param authority: The account that owns this TokenMint. Must be an instruction signer or the account given by the `signer` param.
+        @param to: The recipient TokenAccount.
+        @param amount: How much (in *native* token units) to mint.
+        @param signer: (Optional) seeds for the signature of a PDA.
+        """
+
+    def burn(self, authority: SolanaAccount, holder: TokenAccount, amount: u64, signer: List[Union[str, Account, u8]] = None):
+        """
+        Burn tokens from a token account.
+
+        @param authority: The account that owns the `holder` TokenAccount. Must be an instruction signer or the account given by the `signer` param.
+        @param holder: The TokenAccount to burn from.
+        @param amount: How much (in *native* token units) to burn.
+        @param signer: (Optional) seeds for the signature of a PDA.
+        """
+
+
+# ================
+# Helper functions
+# ================
+
+def declare_id(id: str):
+    """Inform Anchor what this program's ID is.
+
+    @param id: The program's ID, generated by Anchor in /target/idl/<program>.json. This must be copied-pasted straight from there as a string literal.
+    """
+
+def instruction(function: Callable[..., None]) -> Callable[..., ProgramResult]:
+    """Decorator to turn a function into a program instruction."""

+ 43 - 0
basics/counter/seahorse/tests/counter_seahorse.ts

@@ -0,0 +1,43 @@
+import * as anchor from "@project-serum/anchor";
+import { Program } from "@project-serum/anchor";
+import {
+  Keypair,
+  PublicKey,
+  SystemProgram
+} from '@solana/web3.js';
+import { assert } from "chai";
+import { CounterSeahorse } from "../target/types/counter_seahorse";
+
+describe("counter_seahorse", () => {
+  // Configure the client to use the local cluster.
+  anchor.setProvider(anchor.AnchorProvider.env());
+
+  const program = anchor.workspace.CounterSeahorse as Program<CounterSeahorse>;
+
+  it("Increment counter", async () => {
+    const seed = 69;
+    const counter = PublicKey.findProgramAddressSync(
+      [Buffer.from([0x45])],
+      program.programId
+    )[0];
+
+    // Initialize counter
+    await program.methods
+      .initializeCounter(seed)
+      .accounts({
+        payer: program.provider.publicKey,
+      })
+      .rpc();
+
+    // Increment counter
+    await program.methods
+      .increment()
+      .accounts({
+        counter
+      })
+      .rpc();
+
+    const count = (await program.account.counter.fetch(counter)).count.toNumber();
+    assert(count === 1, "Expected count to be 1");
+  });
+});

+ 10 - 0
basics/counter/seahorse/tsconfig.json

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

+ 6 - 6
basics/create-account/native/program/src/lib.rs

@@ -30,14 +30,14 @@ fn process_instruction(
     
     invoke(
         &system_instruction::create_account(
-            &payer.key,             // From pubkey
-            &new_account.key,       // To pubkey
-            1 * LAMPORTS_PER_SOL,   // Lamports (1 SOL)
-            0,                      // Space
-            &system_program::ID,    // Owner
+            &payer.key,
+            &new_account.key,
+            1 * LAMPORTS_PER_SOL,
+            0,
+            &system_program::ID,
         ),
         &[
-            payer.clone(), new_account.clone(), system_program.clone()  // Accounts involved
+            payer.clone(), new_account.clone(), system_program.clone()
         ]
     )?;
 

+ 8 - 22
basics/create-account/native/tests/test.ts

@@ -1,6 +1,7 @@
 import {
     Connection,
-    Keypair, LAMPORTS_PER_SOL,
+    Keypair,
+    PublicKey,
     sendAndConfirmTransaction,
     SystemProgram,
     Transaction,
@@ -19,9 +20,12 @@ describe("Create a system 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/so/program-keypair.json');
+    
+    const PROGRAM_ID: PublicKey = new PublicKey(
+        "Au21huMZuDQrbzu2Ec5ohpW5CKRqhcGV6qLawfydStGs"
+    );
   
-    it("Create the account via a cross program invocation", async () => {
+    it("Create the account", async () => {
 
         const newKeypair = Keypair.generate();
 
@@ -31,7 +35,7 @@ describe("Create a system account", async () => {
                 {pubkey: newKeypair.publicKey, isSigner: true, isWritable: true},
                 {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
             ],
-            programId: program.publicKey,
+            programId: PROGRAM_ID,
             data: Buffer.alloc(0),
         });
 
@@ -41,23 +45,5 @@ describe("Create a system account", async () => {
             [payer, newKeypair]
         );
     });
-
-    it("Create the account via direct call to system program", async () => {
-
-        const newKeypair = Keypair.generate();
-
-        const ix = SystemProgram.createAccount({
-            fromPubkey: payer.publicKey,
-            newAccountPubkey: newKeypair.publicKey,
-            lamports: LAMPORTS_PER_SOL,
-            space: 0,
-            programId: SystemProgram.programId
-        })
-
-
-        await sendAndConfirmTransaction(connection,
-            new Transaction().add(ix),
-            [payer, newKeypair]);
-    });
   });
   

+ 48 - 11
basics/hello-solana/seahorse/hello_solana/programs/hello_solana/src/lib.rs

@@ -1,35 +1,72 @@
 use anchor_lang::prelude::*;
 use anchor_lang::solana_program;
-use anchor_spl::associated_token;
 use anchor_spl::token;
 use std::convert::TryFrom;
 
 declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
 
-pub fn hello_handler(mut ctx: Context<Hello>) -> Result<()> {
-    let mut signer = &mut ctx.accounts.signer;
+#[derive(Debug)]
+#[account]
+pub struct Counter {
+    authority: Pubkey,
+    value: u8,
+}
+
+pub fn initialize_handler(mut ctx: Context<Initialize>) -> Result<()> {
+    let mut authority = &mut ctx.accounts.authority;
+    let mut counter = &mut ctx.accounts.counter;
+    let mut counter = counter;
+
+    counter.authority = authority.key();
+
+    counter.value = 0;
 
     msg!("{}", "Hello, Solana from Seahorse!");
 
-    msg!(
-        "{}",
-        format!("This is the public key of the signer: {}", signer.key())
-    );
+    Ok(())
+}
+
+pub fn increment_handler(mut ctx: Context<Increment>) -> Result<()> {
+    let mut authority = &mut ctx.accounts.authority;
+    let mut counter = &mut ctx.accounts.counter;
+
+    counter.value += 1;
 
     Ok(())
 }
 
 #[derive(Accounts)]
-pub struct Hello<'info> {
+pub struct Initialize<'info> {
     #[account(mut)]
-    pub signer: Signer<'info>,
+    pub authority: Signer<'info>,
+    #[account(
+        init,
+        payer = authority,
+        seeds = ["new_delhi_hh".as_bytes().as_ref(), authority.key().as_ref()],
+        bump,
+        space = 8 + std::mem::size_of::<Counter>()
+    )]
+    pub counter: Box<Account<'info, Counter>>,
+    pub system_program: Program<'info, System>,
+}
+
+#[derive(Accounts)]
+pub struct Increment<'info> {
+    #[account(mut)]
+    pub authority: Signer<'info>,
+    #[account(mut)]
+    pub counter: Box<Account<'info, Counter>>,
 }
 
 #[program]
 pub mod hello_solana {
     use super::*;
 
-    pub fn hello(ctx: Context<Hello>) -> Result<()> {
-        hello_handler(ctx)
+    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
+        initialize_handler(ctx)
+    }
+
+    pub fn increment(ctx: Context<Increment>) -> Result<()> {
+        increment_handler(ctx)
     }
 }

+ 24 - 5
basics/hello-solana/seahorse/hello_solana/programs_py/hello_solana.py

@@ -1,13 +1,32 @@
-# hello_solana
-# Built with Seahorse v0.1.5
-
 from seahorse.prelude import *
 
 declare_id('Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS')
 
+class Counter(Account):
+    authority: Pubkey
+    value: u8
+
 
 @instruction
-def hello(signer: Signer):
+def initialize(
+    authority: Signer, 
+    counter: Empty[Counter]
+):
+    # Initialize the counter and set the authority
+    counter = counter.init(
+        payer = authority,
+        seeds = ['new_delhi_hh', authority]
+    )
+
+    counter.authority = authority.key()
+    counter.value = 0
+    
     print("Hello, Solana from Seahorse!")
 
-    print(f"This is the public key of the signer: {signer.key()}")
+
+@instruction
+def increment(
+    authority: Signer, 
+    counter: Counter
+):
+    counter.value += 1

+ 14 - 0
basics/program-derived-addresses/anchor/Anchor.toml

@@ -0,0 +1,14 @@
+[features]
+seeds = false
+[programs.localnet]
+anchor_program_example = "FFKtnYFyzPj1qFjE9epkrfYHJwZMdh8CvJrB6XsKeFVz"
+
+[registry]
+url = "https://anchor.projectserum.com"
+
+[provider]
+cluster = "localnet"
+wallet = "~/.config/solana/id.json"
+
+[scripts]
+test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

+ 13 - 0
basics/program-derived-addresses/anchor/Cargo.toml

@@ -0,0 +1,13 @@
+[workspace]
+members = [
+    "programs/*"
+]
+
+[profile.release]
+overflow-checks = true
+lto = "fat"
+codegen-units = 1
+[profile.release.build-override]
+opt-level = 3
+incremental = false
+codegen-units = 1

+ 14 - 0
basics/program-derived-addresses/anchor/package.json

@@ -0,0 +1,14 @@
+{
+    "dependencies": {
+        "@project-serum/anchor": "^0.24.2"
+    },
+    "devDependencies": {
+        "@types/bn.js": "^5.1.0",
+        "@types/chai": "^4.3.0",
+        "@types/mocha": "^9.0.0",
+        "chai": "^4.3.4",
+        "mocha": "^9.0.3",
+        "ts-mocha": "^10.0.0",
+        "typescript": "^4.3.5"
+    }
+}

+ 19 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/Cargo.toml

@@ -0,0 +1,19 @@
+[package]
+name = "anchor-program-example"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "anchor_program_example"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang = "0.24.2"

+ 2 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/Xargo.toml

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

+ 36 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/create.rs

@@ -0,0 +1,36 @@
+use anchor_lang::prelude::*;
+
+use crate::state::PageVisits;
+
+
+pub fn create_page_visits(
+    ctx: Context<CreatePageVisits>
+) -> Result<()> {
+
+    ctx.accounts.page_visits.set_inner(
+        PageVisits::new(
+            0,
+            *ctx.bumps.get(PageVisits::SEED_PREFIX).expect("Bump not found."),
+        )
+    );
+    Ok(())
+}
+
+#[derive(Accounts)]
+pub struct CreatePageVisits<'info> {
+    #[account(
+        init,
+        space = PageVisits::ACCOUNT_SPACE,
+        payer = payer,
+        seeds = [
+            PageVisits::SEED_PREFIX.as_bytes().as_ref(),
+            user.key().as_ref(),
+        ],
+        bump,
+    )]
+    page_visits: Account<'info, PageVisits>,
+    user: SystemAccount<'info>,
+    #[account(mut)]
+    payer: Signer<'info>,
+    system_program: Program<'info, System>,
+}

+ 30 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/increment.rs

@@ -0,0 +1,30 @@
+use anchor_lang::prelude::*;
+
+use crate::state::PageVisits;
+
+
+pub fn increment_page_visits(
+    ctx: Context<IncrementPageVisits>
+) -> Result<()> {
+
+    let page_visits = &mut ctx.accounts.page_visits;
+    page_visits.increment();
+    Ok(())
+}
+
+#[derive(Accounts)]
+pub struct IncrementPageVisits<'info> {
+    #[account(
+        mut,
+        seeds = [
+            PageVisits::SEED_PREFIX.as_bytes().as_ref(),
+            user.key().as_ref(),
+        ],
+        bump,
+    )]
+    page_visits: Account<'info, PageVisits>,
+    user: SystemAccount<'info>,
+    #[account(mut)]
+    payer: Signer<'info>,
+    system_program: Program<'info, System>,
+}

+ 5 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/mod.rs

@@ -0,0 +1,5 @@
+pub mod create;
+pub mod increment;
+
+pub use create::*;
+pub use increment::*;

+ 29 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/src/lib.rs

@@ -0,0 +1,29 @@
+use anchor_lang::prelude::*;
+
+use instructions::*;
+
+pub mod instructions;
+pub mod state;
+
+
+declare_id!("FFKtnYFyzPj1qFjE9epkrfYHJwZMdh8CvJrB6XsKeFVz");
+
+
+#[program]
+pub mod anchor_program_example {
+    use super::*;
+
+    pub fn create_page_visits(
+        ctx: Context<CreatePageVisits>
+    ) -> Result<()> {
+        
+        instructions::create::create_page_visits(ctx)
+    }
+
+    pub fn increment_page_visits(
+        ctx: Context<IncrementPageVisits>
+    ) -> Result<()> {
+        
+        instructions::increment::increment_page_visits(ctx)
+    }
+}

+ 3 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/src/state/mod.rs

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

+ 26 - 0
basics/program-derived-addresses/anchor/programs/anchor-program-example/src/state/page_visits.rs

@@ -0,0 +1,26 @@
+use anchor_lang::prelude::*;
+
+
+#[account]
+pub struct PageVisits {
+    pub page_visits: u32,
+    pub bump: u8,
+}
+
+impl PageVisits {
+
+    pub const ACCOUNT_SPACE: usize = 8 + 32;
+
+    pub const SEED_PREFIX: &'static str = "page_visits";
+
+    pub fn new(page_visits: u32, bump: u8) -> Self {
+        PageVisits {
+            page_visits,
+            bump,
+        }
+    }
+
+    pub fn increment(&mut self) {
+        self.page_visits += 1;
+    }
+}

+ 77 - 0
basics/program-derived-addresses/anchor/tests/test.ts

@@ -0,0 +1,77 @@
+import * as anchor from "@project-serum/anchor";
+import { AnchorProgramExample } from "../target/types/anchor_program_example";
+
+describe("PDAs", () => {
+
+  const provider = anchor.AnchorProvider.env();
+  anchor.setProvider(provider);
+  const payer = provider.wallet as anchor.Wallet;
+  const program = anchor.workspace.AnchorProgramExample as anchor.Program<AnchorProgramExample>;
+
+  let testUser = anchor.web3.Keypair.generate();
+
+  it("Create a test user", async () => {
+    let ix = anchor.web3.SystemProgram.createAccount({
+      fromPubkey: payer.publicKey,
+      lamports: await provider.connection.getMinimumBalanceForRentExemption(0),
+      newAccountPubkey: testUser.publicKey,
+      programId: anchor.web3.SystemProgram.programId,
+      space: 0,
+    });
+    await anchor.web3.sendAndConfirmTransaction(
+      provider.connection,
+      new anchor.web3.Transaction().add(ix),
+      [payer.payer, testUser]
+    );
+    console.log(`Local Wallet: ${payer.publicKey}`);
+    console.log(`Created User: ${testUser.publicKey}`);
+  });
+
+  function derivePageVisitsPda(userPubkey: anchor.web3.PublicKey) {
+    return anchor.web3.PublicKey.findProgramAddressSync(
+      [Buffer.from("page_visits"), userPubkey.toBuffer()],
+      program.programId,
+    )[0]
+  }
+
+  it("Create the page visits tracking PDA", async () => {
+    await program.methods.createPageVisits()
+      .accounts({
+        pageVisits: derivePageVisitsPda(testUser.publicKey),
+        user: testUser.publicKey,
+        payer: payer.publicKey,
+        systemProgram: anchor.web3.SystemProgram.programId,
+      })
+      .signers([payer.payer])
+      .rpc();
+  });
+
+  it("Visit the page!", async () => {
+    await program.methods.incrementPageVisits()
+      .accounts({
+        pageVisits: derivePageVisitsPda(testUser.publicKey),
+        user: testUser.publicKey,
+        payer: payer.publicKey,
+      })
+      .signers([payer.payer])
+      .rpc();
+  });
+
+  it("Visit the page!", async () => {
+    await program.methods.incrementPageVisits()
+      .accounts({
+        pageVisits: derivePageVisitsPda(testUser.publicKey),
+        user: testUser.publicKey,
+        payer: payer.publicKey,
+      })
+      .signers([payer.payer])
+      .rpc();
+  });
+
+  it("View page visits", async () => {
+    const pageVisits = await program.account.pageVisits.fetch(
+      await derivePageVisitsPda(testUser.publicKey)
+    );
+    console.log(`Number of page visits: ${pageVisits.pageVisits}`);
+  });
+});

+ 10 - 0
basics/program-derived-addresses/anchor/tsconfig.json

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

+ 8 - 0
basics/program-derived-addresses/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

Some files were not shown because too many files changed in this diff