jpcaulfi 3 роки тому
батько
коміт
e76ac6c520
55 змінених файлів з 1199 додано та 30 видалено
  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. 6 6
      basics/create-account/native/program/src/lib.rs
  28. 6 2
      basics/create-account/native/tests/test.ts
  29. 48 11
      basics/hello-solana/seahorse/hello_solana/programs/hello_solana/src/lib.rs
  30. 24 5
      basics/hello-solana/seahorse/hello_solana/programs_py/hello_solana.py
  31. 14 0
      basics/program-derived-addresses/anchor/Anchor.toml
  32. 13 0
      basics/program-derived-addresses/anchor/Cargo.toml
  33. 14 0
      basics/program-derived-addresses/anchor/package.json
  34. 19 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/Cargo.toml
  35. 2 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/Xargo.toml
  36. 36 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/create.rs
  37. 30 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/increment.rs
  38. 5 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/instructions/mod.rs
  39. 29 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/lib.rs
  40. 3 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/state/mod.rs
  41. 26 0
      basics/program-derived-addresses/anchor/programs/anchor-program-example/src/state/page_visits.rs
  42. 77 0
      basics/program-derived-addresses/anchor/tests/test.ts
  43. 10 0
      basics/program-derived-addresses/anchor/tsconfig.json
  44. 8 0
      basics/program-derived-addresses/native/cicd.sh
  45. 18 0
      basics/program-derived-addresses/native/package.json
  46. 12 0
      basics/program-derived-addresses/native/program/Cargo.toml
  47. 51 0
      basics/program-derived-addresses/native/program/src/instructions/create.rs
  48. 21 0
      basics/program-derived-addresses/native/program/src/instructions/increment.rs
  49. 5 0
      basics/program-derived-addresses/native/program/src/instructions/mod.rs
  50. 10 0
      basics/program-derived-addresses/native/program/src/lib.rs
  51. 35 0
      basics/program-derived-addresses/native/program/src/processor.rs
  52. 3 0
      basics/program-derived-addresses/native/program/src/state/mod.rs
  53. 29 0
      basics/program-derived-addresses/native/program/src/state/page_visits.rs
  54. 146 0
      basics/program-derived-addresses/native/tests/test.ts
  55. 10 0
      basics/program-derived-addresses/native/tsconfig.json

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

+ 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()
         ]
     )?;
 

+ 6 - 2
basics/create-account/native/tests/test.ts

@@ -1,6 +1,7 @@
 import {
     Connection,
     Keypair,
+    PublicKey,
     sendAndConfirmTransaction,
     SystemProgram,
     Transaction,
@@ -19,7 +20,10 @@ 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", async () => {
 
@@ -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),
         });
 

+ 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

+ 18 - 0
basics/program-derived-addresses/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/program-derived-addresses/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"]

+ 51 - 0
basics/program-derived-addresses/native/program/src/instructions/create.rs

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

+ 21 - 0
basics/program-derived-addresses/native/program/src/instructions/increment.rs

@@ -0,0 +1,21 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+use solana_program::{
+    account_info::{ AccountInfo, next_account_info },
+    entrypoint::ProgramResult, 
+};
+
+use crate::state::PageVisits;
+
+
+pub fn increment_page_visits(
+    accounts: &[AccountInfo],
+) -> ProgramResult {
+
+    let accounts_iter = &mut accounts.iter();
+    let page_visits_account = next_account_info(accounts_iter)?;
+
+    let page_visits = &mut PageVisits::try_from_slice(&page_visits_account.data.borrow())?;
+    page_visits.increment();
+    page_visits.serialize(&mut &mut page_visits_account.data.borrow_mut()[..])?;
+    Ok(())
+}

+ 5 - 0
basics/program-derived-addresses/native/program/src/instructions/mod.rs

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

+ 10 - 0
basics/program-derived-addresses/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);

+ 35 - 0
basics/program-derived-addresses/native/program/src/processor.rs

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

+ 3 - 0
basics/program-derived-addresses/native/program/src/state/mod.rs

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

+ 29 - 0
basics/program-derived-addresses/native/program/src/state/page_visits.rs

