Que sont les frais de transfert ?
L'extension de mint TransferFeeConfig du Token Extensions Program applique
des frais à chaque transfert pour ce mint.
Lors du transfert :
- Le montant transféré est réduit des frais configurés
- Les frais retenus sont suivis sur le compte de jetons de destination
- Les frais retenus peuvent être retirés directement des comptes de jetons ou récupérés vers le mint
- Le
withdraw_withheld_authoritypeut déplacer les frais récupérés vers un compte de jetons récepteur de frais
Extensions de compte de jetons
Lorsqu'un compte de jetons est initialisé pour un mint avec
TransferFeeConfig, le compte de jetons est initialisé avec
TransferFeeAmount. Lorsque le compte de jetons est créé via
l'Associated Token
Program,
la taille requise du compte est calculée et le compte de jetons est créé avec
la taille et les lamports exempts de loyer nécessaires.
Comment créer un mint avec frais de transfert
Pour créer un mint avec frais de transfert :
- Calculez la taille du compte mint et le loyer nécessaire pour le mint et
l'extension
TransferFeeConfig. - Créez le compte mint avec
CreateAccount, initialisezTransferFeeConfig, et initialisez le mint avecInitializeMint. - Créez des comptes de jetons pour le mint.
TransferFeeAmountest automatiquement activé pour les comptes de jetons. - Utilisez
TransferCheckedWithFeepour transférer des jetons et vérifier les frais attendus.TransferCheckedfonctionne également et retient automatiquement les frais configurés sur le compte de jetons de destination. - Utilisez
WithdrawWithheldTokensFromAccountspour déplacer les frais retenus directement des comptes de jetons vers un compte de jetons récepteur de frais, signé par lewithdraw_withheld_authoritydu mint. - Utilisez
HarvestWithheldTokensToMintsans permission pour déplacer les frais retenus des comptes de jetons vers le compte mint, puis utilisezWithdrawWithheldTokensFromMintpour retirer les frais retenus du compte mint vers un compte de jetons récepteur de frais, signé par lewithdraw_withheld_authoritydu mint. - Utilisez
SetTransferFeepour mettre à jour la prochaine configuration des frais de transfert, qui prend effet à partir de deux époques plus tard.
Calculer la taille du compte
Calculez la taille du compte mint pour le mint de base plus l'extension
TransferFeeConfig. C'est la taille utilisée dans CreateAccount.
Calculer le rent
Calculez le rent en utilisant la taille nécessaire pour le mint plus l'extension
TransferFeeConfig.
Créer le compte mint
Créez le compte mint avec l'espace et les lamports calculés.
Initialiser TransferFeeConfig
Initialisez l'extension TransferFeeConfig sur le mint.
Initialiser le mint
Initialisez le mint avec InitializeMint dans la même transaction.
Créer des token accounts et émettre des tokens
Créez des token accounts pour la source, les destinataires et le receveur de frais, puis émettez des tokens vers le token account source.
Transférer avec TransferCheckedWithFee
Transférez des jetons avec TransferCheckedWithFee et vérifiez les frais
attendus.
Transférer avec TransferChecked
Transférez des jetons avec TransferChecked. Les frais configurés sont
toujours retenus sur le token account de destination.
Retirer les frais retenus des token accounts
Utilisez WithdrawWithheldTokensFromAccounts pour déplacer les frais
retenus directement des token accounts vers le token account du destinataire des
frais.
Collecter les frais retenus vers le mint
Utilisez HarvestWithheldTokensToMint pour déplacer les frais retenus des
token accounts vers l'accumulateur de frais retenus du mint account.
Retirer les frais retenus du mint
Utilisez WithdrawWithheldTokensFromMint pour déplacer les frais collectés
du mint account vers le token account du destinataire des frais.
Mettre à jour les prochains frais de transfert
Utilisez SetTransferFee pour mettre à jour la configuration des prochains
frais de transfert.
Ordre des instructions
InitializeTransferFeeConfig doit être exécuté avant
InitializeMint. CreateAccount, InitializeTransferFeeConfig
et InitializeMint doivent être inclus dans la même transaction.
Référence source
| Élément | Description | Source |
|---|---|---|
TransferFeeConfig | Extension de mint qui stocke les autorités de frais, le montant retenu accumulé et les configurations de frais de transfert anciennes et nouvelles. | Source |
TransferFee | Structure de configuration des frais stockée dans TransferFeeConfig qui définit l'epoch à laquelle les frais prennent effet, les frais maximum et les frais en points de base. | Source |
TransferFeeAmount | Extension de token account qui stocke le montant retenu lors des transferts pour un token account. | Source |
TransferFeeInstruction::InitializeTransferFeeConfig | Instruction qui initialise les paramètres de frais de transfert avant InitializeMint. | Source |
TransferFeeInstruction::TransferCheckedWithFee | Instruction de transfert qui vérifie que les frais fournis par l'appelant correspondent à la configuration actuelle des frais de transfert du mint. | Source |
TransferFeeInstruction::WithdrawWithheldTokensFromAccounts | Instruction qui transfère les frais retenus directement des token accounts vers un token account de réception des frais, signée par le withdraw_withheld_authority du mint. | Source |
TransferFeeInstruction::HarvestWithheldTokensToMint | Instruction sans permission qui transfère les frais retenus des token accounts vers l'accumulateur de frais retenus du mint account. | Source |
TransferFeeInstruction::WithdrawWithheldTokensFromMint | Instruction qui retire les frais retenus du mint account vers un token account de réception des frais, signée par le withdraw_withheld_authority du mint. | Source |
TransferFeeInstruction::SetTransferFee | Instruction qui met à jour la nouvelle configuration de frais de transfert, qui prend effet à partir de deux epochs plus tard. | Source |
process_initialize_transfer_fee_config | Logique de traitement qui initialise l'extension TransferFeeConfig sur un mint non initialisé et définit les configurations de frais de transfert anciennes et nouvelles sur les frais initiaux. | Source |
process_withdraw_withheld_tokens_from_accounts | Logique de traitement qui valide le withdraw_withheld_authority et transfère les frais retenus des token accounts vers un token account de destination. | Source |
process_harvest_withheld_tokens_to_mint | Logique de traitement qui collecte les frais retenus des token accounts vers l'accumulateur de frais retenus du mint account. | Source |
process_withdraw_withheld_tokens_from_mint | Logique de traitement qui valide le withdraw_withheld_authority et transfère le montant retenu du mint account vers un token account de destination. | Source |
process_set_transfer_fee | Logique de traitement qui valide l'autorité de configuration des frais de transfert et met à jour la nouvelle configuration de frais de transfert pour prendre effet à partir de deux epochs plus tard. | Source |
get_required_init_account_extensions | Utilisé pour ajouter automatiquement des extensions de token account lors de l'initialisation des token accounts en fonction des extensions activées sur le mint. Pour les mints TransferFeeConfig, cela ajoute TransferFeeAmount. | Source |
Typescript
L'exemple Kit ci-dessous utilise des instructions Token-2022 explicites. Des
exemples hérités utilisant @solana/web3.js et @solana/spl-token sont inclus
à titre de référence.
Kit
import {lamports,createClient,generateKeyPairSigner,unwrapOption} from "@solana/kit";import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";import { getCreateAccountInstruction } from "@solana-program/system";import {extension,fetchMint,fetchToken,findAssociatedTokenPda,getCreateAssociatedTokenInstructionAsync,getHarvestWithheldTokensToMintInstruction,getInitializeMintInstruction,getInitializeTransferFeeConfigInstruction,getMintSize,getMintToCheckedInstruction,getSetTransferFeeInstruction,getTransferCheckedWithFeeInstruction,getWithdrawWithheldTokensFromAccountsInstruction,getWithdrawWithheldTokensFromMintInstruction,isExtension,TOKEN_2022_PROGRAM_ADDRESS} from "@solana-program/token-2022";const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));const mint = await generateKeyPairSigner();const recipientA = await generateKeyPairSigner();const recipientB = await generateKeyPairSigner();const feeReceiver = await generateKeyPairSigner();const transferFeeConfigExtension = extension("TransferFeeConfig", {transferFeeConfigAuthority: client.payer.address,withdrawWithheldAuthority: client.payer.address,withheldAmount: 0n,olderTransferFee: {epoch: 0n,maximumFee: 10n,transferFeeBasisPoints: 150},newerTransferFee: {epoch: 0n,maximumFee: 10n,transferFeeBasisPoints: 150}});const mintSpace = BigInt(getMintSize([transferFeeConfigExtension]));const mintRent = await client.rpc.getMinimumBalanceForRentExemption(mintSpace).send();await client.sendTransaction([getCreateAccountInstruction({payer: client.payer, // Account funding account creation.newAccount: mint, // New mint account to create.lamports: mintRent, // Lamports funding the mint account rent.space: mintSpace, // Account size in bytes for the mint plus TransferFeeConfig.programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.}),getInitializeTransferFeeConfigInstruction({mint: mint.address, // Mint account that stores the TransferFeeConfig extension.transferFeeConfigAuthority: client.payer.address, // Authority allowed to update the transfer fee later.withdrawWithheldAuthority: client.payer.address, // Value stored in the mint's `withdraw_withheld_authority` field.transferFeeBasisPoints: 150, // Transfer fee in basis points.maximumFee: 10n // Maximum fee charged on each transfer.}),getInitializeMintInstruction({mint: mint.address, // Mint account to initialize.decimals: 2, // Number of decimals for the token.mintAuthority: client.payer.address, // Authority allowed to mint new tokens.freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.})]);const [sourceToken] = await findAssociatedTokenPda({mint: mint.address,owner: client.payer.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const [destinationAToken] = await findAssociatedTokenPda({mint: mint.address,owner: recipientA.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const [destinationBToken] = await findAssociatedTokenPda({mint: mint.address,owner: recipientB.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const [feeReceiverToken] = await findAssociatedTokenPda({mint: mint.address,owner: feeReceiver.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});await client.sendTransaction([await getCreateAssociatedTokenInstructionAsync({payer: client.payer,mint: mint.address,owner: client.payer.address}),await getCreateAssociatedTokenInstructionAsync({payer: client.payer,mint: mint.address,owner: recipientA.address}),await getCreateAssociatedTokenInstructionAsync({payer: client.payer,mint: mint.address,owner: recipientB.address}),await getCreateAssociatedTokenInstructionAsync({payer: client.payer,mint: mint.address,owner: feeReceiver.address}),getMintToCheckedInstruction({mint: mint.address,token: sourceToken,mintAuthority: client.payer,amount: 1_000n,decimals: 2})]);await client.sendTransaction([getTransferCheckedWithFeeInstruction({source: sourceToken, // Token account sending the transfer.mint: mint.address, // Mint with the transfer fee configuration.destination: destinationAToken, // Token account receiving the transfer.authority: client.payer, // Signer approving the transfer.amount: 200n, // Token amount in base units.decimals: 2, // Decimals defined on the mint.fee: 3n // Expected transfer fee for this transfer.})]);await client.sendTransaction([getWithdrawWithheldTokensFromAccountsInstruction({mint: mint.address, // Mint with the transfer fee configuration.feeReceiver: feeReceiverToken, // Token account receiving the withdrawn fees.withdrawWithheldAuthority: client.payer, // Signer matching the mint's `withdraw_withheld_authority`.numTokenAccounts: 1, // Number of token accounts listed in `sources`.sources: [destinationAToken] // Token accounts to withdraw withheld fees from.})]);await client.sendTransaction([getTransferCheckedWithFeeInstruction({source: sourceToken, // Token account sending the transfer.mint: mint.address, // Mint with the transfer fee configuration.destination: destinationBToken, // Token account receiving the transfer.authority: client.payer, // Signer approving the transfer.amount: 200n, // Token amount in base units.decimals: 2, // Decimals defined on the mint.fee: 3n // Expected transfer fee for this transfer.})]);await client.sendTransaction([getHarvestWithheldTokensToMintInstruction({mint: mint.address, // Mint that collects harvested withheld fees.sources: [destinationBToken] // Token accounts to harvest withheld fees from.})]);await client.sendTransaction([getWithdrawWithheldTokensFromMintInstruction({mint: mint.address, // Mint storing harvested withheld fees.feeReceiver: feeReceiverToken, // Token account receiving withdrawn fees.withdrawWithheldAuthority: client.payer // Signer matching the mint's `withdraw_withheld_authority`.}),getSetTransferFeeInstruction({mint: mint.address, // Mint whose next transfer fee configuration is updated.transferFeeConfigAuthority: client.payer, // Signer authorized to update the transfer fee later.transferFeeBasisPoints: 250, // New transfer fee in basis points.maximumFee: 25n // New maximum fee for the next transfer fee configuration.})]);const destinationAAccount = await fetchToken(client.rpc, destinationAToken);const destinationBAccount = await fetchToken(client.rpc, destinationBToken);const feeReceiverAccount = await fetchToken(client.rpc, feeReceiverToken);const mintAccount = await fetchMint(client.rpc, mint.address);const transferFeeConfig = (unwrapOption(mintAccount.data.extensions) ?? []).find((item) => isExtension("TransferFeeConfig", item));console.log("Mint Address:", mint.address);console.dir({destinationA: {amount: destinationAAccount.data.amount,extensions: destinationAAccount.data.extensions},destinationB: {amount: destinationBAccount.data.amount,extensions: destinationBAccount.data.extensions},feeReceiver: {amount: feeReceiverAccount.data.amount,extensions: feeReceiverAccount.data.extensions},transferFeeConfig},{ depth: null });
Web3.js
import {Connection,Keypair,LAMPORTS_PER_SOL,sendAndConfirmTransaction,SystemProgram,Transaction} from "@solana/web3.js";import {ASSOCIATED_TOKEN_PROGRAM_ID,ExtensionType,createAssociatedTokenAccountInstruction,createHarvestWithheldTokensToMintInstruction,createInitializeMintInstruction,createInitializeTransferFeeConfigInstruction,createMintToCheckedInstruction,createSetTransferFeeInstruction,createTransferCheckedWithFeeInstruction,createWithdrawWithheldTokensFromAccountsInstruction,createWithdrawWithheldTokensFromMintInstruction,getAccount,getAssociatedTokenAddressSync,getMint,getMintLen,getTransferFeeAmount,getTransferFeeConfig,TOKEN_2022_PROGRAM_ID} from "@solana/spl-token";const connection = new Connection("http://localhost:8899", "confirmed");const latestBlockhash = await connection.getLatestBlockhash();const feePayer = Keypair.generate();const recipientA = Keypair.generate();const recipientB = Keypair.generate();const feeReceiver = Keypair.generate();const airdropSignature = await connection.requestAirdrop(feePayer.publicKey,5 * LAMPORTS_PER_SOL);await connection.confirmTransaction({blockhash: latestBlockhash.blockhash,lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,signature: airdropSignature});const mint = Keypair.generate();const transferFeeConfigExtensions = [ExtensionType.TransferFeeConfig];const mintLen = getMintLen(transferFeeConfigExtensions);const mintRent = await connection.getMinimumBalanceForRentExemption(mintLen);const sourceToken = getAssociatedTokenAddressSync(mint.publicKey,feePayer.publicKey,false,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID);const destinationAToken = getAssociatedTokenAddressSync(mint.publicKey,recipientA.publicKey,false,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID);const destinationBToken = getAssociatedTokenAddressSync(mint.publicKey,recipientB.publicKey,false,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID);const feeReceiverToken = getAssociatedTokenAddressSync(mint.publicKey,feeReceiver.publicKey,false,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID);await sendAndConfirmTransaction(connection,new Transaction().add(SystemProgram.createAccount({fromPubkey: feePayer.publicKey, // Account funding account creation.newAccountPubkey: mint.publicKey, // New mint account to create.lamports: mintRent, // Lamports funding the mint account rent.space: mintLen, // Account size in bytes for the mint plus TransferFeeConfig.programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.}),createInitializeTransferFeeConfigInstruction(mint.publicKey, // Mint account that stores the TransferFeeConfig extension.feePayer.publicKey, // Authority allowed to update the transfer fee later.feePayer.publicKey, // Value stored in the mint's `withdraw_withheld_authority` field.150, // Transfer fee in basis points.10n, // Maximum fee charged on each transfer.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.),createInitializeMintInstruction(mint.publicKey, // Mint account to initialize.2, // Number of decimals for the token.feePayer.publicKey, // Authority allowed to mint new tokens.feePayer.publicKey, // Authority allowed to freeze token accounts.TOKEN_2022_PROGRAM_ID // Program that owns the mint account.)),[feePayer, mint],{ commitment: "confirmed" });await sendAndConfirmTransaction(connection,new Transaction().add(createAssociatedTokenAccountInstruction(feePayer.publicKey,sourceToken,feePayer.publicKey,mint.publicKey,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID),createAssociatedTokenAccountInstruction(feePayer.publicKey,destinationAToken,recipientA.publicKey,mint.publicKey,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID),createAssociatedTokenAccountInstruction(feePayer.publicKey,destinationBToken,recipientB.publicKey,mint.publicKey,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID),createAssociatedTokenAccountInstruction(feePayer.publicKey,feeReceiverToken,feeReceiver.publicKey,mint.publicKey,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID),createMintToCheckedInstruction(mint.publicKey,sourceToken,feePayer.publicKey,1_000,2,[],TOKEN_2022_PROGRAM_ID)),[feePayer],{ commitment: "confirmed" });await sendAndConfirmTransaction(connection,new Transaction().add(createTransferCheckedWithFeeInstruction(sourceToken, // Token account sending the transfer.mint.publicKey, // Mint with the transfer fee configuration.destinationAToken, // Token account receiving the transfer.feePayer.publicKey, // Signer approving the transfer.200n, // Token amount in base units.2, // Decimals defined on the mint.3n, // Expected transfer fee for this transfer.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that processes the transfer.)),[feePayer],{ commitment: "confirmed" });await sendAndConfirmTransaction(connection,new Transaction().add(createWithdrawWithheldTokensFromAccountsInstruction(mint.publicKey, // Mint with the transfer fee configuration.feeReceiverToken, // Token account receiving the withdrawn fees.feePayer.publicKey, // Signer matching the mint's `withdraw_withheld_authority`.[], // Additional multisig signers.[destinationAToken], // Token accounts to withdraw withheld fees from.TOKEN_2022_PROGRAM_ID // Token program that processes the withdraw.)),[feePayer],{ commitment: "confirmed" });await sendAndConfirmTransaction(connection,new Transaction().add(createTransferCheckedWithFeeInstruction(sourceToken, // Token account sending the transfer.mint.publicKey, // Mint with the transfer fee configuration.destinationBToken, // Token account receiving the transfer.feePayer.publicKey, // Signer approving the transfer.200n, // Token amount in base units.2, // Decimals defined on the mint.3n, // Expected transfer fee for this transfer.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that processes the transfer.)),[feePayer],{ commitment: "confirmed" });await sendAndConfirmTransaction(connection,new Transaction().add(createHarvestWithheldTokensToMintInstruction(mint.publicKey, // Mint that collects harvested withheld fees.[destinationBToken], // Token accounts to harvest withheld fees from.TOKEN_2022_PROGRAM_ID // Token program that processes the harvest.)),[feePayer],{ commitment: "confirmed" });await sendAndConfirmTransaction(connection,new Transaction().add(createWithdrawWithheldTokensFromMintInstruction(mint.publicKey, // Mint storing harvested withheld fees.feeReceiverToken, // Token account receiving withdrawn fees.feePayer.publicKey, // Signer matching the mint's `withdraw_withheld_authority`.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that processes the withdraw.),createSetTransferFeeInstruction(mint.publicKey, // Mint whose next transfer fee configuration is updated.feePayer.publicKey, // Authority allowed to update the transfer fee later.[], // Additional multisig signers.250, // New transfer fee in basis points.25n, // New maximum fee for the next transfer fee configuration.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.)),[feePayer],{ commitment: "confirmed" });const destinationAAccount = await getAccount(connection,destinationAToken,"confirmed",TOKEN_2022_PROGRAM_ID);const destinationBAccount = await getAccount(connection,destinationBToken,"confirmed",TOKEN_2022_PROGRAM_ID);const feeReceiverAccount = await getAccount(connection,feeReceiverToken,"confirmed",TOKEN_2022_PROGRAM_ID);const mintAccount = await getMint(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);console.log("Mint Address:", mint.publicKey.toBase58());console.log("Destination A Amount:", destinationAAccount.amount.toString());console.log("Destination A Transfer Fee Amount:",getTransferFeeAmount(destinationAAccount));console.log("Destination B Amount:", destinationBAccount.amount.toString());console.log("Destination B Transfer Fee Amount:",getTransferFeeAmount(destinationBAccount));console.log("Fee Receiver Amount:", feeReceiverAccount.amount.toString());console.log("Fee Receiver Transfer Fee Amount:",getTransferFeeAmount(feeReceiverAccount));console.log("Transfer Fee Config:", getTransferFeeConfig(mintAccount));
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_commitment_config::CommitmentConfig;use solana_sdk::{signature::{Keypair, Signer},transaction::Transaction,};use solana_system_interface::instruction::create_account;use spl_associated_token_account_interface::{address::get_associated_token_address_with_program_id,instruction::create_associated_token_account,};use spl_token_2022_interface::{extension::{transfer_fee::{instruction::{harvest_withheld_tokens_to_mint, initialize_transfer_fee_config,set_transfer_fee, transfer_checked_with_fee,withdraw_withheld_tokens_from_accounts, withdraw_withheld_tokens_from_mint,},TransferFeeAmount, TransferFeeConfig,},BaseStateWithExtensions, ExtensionType, StateWithExtensions,},instruction::{initialize_mint, mint_to_checked},state::{Account, Mint},ID as TOKEN_2022_PROGRAM_ID,};#[tokio::main]async fn main() -> Result<()> {let client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let fee_payer = Keypair::new();let recipient_a = Keypair::new();let recipient_b = Keypair::new();let fee_receiver = Keypair::new();let decimals = 2;let airdrop_signature = client.request_airdrop(&fee_payer.pubkey(), 5_000_000_000).await?;loop {let confirmed = client.confirm_transaction(&airdrop_signature).await?;if confirmed {break;}}let mint = Keypair::new();let mint_space =ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::TransferFeeConfig])?;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space).await?;let mint_blockhash = client.get_latest_blockhash().await?;let mint_transaction = Transaction::new_signed_with_payer(&[create_account(&fee_payer.pubkey(), // Account funding account creation.&mint.pubkey(), // New mint account to create.mint_rent, // Lamports funding the mint account rent.mint_space as u64, // Account size in bytes for the mint plus TransferFeeConfig.&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.),initialize_transfer_fee_config(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint account that stores the TransferFeeConfig extension.Some(&fee_payer.pubkey()), // Authority allowed to update the transfer fee later.Some(&fee_payer.pubkey()), // Value stored in the mint's `withdraw_withheld_authority` field.150, // Transfer fee in basis points.10, // Maximum fee charged on each transfer.)?,initialize_mint(&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.&mint.pubkey(), // Mint account to initialize.&fee_payer.pubkey(), // Authority allowed to mint new tokens.Some(&fee_payer.pubkey()), // Authority allowed to freeze token accounts.decimals, // Number of decimals for the token.)?,],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],mint_blockhash,);client.send_and_confirm_transaction(&mint_transaction).await?;let source_token_address = get_associated_token_address_with_program_id(&fee_payer.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let destination_a_token_address = get_associated_token_address_with_program_id(&recipient_a.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let destination_b_token_address = get_associated_token_address_with_program_id(&recipient_b.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let fee_receiver_token_address = get_associated_token_address_with_program_id(&fee_receiver.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let setup_blockhash = client.get_latest_blockhash().await?;let setup_transaction = Transaction::new_signed_with_payer(&[create_associated_token_account(&fee_payer.pubkey(),&fee_payer.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,),create_associated_token_account(&fee_payer.pubkey(),&recipient_a.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,),create_associated_token_account(&fee_payer.pubkey(),&recipient_b.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,),create_associated_token_account(&fee_payer.pubkey(),&fee_receiver.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,),mint_to_checked(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),&source_token_address,&fee_payer.pubkey(),&[],1_000,decimals,)?,],Some(&fee_payer.pubkey()),&[&fee_payer],setup_blockhash,);client.send_and_confirm_transaction(&setup_transaction).await?;let first_transfer_blockhash = client.get_latest_blockhash().await?;let first_transfer_transaction = Transaction::new_signed_with_payer(&[transfer_checked_with_fee(&TOKEN_2022_PROGRAM_ID, // Token program that processes the transfer.&source_token_address, // Token account sending the transfer.&mint.pubkey(), // Mint with the transfer fee configuration.&destination_a_token_address, // Token account receiving the transfer.&fee_payer.pubkey(), // Signer approving the transfer.&[], // Additional multisig signers.200, // Token amount in base units.decimals, // Decimals defined on the mint.3, // Expected transfer fee for this transfer.)?,],Some(&fee_payer.pubkey()),&[&fee_payer],first_transfer_blockhash,);client.send_and_confirm_transaction(&first_transfer_transaction).await?;let withdraw_accounts_blockhash = client.get_latest_blockhash().await?;let withdraw_accounts_transaction = Transaction::new_signed_with_payer(&[withdraw_withheld_tokens_from_accounts(&TOKEN_2022_PROGRAM_ID, // Token program that processes the withdraw.&mint.pubkey(), // Mint with the transfer fee configuration.&fee_receiver_token_address, // Token account receiving withdrawn fees.&fee_payer.pubkey(), // Signer matching the mint's `withdraw_withheld_authority`.&[], // Additional multisig signers.&[&destination_a_token_address], // Token accounts to withdraw withheld fees from.)?,],Some(&fee_payer.pubkey()),&[&fee_payer],withdraw_accounts_blockhash,);client.send_and_confirm_transaction(&withdraw_accounts_transaction).await?;let second_transfer_blockhash = client.get_latest_blockhash().await?;let second_transfer_transaction = Transaction::new_signed_with_payer(&[transfer_checked_with_fee(&TOKEN_2022_PROGRAM_ID, // Token program that processes the transfer.&source_token_address, // Token account sending the transfer.&mint.pubkey(), // Mint with the transfer fee configuration.&destination_b_token_address, // Token account receiving the transfer.&fee_payer.pubkey(), // Signer approving the transfer.&[], // Additional multisig signers.200, // Token amount in base units.decimals, // Decimals defined on the mint.3, // Expected transfer fee for this transfer.)?,],Some(&fee_payer.pubkey()),&[&fee_payer],second_transfer_blockhash,);client.send_and_confirm_transaction(&second_transfer_transaction).await?;let harvest_blockhash = client.get_latest_blockhash().await?;let harvest_transaction = Transaction::new_signed_with_payer(&[harvest_withheld_tokens_to_mint(&TOKEN_2022_PROGRAM_ID, // Token program that processes the harvest.&mint.pubkey(), // Mint that collects harvested withheld fees.&[&destination_b_token_address], // Token accounts to harvest withheld fees from.)?,],Some(&fee_payer.pubkey()),&[&fee_payer],harvest_blockhash,);client.send_and_confirm_transaction(&harvest_transaction).await?;let finalize_blockhash = client.get_latest_blockhash().await?;let finalize_transaction = Transaction::new_signed_with_payer(&[withdraw_withheld_tokens_from_mint(&TOKEN_2022_PROGRAM_ID, // Token program that processes the withdraw.&mint.pubkey(), // Mint storing harvested withheld fees.&fee_receiver_token_address, // Token account receiving withdrawn fees.&fee_payer.pubkey(), // Signer matching the mint's `withdraw_withheld_authority`.&[], // Additional multisig signers.)?,set_transfer_fee(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint whose next transfer fee configuration is updated.&fee_payer.pubkey(), // Authority allowed to update the transfer fee later.&[], // Additional multisig signers.250, // New transfer fee in basis points.25, // New maximum fee for the next transfer fee configuration.)?,],Some(&fee_payer.pubkey()),&[&fee_payer],finalize_blockhash,);client.send_and_confirm_transaction(&finalize_transaction).await?;let destination_a_account = client.get_account(&destination_a_token_address).await?;let destination_a_state = StateWithExtensions::<Account>::unpack(&destination_a_account.data)?;let destination_b_account = client.get_account(&destination_b_token_address).await?;let destination_b_state = StateWithExtensions::<Account>::unpack(&destination_b_account.data)?;let fee_receiver_account = client.get_account(&fee_receiver_token_address).await?;let fee_receiver_state = StateWithExtensions::<Account>::unpack(&fee_receiver_account.data)?;let mint_account = client.get_account(&mint.pubkey()).await?;let mint_state = StateWithExtensions::<Mint>::unpack(&mint_account.data)?;println!("Mint: {}", mint.pubkey());println!("Destination A Balance: {}", destination_a_state.base.amount);println!("Destination A Transfer Fee Amount: {:#?}",destination_a_state.get_extension::<TransferFeeAmount>()?);println!("Destination B Balance: {}", destination_b_state.base.amount);println!("Destination B Transfer Fee Amount: {:#?}",destination_b_state.get_extension::<TransferFeeAmount>()?);println!("Fee Receiver Balance: {}", fee_receiver_state.base.amount);println!("Fee Receiver Transfer Fee Amount: {:#?}",fee_receiver_state.get_extension::<TransferFeeAmount>()?);println!("Transfer Fee Config: {:#?}",mint_state.get_extension::<TransferFeeConfig>()?);Ok(())}
Is this page helpful?