jpcaulfi 3 anos atrás
pai
commit
9ca9c74f20

+ 8 - 7
README.md

@@ -26,7 +26,7 @@ Regardless of what you may want to add on top of existing Solana programs, the n
 
 ## Navigating this Repo
 
-This collection is organized into the following sections:
+:blue_book: This collection is organized into the following sections:
 - `program-basics` - The basics of writing Solana programs in Rust.
 - `accounts`- Examples of creating & modifying accounts from on-chain.
 - `tokens` - Examples of working with SPL Tokens from on-chain.
@@ -34,24 +34,24 @@ This collection is organized into the following sections:
 - `pdas` - Everything you need to know about working with Program Derived Addresses & program-owned accounts.
 - `advanced-pdas` - Common Web2 design patterns implemented with PDAs on Solana.
 
-Each example contains two folders:
+:file_folder: Each example contains two folders:
 - `native` - Written using Solana's native Rust crates and vanilla Rust.
 - `anchor` - Written using Anchor's `anchor_lang` Rust crate and the associated Anchor framework to build & deploy.
 
-How to build & run:
+:wrench: How to build & run:
 - Before running anything in any folder make sure you pull in the dependencies with `yarn install`.
 - `native` - Use `cicd.sh` to build & deploy the program. Run `yarn run test` to test it.
 - `anchor` - Use `anchor build && anchor deploy` to build & deploy the program. Run `anchor run test` to test it.
 
 ## To-Do:
-**Got something you want to see here? Add it to the list. Or better yet, write one & create a PR!*
+:mag: *Got something you want to see here? Add it to the list. Or better yet, write one & create a PR!*
 - ### [x] Program Basics
 - [x] 1. Hello Solana
 - [x] 2. Custom instruction data
 - [x] 3. Recommended program layout
 - ### [ ] Accounts
 - [x] 1. Creating a system account
-- [ ] 2. Rent
+- [x] 2. Rent
 - [ ] 3. Transferring SOL
 - [ ] 4. Transferring an account's ownership
 - [ ] 5. Destroying an account
@@ -61,8 +61,9 @@ How to build & run:
 - ### [ ] NFTs
 - [ ] 1. Creating an NFT
 - [ ] 2. Transferring an NFT
-- [ ] 3. Adding metadata to an NFT
-- [ ] 4. NFT metadata expanded
+- [ ] 3. Selling an NFT
+- [ ] 4. Adding metadata to an NFT
+- [ ] 5. NFT metadata expanded
 - ### [ ] PDAs
 - [ ] 1. A simple PDA
 - [ ] 2. Dynamic PDA creation

+ 5 - 5
accounts/create-system-account/anchor/programs/create-system-account/src/lib.rs

@@ -19,13 +19,13 @@ pub mod create_system_account {
             CpiContext::new(
                 ctx.accounts.system_program.to_account_info(),
                 system_program::CreateAccount {
-                    from: ctx.accounts.payer.to_account_info(),
-                    to: ctx.accounts.new_account.to_account_info(),
+                    from: ctx.accounts.payer.to_account_info(),         // From pubkey
+                    to: ctx.accounts.new_account.to_account_info(),     // To pubkey
                 },
             ),
-            LAMPORTS_PER_SOL,
-            32,
-            &ctx.accounts.system_program.key(),
+            LAMPORTS_PER_SOL,                           // Lamports (1 SOL)
+            32,                                         // Space
+            &ctx.accounts.system_program.key(),         // Owner
         )?;
 
         msg!("Account created succesfully.");

+ 1 - 1
accounts/create-system-account/native/program/src/lib.rs

