Browse Source

token mint to

jpcaulfi 3 years ago
parent
commit
d4819330d7
29 changed files with 901 additions and 3 deletions
  1. 3 3
      README.md
  2. 1 0
      nfts/mint/native/tests/test.ts
  3. 12 0
      tokens/mint-to/README.md
  4. 14 0
      tokens/mint-to/anchor/Anchor.toml
  5. 13 0
      tokens/mint-to/anchor/Cargo.toml
  6. 6 0
      tokens/mint-to/anchor/assets/token_metadata.json
  7. 14 0
      tokens/mint-to/anchor/package.json
  8. 21 0
      tokens/mint-to/anchor/programs/mint-token-to/Cargo.toml
  9. 2 0
      tokens/mint-to/anchor/programs/mint-token-to/Xargo.toml
  10. 100 0
      tokens/mint-to/anchor/programs/mint-token-to/src/instructions/create_token_mint.rs
  11. 66 0
      tokens/mint-to/anchor/programs/mint-token-to/src/instructions/mint_to_wallet.rs
  12. 2 0
      tokens/mint-to/anchor/programs/mint-token-to/src/instructions/mod.rs
  13. 37 0
      tokens/mint-to/anchor/programs/mint-token-to/src/lib.rs
  14. 52 0
      tokens/mint-to/anchor/tests/test.ts
  15. 10 0
      tokens/mint-to/anchor/tsconfig.json
  16. 6 0
      tokens/mint-to/native/assets/token_metadata.json
  17. 8 0
      tokens/mint-to/native/cicd.sh
  18. 21 0
      tokens/mint-to/native/package.json
  19. 15 0
      tokens/mint-to/native/program/Cargo.toml
  20. 107 0
      tokens/mint-to/native/program/src/instructions/create_mint.rs
  21. 74 0
      tokens/mint-to/native/program/src/instructions/mint_to.rs
  22. 2 0
      tokens/mint-to/native/program/src/instructions/mod.rs
  23. 23 0
      tokens/mint-to/native/program/src/lib.rs
  24. 38 0
      tokens/mint-to/native/program/src/processor.rs
  25. 14 0
      tokens/mint-to/native/program/src/state/mint.rs
  26. 1 0
      tokens/mint-to/native/program/src/state/mod.rs
  27. 228 0
      tokens/mint-to/native/tests/test.ts
  28. 10 0
      tokens/mint-to/native/tsconfig.json
  29. 1 0
      tokens/mint/native/tests/test.ts

+ 3 - 3
README.md

@@ -57,13 +57,13 @@ Regardless of what you may want to add on top of existing Solana programs, the n
 - [ ] 5. Destroying an account
 - ### [ ] Tokens
 - [x] 1. Creating an SPL Token
-- [ ] 2. Transferring tokens
+- [x] 2. Mint tokens to a wallet
+- [ ] 3. Transferring tokens between wallets
 - ### [ ] NFTs
 - [x] 1. Creating an NFT
 - [ ] 2. Transferring an NFT
 - [ ] 3. Selling an NFT
-- [ ] 4. Adding metadata to an NFT
-- [ ] 5. NFT metadata expanded
+- [ ] 4. NFT metadata expanded
 - ### [ ] PDAs
 - [ ] 1. A simple PDA
 - [ ] 2. Dynamic PDA creation

+ 1 - 0
nfts/mint/native/tests/test.ts

