Skip to content

Commit

Permalink
add create token steel example
Browse files Browse the repository at this point in the history
  • Loading branch information
Perelyn-sama committed Oct 21, 2024
1 parent dfaa6d9 commit d24b5b0
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 0 deletions.
2 changes: 2 additions & 0 deletions tokens/create-token/steel/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
test-ledger
23 changes: 23 additions & 0 deletions tokens/create-token/steel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[workspace]
resolver = "2"
members = ["api", "program"]

[workspace.package]
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
homepage = ""
documentation = ""
respository = ""
readme = "./README.md"
keywords = ["solana"]

[workspace.dependencies]
steel-api = { path = "./api", version = "0.1.0" }
bytemuck = "1.14"
num_enum = "0.7"
solana-program = "1.18"
steel = { version = "1.3", features = ["spl"] }
thiserror = "1.0"
spl-token = "^4"
mpl-token-metadata = { version = "4.1.2" }
22 changes: 22 additions & 0 deletions tokens/create-token/steel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Steel

**Steel** is a ...

## API
- [`Consts`](api/src/consts.rs) – Program constants.
- [`Error`](api/src/error.rs) – Custom program errors.
- [`Event`](api/src/event.rs) – Custom program events.
- [`Instruction`](api/src/instruction.rs) – Declared instructions.

## Instructions
- [`Hello`](program/src/hello.rs) – Hello ...

## State
- [`User`](api/src/state/user.rs) – User ...

## Tests

To run the test suit, use the Solana toolchain:
```
cargo test-sbf
```
13 changes: 13 additions & 0 deletions tokens/create-token/steel/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "steel-api"
version = "0.1.0"
edition = "2021"

[dependencies]
bytemuck.workspace = true
num_enum.workspace = true
solana-program.workspace = true
steel.workspace = true
thiserror.workspace = true
spl-token.workspace = true
mpl-token-metadata.workspace = true
10 changes: 10 additions & 0 deletions tokens/create-token/steel/api/src/consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/// The seed of the mint account PDA.
pub const MINT: &[u8] = b"mint";

/// Noise for deriving the mint pda
pub const MINT_NOISE: [u8; 16] = [
89, 157, 88, 232, 243, 249, 197, 132, 199, 49, 19, 234, 91, 94, 150, 41,
];

/// The seed of the metadata account PDA.
pub const METADATA: &[u8] = b"metadata";
10 changes: 10 additions & 0 deletions tokens/create-token/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("This is a dummy error")]
Dummy = 0,
}

error!(SteelError);
18 changes: 18 additions & 0 deletions tokens/create-token/steel/api/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::str;
use steel::*;

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

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Create {
pub token_name: [u8; 32],
pub token_symbol: [u8; 8],
pub token_uri: [u8; 64],
}

instruction!(SteelInstruction, Create);
16 changes: 16 additions & 0 deletions tokens/create-token/steel/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pub mod consts;
pub mod error;
pub mod instruction;
pub mod sdk;

pub mod prelude {
pub use crate::consts::*;
pub use crate::error::*;
pub use crate::instruction::*;
pub use crate::sdk::*;
}

use steel::*;

// TODO Set program id
declare_id!("z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35");
54 changes: 54 additions & 0 deletions tokens/create-token/steel/api/src/sdk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use steel::*;

use crate::prelude::*;

pub fn create(
payer: Pubkey,
mint_authority: Pubkey,
token_name: String,
token_symbol: String,
token_uri: String,
) -> Instruction {
let token_name_bytes: [u8; 32] = token_name
.as_bytes()
.try_into()
.expect("String wrong length, expected 32 bytes");
let token_symbol_bytes: [u8; 8] = token_symbol
.as_bytes()
.try_into()
.expect("String wrong length, expected 32 bytes");
let token_uri_bytes: [u8; 64] = token_uri
.as_bytes()
.try_into()
.expect("String wrong length, expected 32 bytes");

let mint_pda = Pubkey::find_program_address(&[MINT, MINT_NOISE.as_slice()], &crate::ID);
let metadata_pda = Pubkey::find_program_address(
&[
METADATA,
mpl_token_metadata::ID.as_ref(),
mint_pda.0.as_ref(),
],
&mpl_token_metadata::ID,
);

Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(mint_pda.0, false),
AccountMeta::new(mint_authority, false),
AccountMeta::new(metadata_pda.0, false),
AccountMeta::new_readonly(system_program::ID, false),
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(mpl_token_metadata::ID, false),
AccountMeta::new_readonly(sysvar::rent::ID, false),
],
data: Create {
token_name: token_name_bytes,
token_symbol: token_symbol_bytes,
token_uri: token_uri_bytes,
}
.to_bytes(),
}
}
21 changes: 21 additions & 0 deletions tokens/create-token/steel/program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "steel-program"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
steel-api.workspace = true
solana-program.workspace = true
steel.workspace = true
spl-token.workspace = true
mpl-token-metadata.workspace = true

