Browse Source

counter: add seahorse example program

ngundotra 3 years ago
parent
commit
ae4615d245

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