@@ -119,6 +119,7 @@ describe("mint-token", () => {
                     isSigner: false,
                     isWritable: false,
                 },
+                // Token metadata program
                 {
                     pubkey: TOKEN_METADATA_PROGRAM_ID,
                     isSigner: false,

+ 12 - 0
tokens/mint-to/README.md

@@ -0,0 +1,12 @@
+# Create a New SPL Token Mint
+
+This example demonstrates how to create an SPl Token on Solana with some metadata such as a token symbol and icon.
+
+### :key: Keys:
+
+- SPL Tokens by default have **9 decimals**, and **NFTs have 0 decimals**. "Decimals" here means the number of decimal; ie. a token with 3 decimals will be tracked in increments of 0.001.   
+- You can use [Metaplex's Token Metadata Program](https://docs.metaplex.com/) to create metadata for your token.
+- Steps:
+    1. Create an account for the Mint.
+    2. Initialize that account as a Mint Account.
+    3. Create a metadata account associated with that Mint Account.

+ 14 - 0
tokens/mint-to/anchor/Anchor.toml

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

+ 13 - 0
tokens/mint-to/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

+ 6 - 0
tokens/mint-to/anchor/assets/token_metadata.json

@@ -0,0 +1,6 @@
+{
+    "name": "Solana Gold",
+    "symbol": "GOLDSOL",
+    "description": "A gold Solana SPL token :)",
+    "image": "https://images.all-free-download.com/images/graphiclarge/solana_coin_sign_icon_shiny_golden_symmetric_geometrical_design_6919941.jpg"
+}

+ 14 - 0
tokens/mint-to/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"
+    }
+}

+ 21 - 0
tokens/mint-to/anchor/programs/mint-token-to/Cargo.toml

@@ -0,0 +1,21 @@
+[package]
+name = "mint-token-to"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "mint_token_to"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang = "0.24.2"
+anchor-spl = "0.24.2"
+mpl-token-metadata = { version="1.2.5", features = [ "no-entrypoint" ] }

+ 2 - 0
tokens/mint-to/anchor/programs/mint-token-to/Xargo.toml

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

+ 100 - 0
tokens/mint-to/anchor/programs/mint-token-to/src/instructions/create_token_mint.rs

@@ -0,0 +1,100 @@
+use {
+    anchor_lang::{
+        prelude::*,
+        solana_program::program::invoke,
+        system_program,
+    },
+    anchor_spl::token,
+    mpl_token_metadata::instruction as mpl_instruction,
+};
+
+
+pub fn create_token_mint(
+    ctx: Context<CreateTokenMint>, 
+    metadata_title: String, 
+    metadata_symbol: String, 
+    metadata_uri: String,
+) -> Result<()> {
+
+    const MINT_SIZE: u64 = 82;
+
+    msg!("Creating mint account...");
+    msg!("Mint: {}", &ctx.accounts.mint_account.key());
+    system_program::create_account(
+        CpiContext::new(
+            ctx.accounts.token_program.to_account_info(),
+            system_program::CreateAccount {
+                from: ctx.accounts.mint_authority.to_account_info(),
+                to: ctx.accounts.mint_account.to_account_info(),
+            },
+        ),
+        (Rent::get()?).minimum_balance(MINT_SIZE as usize),
+        MINT_SIZE,
+        &ctx.accounts.token_program.key(),
+    )?;
+
+    msg!("Initializing mint account...");
+    msg!("Mint: {}", &ctx.accounts.mint_account.key());
+    token::initialize_mint(
+        CpiContext::new(
+            ctx.accounts.token_program.to_account_info(),
+            token::InitializeMint {
+                mint: ctx.accounts.mint_account.to_account_info(),
+                rent: ctx.accounts.rent.to_account_info(),
+            },
+        ),
+        9,                                              // 9 Decimals
+        &ctx.accounts.mint_authority.key(),
+        Some(&ctx.accounts.mint_authority.key()),
+    )?;
+
+    msg!("Creating metadata account...");
+    msg!("Metadata account address: {}", &ctx.accounts.metadata_account.key());
+    invoke(
+        &mpl_instruction::create_metadata_accounts_v2(
+            ctx.accounts.token_metadata_program.key(),      // Program ID (the Token Metadata Program)
+            ctx.accounts.metadata_account.key(),            // Metadata account
+            ctx.accounts.mint_account.key(),                // Mint account
+            ctx.accounts.mint_authority.key(),              // Mint authority
+            ctx.accounts.mint_authority.key(),              // Payer
+            ctx.accounts.mint_authority.key(),              // Update authority
+            metadata_title,                                 // Name
+            metadata_symbol,                                // Symbol
+            metadata_uri,                                   // URI
+            None,                                           // Creators
+            0,                                              // Seller fee basis points
+            true,                                           // Update authority is signer
+            false,                                          // Is mutable
+            None,                                           // Collection
+            None,                                           // Uses
+        ),
+        &[
+            ctx.accounts.metadata_account.to_account_info(),
+            ctx.accounts.mint_account.to_account_info(),
+            ctx.accounts.mint_authority.to_account_info(),
+            ctx.accounts.token_metadata_program.to_account_info(),
+            ctx.accounts.rent.to_account_info(),
+        ],
+    )?;
+
+    msg!("Token mint created successfully.");
+
+    Ok(())
+}
+
+
+#[derive(Accounts)]
+pub struct CreateTokenMint<'info> {
+    /// CHECK: We're about to create this with Metaplex
+    #[account(mut)]
+    pub metadata_account: UncheckedAccount<'info>,
+    #[account(mut)]
+    pub mint_account: Signer<'info>,
+    #[account(mut)]
+    pub mint_authority: Signer<'info>,
+    pub rent: Sysvar<'info, Rent>,
+    pub system_program: Program<'info, System>,
+    pub token_program: Program<'info, token::Token>,
+    /// CHECK: Metaplex will check this
+    pub token_metadata_program: UncheckedAccount<'info>,
+}

