Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tokens/pda-mint-authority/steel #303

Closed
14 changes: 14 additions & 0 deletions tokens/pda-mint-authority/steel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[workspace]
members = ["api","program"]
resolver = "2"

[workspace.dependencies]
solana-program = "=1.18.17"
steel = {version = "2.0", features = ["spl"]}
bytemuck = "1.4"
num_enum = "0.7"
borsh = "1.5.1"
spl-token = { version = "4.0.0", features = [ "no-entrypoint" ] }
spl-associated-token-account = { version = "3.0.4", features = [ "no-entrypoint" ] }
mpl-token-metadata = { version = "4.1.2" }
thiserror = "2.0.3"
15 changes: 15 additions & 0 deletions tokens/pda-mint-authority/steel/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "pda-mint-authority-steel-api"
version = "0.1.0"
edition = "2021"

[dependencies]
solana-program.workspace = true
steel.workspace = true
bytemuck.workspace = true
num_enum.workspace = true
borsh.workspace = true
spl-token.workspace = true
spl-associated-token-account.workspace = true
mpl-token-metadata.workspace = true
thiserror.workspace = true
10 changes: 10 additions & 0 deletions tokens/pda-mint-authority/steel/api/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use steel::*;

#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)]
#[repr(u32)]
pub enum SteelError {
#[error("Failed to parse string from bytes")]
ParseError = 0,
}

error!(SteelError);
119 changes: 119 additions & 0 deletions tokens/pda-mint-authority/steel/api/src/instruction/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use super::SteelInstruction;
use crate::{error::*, state::*};
use mpl_token_metadata::{instructions as mpl_instruction, types::DataV2};
use solana_program::{msg, program::invoke, program_pack::Pack, rent::Rent, system_instruction};
use spl_token::state::Mint;
use std::ffi::CStr;
use steel::*;

instruction!(SteelInstruction, CreateToken);
// CreateToken instruction
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct CreateToken {
pub token_name: [u8; 32], // Metaplex metadata name: 32 bytes max
pub token_symbol: [u8; 10], // Metaplex metadata symbol: 10 bytes max
pub token_uri: [u8; 256], // Metaplex metadata uri: 200 bytes max
}

impl CreateToken {
pub fn process(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
let args = Self::try_from_bytes(data)?;

let [mint_account, mint_authority, metadata_account, payer, rent, system_program, token_program, token_metadata_program] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};

let (mint_authority_key, bump) =
Pubkey::find_program_address(&[MintAuthorityPda::SEED_PREFIX], &crate::ID);

mint_authority.has_address(&mint_authority_key)?;

// First create the account for the Mint
//
msg!("Creating mint account...");
msg!("Mint: {}", mint_account.key);
invoke(
&system_instruction::create_account(
payer.key,
mint_account.key,
(Rent::get()?).minimum_balance(Mint::LEN),
Mint::LEN as u64,
token_program.key,
),
&[
mint_account.clone(),
payer.clone(),
system_program.clone(),
token_program.clone(),
],
)?;

// Now initialize that account as a Mint (standard Mint)
//
msg!("Initializing mint account...");
msg!("Mint: {}", mint_account.key);

initialize_mint(
mint_account,
mint_authority,
Some(mint_authority),
token_program,
rent,
0, // 0 Decimals for the NFT standard
)?;

// Now create the account for that Mint's metadata
//
msg!("Creating metadata account...");
msg!("Metadata account address: {}", metadata_account.key);

let name = Self::str_from_bytes(&mut args.token_name.to_vec())?.to_string();
let symbol = Self::str_from_bytes(&mut args.token_symbol.to_vec())?.to_string();
let uri = Self::str_from_bytes(&mut args.token_uri.to_vec())?.to_string();

mpl_instruction::CreateMetadataAccountV3Cpi {
__program: token_metadata_program,
metadata: metadata_account,
mint: mint_account,
mint_authority,
payer,
update_authority: (mint_authority, true),
system_program,
rent: Some(rent),
__args: mpl_instruction::CreateMetadataAccountV3InstructionArgs {
data: DataV2 {
name,
symbol,
uri,
seller_fee_basis_points: 0,
creators: None,
collection: None,
uses: None,
},
is_mutable: true,
collection_details: None,
},
}
.invoke_signed(&[&[MintAuthorityPda::SEED_PREFIX, &[bump]]])?;

msg!("Token mint created successfully.");

Ok(())
}

fn str_from_bytes(bytes: &mut Vec<u8>) -> Result<&str, ProgramError> {
// add an extra null byte, in the case every position is occupied with a non-null byte
bytes.push(0);

// remove excess null bytes
if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
if let Ok(str) = cstr.to_str() {
return Ok(str);
}
}
Err(SteelError::ParseError.into())
}
}
41 changes: 41 additions & 0 deletions tokens/pda-mint-authority/steel/api/src/instruction/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use super::SteelInstruction;
use crate::state::MintAuthorityPda;
use solana_program::msg;
use steel::*;

