diff --git a/tokens/token-2022/README.md b/tokens/token-2022/README.md deleted file mode 100644 index 14eb1b02f..000000000 --- a/tokens/token-2022/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Token 2022 - -> Coming soon! \ No newline at end of file diff --git a/tokens/token-2022/anchor/.gitignore b/tokens/token-2022/anchor/.gitignore new file mode 100644 index 000000000..d243ecc13 --- /dev/null +++ b/tokens/token-2022/anchor/.gitignore @@ -0,0 +1,7 @@ + +.anchor +.DS_Store +target +**/*.rs.bk +node_modules +test-ledger diff --git a/tokens/token-2022/anchor/.prettierignore b/tokens/token-2022/anchor/.prettierignore new file mode 100644 index 000000000..c1a0b75f0 --- /dev/null +++ b/tokens/token-2022/anchor/.prettierignore @@ -0,0 +1,8 @@ + +.anchor +.DS_Store +target +node_modules +dist +build +test-ledger diff --git a/tokens/token-2022/anchor/Anchor.toml b/tokens/token-2022/anchor/Anchor.toml new file mode 100644 index 000000000..9b3a79048 --- /dev/null +++ b/tokens/token-2022/anchor/Anchor.toml @@ -0,0 +1,15 @@ +[features] +seeds = false +skip-lint = false +[programs.localnet] +anchor = "6qNqxkRF791FXFeQwqYQLEzAbGiqDULC5SSHVsfRoG89" + +[registry] +url = "https://api.apr.dev" + +[provider] +cluster = "Localnet" +wallet = "/Users/devenv/.config/solana/id.json" + +[scripts] +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tokens/token-2022/anchor/Cargo.toml b/tokens/token-2022/anchor/Cargo.toml new file mode 100644 index 000000000..ef17a63c0 --- /dev/null +++ b/tokens/token-2022/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 diff --git a/tokens/token-2022/anchor/migrations/deploy.ts b/tokens/token-2022/anchor/migrations/deploy.ts new file mode 100644 index 000000000..82fb175fa --- /dev/null +++ b/tokens/token-2022/anchor/migrations/deploy.ts @@ -0,0 +1,12 @@ +// Migrations are an early feature. Currently, they're nothing more than this +// single deploy script that's invoked from the CLI, injecting a provider +// configured from the workspace's Anchor.toml. + +const anchor = require("@coral-xyz/anchor"); + +module.exports = async function (provider) { + // Configure client to use the provider. + anchor.setProvider(provider); + + // Add your deploy script here. +}; diff --git a/tokens/token-2022/anchor/package.json b/tokens/token-2022/anchor/package.json new file mode 100644 index 000000000..b5355136b --- /dev/null +++ b/tokens/token-2022/anchor/package.json @@ -0,0 +1,19 @@ +{ + "scripts": { + "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", + "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.27.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" + } +} diff --git a/tokens/token-2022/anchor/programs/basics/Cargo.toml b/tokens/token-2022/anchor/programs/basics/Cargo.toml new file mode 100644 index 000000000..c2daf5720 --- /dev/null +++ b/tokens/token-2022/anchor/programs/basics/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "anchor" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "anchor" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-spl = "0.27.0" +anchor-lang = { version = "0.27.0", features= ["init-if-needed"]} +spl-token = { version = "3.1.1", features = ["no-entrypoint"] } +spl-token-2022 = { version = "0.5.0", features = ["no-entrypoint"] } + diff --git a/tokens/token-2022/anchor/programs/basics/Xargo.toml b/tokens/token-2022/anchor/programs/basics/Xargo.toml new file mode 100644 index 000000000..475fb71ed --- /dev/null +++ b/tokens/token-2022/anchor/programs/basics/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/tokens/token-2022/anchor/programs/basics/src/lib.rs b/tokens/token-2022/anchor/programs/basics/src/lib.rs new file mode 100644 index 000000000..32e4a84d1 --- /dev/null +++ b/tokens/token-2022/anchor/programs/basics/src/lib.rs @@ -0,0 +1,139 @@ +use anchor_lang::prelude::*; +use anchor_spl::associated_token::AssociatedToken; +use anchor_spl::token_interface::{ + self, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked, +}; + +declare_id!("6qNqxkRF791FXFeQwqYQLEzAbGiqDULC5SSHVsfRoG89"); + +#[program] +pub mod anchor { + + use super::*; + + pub fn create_token(_ctx: Context, _token_name: String) -> Result<()> { + msg!("Create Token"); + Ok(()) + } + pub fn create_token_account(_ctx: Context) -> Result<()> { + msg!("Create Token Account"); + Ok(()) + } + pub fn create_associated_token_account( + _ctx: Context, + ) -> Result<()> { + msg!("Create Associated Token Account"); + Ok(()) + } + pub fn transfer_token(ctx: Context, amount: u64) -> Result<()> { + let cpi_accounts = TransferChecked { + from: ctx.accounts.from.to_account_info().clone(), + mint: ctx.accounts.mint.to_account_info().clone(), + to: ctx.accounts.to_ata.to_account_info().clone(), + authority: ctx.accounts.signer.to_account_info(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_context = CpiContext::new(cpi_program, cpi_accounts); + token_interface::transfer_checked(cpi_context, amount, ctx.accounts.mint.decimals)?; + msg!("Transfer Token"); + Ok(()) + } + pub fn mint_token(ctx: Context, amount: u64) -> Result<()> { + let cpi_accounts = MintTo { + mint: ctx.accounts.mint.to_account_info().clone(), + to: ctx.accounts.receiver.to_account_info().clone(), + authority: ctx.accounts.signer.to_account_info(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_context = CpiContext::new(cpi_program, cpi_accounts); + token_interface::mint_to(cpi_context, amount)?; + msg!("Mint Token"); + Ok(()) + } +} + +#[derive(Accounts)] +#[instruction(token_name: String)] +pub struct CreateToken<'info> { + #[account(mut)] + pub signer: Signer<'info>, + #[account( + init, + payer = signer, + mint::decimals = 6, + mint::authority = signer.key(), + seeds = [b"token-2022-token", signer.key().as_ref(), token_name.as_bytes()], + bump, + )] + pub mint: InterfaceAccount<'info, Mint>, + pub system_program: Program<'info, System>, + pub token_program: Interface<'info, TokenInterface>, +} + +#[derive(Accounts)] +pub struct CreateTokenAccount<'info> { + #[account(mut)] + pub signer: Signer<'info>, + pub mint: InterfaceAccount<'info, Mint>, + #[account( + init, + token::mint = mint, + token::authority = signer, + payer = signer, + seeds = [b"token-2022-token-account", signer.key().as_ref(), mint.key().as_ref()], + bump, + )] + pub token_account: InterfaceAccount<'info, TokenAccount>, + pub system_program: Program<'info, System>, + pub token_program: Interface<'info, TokenInterface>, +} + +#[derive(Accounts)] +pub struct CreateAssociatedTokenAccount<'info> { + #[account(mut)] + pub signer: Signer<'info>, + pub mint: InterfaceAccount<'info, Mint>, + #[account( + init, + associated_token::mint = mint, + payer = signer, + associated_token::authority = signer, + )] + pub token_account: InterfaceAccount<'info, TokenAccount>, + pub system_program: Program<'info, System>, + pub token_program: Interface<'info, TokenInterface>, + pub associated_token_program: Program<'info, AssociatedToken>, +} + +#[derive(Accounts)] + +pub struct TransferToken<'info> { + #[account(mut)] + pub signer: Signer<'info>, + #[account(mut)] + pub from: InterfaceAccount<'info, TokenAccount>, + pub to: SystemAccount<'info>, + #[account( + init, + associated_token::mint = mint, + payer = signer, + associated_token::authority = to + )] + pub to_ata: InterfaceAccount<'info, TokenAccount>, + #[account(mut)] + pub mint: InterfaceAccount<'info, Mint>, + pub token_program: Interface<'info, TokenInterface>, + pub system_program: Program<'info, System>, + pub associated_token_program: Program<'info, AssociatedToken>, +} + +#[derive(Accounts)] +pub struct MintToken<'info> { + #[account(mut)] + pub signer: Signer<'info>, + #[account(mut)] + pub mint: InterfaceAccount<'info, Mint>, + #[account(mut)] + pub receiver: InterfaceAccount<'info, TokenAccount>, + pub token_program: Interface<'info, TokenInterface>, +} diff --git a/tokens/token-2022/anchor/tests/anchor.ts b/tokens/token-2022/anchor/tests/anchor.ts new file mode 100644 index 000000000..a749c0646 --- /dev/null +++ b/tokens/token-2022/anchor/tests/anchor.ts @@ -0,0 +1,170 @@ +import * as anchor from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; +import { Anchor } from "../target/types/anchor"; + +describe("anchor", () => { + // Configure the client to use the local cluster. + anchor.setProvider(anchor.AnchorProvider.env()); + + const program = anchor.workspace.Anchor as Program; + const connection = program.provider.connection; + const TOKEN_2022_PROGRAM_ID = new anchor.web3.PublicKey( + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + ); + const payer = anchor.web3.Keypair.generate(); + const ATA_PROGRAM_ID = new anchor.web3.PublicKey( + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + ); + + const tokenName = "TestToken"; + const [mint] = anchor.web3.PublicKey.findProgramAddressSync( + [ + Buffer.from("token-2022-token"), + payer.publicKey.toBytes(), + Buffer.from(tokenName), + ], + program.programId + ); + const [payerATA] = anchor.web3.PublicKey.findProgramAddressSync( + [ + payer.publicKey.toBytes(), + TOKEN_2022_PROGRAM_ID.toBytes(), + mint.toBytes(), + ], + ATA_PROGRAM_ID + ); + + const receiver = anchor.web3.Keypair.generate(); + + const [receiverATA] = anchor.web3.PublicKey.findProgramAddressSync( + [ + receiver.publicKey.toBytes(), + TOKEN_2022_PROGRAM_ID.toBytes(), + mint.toBytes(), + ], + ATA_PROGRAM_ID + ); + + it("Create Token-2022 Token", async () => { + await connection.requestAirdrop(receiver.publicKey, 1000000000); + await connection.requestAirdrop(payer.publicKey, 1000000000); + const tx = new anchor.web3.Transaction(); + + const ix = await program.methods + .createToken(tokenName) + .accounts({ + mint: mint, + signer: payer.publicKey, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .instruction(); + + tx.add(ix); + + const sig = await anchor.web3.sendAndConfirmTransaction( + program.provider.connection, + tx, + [payer] + ); + console.log("Your transaction signature", sig); + }); + + it("Initialize payer ATA", async () => { + const tx = new anchor.web3.Transaction(); + + const ix = await program.methods + .createAssociatedTokenAccount() + .accounts({ + tokenAccount: payerATA, + mint: mint, + signer: payer.publicKey, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .instruction(); + + tx.add(ix); + + const sig = await anchor.web3.sendAndConfirmTransaction( + program.provider.connection, + tx, + [payer] + ); + console.log("Your transaction signature", sig); + }); + + /* + // This instruction is included only as a reference, but is not required to run this test, because we are using "init" in the program's transfer instruction. The create_associated_token_account instruction on the program is provided as a reference as well. + it("Initialize receiver ATA", async () => { + const tx = new anchor.web3.Transaction(); + const ix = await program.methods + .createAssociatedTokenAccount() + .accounts({ + tokenAccount: receiverATA, + mint: mint, + signer: receiver.publicKey, + tokenProgram: TOKEN_2022_PROGRAM_ID, + associatedTokenProgram: ATA_PROGRAM_ID, + }) + .signers([receiver]) + .instruction(); + tx.add(ix); + const sig = await anchor.web3.sendAndConfirmTransaction( + program.provider.connection, + tx, + [receiver] + ); + console.log("Your transaction signature", sig); + }); +*/ + + it("Mint Token to payer", async () => { + const tx = new anchor.web3.Transaction(); + + const ix = await program.methods + .mintToken(new anchor.BN(200000000)) + .accounts({ + mint: mint, + signer: payer.publicKey, + receiver: payerATA, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .signers([payer]) + .instruction(); + + tx.add(ix); + + const sig = await anchor.web3.sendAndConfirmTransaction( + program.provider.connection, + tx, + [payer] + ); + console.log("Your transaction signature", sig); + }); + + // Using init in the transfer instruction, as init if needed is bot working with Token 2022 yet. + it("Transfer Token", async () => { + const tx = new anchor.web3.Transaction(); + + const ix = await program.methods + .transferToken(new anchor.BN(100)) + .accounts({ + mint: mint, + signer: payer.publicKey, + from: payerATA, + to: receiver.publicKey, + tokenProgram: TOKEN_2022_PROGRAM_ID, + associatedTokenProgram: ATA_PROGRAM_ID, + toAta: receiverATA, + }) + .instruction(); + + tx.add(ix); + + const sig = await anchor.web3.sendAndConfirmTransaction( + program.provider.connection, + tx, + [payer] + ); + console.log("Your transaction signature", sig); + }); +}); diff --git a/tokens/token-2022/anchor/tsconfig.json b/tokens/token-2022/anchor/tsconfig.json new file mode 100644 index 000000000..558b83e5e --- /dev/null +++ b/tokens/token-2022/anchor/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } + } + \ No newline at end of file