Token Program
The APL Token Program is the foundation for creating and managing fungible tokens on the Arch Network. This documentation provides a comprehensive guide for developers implementing token functionality in their applications.
Overview
The Token Program enables:
- Creation and management of fungible tokens (mints)
- Token account management
- Token transfers and delegations
- Multisignature authorities
- Account freezing and thawing
Program ID
apl-token00000000000000000000000
Account Types
Mint Account
The central record for a token type, containing:
Field | Type | Description |
---|---|---|
mint_authority | COption<Pubkey> | Optional authority to mint new tokens |
supply | u64 | Total number of tokens in circulation |
decimals | u8 | Number of decimal places |
is_initialized | bool | Has this mint been initialized |
freeze_authority | COption<Pubkey> | Optional authority to freeze token accounts |
Token Account
Holds token balances for a specific mint:
Field | Type | Description |
---|---|---|
mint | Pubkey | The token mint this account holds |
owner | Pubkey | Owner of this account |
amount | u64 | Number of tokens held |
delegate | COption<Pubkey> | Optional delegate authority |
state | AccountState | Account state (Uninitialized/Initialized/Frozen) |
delegated_amount | u64 | Amount delegated |
close_authority | COption<Pubkey> | Optional authority to close the account |
Multisig Account
Enables shared authority over token operations:
Field | Type | Description |
---|---|---|
m | u8 | Number of required signers |
n | u8 | Number of valid signers |
is_initialized | bool | Has this multisig been initialized |
signers | [Pubkey; MAX_SIGNERS] | Array of valid signer addresses |
Instructions
Token Creation and Initialization
InitializeMint
Creates a new token type.
pub struct InitializeMint {
pub decimals: u8,
pub mint_authority: Pubkey,
pub freeze_authority: COption<Pubkey>,
}
Required accounts:
[writable]
The mint to initialize
Example:
let mint = Keypair::new();
let mint_authority = Keypair::new();
let decimals = 9;
let instruction = initialize_mint(
&mint.pubkey(),
&mint_authority.pubkey(),
None, // No freeze authority
decimals,
)?;
InitializeAccount
Creates a new account to hold tokens.
Required accounts:
[writable]
The account to initialize[]
The mint this account is for[]
The owner of the new account
Example:
let account = Keypair::new();
let owner = Keypair::new();
let instruction = initialize_account(
&account.pubkey(),
&mint.pubkey(),
&owner.pubkey(),
)?;
InitializeMultisig
Creates a new multisignature authority.
#![allow(unused)] fn main() { pub struct InitializeMultisig { pub m: u8, // Number of required signers } }
Required accounts:
[writable]
The multisig to initialize[]
The signer accounts (1 to 11)
Example:
let multisig = Keypair::new();
let signers = vec![signer1.pubkey(), signer2.pubkey(), signer3.pubkey()];
let min_signers = 2;
let instruction = initialize_multisig(
&multisig.pubkey(),
&signers,
min_signers,
)?;
Token Operations
MintTo
Creates new tokens in an account.
#![allow(unused)] fn main() { pub struct MintTo { pub amount: u64, } }
Required accounts:
[writable]
The mint[writable]
The account to mint to[signer]
The mint authority
Example:
let amount = 1_000_000_000; // 1 token with 9 decimals
let instruction = mint_to(
&mint.pubkey(),
&destination.pubkey(),
&mint_authority.pubkey(),
amount,
)?;
Transfer
Moves tokens between accounts.
#![allow(unused)] fn main() { pub struct Transfer { pub amount: u64, } }
Required accounts:
[writable]
Source account[writable]
Destination account[signer]
Owner/delegate authority
Example:
let amount = 50_000_000; // 0.05 tokens with 9 decimals
let instruction = transfer(
&source.pubkey(),
&destination.pubkey(),
&owner.pubkey(),
amount,
)?;
Burn
Removes tokens from circulation.
#![allow(unused)] fn main() { pub struct Burn { pub amount: u64, } }
Required accounts:
[writable]
The account to burn from[writable]
The token mint[signer]
The owner/delegate
Example:
let amount = 1_000_000_000; // 1 token with 9 decimals
let instruction = burn(
&account.pubkey(),
&mint.pubkey(),
&owner.pubkey(),
amount,
)?;
Delegation
Approve
Delegates authority over tokens.
#![allow(unused)] fn main() { pub struct Approve { pub amount: u64, } }
Required accounts:
[writable]
Source account[]
Delegate[signer]
Source account owner
Example:
let amount = 5_000_000_000; // 5 tokens with 9 decimals
let instruction = approve(
&source.pubkey(),
&delegate.pubkey(),
&owner.pubkey(),
amount,
)?;
Revoke
Removes delegated authority.
Required accounts:
[writable]
Source account[signer]
Source account owner
Example:
let instruction = revoke(
&source.pubkey(),
&owner.pubkey(),
)?;
Account Management
SetAuthority
Changes an authority on a mint or account.
pub struct SetAuthority {
pub authority_type: AuthorityType,
pub new_authority: COption<Pubkey>,
}
Required accounts:
[writable]
Mint/account to change[signer]
Current authority
Example:
let instruction = set_authority(
&mint.pubkey(),
¤t_authority.pubkey(),
Some(&new_authority.pubkey()),
AuthorityType::MintTokens,
)?;
CloseAccount
Closes a token account with zero balance.
Required accounts:
[writable]
Account to close[writable]
Destination for rent funds[signer]
Account owner
Example:
let instruction = close_account(
&account.pubkey(),
&destination.pubkey(),
&owner.pubkey(),
)?;
Error Handling
The program defines specific error types for common failure cases:
#![allow(unused)] fn main() { pub enum TokenError { NotRentExempt, // Account balance too low InsufficientFunds, // Not enough tokens InvalidMint, // Invalid mint account MintMismatch, // Mint doesn't match OwnerMismatch, // Wrong account owner FixedSupply, // Mint authority disabled AlreadyInUse, // Account already initialized InvalidNumberOfProvidedSigners, InvalidNumberOfRequiredSigners, UninitializedState, // Account not initialized NativeNotSupported, // Instruction not for native tokens NonNativeHasBalance, // Non-native account with balance InvalidInstruction, // Invalid instruction data InvalidState, // Account in invalid state Overflow, // Operation overflowed AuthorityTypeNotSupported, MintCannotFreeze, // Mint has no freeze authority AccountFrozen, // Account is frozen // ... other errors } }
Best Practices
Security
-
Account Validation
- Always verify account ownership
- Check account states before operations
- Validate mint associations
-
Authority Management
- Use multisig for sensitive operations
- Carefully manage mint/freeze authorities
- Have clear authority transfer procedures
-
Operation Safety
- Use checked math operations
- Handle frozen accounts appropriately
- Implement proper error handling
Performance
-
Transaction Optimization
- Combine related operations in one transaction
- Minimize account lookups
- Pre-allocate accounts when possible
-
Account Management
- Close unused accounts
- Maintain rent-exempt balances
- Use Associated Token Accounts when appropriate
Common Scenarios
Creating a New Token
// 1. Create mint account
let mint = Keypair::new();
let mint_rent = get_minimum_balance_for_rent_exemption(Mint::LEN)?;
let create_mint_account = system_instruction::create_account(
&payer.pubkey(),
&mint.pubkey(),
mint_rent,
Mint::LEN as u64,
&token_program_id,
);
// 2. Initialize mint
let init_mint = initialize_mint(
&mint.pubkey(),
&mint_authority.pubkey(),
Some(&freeze_authority.pubkey()),
9, // decimals
)?;
// 3. Create token account
let account = Keypair::new();
let account_rent = get_minimum_balance_for_rent_exemption(Account::LEN)?;
let create_account = system_instruction::create_account(
&payer.pubkey(),
&account.pubkey(),
account_rent,
Account::LEN as u64,
&token_program_id,
);
// 4. Initialize token account
let init_account = initialize_account(
&account.pubkey(),
&mint.pubkey(),
&owner.pubkey(),
)?;
// 5. Combine into a single transaction
let transaction = Transaction::new_signed_with_payer(
&[
create_mint_account,
init_mint,
create_account,
init_account,
],
Some(&payer.pubkey()),
&[&payer, &mint, &account],
recent_blockhash,
);
Implementing a Token Transfer
// 1. Get token accounts
let source = get_associated_token_address(&source_owner, &mint);
let destination = get_associated_token_address(&destination_owner, &mint);
// 2. Create transfer instruction
let transfer = transfer(
&source,
&destination,
&source_owner,
amount,
)?;
// 3. Send transaction
let transaction = Transaction::new_signed_with_payer(
&[transfer],
Some(&payer.pubkey()),
&[&payer, &source_owner],
recent_blockhash,
);
Testing
The Token Program includes comprehensive tests. When implementing token functionality, you should test:
-
Basic Operations
- Mint initialization
- Account creation
- Token transfers
- Balance checks
-
Authority Controls
- Authority validation
- Multisig operations
- Authority transfers
-
Error Cases
- Insufficient funds
- Invalid authorities
- Account state violations
Example test:
#![allow(unused)] fn main() { #[cfg(test)] mod tests { #[test] fn test_transfer() { let program_id = token_program_id(); let mint = Keypair::new(); let source = Keypair::new(); let destination = Keypair::new(); let owner = Keypair::new(); // Initialize mint and accounts // ... setup code ... // Test transfer let amount = 100; let result = transfer( &source.pubkey(), &destination.pubkey(), &owner.pubkey(), amount, ); assert!(result.is_ok()); // Verify balances // ... verification code ... } } }