+ 66 - 0
tokens/mint-to/anchor/programs/mint-token-to/src/instructions/mint_to_wallet.rs

@@ -0,0 +1,66 @@
+use {
+    anchor_lang::prelude::*,
+    anchor_spl::{
+        token,
+        associated_token,
+    },
+};
+
+
+pub fn mint_to_wallet(
+    ctx: Context<MintToWallet>, 
+    amount: u64,
+) -> Result<()> {
+
+    msg!("Creating token account...");
+    msg!("Token Address: {}", &ctx.accounts.token_account.key());    
+    associated_token::create(
+        CpiContext::new(
+            ctx.accounts.associated_token_program.to_account_info(),
+            associated_token::Create {
+                payer: ctx.accounts.mint_authority.to_account_info(),
+                associated_token: ctx.accounts.token_account.to_account_info(),
+                authority: ctx.accounts.mint_authority.to_account_info(),
+                mint: ctx.accounts.mint_account.to_account_info(),
+                system_program: ctx.accounts.system_program.to_account_info(),
+                token_program: ctx.accounts.token_program.to_account_info(),
+                rent: ctx.accounts.rent.to_account_info(),
+            },
+        ),
+    )?;
+
+    msg!("Minting token to token account...");
+    msg!("Mint: {}", &ctx.accounts.mint_account.to_account_info().key());   
+    msg!("Token Address: {}", &ctx.accounts.token_account.key());     
+    token::mint_to(
+        CpiContext::new(
+            ctx.accounts.token_program.to_account_info(),
+            token::MintTo {
+                mint: ctx.accounts.mint_account.to_account_info(),
+                to: ctx.accounts.token_account.to_account_info(),
+                authority: ctx.accounts.mint_authority.to_account_info(),
+            },
+        ),
+        1,
+    )?;
+
+    msg!("Token minted to wallet successfully.");
+
+    Ok(())
+}
+
+
+#[derive(Accounts)]
+pub struct MintToWallet<'info> {
+    #[account(mut)]
+    pub mint_account: Signer<'info>,
+    /// CHECK: We're about to create this with Anchor
+    #[account(mut)]
+    pub token_account: UncheckedAccount<'info>,
+    #[account(mut)]
+    pub mint_authority: Signer<'info>,
+    pub rent: Sysvar<'info, Rent>,
+    pub system_program: Program<'info, System>,
+    pub token_program: Program<'info, token::Token>,
+    pub associated_token_program: Program<'info, associated_token::AssociatedToken>,
+}

+ 2 - 0
tokens/mint-to/anchor/programs/mint-token-to/src/instructions/mod.rs

@@ -0,0 +1,2 @@
+pub mod create_token_mint;
+pub mod mint_to_wallet;

+ 37 - 0
tokens/mint-to/anchor/programs/mint-token-to/src/lib.rs

@@ -0,0 +1,37 @@
+use anchor_lang::prelude::*;
+
+pub mod instructions;
+
+use instructions::*;
+
+
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
+
+#[program]
+pub mod mint_token_to {
+    use super::*;
+
+    pub fn create_token_mint(
+        ctx: Context<CreateTokenMint>, 
+        metadata_title: String, 
+        metadata_symbol: String, 
+        metadata_uri: String,
+    ) -> Result<()> {
+
+        instructions::create_token_mint::create_token_mint(
+            ctx, 
+            metadata_title, 
+            metadata_symbol, 
+            metadata_uri
+        )
+    }
+
+    pub fn mint_to_wallet(
+        ctx: Context<MintToWallet>, 
+        amount: u64,
+    ) -> Result<()> {
+
+        instructions::mint_to_wallet::mint_to_wallet(ctx, amount)
+    }
+}

+ 52 - 0
tokens/mint-to/anchor/tests/test.ts