@@ -32,7 +32,7 @@ fn process_instruction(
         &system_instruction::create_account(
             &payer.key,             // From pubkey
             &new_account.key,       // To pubkey
-            LAMPORTS_PER_SOL,       // Lamports
+            LAMPORTS_PER_SOL,       // Lamports (1 SOL)
             32,                     // Space
             &system_program::ID,    // Owner
         ),

+ 17 - 0
accounts/rent/README.md

@@ -0,0 +1,17 @@
+# Rent
+
+Ah, rent. Everybody's favorite thing to deal with.   
+
+Luckily, rent is much less daunting on Solana than in the real world (sorry, best we could do).   
+
+___
+
+Simply put, all storage on Solana costs **rent**. After all, this thing ain't free!   
+
+Rent is typically a small amount and if you load your account with enough rent for **two years** it's actually exempt! That's right: **if your account holds more than the cost of rent for two years, that account is not charged any rent**.   
+
+___
+
+Rent itself is based off of the size of the data you're seeking to store in the account.   
+
+Let's take a look.

+ 14 - 0
accounts/rent/anchor/Anchor.toml

@@ -0,0 +1,14 @@
+[features]
+seeds = false
+[programs.localnet]
+create_system_account = "6gUwvaZPvC8ZxKuC1h5aKz4mRd7pFyEfUZckiEsBZSbk"
+
+[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
accounts/rent/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
accounts/rent/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
accounts/rent/anchor/programs/create-system-account/Cargo.toml

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

+ 2 - 0
accounts/rent/anchor/programs/create-system-account/Xargo.toml

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

+ 43 - 0
accounts/rent/anchor/programs/create-system-account/src/lib.rs

@@ -0,0 +1,43 @@
+use anchor_lang::prelude::*;
+use anchor_lang::system_program;
+
+
+declare_id!("6gUwvaZPvC8ZxKuC1h5aKz4mRd7pFyEfUZckiEsBZSbk");
+
+const LAMPORTS_PER_SOL: u64 = 1000000000;
+
+#[program]
+pub mod create_system_account {
+    use super::*;
+
+    pub fn create_system_account(ctx: Context<CreateSystemAccount>) -> Result<()> {
+
+        msg!("Program invoked. Creating a system account...");
+        msg!("  New public key will be: {}", &ctx.accounts.new_account.key().to_string());
+
+        system_program::create_account(
+            CpiContext::new(
+                ctx.accounts.system_program.to_account_info(),
+                system_program::CreateAccount {
+                    from: ctx.accounts.payer.to_account_info(),         // From pubkey
+                    to: ctx.accounts.new_account.to_account_info(),     // To pubkey
+                },
+            ),
+            LAMPORTS_PER_SOL,                           // Lamports (1 SOL)
+            32,                                         // Space
+            &ctx.accounts.system_program.key(),         // Owner
+        )?;
+
+        msg!("Account created succesfully.");
+        Ok(())
+    }
+}
+
+#[derive(Accounts)]
+pub struct CreateSystemAccount<'info> {
+    #[account(mut)]
+    pub payer: Signer<'info>,
+    #[account(mut)]
+    pub new_account: Signer<'info>,
+    pub system_program: Program<'info, System>,
+}

+ 26 - 0
accounts/rent/anchor/tests/test.ts

@@ -0,0 +1,26 @@
+import * as anchor from "@project-serum/anchor";
+import { CreateSystemAccount } from "../target/types/create_system_account";
+
+
+describe("Create a system account", () => {
+
+  const provider = anchor.AnchorProvider.env();
+  anchor.setProvider(provider);
+  const wallet = provider.wallet as anchor.Wallet;
+  const program = anchor.workspace.CreateSystemAccount as anchor.Program<CreateSystemAccount>;
+
+  it("Create the account", async () => {
+
+    const newKeypair = anchor.web3.Keypair.generate();
+    
+    await program.methods.createSystemAccount()
+    .accounts({
+      payer: wallet.publicKey,
+      newAccount: newKeypair.publicKey,
+      systemProgram: anchor.web3.SystemProgram.programId
+    })
+    .signers([wallet.payer, newKeypair])
+    .rpc();
+
+  });
+});

+ 10 - 0
accounts/rent/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
accounts/rent/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
accounts/rent/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
accounts/rent/native/program/Cargo.toml

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

+ 68 - 0
accounts/rent/native/program/src/lib.rs

@@ -0,0 +1,68 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use solana_program::{
+    account_info::{AccountInfo, next_account_info}, 
+    entrypoint, 
+    entrypoint::ProgramResult, 
+    msg, 
+    program::invoke,
+    program_error::ProgramError,
+    pubkey::Pubkey,
+    rent::Rent,
+    system_instruction,
+    system_program,
+    sysvar::Sysvar,
+};
+
+
+entrypoint!(process_instruction);
+
+
+fn process_instruction(
+    _program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    _instruction_data: &[u8],
+) -> ProgramResult {
+
+    let accounts_iter = &mut accounts.iter();
+    let payer = next_account_info(accounts_iter)?;
+    let new_account = next_account_info(accounts_iter)?;
+    let system_program = next_account_info(accounts_iter)?;
+    
+    msg!("Program invoked. Creating a system account...");
+    msg!("  New public key will be: {}", &new_account.key.to_string());
+
+    // Determine the necessary minimum rent by calculating the account's size
+    //
+    let account_span = instruction_data
+        .get(..8)
+        .and_then(|slice| slice.try_into().ok())
+        .map(AddressData::from_le_bytes)
+        .ok_or(ProgramError::InvalidAccountData)?;
+
+    let lamports_required = (Rent::get()?).minimum_balance(account_span as usize);
+    
+    invoke(
+        &system_instruction::create_account(
+            &payer.key,
+            &new_account.key,
+            lamports_required,
+            account_span,
+            &system_program::ID,
+        ),
+        &[
+            payer.clone(), new_account.clone(), system_program.clone()
+        ]
+    )?;
+
+    msg!("Account created succesfully.");
+    Ok(())
+}
+
+
+// Say this is the data structure we intend our account to have
+//
+#[derive(BorshSerialize, BorshDeserialize, Debug)]
+pub struct AddressData {
+    name: String,
+    address: String,
+}

+ 45 - 0
accounts/rent/native/tests/test.ts

@@ -0,0 +1,45 @@
+import {
+    Connection,
+    Keypair,
+    sendAndConfirmTransaction,
+    SystemProgram,
+    Transaction,
+    TransactionInstruction,
+} from '@solana/web3.js';
+
+
+function createKeypairFromFile(path: string): Keypair {
+    return Keypair.fromSecretKey(
+        Buffer.from(JSON.parse(require('fs').readFileSync(path, "utf-8")))
+    )
+};
+
+
+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');
+  
+    it("Create the account", async () => {
+
+        const newKeypair = Keypair.generate();
+
+        let ix = new TransactionInstruction({
+            keys: [
+                {pubkey: payer.publicKey, isSigner: true, isWritable: true},
+                {pubkey: newKeypair.publicKey, isSigner: true, isWritable: true},
+                {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
+            ],
+            programId: program.publicKey,
+            data: Buffer.alloc(0),
+        });
+
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer, newKeypair]
+        );
+    });
+  });
+  

+ 10 - 0
accounts/rent/native/tsconfig.json

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