[dev-dependencies]
bs64 = "0.1.2"
rand = "0.8.5"
solana-program-test = "1.18"
solana-sdk = "1.18"
tokio = { version = "1.35", features = ["full"] }
96 changes: 96 additions & 0 deletions tokens/create-token/steel/program/src/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use solana_program::msg;
use solana_program::program_pack::Pack;
use spl_token::state::Mint;
use steel::*;
use steel_api::prelude::*;

pub fn process_create(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// parse args.
let args = Create::try_from_bytes(data)?;
let token_name = String::from_utf8(args.token_name.to_vec()).expect("Invalid UTF-8");
let token_symbol = String::from_utf8(args.token_symbol.to_vec()).expect("Invalid UTF-8");
let token_uri = String::from_utf8(args.token_uri.to_vec()).expect("Invalid UTF-8");

// Load accounts.
let [payer_info, mint_info, mint_authority_info, metadata_info, token_program, system_program, rent_sysvar, token_metadata_program] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};

// validation
payer_info.is_signer()?;
mint_info.to_mint()?;
token_program.is_program(&spl_token::ID)?;
rent_sysvar.is_sysvar(&sysvar::rent::ID)?;
system_program.is_program(&system_program::ID)?;
token_program.is_program(&spl_token::ID)?;
token_metadata_program.is_program(&mpl_token_metadata::ID)?;
rent_sysvar.is_sysvar(&sysvar::rent::ID)?;

// First create the account for the Mint
//
msg!("Creating mint account...");
msg!("Mint: {}", mint_info.key);
allocate_account(
mint_info,
&spl_token::ID,
Mint::LEN,
&[MINT, MINT_NOISE.as_slice()],
system_program,
payer_info,
)?;

// Now initialize that account as a Mint (standard Mint)
//
msg!("Initializing mint account...");
msg!("Mint: {}", mint_info.key);
solana_program::program::invoke(
&spl_token::instruction::initialize_mint(
&spl_token::ID,
mint_info.key,
mint_authority_info.key,
Some(mint_authority_info.key),
9, // 9 Decimals for the default SPL Token standard,
)?,
&[
token_program.clone(),
mint_info.clone(),
mint_authority_info.clone(),
rent_sysvar.clone(),
],
)?;

// Now create the account for that Mint's metadata
//
msg!("Creating metadata account...");
msg!("Metadata account address: {}", metadata_info.key);
mpl_token_metadata::instructions::CreateMetadataAccountV3Cpi {
__program: token_metadata_program,
metadata: metadata_info,
mint: mint_info,
mint_authority: mint_authority_info,
payer: payer_info,
update_authority: (payer_info, true),
system_program,
rent: Some(rent_sysvar),
__args: mpl_token_metadata::instructions::CreateMetadataAccountV3InstructionArgs {
data: mpl_token_metadata::types::DataV2 {
name: token_name,
symbol: token_symbol,
uri: token_uri,
seller_fee_basis_points: 0,
creators: None,
collection: None,
uses: None,
},
is_mutable: true,
collection_details: None,
},
}
.invoke()?;

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

Ok(())
}
22 changes: 22 additions & 0 deletions tokens/create-token/steel/program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
mod create;

use create::*;

use steel::*;
use steel_api::prelude::*;

pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let (ix, data) = parse_instruction(&steel_api::ID, program_id, data)?;

match ix {
SteelInstruction::Create => process_create(accounts, data)?,
}

Ok(())
}

entrypoint!(process_instruction);
46 changes: 46 additions & 0 deletions tokens/create-token/steel/program/tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use steel_api::prelude::*;
use solana_program::hash::Hash;
use solana_program_test::{processor, BanksClient, ProgramTest};
use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction};
use steel::*;

async fn setup() -> (BanksClient, Keypair, Hash) {
let mut program_test = ProgramTest::new(
"steel",
steel_api::ID,
processor!(steel_program::process_instruction),
);
program_test.prefer_bpf(true);
program_test.start().await
}

#[tokio::test]
async fn run_test() {
// Setup test
let (mut banks, payer, blockhash) = setup().await;

// Submit initialize transaction.
let ix = initialize(payer.pubkey());
let tx = Transaction::new_signed_with_payer(&[ix], Some(&payer.pubkey()), &[&payer], blockhash);
let res = banks.process_transaction(tx).await;
assert!(res.is_ok());

// Verify counter was initialized.
let counter_address = counter_pda().0;
let counter_account = banks.get_account(counter_address).await.unwrap().unwrap();
let counter = Counter::try_from_bytes(&counter_account.data).unwrap();
assert_eq!(counter_account.owner, steel_api::ID);
assert_eq!(counter.value, 0);

// Submit add transaction.
let ix = add(payer.pubkey(), 42);
let tx = Transaction::new_signed_with_payer(&[ix], Some(&payer.pubkey()), &[&payer], blockhash);
let res = banks.process_transaction(tx).await;
assert!(res.is_ok());

// Verify counter was incremented.
let counter_account = banks.get_account(counter_address).await.unwrap().unwrap();
let counter = Counter::try_from_bytes(&counter_account.data).unwrap();
assert_eq!(counter.value, 42);
}

0 comments on commit d24b5b0

Please sign in to comment.