@@ -0,0 +1,29 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+
+
+#[derive(BorshDeserialize, BorshSerialize, Debug)]
+pub struct IncrementPageVisits {}
+
+#[derive(BorshDeserialize, BorshSerialize, Debug)]
+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;
+    }
+}

+ 146 - 0
basics/program-derived-addresses/native/tests/test.ts

@@ -0,0 +1,146 @@
+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("PDAs", () => {
+
+    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 PageVisits extends Assignable {
+        toBuffer() { return Buffer.from(borsh.serialize(PageVisitsSchema, this)) }
+        
+        static fromBuffer(buffer: Buffer) {
+            return borsh.deserialize(PageVisitsSchema, PageVisits, buffer);
+        };
+    };
+    const PageVisitsSchema = new Map([
+        [ PageVisits, { 
+            kind: 'struct', 
+            fields: [ ['page_visits', 'u32'], ['bump', 'u8'] ],
+        }]
+    ]);
+
+    class IncrementPageVisits extends Assignable {
+        toBuffer() { return Buffer.from(borsh.serialize(IncrementPageVisitsSchema, this)) }
+    };
+    const IncrementPageVisitsSchema = new Map([
+        [ IncrementPageVisits, { 
+            kind: 'struct', 
+            fields: [],
+        }]
+    ]);
+
+    const testUser = Keypair.generate();
+
+    it("Create a test user", async () => {
+        let ix = SystemProgram.createAccount({
+            fromPubkey: payer.publicKey,
+            lamports: await connection.getMinimumBalanceForRentExemption(0),
+            newAccountPubkey: testUser.publicKey,
+            programId: SystemProgram.programId,
+            space: 0,
+        });
+        await sendAndConfirmTransaction(
+            connection,
+            new Transaction().add(ix),
+            [payer, testUser]
+        );
+        console.log(`Local Wallet: ${payer.publicKey}`);
+        console.log(`Created User: ${testUser.publicKey}`);
+    });
+
+    function derivePageVisitsPda(userPubkey: PublicKey) {
+        return PublicKey.findProgramAddressSync(
+            [Buffer.from("page_visits"), userPubkey.toBuffer()],
+            PROGRAM_ID,
+        )
+    }
+
+    it("Create the page visits tracking PDA", async () => {
+        const [pageVisitsPda, pageVisitsBump] = derivePageVisitsPda(testUser.publicKey);
+        let ix = new TransactionInstruction({
+            keys: [
+                {pubkey: pageVisitsPda, isSigner: false, isWritable: true},
+                {pubkey: testUser.publicKey, isSigner: false, isWritable: false},
+                {pubkey: payer.publicKey, isSigner: true, isWritable: true},
+                {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
+            ],
+            programId: PROGRAM_ID,
+            data: (new PageVisits({page_visits: 0, bump: pageVisitsBump})).toBuffer(),
+        });
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer]
+        );
+    });
+
+    it("Visit the page!", async () => {
+        const [pageVisitsPda, _] = derivePageVisitsPda(testUser.publicKey);
+        let ix = new TransactionInstruction({
+            keys: [
+                {pubkey: pageVisitsPda, isSigner: false, isWritable: true},
+                {pubkey: payer.publicKey, isSigner: true, isWritable: true},
+            ],
+            programId: PROGRAM_ID,
+            data: new IncrementPageVisits({}).toBuffer(),
+        });
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer]
+        );
+    });
+
+    it("Visit the page!", async () => {
+        const [pageVisitsPda, _] = derivePageVisitsPda(testUser.publicKey);
+        let ix = new TransactionInstruction({
+            keys: [
+                {pubkey: pageVisitsPda, isSigner: false, isWritable: true},
+                {pubkey: payer.publicKey, isSigner: true, isWritable: true},
+            ],
+            programId: PROGRAM_ID,
+            data: new IncrementPageVisits({}).toBuffer(),
+        });
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer]
+        );
+    });
+
+    it("Read page visits", async () => {
+        const [pageVisitsPda, _] = derivePageVisitsPda(testUser.publicKey);
+        const accountInfo = await connection.getAccountInfo(pageVisitsPda);
+        const readPageVisits = PageVisits.fromBuffer(accountInfo.data);
+        console.log(`Number of page visits: ${readPageVisits.page_visits}`);
+    });
+});

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

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