@@ -0,0 +1,52 @@
+import * as anchor from "@project-serum/anchor";
+import { MintTokenTo } from "../target/types/mint_token_to";
+
+
+const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey(
+  "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
+);
+
+
+describe("mint-token", () => {
+  
+  const provider = anchor.AnchorProvider.env();
+  anchor.setProvider(provider);
+  const payer = provider.wallet as anchor.Wallet;
+  const program = anchor.workspace.MintTokenTo as anchor.Program<MintTokenTo>;
+
+  it("Mint!", async () => {
+
+    const mintKeypair: anchor.web3.Keypair = anchor.web3.Keypair.generate();
+    console.log(`New token: ${mintKeypair.publicKey}`);
+
+    // Derive the metadata account's address and set the metadata
+    //
+    const metadataAddress = (await anchor.web3.PublicKey.findProgramAddress(
+      [
+        Buffer.from("metadata"),
+        TOKEN_METADATA_PROGRAM_ID.toBuffer(),
+        mintKeypair.publicKey.toBuffer(),
+      ],
+      TOKEN_METADATA_PROGRAM_ID
+    ))[0];
+    const testTokenTitle = "Solana Gold";
+    const testTokenSymbol = "GOLDSOL";
+    const testTokenUri = "https://raw.githubusercontent.com/solana-developers/program-examples/main/tokens/mint/native/assets/token_metadata.json";
+
+    // Transact with the "mint_token" function in our on-chain program
+    //
+    await program.methods.mintToken(
+      testTokenTitle, testTokenSymbol, testTokenUri
+    )
+    .accounts({
+      metadataAccount: metadataAddress,
+      mintAccount: mintKeypair.publicKey,
+      mintAuthority: payer.publicKey,
+      systemProgram: anchor.web3.SystemProgram.programId,
+      tokenProgram: anchor.utils.token.TOKEN_PROGRAM_ID,
+      tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
+    })
+    .signers([payer.payer, mintKeypair])
+    .rpc();
+  });
+});

+ 10 - 0
tokens/mint-to/anchor/tsconfig.json

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

+ 6 - 0
tokens/mint-to/native/assets/token_metadata.json

@@ -0,0 +1,6 @@
+{
+    "name": "Solana Gold",
+    "symbol": "GOLDSOL",
+    "description": "A gold Solana SPL token :)",
+    "image": "https://images.all-free-download.com/images/graphiclarge/solana_coin_sign_icon_shiny_golden_symmetric_geometrical_design_6919941.jpg"
+}

+ 8 - 0
tokens/mint-to/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

+ 21 - 0
tokens/mint-to/native/package.json

@@ -0,0 +1,21 @@
+{
+  "scripts": {
+    "test": "yarn run ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts"
+  },
+  "dependencies": {
+    "@solana/spl-token": "^0.2.0",
+    "@solana/web3.js": "^1.47.3",
+    "borsh": "^0.7.0",
+    "buffer": "^6.0.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"
+  }
+}

+ 15 - 0
tokens/mint-to/native/program/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "program"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+borsh = "0.9.3"
+borsh-derive = "0.9.1"
+solana-program = "1.10.26"
+spl-token = { version="3.3.0", features = [ "no-entrypoint" ] }
+spl-associated-token-account = { version="1.0.5", features = [ "no-entrypoint" ] }
+mpl-token-metadata = { version="1.2.5", features = [ "no-entrypoint" ] }
+
+[lib]
+crate-type = ["cdylib", "lib"]

+ 107 - 0
tokens/mint-to/native/program/src/instructions/create_mint.rs

