token metadata

This commit is contained in:
Valentyn Faychuk 2025-01-10 19:16:17 +02:00
parent bfb4a03b88
commit 85e0215869
Signed by: valy
GPG Key ID: F1AB995E20FEADC5
3 changed files with 142 additions and 41 deletions

49
Cargo.lock generated

@ -2104,6 +2104,7 @@ dependencies = [
"hyper 1.4.1", "hyper 1.4.1",
"hyper-rustls 0.27.3", "hyper-rustls 0.27.3",
"hyper-util", "hyper-util",
"mpl-token-metadata",
"prost", "prost",
"prost-types", "prost-types",
"public-ip", "public-ip",
@ -2879,6 +2880,19 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1"
[[package]]
name = "mpl-token-metadata"
version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6a3000e761d3b2d685662a3a9ee99826f9369fb033bd1bc7011b1cf02ed9"
dependencies = [
"borsh 0.10.4",
"num-derive 0.3.3",
"num-traits",
"solana-program",
"thiserror",
]
[[package]] [[package]]
name = "multimap" name = "multimap"
version = "0.10.0" version = "0.10.0"
@ -2968,6 +2982,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-derive"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.4.2" version = "0.4.2"
@ -4471,7 +4496,7 @@ dependencies = [
"log", "log",
"memoffset", "memoffset",
"num-bigint 0.4.6", "num-bigint 0.4.6",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"parking_lot", "parking_lot",
"rand 0.8.5", "rand 0.8.5",
@ -4500,7 +4525,7 @@ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"libc", "libc",
"log", "log",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"percentage", "percentage",
"rand 0.8.5", "rand 0.8.5",
@ -4586,7 +4611,7 @@ dependencies = [
"console", "console",
"dialoguer", "dialoguer",
"log", "log",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"parking_lot", "parking_lot",
"qstring", "qstring",
@ -4906,7 +4931,7 @@ checksum = "cfd8e539a9963c2914ff8426dfe92351a902892aea465cd507e36d638ca0b7d6"
dependencies = [ dependencies = [
"bincode", "bincode",
"log", "log",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"rustc_version", "rustc_version",
"serde", "serde",
@ -4933,7 +4958,7 @@ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"lazy_static", "lazy_static",
"merlin", "merlin",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
@ -4963,7 +4988,7 @@ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"lazy_static", "lazy_static",
"merlin", "merlin",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
@ -5017,7 +5042,7 @@ checksum = "68034596cf4804880d265f834af1ff2f821ad5293e41fa0f8f59086c181fc38e"
dependencies = [ dependencies = [
"assert_matches", "assert_matches",
"borsh 1.5.1", "borsh 1.5.1",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"solana-program", "solana-program",
"spl-token", "spl-token",
@ -5032,7 +5057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714b53f7312c2802c62f14bc8a07916c2c872761e3d6be97e99fd432be7799ca" checksum = "714b53f7312c2802c62f14bc8a07916c2c872761e3d6be97e99fd432be7799ca"
dependencies = [ dependencies = [
"borsh 1.5.1", "borsh 1.5.1",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"solana-program", "solana-program",
"spl-associated-token-account-client", "spl-associated-token-account-client",
@ -5128,7 +5153,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7b28bed65356558133751cc32b48a7a5ddfc59ac4e941314630bbed1ac10532" checksum = "d7b28bed65356558133751cc32b48a7a5ddfc59ac4e941314630bbed1ac10532"
dependencies = [ dependencies = [
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"solana-program", "solana-program",
"spl-program-error-derive", "spl-program-error-derive",
@ -5183,7 +5208,7 @@ checksum = "70a0f06ac7f23dc0984931b1fe309468f14ea58e32660439c1cef19456f5d0e3"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"bytemuck", "bytemuck",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"num_enum", "num_enum",
"solana-program", "solana-program",
@ -5198,7 +5223,7 @@ checksum = "d9c10f3483e48679619c76598d4e4aebb955bc49b0a5cc63323afbf44135c9bf"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"bytemuck", "bytemuck",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"num_enum", "num_enum",
"solana-program", "solana-program",
@ -5222,7 +5247,7 @@ checksum = "c0b788a8c34a917b68b4ed2cdec255d03cc09ccba21545dac39c08a97fce640f"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"bytemuck", "bytemuck",
"num-derive", "num-derive 0.4.2",
"num-traits", "num-traits",
"num_enum", "num_enum",
"solana-program", "solana-program",

@ -22,6 +22,7 @@ solana-program = "2.0"
solana-sdk = "2.0" solana-sdk = "2.0"
spl-associated-token-account = "5.0" spl-associated-token-account = "5.0"
spl-token = "6.0" spl-token = "6.0"
mpl-token-metadata = "5.0"
tokio = { version = "1.40", features = ["macros", "rt-multi-thread", "fs"] } # this can be "full" tokio = { version = "1.40", features = ["macros", "rt-multi-thread", "fs"] } # this can be "full"
tokio-stream = { version = "0.1", features = ["sync"] } tokio-stream = { version = "0.1", features = ["sync"] }
# sgx poc dependencies # sgx poc dependencies

@ -1,5 +1,10 @@
#![allow(dead_code)] #![allow(dead_code)]
use crate::{grpc::challenge::Keys, persistence::KeysFile}; use crate::{grpc::challenge::Keys, persistence::KeysFile};
use mpl_token_metadata::{
instructions::{CreateMetadataAccountV3, CreateMetadataAccountV3InstructionArgs},
types::DataV2,
ID as mpl_token_metadata_id,
};
use solana_client::nonblocking::rpc_client::RpcClient; use solana_client::nonblocking::rpc_client::RpcClient;
use solana_program::program_pack::Pack; use solana_program::program_pack::Pack;
use solana_sdk::{ use solana_sdk::{
@ -127,40 +132,48 @@ impl TryFrom<KeysFile> for SolClient {
} }
} }
async fn wait_for_enough_sol(client: &RpcClient, pubkey: &Pubkey) { async fn create_token(client: &RpcClient, payer_keypair: &Keypair) -> Pubkey {
println!("Waiting for at least 0.01 SOL in address {pubkey}"); // #1 top up the payer
loop { println!("Waiting for at least 0.01 SOL in address {}", payer_keypair.pubkey());
match client.get_balance(pubkey).await { while !has_enough_lamports(client, &payer_keypair.pubkey(), 10_000_000).await {
Ok(balance) => {
println!("Got {balance} lamports.");
if balance > 10_000_000 {
return;
}
}
Err(e) => {
println!("Could not get balance: {e:?}");
}
}
sleep(Duration::from_secs(30)).await; sleep(Duration::from_secs(30)).await;
} }
// #2 create token mint
println!("Creating Token Mint");
let mut mint_pubkey = None;
while mint_pubkey.is_none() {
sleep(Duration::from_secs(30)).await;
mint_pubkey = create_mint(client, payer_keypair).await;
}
let mint_pubkey = mint_pubkey.unwrap();
println!("Token Mint created: {mint_pubkey}");
// #3 create token meta
println!("Creating Token Metadata");
while !create_meta(client, payer_keypair, &mint_pubkey).await {
sleep(Duration::from_secs(30)).await;
}
println!("Token Metadata created");
mint_pubkey
} }
async fn create_token(client: &RpcClient, keypair: &Keypair) -> Pubkey { async fn has_enough_lamports(client: &RpcClient, pubkey: &Pubkey, threshold: u64) -> bool {
loop { match client.get_balance(pubkey).await {
wait_for_enough_sol(client, &keypair.pubkey()).await; Ok(balance) => {
match create_token_int(client, keypair).await { println!("{pubkey} needs {threshold} and has {balance}");
None => sleep(Duration::from_secs(30)).await, balance >= threshold
Some(pubkey) => { }
println!("Mint created: {pubkey}"); Err(e) => {
return pubkey; println!("Could not get balance: {e:?}");
} false
} }
} }
} }
async fn create_token_int(client: &RpcClient, keypair: &Keypair) -> Option<Pubkey> { async fn create_mint(client: &RpcClient, payer_keypair: &Keypair) -> Option<Pubkey> {
let mint_keypair = Keypair::new(); let mint_keypair = Keypair::new();
let payer = Keypair::from_base58_string(&keypair.to_base58_string());
let mint_rent = client let mint_rent = client
.get_minimum_balance_for_rent_exemption(Mint::LEN) .get_minimum_balance_for_rent_exemption(Mint::LEN)
.await .await
@ -168,7 +181,7 @@ async fn create_token_int(client: &RpcClient, keypair: &Keypair) -> Option<Pubke
.ok()?; .ok()?;
let create_mint_account_ix = system_instruction::create_account( let create_mint_account_ix = system_instruction::create_account(
&payer.pubkey(), &payer_keypair.pubkey(),
&mint_keypair.pubkey(), &mint_keypair.pubkey(),
mint_rent, mint_rent,
Mint::LEN as u64, Mint::LEN as u64,
@ -176,7 +189,7 @@ async fn create_token_int(client: &RpcClient, keypair: &Keypair) -> Option<Pubke
); );
let init_mint_ix = let init_mint_ix =
initialize_mint(&spl_token::id(), &mint_keypair.pubkey(), &payer.pubkey(), None, 9) initialize_mint(&spl_token::id(), &mint_keypair.pubkey(), &payer_keypair.pubkey(), None, 9)
.map_err(|e| println!("Can't initialize mint: {e}")) .map_err(|e| println!("Can't initialize mint: {e}"))
.ok()?; .ok()?;
@ -185,10 +198,11 @@ async fn create_token_int(client: &RpcClient, keypair: &Keypair) -> Option<Pubke
.await .await
.map_err(|e| println!("Can't get latest blockhash: {e}")) .map_err(|e| println!("Can't get latest blockhash: {e}"))
.ok()?; .ok()?;
let tx = Transaction::new_signed_with_payer( let tx = Transaction::new_signed_with_payer(
&[create_mint_account_ix, init_mint_ix], &[create_mint_account_ix, init_mint_ix],
Some(&payer.pubkey()), Some(&payer_keypair.pubkey()),
&[&payer, &mint_keypair], &[&payer_keypair, &mint_keypair],
recent_blockhash, recent_blockhash,
); );
@ -202,3 +216,64 @@ async fn create_token_int(client: &RpcClient, keypair: &Keypair) -> Option<Pubke
}) })
.ok() .ok()
} }
async fn create_meta(client: &RpcClient, payer_keypair: &Keypair, mint_pubkey: &Pubkey) -> bool {
create_meta_int(client, payer_keypair, mint_pubkey).await.is_some()
}
async fn create_meta_int(
client: &RpcClient,
payer_keypair: &Keypair,
mint_pubkey: &Pubkey,
) -> Option<()> {
let metadata_seeds = &[b"metadata", mpl_token_metadata_id.as_ref(), mint_pubkey.as_ref()];
let (metadata_pda, _bump) =
Pubkey::find_program_address(metadata_seeds, &mpl_token_metadata_id);
let data_v2 = DataV2 {
name: "DeTEE Hacker Challenge Token".to_string(),
symbol: "DTHC".to_string(),
uri: "https://detee.ltd/dthc/meta.json".to_string(),
seller_fee_basis_points: 0, // usually for NFTs, can be 0 for fungible
creators: None, // or Some(vec![Creator { ... }]) if you want to specify creators
collection: None,
uses: None,
};
let create_metadata_account_ix = CreateMetadataAccountV3 {
metadata: metadata_pda,
mint: *mint_pubkey,
mint_authority: payer_keypair.pubkey(),
payer: payer_keypair.pubkey(),
update_authority: (payer_keypair.pubkey(), true),
system_program: solana_program::system_program::id(),
rent: None,
}
.instruction(CreateMetadataAccountV3InstructionArgs {
data: data_v2,
is_mutable: true,
collection_details: None,
});
let recent_blockhash = client
.get_latest_blockhash()
.await
.map_err(|e| println!("Can't get latest blockhash: {e}"))
.ok()?;
let tx = Transaction::new_signed_with_payer(
&[create_metadata_account_ix],
Some(&payer_keypair.pubkey()),
&[&payer_keypair],
recent_blockhash,
);
client
.send_and_confirm_transaction(&tx)
.await
.map_err(|e| println!("Can't execute transaction: {e}"))
.map(|s| {
println!("Transaction signature: {}", s);
})
.ok()
}