import { Buffer } from 'node:buffer'; import { PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID } from '@metaplex-foundation/mpl-token-metadata'; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddress } from '@solana/spl-token'; import { Connection, Keypair, PublicKey, SYSVAR_RENT_PUBKEY, SystemProgram, Transaction, TransactionInstruction, sendAndConfirmTransaction, } from '@solana/web3.js'; import { BN } from 'bn.js'; import { CreateTokenArgs, MintNftArgs, MintSplArgs, MyInstruction, TransferTokensArgs } from './instructions'; function createKeypairFromFile(path: string): Keypair { return Keypair.fromSecretKey(Buffer.from(JSON.parse(require('node:fs').readFileSync(path, 'utf-8')))); } describe('Transferring Tokens', async () => { // const connection = new Connection(`http://localhost:8899`, 'confirmed'); const connection = new Connection('https://api.devnet.solana.com/', 'confirmed'); const payer = createKeypairFromFile(`${require('node:os').homedir()}/.config/solana/id.json`); const program = createKeypairFromFile('./program/target/deploy/program-keypair.json'); const tokenMintKeypair: Keypair = Keypair.generate(); const nftMintKeypair: Keypair = Keypair.generate(); const recipientWallet = Keypair.generate(); it('Create an SPL Token!', async () => { const metadataAddress = PublicKey.findProgramAddressSync( [Buffer.from('metadata'), TOKEN_METADATA_PROGRAM_ID.toBuffer(), tokenMintKeypair.publicKey.toBuffer()], TOKEN_METADATA_PROGRAM_ID, )[0]; const instructionData = new CreateTokenArgs({ instruction: MyInstruction.Create, token_title: 'Solana Gold', token_symbol: 'GOLDSOL', token_uri: 'https://raw.githubusercontent.com/solana-developers/program-examples/new-examples/tokens/tokens/.assets/spl-token.json', decimals: 9, }); const ix = new TransactionInstruction({ keys: [ { pubkey: tokenMintKeypair.publicKey, isSigner: true, isWritable: true, }, // Mint account { pubkey: payer.publicKey, isSigner: false, isWritable: true }, // Mint authority account { pubkey: metadataAddress, isSigner: false, isWritable: true }, // Metadata account { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Payer { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false }, // Rent account { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // System program { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // Token program { pubkey: TOKEN_METADATA_PROGRAM_ID, isSigner: false, isWritable: false, }, // Token metadata program ], programId: program.publicKey, data: instructionData.toBuffer(), }); const sx = await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer, tokenMintKeypair]); console.log('Success!'); console.log(` Mint Address: ${tokenMintKeypair.publicKey}`); console.log(` Tx Signature: ${sx}`); }); it('Create an NFT!', async () => { const metadataAddress = PublicKey.findProgramAddressSync( [Buffer.from('metadata'), TOKEN_METADATA_PROGRAM_ID.toBuffer(), nftMintKeypair.publicKey.toBuffer()], TOKEN_METADATA_PROGRAM_ID, )[0]; const instructionData = new CreateTokenArgs({ instruction: MyInstruction.Create, token_title: 'Homer NFT', token_symbol: 'HOMR', token_uri: 'https://raw.githubusercontent.com/solana-developers/program-examples/new-examples/tokens/tokens/.assets/nft.json', decimals: 0, }); const ix = new TransactionInstruction({ keys: [ { pubkey: nftMintKeypair.publicKey, isSigner: true, isWritable: true }, // Mint account { pubkey: payer.publicKey, isSigner: false, isWritable: true }, // Mint authority account { pubkey: metadataAddress, isSigner: false, isWritable: true }, // Metadata account { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Payer { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false }, // Rent account { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // System program { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // Token program { pubkey: TOKEN_METADATA_PROGRAM_ID, isSigner: false, isWritable: false, }, // Token metadata program ], programId: program.publicKey, data: instructionData.toBuffer(), }); const sx = await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer, nftMintKeypair]); console.log('Success!'); console.log(` Mint Address: ${nftMintKeypair.publicKey}`); console.log(` Tx Signature: ${sx}`); }); it('Mint some tokens to your wallet!', async () => { const associatedTokenAccountAddress = await getAssociatedTokenAddress(tokenMintKeypair.publicKey, payer.publicKey); const instructionData = new MintSplArgs({ instruction: MyInstruction.MintSpl, quantity: new BN(150), }); const ix = new TransactionInstruction({ keys: [ { pubkey: tokenMintKeypair.publicKey, isSigner: false, isWritable: true, }, // Mint account { pubkey: payer.publicKey, isSigner: false, isWritable: true }, // Mint authority account { pubkey: associatedTokenAccountAddress, isSigner: false, isWritable: true, }, // ATA { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Payer { pubkey: SystemProgram.programId, isSigner: false, isWritable: true }, // System program { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // Token program { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false, }, // Token metadata program ], programId: program.publicKey, data: instructionData.toBuffer(), }); const sx = await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer]); console.log('Success!'); console.log(` ATA Address: ${associatedTokenAccountAddress}`); console.log(` Tx Signature: ${sx}`); }); it('Mint the NFT to your wallet!', async () => { const metadataAddress = PublicKey.findProgramAddressSync( [Buffer.from('metadata'), TOKEN_METADATA_PROGRAM_ID.toBuffer(), nftMintKeypair.publicKey.toBuffer()], TOKEN_METADATA_PROGRAM_ID, )[0]; const editionAddress = PublicKey.findProgramAddressSync( [Buffer.from('metadata'), TOKEN_METADATA_PROGRAM_ID.toBuffer(), nftMintKeypair.publicKey.toBuffer(), Buffer.from('edition')], TOKEN_METADATA_PROGRAM_ID, )[0]; const associatedTokenAccountAddress = await getAssociatedTokenAddress(nftMintKeypair.publicKey, payer.publicKey); const instructionData = new MintNftArgs({ instruction: MyInstruction.MintNft, }); const ix = new TransactionInstruction({ keys: [ { pubkey: nftMintKeypair.publicKey, isSigner: false, isWritable: true }, // Mint account { pubkey: metadataAddress, isSigner: false, isWritable: true }, // Metadata account { pubkey: editionAddress, isSigner: false, isWritable: true }, // Edition account { pubkey: payer.publicKey, isSigner: false, isWritable: true }, // Mint authority account { pubkey: associatedTokenAccountAddress, isSigner: false, isWritable: true, }, // ATA { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Payer { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false }, // Rent account { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // System program { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // Token program { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false, }, // Associated token program { pubkey: TOKEN_METADATA_PROGRAM_ID, isSigner: false, isWritable: false, }, // Token metadata program ], programId: program.publicKey, data: instructionData.toBuffer(), }); const sx = await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer]); console.log('Success!'); console.log(` ATA Address: ${associatedTokenAccountAddress}`); console.log(` Tx Signature: ${sx}`); }); it('Prep a new test wallet for transfers', async () => { await connection.confirmTransaction( await connection.requestAirdrop(recipientWallet.publicKey, await connection.getMinimumBalanceForRentExemption(0)), ); console.log(`Recipient Pubkey: ${recipientWallet.publicKey}`); }); it('Transfer tokens to another wallet!', async () => { const fromAssociatedTokenAddress = await getAssociatedTokenAddress(tokenMintKeypair.publicKey, payer.publicKey); console.log(`Owner Token Address: ${fromAssociatedTokenAddress}`); const toAssociatedTokenAddress = await getAssociatedTokenAddress(tokenMintKeypair.publicKey, recipientWallet.publicKey); console.log(`Recipient Token Address: ${toAssociatedTokenAddress}`); const transferToInstructionData = new TransferTokensArgs({ instruction: MyInstruction.TransferTokens, quantity: new BN(15), }); const ix = new TransactionInstruction({ keys: [ { pubkey: tokenMintKeypair.publicKey, isSigner: false, isWritable: true, }, // Mint account { pubkey: fromAssociatedTokenAddress, isSigner: false, isWritable: true, }, // Owner Token account { pubkey: toAssociatedTokenAddress, isSigner: false, isWritable: true }, // Recipient Token account { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Owner { pubkey: recipientWallet.publicKey, isSigner: true, isWritable: true }, // Recipient { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Payer { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // System program { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // Token program { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false, }, // Associated token program ], programId: program.publicKey, data: transferToInstructionData.toBuffer(), }); await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer, recipientWallet], { skipPreflight: true }); }); it('Transfer NFT to another wallet!', async () => { const fromAssociatedTokenAddress = await getAssociatedTokenAddress(nftMintKeypair.publicKey, payer.publicKey); console.log(`Owner Token Address: ${fromAssociatedTokenAddress}`); const toAssociatedTokenAddress = await getAssociatedTokenAddress(nftMintKeypair.publicKey, recipientWallet.publicKey); console.log(`Recipient Token Address: ${toAssociatedTokenAddress}`); const transferToInstructionData = new TransferTokensArgs({ instruction: MyInstruction.TransferTokens, quantity: new BN(1), }); const ix = new TransactionInstruction({ keys: [ { pubkey: nftMintKeypair.publicKey, isSigner: false, isWritable: true }, // Mint account { pubkey: fromAssociatedTokenAddress, isSigner: false, isWritable: true, }, // Owner Token account { pubkey: toAssociatedTokenAddress, isSigner: false, isWritable: true }, // Recipient Token account { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Owner { pubkey: recipientWallet.publicKey, isSigner: true, isWritable: true }, // Recipient { pubkey: payer.publicKey, isSigner: true, isWritable: true }, // Payer { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // System program { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // Token program { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false, }, // Associated token program ], programId: program.publicKey, data: transferToInstructionData.toBuffer(), }); await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer, recipientWallet], { skipPreflight: true }); }); });