@@ -0,0 +1,107 @@
+use {
+    solana_program::{
+        account_info::{next_account_info, AccountInfo}, 
+        entrypoint::ProgramResult, 
+        msg, 
+        program::invoke,
+        rent::Rent,
+        system_instruction,
+        sysvar::Sysvar,
+    },
+    spl_token::{
+        instruction as token_instruction,
+    },
+    mpl_token_metadata::{
+        instruction as mpl_instruction,
+    },
+};
+
+
+pub fn create_mint(
+    accounts: &[AccountInfo],
+    token_title: String,
+    token_symbol: String,
+    token_uri: String,
+) -> ProgramResult {
+
+    const MINT_SIZE: u64 = 82;
+
+    let accounts_iter = &mut accounts.iter();
+
+    let mint_account = next_account_info(accounts_iter)?;
+    let metadata_account = next_account_info(accounts_iter)?;
+    let mint_authority = next_account_info(accounts_iter)?;
+    let rent = next_account_info(accounts_iter)?;
+    let _system_program = next_account_info(accounts_iter)?;
+    let token_program = next_account_info(accounts_iter)?;
+    let token_metadata_program = next_account_info(accounts_iter)?;
+    
+    msg!("Creating mint account...");
+    msg!("Mint: {}", mint_account.key);
+    invoke(
+        &system_instruction::create_account(
+            &mint_authority.key,
+            &mint_account.key,
+            (Rent::get()?).minimum_balance(MINT_SIZE as usize),
+            MINT_SIZE,
+            &token_program.key,
+        ),
+        &[
+            mint_account.clone(),
+            mint_authority.clone(),
+            token_program.clone(),
+        ]
+    )?;
+
+    msg!("Initializing mint account...");
+    msg!("Mint: {}", mint_account.key);
+    invoke(
+        &token_instruction::initialize_mint(
+            &token_program.key,
+            &mint_account.key,
+            &mint_authority.key,
+            Some(&mint_authority.key),
+            9,
+        )?,
+        &[
+            mint_account.clone(),
+            mint_authority.clone(),
+            token_program.clone(),
+            rent.clone(),
+        ]
+    )?;
+
+    msg!("Creating metadata account...");
+    msg!("Metadata account address: {}", metadata_account.key);
+    invoke(
+        &mpl_instruction::create_metadata_accounts_v2(
+            *token_metadata_program.key,
+            *metadata_account.key,
+            *mint_account.key,
+            *mint_authority.key,
+            *mint_authority.key,
+            *mint_authority.key,
+            token_title,
+            token_symbol,
+            token_uri,
+            None,
+            0,
+            true,
+            false,
+            None,
+            None,
+        ),
+        &[
+            metadata_account.clone(),
+            mint_account.clone(),
+            mint_authority.clone(),
+            token_metadata_program.clone(),
+            rent.clone(),
+        ],
+    )?;
+
+    msg!("Token mint created successfully.");
+
+    Ok(())
+}
+

+ 74 - 0
tokens/mint-to/native/program/src/instructions/mint_to.rs

@@ -0,0 +1,74 @@
+use {
+    solana_program::{
+        account_info::{next_account_info, AccountInfo},
+        entrypoint::ProgramResult, 
+        msg, 
+        program::invoke,
+    },
+    spl_token::{
+        instruction as token_instruction,
+    },
+    spl_associated_token_account::{
+        instruction as token_account_instruction,
+    },
+};
+
+
+pub fn mint_to(
+    accounts: &[AccountInfo],
+    amount: u64,
+) -> ProgramResult {
+
+    let accounts_iter = &mut accounts.iter();
+
+    let mint_account = next_account_info(accounts_iter)?;
+    let token_account = next_account_info(accounts_iter)?;
+    let mint_authority = next_account_info(accounts_iter)?;
+    let rent = next_account_info(accounts_iter)?;
+    let _system_program = next_account_info(accounts_iter)?;
+    let token_program = next_account_info(accounts_iter)?;
+    let associated_token_program = next_account_info(accounts_iter)?;
+
+    msg!("Creating token account...");
+    msg!("Token Address: {}", token_account.key);    
+    invoke(
+        &token_account_instruction::create_associated_token_account(
+            &mint_authority.key,
+            &mint_authority.key,
+            &mint_account.key,
+        ),
+        &[
+            mint_account.clone(),
+            token_account.clone(),
+            mint_authority.clone(),
+            token_program.clone(),
+            associated_token_program.clone(),
+        ]
+    )?;
+
+    msg!("Minting token to token account...");
+    msg!("Mint: {}", mint_account.key);   
+    msg!("Token Address: {}", token_account.key);
+    invoke(
+        &token_instruction::mint_to(
+            &token_program.key,
+            &mint_account.key,
+            &token_account.key,
+            &mint_authority.key,
+            &[&mint_authority.key],
+            amount,
+        )?,
+        &[
+            mint_account.clone(),
+            mint_authority.clone(),
+            token_account.clone(),
+            token_program.clone(),
+            rent.clone(),
+        ]
+    )?;
+
+    msg!("Token minted to wallet successfully.");
+
+    Ok(())
+}
+

+ 2 - 0
tokens/mint-to/native/program/src/instructions/mod.rs

@@ -0,0 +1,2 @@
+pub mod create_mint;
+pub mod mint_to;

+ 23 - 0
tokens/mint-to/native/program/src/lib.rs

@@ -0,0 +1,23 @@
+use solana_program::{
+    account_info::AccountInfo, 
+    entrypoint, 
+    entrypoint::ProgramResult, 
+    pubkey::Pubkey,
+};
+
+pub mod processor;
+pub mod instructions;
+pub mod state;
+
+
+entrypoint!(process_instruction);
+
+
+fn process_instruction(
+    _program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+
+    processor::process_instruction(accounts, instruction_data)
+}

+ 38 - 0
tokens/mint-to/native/program/src/processor.rs

@@ -0,0 +1,38 @@
+use {
+    borsh::BorshDeserialize,
+    solana_program::{
+        account_info::AccountInfo, 
+        entrypoint::ProgramResult, 
+        program_error::ProgramError, 
+    },
+};
+
+use crate::instructions::{ create_mint, mint_to };
+use crate::state::mint::{ TokenMetadata, MintTokenTo };
+
+
+pub fn process_instruction(
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+
+    match TokenMetadata::try_from_slice(instruction_data) {
+        Ok(token_metadata_instruction) => return create_mint::create_mint(
+                accounts,
+                token_metadata_instruction.title,
+                token_metadata_instruction.symbol,
+                token_metadata_instruction.uri,
+            ),
+        Err(_) => {},
+    };
+
+    match MintTokenTo::try_from_slice(instruction_data) {
+        Ok(mint_to) => return mint_to::mint_to(
+                accounts,
+                mint_to.amount
+            ),
+        Err(_) => {},
+    };
+
+    Err(ProgramError::InvalidInstructionData)
+}

+ 14 - 0
tokens/mint-to/native/program/src/state/mint.rs

@@ -0,0 +1,14 @@
+use borsh::{ BorshSerialize, BorshDeserialize };
+
+
+#[derive(BorshSerialize, BorshDeserialize, Debug)]
+pub struct TokenMetadata {
+    pub title: String,
+    pub symbol: String,
+    pub uri: String,
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Debug)]
+pub struct MintTokenTo {
+    pub amount: u64,
+}

+ 1 - 0
tokens/mint-to/native/program/src/state/mod.rs

@@ -0,0 +1 @@
+pub mod mint;

+ 228 - 0
tokens/mint-to/native/tests/test.ts

