Created
January 2, 2024 18:42
-
-
Save amilz/7d960c72ad7f87f047d499e150c8f316 to your computer and use it in GitHub Desktop.
Solana Fungible Token (Umi)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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