O Que São Taxas de Transferência?
A extensão de mint TransferFeeConfig do Token Extensions Program aplica
uma taxa a cada transferência para esse mint.
No momento da transferência:
- O valor transferido é reduzido pela taxa configurada
- A taxa retida é rastreada na token account de destino
- As taxas retidas podem ser retiradas diretamente das contas de token ou coletadas no mint
- O
withdraw_withheld_authoritypode mover as taxas coletadas para uma token account receptora de taxas
Extensões de Token Account
Quando uma token account é inicializada para um mint com
TransferFeeConfig, a token account é inicializada com
TransferFeeAmount. Quando a token account é criada através do
Associated Token
Program,
o tamanho necessário da conta é calculado e a token account é criada com o
tamanho necessário e lamports isentos de rent.
Como Criar um Mint com Taxa de Transferência
Para criar um mint com taxa de transferência:
- Calcule o tamanho da mint account e o rent necessário para o mint e a
extensão
TransferFeeConfig. - Crie a mint account com
CreateAccount, inicializeTransferFeeConfige inicialize o mint comInitializeMint. - Crie contas de token para o mint.
TransferFeeAmounté automaticamente habilitado para as contas de token. - Use
TransferCheckedWithFeepara transferir tokens e verificar a taxa esperada.TransferCheckedtambém funciona e retém automaticamente a taxa configurada na token account de destino. - Use
WithdrawWithheldTokensFromAccountspara mover as taxas retidas diretamente das contas de token para uma token account receptora de taxas, assinado pelowithdraw_withheld_authoritydo mint. - Use
HarvestWithheldTokensToMintsem permissão para mover as taxas retidas das contas de token para a mint account e, em seguida, useWithdrawWithheldTokensFromMintpara retirar as taxas retidas da mint account para uma token account receptora de taxas, assinado pelowithdraw_withheld_authoritydo mint. - Use
SetTransferFeepara atualizar a próxima configuração de taxa de transferência, que entra em vigor a partir de duas epochs depois.
Calcular o tamanho da conta
Calcule o tamanho da conta mint para o mint base mais a extensão
TransferFeeConfig. Este é o tamanho usado em CreateAccount.
Calcular o rent
Calcule o rent usando o tamanho necessário para o mint mais a extensão
TransferFeeConfig.
Criar a mint account
Crie a mint account com o espaço e lamports calculados.
Inicializar TransferFeeConfig
Inicialize a extensão TransferFeeConfig no mint.
Inicializar o mint
Inicialize o mint com InitializeMint na mesma transação.
Criar token accounts e cunhar tokens
Crie token accounts para a origem, destinatários e receptor de taxas, e depois cunhe tokens para a token account de origem.
Transferir com TransferCheckedWithFee
Transfira tokens com TransferCheckedWithFee e verifique a taxa esperada.
Transferir com TransferChecked
Transfira tokens com TransferChecked. A taxa configurada ainda é retida na
token account de destino.
Retirar taxas retidas das token accounts
Use WithdrawWithheldTokensFromAccounts para mover taxas retidas
diretamente das token accounts para a token account receptora de taxas.
Coletar taxas retidas para o mint
Use HarvestWithheldTokensToMint para mover taxas retidas das token
accounts para o acumulador de retenção da mint account.
Retirar taxas retidas do mint
Use WithdrawWithheldTokensFromMint para mover taxas coletadas da mint
account para a token account receptora de taxas.
Atualizar a próxima taxa de transferência
Use SetTransferFee para atualizar a próxima configuração de taxa de
transferência.
Ordem das Instruções
InitializeTransferFeeConfig deve vir antes de InitializeMint.
CreateAccount, InitializeTransferFeeConfig e InitializeMint
devem ser incluídos na mesma transação.
Referência de Origem
| Item | Descrição | Origem |
|---|---|---|
TransferFeeConfig | Extensão de mint que armazena as autoridades de taxa, o valor retido acumulado e as configurações de taxa de transferência antiga e mais recente. | Origem |
TransferFee | Estrutura de configuração de taxa armazenada em TransferFeeConfig que define o epoch em que a taxa entra em vigor, a taxa máxima e a taxa em pontos base. | Origem |
TransferFeeAmount | Extensão de token account que armazena o valor retido durante transferências para um token account. | Origem |
TransferFeeInstruction::InitializeTransferFeeConfig | Instrução que inicializa as configurações de taxa de transferência antes de InitializeMint. | Origem |
TransferFeeInstruction::TransferCheckedWithFee | Instrução de transferência que verifica se a taxa fornecida pelo chamador corresponde à configuração de taxa de transferência atual do mint. | Origem |
TransferFeeInstruction::WithdrawWithheldTokensFromAccounts | Instrução que move taxas retidas diretamente de token accounts para um token account receptor de taxas, assinada pelo withdraw_withheld_authority do mint. | Origem |
TransferFeeInstruction::HarvestWithheldTokensToMint | Instrução sem permissão que move taxas retidas de token accounts para o acumulador de retenção do mint account. | Origem |
TransferFeeInstruction::WithdrawWithheldTokensFromMint | Instrução que retira as taxas retidas do mint account para um token account receptor de taxas, assinada pelo withdraw_withheld_authority do mint. | Origem |
TransferFeeInstruction::SetTransferFee | Instrução que atualiza a configuração de taxa de transferência mais recente, que entra em vigor a partir de dois epochs depois. | Origem |
process_initialize_transfer_fee_config | Lógica do processador que inicializa a extensão TransferFeeConfig em um mint não inicializado e define as configurações de taxa de transferência antiga e mais recente para a taxa inicial. | Origem |
process_withdraw_withheld_tokens_from_accounts | Lógica do processador que valida withdraw_withheld_authority e move taxas retidas de token accounts para um token account de destino. | Origem |
process_harvest_withheld_tokens_to_mint | Lógica do processador que coleta taxas retidas de token accounts para o acumulador de retenção do mint account. | Origem |
process_withdraw_withheld_tokens_from_mint | Lógica do processador que valida withdraw_withheld_authority e move o valor retido do mint account para um token account de destino. | Origem |
process_set_transfer_fee | Lógica do processador que valida a autoridade de configuração de taxa de transferência e atualiza a configuração de taxa de transferência mais recente para entrar em vigor a partir de dois epochs depois. | Origem |
get_required_init_account_extensions | Usado para adicionar automaticamente extensões de token account quando token accounts são inicializados com base nas extensões habilitadas no mint. Para mints TransferFeeConfig, adiciona TransferFeeAmount. | Origem |
Typescript
O exemplo Kit abaixo usa instruções explícitas do Token-2022. Exemplos legados
usando @solana/web3.js e @solana/spl-token estão incluídos para referência.
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?