@@ -0,0 +1,228 @@
+import {
+    Connection,
+    Keypair,
+    PublicKey,
+    SystemProgram,
+    SYSVAR_RENT_PUBKEY,
+    TransactionInstruction,
+    Transaction,
+    sendAndConfirmTransaction,
+} from '@solana/web3.js';
+import {
+    ASSOCIATED_TOKEN_PROGRAM_ID,
+    getAssociatedTokenAddress,
+    MINT_SIZE,
+    TOKEN_PROGRAM_ID,
+} from '@solana/spl-token';
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+
+
+const TOKEN_METADATA_PROGRAM_ID = new PublicKey(
+    "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
+);
+
+
+function createKeypairFromFile(path: string): Keypair {
+    return Keypair.fromSecretKey(
+        Buffer.from(JSON.parse(require('fs').readFileSync(path, "utf-8")))
+    )
+};
+
+
+class Assignable {
+    constructor(properties) {
+        Object.keys(properties).map((key) => {
+            return (this[key] = properties[key]);
+        });
+    };
+};
+
+class TokenMetadata extends Assignable {
+    toBuffer() {
+        return Buffer.from(borsh.serialize(TokenMetadataSchema, this));
+    }
+};
+const TokenMetadataSchema = new Map([
+    [
+        TokenMetadata, {
+            kind: 'struct',
+            fields: [
+                ['title', 'string'],
+                ['symbol', 'string'],
+                ['uri', 'string'],
+            ]
+        }
+    ]
+]);
+
+class MintTokenTo extends Assignable {
+    toBuffer() {
+        return Buffer.from(borsh.serialize(MintTokenToSchema, this));
+    }
+};
+const MintTokenToSchema = new Map([
+    [
+        MintTokenTo, {
+            kind: 'struct',
+            fields: [
+                ['amount', 'u64'],
+            ]
+        }
+    ]
+]);
+
+
+describe("mint-token", () => {
+
+    const connection = new Connection(`http://api.devnet.solana.com/`, 'confirmed');
+    const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
+    const program = createKeypairFromFile('./program/target/so/program-keypair.json');
+
+    const mintKeypair: Keypair = Keypair.generate();
+    console.log(`New token: ${mintKeypair.publicKey}`);
+
+    it("Mint!", async () => {
+
+        const metadataAddress = (await PublicKey.findProgramAddress(
+            [
+              Buffer.from("metadata"),
+              TOKEN_METADATA_PROGRAM_ID.toBuffer(),
+              mintKeypair.publicKey.toBuffer(),
+            ],
+            TOKEN_METADATA_PROGRAM_ID
+        ))[0];
+        const metadataInstructionData = new TokenMetadata({
+            title: "Solana Gold",
+            symbol: "GOLDSOL",
+            uri: "https://raw.githubusercontent.com/solana-developers/program-examples/main/tokens/mint/native/assets/token_metadata.json",
+        });
+
+        // Transact with the "mint_token" function in our on-chain program
+        //
+        let ix = new TransactionInstruction({
+            keys: [
+                // Mint account
+                {
+                    pubkey: mintKeypair.publicKey,
+                    isSigner: true,
+                    isWritable: true,
+                },
+                // Metadata account
+                {
+                    pubkey: metadataAddress,
+                    isSigner: false,
+                    isWritable: true,
+                },
+                // Mint Authority
+                {
+                    pubkey: payer.publicKey,
+                    isSigner: true,
+                    isWritable: false,
+                },
+                // Rent account
+                {
+                    pubkey: SYSVAR_RENT_PUBKEY,
+                    isSigner: false,
+                    isWritable: false,
+                },
+                // System program
+                {
+                    pubkey: SystemProgram.programId,
+                    isSigner: false,
+                    isWritable: false,
+                },
+                // Token program
+                {
+                    pubkey: TOKEN_PROGRAM_ID,
+                    isSigner: false,
+                    isWritable: false,
+                },
+                // Token metadata program
+                {
+                    pubkey: TOKEN_METADATA_PROGRAM_ID,
+                    isSigner: false,
+                    isWritable: false,
+                },
+            ],
+            programId: program.publicKey,
+            data: metadataInstructionData.toBuffer(),
+        });
+
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer, mintKeypair]
+        );
+    });
+
+
+    it("Mint to a wallet!", async () => {
+
+        const tokenAddress = await getAssociatedTokenAddress(
+            mintKeypair.publicKey,
+            payer.publicKey
+        );
+        const mintToInstructionData = new MintTokenTo({
+            amount: 1,
+        });
+
+        // Transact with the "mint_to" function in our on-chain program
+        //
+        let ix = new TransactionInstruction({
+            keys: [
+                // Mint account
+                {
+                    pubkey: mintKeypair.publicKey,
+                    isSigner: true,
+                    isWritable: true,
+                },
+                // Token account
+                {
+                    pubkey: tokenAddress,
+                    isSigner: false,
+                    isWritable: true,
+                },
+                // Mint Authority
+                {
+                    pubkey: payer.publicKey,
+                    isSigner: true,
+                    isWritable: false,
+                },
+                // Rent account
+                {
+                    pubkey: SYSVAR_RENT_PUBKEY,
+                    isSigner: false,
+                    isWritable: false,
+                },
+                // System program
+                {
+                    pubkey: SystemProgram.programId,
+                    isSigner: false,
+                    isWritable: false,
+                },
+                // Token program
+                {
+                    pubkey: TOKEN_PROGRAM_ID,
+                    isSigner: false,
+                    isWritable: false,
+                },
+                // Associated token program
+                {
+                    pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
+                    isSigner: false,
+                    isWritable: false,
+                },
+            ],
+            programId: program.publicKey,
+            data: mintToInstructionData.toBuffer(),
+        });
+
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer, mintKeypair]
+        );
+    });
+  });
+  

+ 10 - 0
tokens/mint-to/native/tsconfig.json

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

+ 1 - 0
tokens/mint/native/tests/test.ts

@@ -119,6 +119,7 @@ describe("mint-token", () => {
                     isSigner: false,
                     isWritable: false,
                 },
+                // Token metadata program
                 {
                     pubkey: TOKEN_METADATA_PROGRAM_ID,
                     isSigner: false,