instruction!(SteelInstruction, Init);
/// Init Instruction
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Init {}

impl Init {
pub fn process(accounts: &[AccountInfo<'_>]) -> ProgramResult {
let [mint_authority, payer, system_program] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};

let (mint_authority_key, bump) =
Pubkey::find_program_address(&[MintAuthorityPda::SEED_PREFIX], &crate::ID);

mint_authority.has_address(&mint_authority_key)?;

msg!("Creating mint authority PDA...");
msg!("Mint Authority: {}", &mint_authority.key);
create_account_with_bump::<MintAuthorityPda>(
mint_authority,
system_program,
payer,
&crate::ID,
&[MintAuthorityPda::SEED_PREFIX],
bump,
)?;

let mint_authority_account =
mint_authority.as_account_mut::<MintAuthorityPda>(&crate::ID)?;

mint_authority_account.bump = bump;

Ok(())
}
}
79 changes: 79 additions & 0 deletions tokens/pda-mint-authority/steel/api/src/instruction/mint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::state::MintAuthorityPda;
use mpl_token_metadata::instructions::{self as mpl_instruction};
use solana_program::msg;
use steel::*;

use super::SteelInstruction;

instruction!(SteelInstruction, MintTo);
// MintTo instruction
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct MintTo {}

impl MintTo {
pub fn process(accounts: &[AccountInfo<'_>]) -> ProgramResult {
let [mint_account, metadata_account, edition_account, mint_authority, associated_token_account, payer, _rent, system_program, token_program, associated_token_program, token_metadata_program] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};

mint_authority.has_seeds(&[MintAuthorityPda::SEED_PREFIX], &crate::ID)?;

let bump = mint_authority
.as_account::<MintAuthorityPda>(&crate::ID)?
.bump;

// First create the token account for the user
//
if associated_token_account.lamports() == 0 {
msg!("Creating associated token account...");
create_associated_token_account(
payer,
payer,
associated_token_account,
mint_account,
system_program,
token_program,
associated_token_program,
)?;
} else {
msg!("Associated token account exists.");
}
msg!("Associated Token Address: {}", associated_token_account.key);

msg!("Minting NFT to associated token account...");
mint_to_signed(
mint_account,
associated_token_account,
mint_authority,
token_program,
1,
&[MintAuthorityPda::SEED_PREFIX],
)?;

// We can make this a Limited Edition NFT through Metaplex,
// which will disable minting by setting the Mint & Freeze Authorities to the
// Edition Account.
//
mpl_instruction::CreateMasterEditionV3Cpi {
__program: token_metadata_program,
__args: mpl_instruction::CreateMasterEditionV3InstructionArgs { max_supply: None },
edition: edition_account,
metadata: metadata_account,
mint: mint_account,
mint_authority,
payer,
rent: None,
system_program,
token_program,
update_authority: mint_authority,
}
.invoke_signed(&[&[MintAuthorityPda::SEED_PREFIX, &[bump]]])?;

msg!("NFT minted successfully.");

Ok(())
}
}
17 changes: 17 additions & 0 deletions tokens/pda-mint-authority/steel/api/src/instruction/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pub mod create;
pub mod init;
pub mod mint;

pub use create::*;
pub use init::*;
pub use mint::*;

use steel::*;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
pub enum SteelInstruction {
Init = 0,
CreateToken = 1,
MintTo = 2,
}
12 changes: 12 additions & 0 deletions tokens/pda-mint-authority/steel/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pub mod error;
pub mod instruction;
pub mod state;

pub mod prelude {
pub use crate::instruction::*;
pub use crate::state::*;
}

use steel::*;

declare_id!("z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35");
19 changes: 19 additions & 0 deletions tokens/pda-mint-authority/steel/api/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use steel::*;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
pub enum SteelAccount {
MintAuthorityPda = 0,
}

account!(SteelAccount, MintAuthorityPda);
/// Mint Authority PDA
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct MintAuthorityPda {
pub bump: u8,
}

impl MintAuthorityPda {
pub const SEED_PREFIX: &'static [u8] = b"mint_authority";
}
8 changes: 8 additions & 0 deletions tokens/pda-mint-authority/steel/cicd.sh
Original file line number Diff line number Diff line change
@@ -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-sbf --manifest-path=./program/Cargo.toml
solana program deploy ./program/target/deploy/program.so
29 changes: 29 additions & 0 deletions tokens/pda-mint-authority/steel/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"scripts": {
"test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts",
"build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test",
"build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so",
"deploy": "solana program deploy ./program/target/so/hello_solana_program.so",
"postinstall": "zx prepare.mjs"
},
"dependencies": {
"@metaplex-foundation/mpl-token-metadata": "^2.5.2",
"@solana/spl-token": "^0.4.9",
"@solana/web3.js": "^1.95.4",
"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",
"borsh": "0.7.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"solana-bankrun": "^0.4.0",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5",
"zx": "^8.1.4"
}
}
Loading