import { clusterApiUrl, sendAndConfirmTransaction, Connection, Keypair, SystemProgram, Transaction, LAMPORTS_PER_SOL, } from '@solana/web3.js'; import { ExtensionType, createInitializeMintInstruction, mintTo, createAccount, getMintLen, getTransferFeeAmount, unpackAccount, TOKEN_2022_PROGRAM_ID, } from '../src'; import { createInitializeTransferFeeConfigInstruction, harvestWithheldTokensToMint, transferCheckedWithFee, withdrawWithheldTokensFromAccounts, withdrawWithheldTokensFromMint, } from '../src/extensions/transferFee/index'; (async () => { const payer = Keypair.generate(); const mintAuthority = Keypair.generate(); const mintKeypair = Keypair.generate(); const mint = mintKeypair.publicKey; const transferFeeConfigAuthority = Keypair.generate(); const withdrawWithheldAuthority = Keypair.generate(); const extensions = [ExtensionType.TransferFeeConfig]; const mintLen = getMintLen(extensions); const decimals = 9; const feeBasisPoints = 50; const maxFee = BigInt(5_000); const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); const airdropSignature = await connection.requestAirdrop(payer.publicKey, 2 * LAMPORTS_PER_SOL); await connection.confirmTransaction({ signature: airdropSignature, ...(await connection.getLatestBlockhash()) }); const mintLamports = await connection.getMinimumBalanceForRentExemption(mintLen); const mintTransaction = new Transaction().add( SystemProgram.createAccount({ fromPubkey: payer.publicKey, newAccountPubkey: mint, space: mintLen, lamports: mintLamports, programId: TOKEN_2022_PROGRAM_ID, }), createInitializeTransferFeeConfigInstruction( mint, transferFeeConfigAuthority.publicKey, withdrawWithheldAuthority.publicKey, feeBasisPoints, maxFee, TOKEN_2022_PROGRAM_ID ), createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID) ); await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined); const mintAmount = BigInt(1_000_000_000); const owner = Keypair.generate(); const sourceAccount = await createAccount( connection, payer, mint, owner.publicKey, undefined, undefined, TOKEN_2022_PROGRAM_ID ); await mintTo( connection, payer, mint, sourceAccount, mintAuthority, mintAmount, [], undefined, TOKEN_2022_PROGRAM_ID ); const accountKeypair = Keypair.generate(); const destinationAccount = await createAccount( connection, payer, mint, owner.publicKey, accountKeypair, undefined, TOKEN_2022_PROGRAM_ID ); const transferAmount = BigInt(1_000_000); const fee = (transferAmount * BigInt(feeBasisPoints)) / BigInt(10_000); await transferCheckedWithFee( connection, payer, sourceAccount, mint, destinationAccount, owner, transferAmount, decimals, fee, [], undefined, TOKEN_2022_PROGRAM_ID ); const allAccounts = await connection.getProgramAccounts(TOKEN_2022_PROGRAM_ID, { commitment: 'confirmed', filters: [ { memcmp: { offset: 0, bytes: mint.toString(), }, }, ], }); const accountsToWithdrawFrom = []; for (const accountInfo of allAccounts) { const account = unpackAccount(accountInfo.pubkey, accountInfo.account, TOKEN_2022_PROGRAM_ID); const transferFeeAmount = getTransferFeeAmount(account); if (transferFeeAmount !== null && transferFeeAmount.withheldAmount > BigInt(0)) { accountsToWithdrawFrom.push(accountInfo.pubkey); } } await withdrawWithheldTokensFromAccounts( connection, payer, mint, destinationAccount, withdrawWithheldAuthority, [], accountsToWithdrawFrom, undefined, TOKEN_2022_PROGRAM_ID ); await harvestWithheldTokensToMint(connection, payer, mint, [destinationAccount], undefined, TOKEN_2022_PROGRAM_ID); await withdrawWithheldTokensFromMint( connection, payer, mint, destinationAccount, withdrawWithheldAuthority, [], undefined, TOKEN_2022_PROGRAM_ID ); })();