Skip to content

Instantly share code, notes, and snippets.

@amilz
Created January 2, 2024 18:42
Show Gist options
  • Save amilz/7d960c72ad7f87f047d499e150c8f316 to your computer and use it in GitHub Desktop.
Save amilz/7d960c72ad7f87f047d499e150c8f316 to your computer and use it in GitHub Desktop.
Solana Fungible Token (Umi)
// January 02, 2024
import { Transaction, SystemProgram, Keypair, Connection, PublicKey, TransactionInstruction, AccountMeta } from "@solana/web3.js";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import { CreateMetadataAccountV3InstructionAccounts, DataV2Args, createMetadataAccountV3 } from "@metaplex-foundation/mpl-token-metadata";
import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
import { Instruction, TransactionBuilder, createSignerFromKeypair } from "@metaplex-foundation/umi";
import { MINT_SIZE, TOKEN_PROGRAM_ID, createInitializeMintInstruction, getMinimumBalanceForRentExemptMint, getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createMintToInstruction } from '@solana/spl-token';
import secret from '../wallets/authority.json';
const endpoint = 'https://example.solana-devnet.quiknode.pro/12345/';
const MINT_CONFIG = {
numDecimals: 9,
numberTokens: 1000000000
}
//Reference: https://docs.metaplex.com/programs/token-metadata/token-standard#the-fungible-standard
//this will be uploaded to arweave
const MY_TOKEN_METADATA = {
name: "FakeBONK",
symbol: "xBONK",
description: "This is some Fake BONK. It's not real. Just meant for devnet testing.",
image: "https://arweave.net/hQiPZOsRZXGXBJd_82PhVdlM_hACsT_q6wqwf5cSY7I"
}
//this will be stored on chain
const ON_CHAIN_METADATA = {
name: MY_TOKEN_METADATA.name,
symbol: MY_TOKEN_METADATA.symbol,
uri: 'https://arweave.net/OJjRPNZeGChYZbKm3hKo-dfjZ5XJLeyDnCXp3JgYLds',
sellerFeeBasisPoints: 0,
creators: null,
collection: null,
uses: null
} as DataV2Args;
const createNewMintTransaction = async (endpoint: string, authority: Keypair, mintKeypair: Keypair, destinationWallet: PublicKey) => {
const connection = new Connection(endpoint);
const umi = createUmi(endpoint);
//Get the minimum lamport balance to create a new account and avoid rent payments
const requiredBalance = await getMinimumBalanceForRentExemptMint(connection);
//get associated token account of your wallet
const tokenATA = await getAssociatedTokenAddress(mintKeypair.publicKey, destinationWallet);
const metadataAccounts: CreateMetadataAccountV3InstructionAccounts = {
mint: fromWeb3JsPublicKey(mintKeypair.publicKey),
mintAuthority: createSignerFromKeypair(umi, fromWeb3JsKeypair(authority)),
payer: createSignerFromKeypair(umi, fromWeb3JsKeypair(authority)),
updateAuthority: createSignerFromKeypair(umi, fromWeb3JsKeypair(authority)),
}
let createMetadataArgs = {
...metadataAccounts,
data: ON_CHAIN_METADATA,
isMutable: false,
collectionDetails: null,
}
let metadataIxBuilder: TransactionBuilder = createMetadataAccountV3(
umi,
createMetadataArgs
);
const metadataUmiIx: Instruction = await metadataIxBuilder.getInstructions()[0];
const metadataAccountMetas: AccountMeta[] = metadataUmiIx.keys.map((key) => ({
pubkey: toWeb3JsPublicKey(key.pubkey),
isSigner: key.isSigner,
isWritable: key.isWritable,
}));
const metadataInstruction = new TransactionInstruction({
keys: metadataAccountMetas,
programId: toWeb3JsPublicKey(metadataUmiIx.programId),
data: Buffer.from(metadataUmiIx.data)
});
const createNewTokenTransaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: authority.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: MINT_SIZE,
lamports: requiredBalance,
programId: TOKEN_PROGRAM_ID,
}),
createInitializeMintInstruction(
mintKeypair.publicKey, //Mint Address
MINT_CONFIG.numDecimals, //Number of Decimals of New mint
authority.publicKey, //Mint Authority
authority.publicKey, //Freeze Authority
TOKEN_PROGRAM_ID),
createAssociatedTokenAccountInstruction(
authority.publicKey, //Payer
tokenATA, //Associated token account
authority.publicKey, //token owner
mintKeypair.publicKey, //Mint
),
createMintToInstruction(
mintKeypair.publicKey, //Mint
tokenATA, //Destination Token Account
authority.publicKey, //Authority
MINT_CONFIG.numberTokens * Math.pow(10, MINT_CONFIG.numDecimals),//number of tokens
),
metadataInstruction
);
return createNewTokenTransaction;
}
const main = async () => {
const userWallet = Keypair.fromSecretKey(new Uint8Array(secret));
console.log(`---STEP 1: Creating Mint Transaction---`);
//Create new Keypair for Mint address
let mintKeypair = Keypair.generate();//fromSecretKey(new Uint8Array(lfg));
console.log(`New Mint Address: `, mintKeypair.publicKey.toString());
const newMintTransaction: Transaction = await createNewMintTransaction(
endpoint,
userWallet,
mintKeypair,
userWallet.publicKey
);
console.log(`---STEP 2: Executing Mint Transaction---`);
const solanaConnection = new Connection(endpoint);
const transactionId = await solanaConnection.sendTransaction(newMintTransaction, [userWallet, mintKeypair]);
console.log(`Transaction ID: `, transactionId);
console.log(`Succesfully minted ${MINT_CONFIG.numberTokens} ${ON_CHAIN_METADATA.symbol} to ${userWallet.publicKey.toString()}.`);
console.log(`View Transaction: https://explorer.solana.com/tx/${transactionId}?cluster=devnet`);
console.log(`View Token Mint: https://explorer.solana.com/address/${mintKeypair.publicKey.toString()}?cluster=devnet`)
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment