CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-solders

Python bindings for Solana Rust tools providing high-performance blockchain development primitives, RPC functionality, and testing infrastructure.

Pending
Overview
Eval results
Files

token-operations.mddocs/

Token Operations

SPL Token program support including token account management, associated token addresses, token state parsing, and mint operations. This provides comprehensive support for fungible and non-fungible tokens on Solana.

Capabilities

SPL Token Program

Core token program functionality for managing fungible tokens on Solana.

# SPL Token Program ID
ID: Final[Pubkey] = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")

# SPL Token 2022 Program ID (extended token program)
TOKEN_2022_PROGRAM_ID: Final[Pubkey] = Pubkey.from_string("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")

Associated Token Addresses

Deterministic token account address derivation for user-friendly token management.

def get_associated_token_address(owner: Pubkey, mint: Pubkey) -> Pubkey:
    """
    Calculate associated token account address for owner and mint.
    
    Parameters:
    - owner: Pubkey, token account owner (user's wallet)
    - mint: Pubkey, token mint address
    
    Returns:
    Pubkey, deterministic associated token account address
    """

def get_associated_token_address_with_program_id(
    owner: Pubkey, 
    mint: Pubkey, 
    program_id: Pubkey
) -> Pubkey:
    """
    Calculate associated token address for specific token program.
    
    Parameters:
    - owner: Pubkey, token account owner
    - mint: Pubkey, token mint address
    - program_id: Pubkey, token program ID (SPL Token or Token-2022)
    
    Returns:
    Pubkey, associated token account address
    """

def create_associated_token_account_instruction(
    payer: Pubkey,
    owner: Pubkey,
    mint: Pubkey
) -> Instruction:
    """
    Create instruction to initialize associated token account.
    
    Parameters:
    - payer: Pubkey, account that pays for creation (must sign)
    - owner: Pubkey, token account owner
    - mint: Pubkey, token mint
    
    Returns:
    Instruction for creating associated token account
    """

Token Account State

Token account data structures and state management.

class TokenAccount:
    """
    SPL token account data structure containing balance and metadata.
    """
    def __init__(
        self,
        mint: Pubkey,
        owner: Pubkey,
        amount: int,
        delegate: Optional[Pubkey],
        state: TokenAccountState,
        is_native: Optional[int],
        delegated_amount: int,
        close_authority: Optional[Pubkey]
    ):
        """
        Create token account with balance and permissions.
        
        Parameters:
        - mint: Pubkey, token mint this account holds
        - owner: Pubkey, account owner (can transfer tokens)
        - amount: int, token balance (raw amount, not adjusted for decimals)
        - delegate: Optional[Pubkey], delegated authority for spending
        - state: TokenAccountState, account state (initialized/frozen/etc)
        - is_native: Optional[int], native SOL amount if wrapped SOL account
        - delegated_amount: int, amount delegated to delegate
        - close_authority: Optional[Pubkey], authority that can close account
        """

    @classmethod
    def deserialize(cls, data: bytes) -> 'TokenAccount':
        """
        Deserialize token account from account data.
        
        Parameters:
        - data: bytes, 165-byte token account data
        
        Returns:
        TokenAccount object
        
        Raises:
        - ValueError: if data is invalid or wrong size
        """

    def serialize(self) -> bytes:
        """
        Serialize token account to bytes.
        
        Returns:
        bytes, 165-byte serialized account data
        """

    @property
    def mint(self) -> Pubkey:
        """Token mint address."""

    @property
    def owner(self) -> Pubkey:
        """Account owner."""

    @property
    def amount(self) -> int:
        """Token balance (raw amount)."""

    @property
    def delegate(self) -> Optional[Pubkey]:
        """Delegate authority."""

    @property
    def state(self) -> TokenAccountState:
        """Account state."""

    @property
    def is_native(self) -> Optional[int]:
        """Native SOL amount for wrapped SOL."""

    @property
    def delegated_amount(self) -> int:
        """Amount available to delegate."""

    @property
    def close_authority(self) -> Optional[Pubkey]:
        """Close authority."""

    def is_frozen(self) -> bool:
        """
        Check if account is frozen.
        
        Returns:
        bool, True if account is frozen
        """

    def is_initialized(self) -> bool:
        """
        Check if account is initialized.
        
        Returns:
        bool, True if account is initialized
        """

    def ui_amount(self, decimals: int) -> float:
        """
        Convert raw amount to UI amount with decimals.
        
        Parameters:
        - decimals: int, number of decimal places for this token
        
        Returns:
        float, human-readable token amount
        """
class TokenAccountState:
    """
    Token account state enumeration.
    """
    Uninitialized: 'TokenAccountState'  # Account not yet initialized
    Initialized: 'TokenAccountState'    # Account ready for use
    Frozen: 'TokenAccountState'         # Account frozen (cannot transfer)

    def __str__(self) -> str:
        """String representation of state."""

    def __eq__(self, other) -> bool:
        """Compare equality with another state."""

Mint Account State

Token mint data structures defining token properties and supply.

class Mint:
    """
    SPL token mint account containing supply and authority information.
    """
    def __init__(
        self,
        mint_authority: Optional[Pubkey],
        supply: int,
        decimals: int,
        is_initialized: bool,
        freeze_authority: Optional[Pubkey]
    ):
        """
        Create mint account with supply and authorities.
        
        Parameters:
        - mint_authority: Optional[Pubkey], authority that can mint tokens (None = fixed supply)
        - supply: int, total token supply (raw amount)
        - decimals: int, number of decimal places for UI representation
        - is_initialized: bool, whether mint is initialized
        - freeze_authority: Optional[Pubkey], authority that can freeze accounts
        """

    @classmethod
    def deserialize(cls, data: bytes) -> 'Mint':
        """
        Deserialize mint from account data.
        
        Parameters:
        - data: bytes, 82-byte mint account data
        
        Returns:
        Mint object
        
        Raises:
        - ValueError: if data is invalid or wrong size
        """

    def serialize(self) -> bytes:
        """
        Serialize mint to bytes.
        
        Returns:
        bytes, 82-byte serialized mint data
        """

    @property
    def mint_authority(self) -> Optional[Pubkey]:
        """Mint authority (can create new tokens)."""

    @property
    def supply(self) -> int:
        """Total token supply (raw amount)."""

    @property
    def decimals(self) -> int:
        """Decimal places for UI representation."""

    @property
    def is_initialized(self) -> bool:
        """Whether mint is initialized."""

    @property
    def freeze_authority(self) -> Optional[Pubkey]:
        """Freeze authority (can freeze token accounts)."""

    def ui_supply(self) -> float:
        """
        Convert raw supply to UI amount with decimals.
        
        Returns:
        float, human-readable total supply
        """

    def has_mint_authority(self) -> bool:
        """
        Check if mint has active mint authority.
        
        Returns:
        bool, True if tokens can still be minted
        """

    def has_freeze_authority(self) -> bool:
        """
        Check if mint has freeze authority.
        
        Returns:
        bool, True if accounts can be frozen
        """

Multisig Account Support

Multi-signature account management for enhanced security.

class Multisig:
    """
    Multi-signature account requiring multiple signatures for operations.
    """
    def __init__(
        self,
        m: int,
        n: int,
        is_initialized: bool,
        signers: List[Pubkey]
    ):
        """
        Create multisig account configuration.
        
        Parameters:
        - m: int, number of signatures required (threshold)
        - n: int, total number of signers
        - is_initialized: bool, whether multisig is initialized
        - signers: List[Pubkey], list of authorized signers (up to 11)
        """

    @classmethod
    def deserialize(cls, data: bytes) -> 'Multisig':
        """
        Deserialize multisig from account data.
        
        Parameters:
        - data: bytes, multisig account data
        
        Returns:
        Multisig object
        """

    def serialize(self) -> bytes:
        """
        Serialize multisig to bytes.
        
        Returns:
        bytes, serialized multisig data
        """

    @property
    def m(self) -> int:
        """Required signature threshold."""

    @property
    def n(self) -> int:
        """Total number of signers."""

    @property
    def is_initialized(self) -> bool:
        """Whether multisig is initialized."""

    @property
    def signers(self) -> List[Pubkey]:
        """List of authorized signers."""

    def is_valid_signer(self, signer: Pubkey) -> bool:
        """
        Check if pubkey is authorized signer.
        
        Parameters:
        - signer: Pubkey, potential signer
        
        Returns:
        bool, True if signer is authorized
        """

    def has_sufficient_signatures(self, signatures: List[Pubkey]) -> bool:
        """
        Check if signature list meets threshold.
        
        Parameters:
        - signatures: List[Pubkey], provided signatures
        
        Returns:
        bool, True if enough valid signatures
        """

Usage Examples

Basic Token Operations

from solders.token.associated import get_associated_token_address
from solders.token.state import TokenAccount, Mint, TokenAccountState
from solders.pubkey import Pubkey

# Calculate associated token account address
owner = Pubkey.from_string("7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj")
mint = Pubkey.from_string("So11111111111111111111111111111111111111112")  # Wrapped SOL

ata_address = get_associated_token_address(owner, mint)
print(f"Associated token account: {ata_address}")

Token Account Analysis

# Parse token account data from RPC response
token_account_data = bytes(165)  # Retrieved from RPC call

try:
    token_account = TokenAccount.deserialize(token_account_data)
    
    print(f"Mint: {token_account.mint}")
    print(f"Owner: {token_account.owner}")
    print(f"Balance (raw): {token_account.amount}")
    print(f"State: {token_account.state}")
    print(f"Is frozen: {token_account.is_frozen()}")
    
    if token_account.delegate:
        print(f"Delegate: {token_account.delegate}")
        print(f"Delegated amount: {token_account.delegated_amount}")
    
    # Convert to UI amount (need decimals from mint)
    decimals = 9  # SOL has 9 decimals
    ui_amount = token_account.ui_amount(decimals)
    print(f"Balance (UI): {ui_amount:.9f}")
    
except ValueError as e:
    print(f"Failed to parse token account: {e}")

Mint Account Analysis

# Parse mint account data
mint_account_data = bytes(82)  # Retrieved from RPC call

try:
    mint = Mint.deserialize(mint_account_data)
    
    print(f"Supply (raw): {mint.supply}")
    print(f"Decimals: {mint.decimals}")
    print(f"UI Supply: {mint.ui_supply()}")
    print(f"Can mint more: {mint.has_mint_authority()}")
    print(f"Can freeze accounts: {mint.has_freeze_authority()}")
    
    if mint.mint_authority:
        print(f"Mint authority: {mint.mint_authority}")
    else:
        print("Fixed supply (no mint authority)")
    
    if mint.freeze_authority:
        print(f"Freeze authority: {mint.freeze_authority}")
        
except ValueError as e:
    print(f"Failed to parse mint: {e}")

Creating Associated Token Account

from solders.token.associated import create_associated_token_account_instruction
from solders.transaction import Transaction
from solders.keypair import Keypair

# Create associated token account
payer = Keypair()
owner = Keypair()
mint = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")  # USDC mint

# Check if ATA already exists (in real usage)
ata = get_associated_token_address(owner.pubkey(), mint)

# Create instruction to initialize ATA
create_ata_ix = create_associated_token_account_instruction(
    payer=payer.pubkey(),
    owner=owner.pubkey(),
    mint=mint
)

# Add to transaction
tx = Transaction.new_with_payer([create_ata_ix], payer.pubkey())

Token Transfer Setup

from solders.instruction import Instruction, AccountMeta
from solders.token import ID as TOKEN_PROGRAM_ID

# Token transfer instruction data (instruction discriminator + amount)
def create_transfer_instruction(
    source: Pubkey,
    destination: Pubkey,
    owner: Pubkey,
    amount: int
) -> Instruction:
    """Create SPL token transfer instruction."""
    
    # Transfer instruction data: [3, amount_bytes...]
    instruction_data = bytearray([3])  # Transfer instruction discriminator
    instruction_data.extend(amount.to_bytes(8, 'little'))
    
    accounts = [
        AccountMeta(source, is_signer=False, is_writable=True),      # Source account
        AccountMeta(destination, is_signer=False, is_writable=True), # Destination account
        AccountMeta(owner, is_signer=True, is_writable=False),      # Owner authority
    ]
    
    return Instruction(TOKEN_PROGRAM_ID, accounts, bytes(instruction_data))

# Transfer 100 tokens (with 6 decimals = 100,000,000 raw amount)
source_ata = get_associated_token_address(sender.pubkey(), mint)
dest_ata = get_associated_token_address(recipient.pubkey(), mint)

transfer_ix = create_transfer_instruction(
    source=source_ata,
    destination=dest_ata,
    owner=sender.pubkey(),
    amount=100_000_000  # 100 tokens with 6 decimals
)

Multisig Operations

from solders.token.state import Multisig

# Parse multisig account
multisig_data = bytes(355)  # Retrieved from RPC call

try:
    multisig = Multisig.deserialize(multisig_data)
    
    print(f"Required signatures: {multisig.m}")
    print(f"Total signers: {multisig.n}")
    print(f"Signers: {[str(s) for s in multisig.signers]}")
    
    # Check if specific signer is authorized
    potential_signer = Pubkey.from_string("...")
    if multisig.is_valid_signer(potential_signer):
        print("Authorized signer")
    
    # Check if we have enough signatures
    provided_signatures = [signer1.pubkey(), signer2.pubkey()]
    if multisig.has_sufficient_signatures(provided_signatures):
        print("Sufficient signatures for execution")
        
except ValueError as e:
    print(f"Failed to parse multisig: {e}")

Token Account State Management

# Check token account state
if token_account.state == TokenAccountState.Initialized:
    print("Account is ready for transfers")
elif token_account.state == TokenAccountState.Frozen:
    print("Account is frozen - cannot transfer")
elif token_account.state == TokenAccountState.Uninitialized:
    print("Account needs initialization")

# Check for delegation
if token_account.delegate:
    print(f"Tokens delegated to: {token_account.delegate}")
    print(f"Delegated amount: {token_account.delegated_amount}")
    
    # Remaining balance available to owner
    owner_available = token_account.amount - token_account.delegated_amount
    print(f"Owner available: {owner_available}")

# Check for wrapped SOL (native token account)
if token_account.is_native:
    print(f"Wrapped SOL account with {token_account.is_native} lamports")

Token Metadata and Extensions

# Working with Token-2022 extended tokens
from solders.token import TOKEN_2022_PROGRAM_ID

# Calculate ATA for Token-2022 program
token_2022_ata = get_associated_token_address_with_program_id(
    owner=owner.pubkey(),
    mint=mint,
    program_id=TOKEN_2022_PROGRAM_ID
)

# Different program IDs create different ATAs for same owner+mint combination
spl_token_ata = get_associated_token_address(owner.pubkey(), mint)
print(f"SPL Token ATA: {spl_token_ata}")
print(f"Token-2022 ATA: {token_2022_ata}")

Token Program Constants

Well-Known Mints

# Wrapped SOL (native token)
NATIVE_MINT: Final[Pubkey] = Pubkey.from_string("So11111111111111111111111111111111111111112")

# Common stablecoins (examples)
USDC_MINT: Final[Pubkey] = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
USDT_MINT: Final[Pubkey] = Pubkey.from_string("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")

Account Sizes

# Account data sizes
TOKEN_ACCOUNT_SIZE: Final[int] = 165    # SPL token account
MINT_ACCOUNT_SIZE: Final[int] = 82      # SPL token mint
MULTISIG_ACCOUNT_SIZE: Final[int] = 355 # Multisig account (11 signers)

# Minimum rent-exempt balance calculation
def calculate_rent_exempt_balance(account_size: int) -> int:
    """Calculate minimum lamports for rent exemption."""
    # This is a simplified calculation - use Rent sysvar in production
    return 890880 + (account_size * 6960)  # Approximate values

Instruction Discriminators

# SPL Token instruction types
class TokenInstruction:
    InitializeMint = 0
    InitializeAccount = 1
    InitializeMultisig = 2
    Transfer = 3
    Approve = 4
    Revoke = 5
    SetAuthority = 6
    MintTo = 7
    Burn = 8
    CloseAccount = 9
    FreezeAccount = 10
    ThawAccount = 11
    TransferChecked = 12
    ApproveChecked = 13
    MintToChecked = 14
    BurnChecked = 15

Error Handling

Token Operation Errors

from solders.token.state import TokenAccount, TokenAccountState

def validate_token_transfer(
    source_account: TokenAccount,
    amount: int,
    owner: Pubkey
) -> bool:
    """Validate token transfer before sending."""
    
    # Check account state
    if source_account.state != TokenAccountState.Initialized:
        raise ValueError("Source account not initialized")
    
    if source_account.state == TokenAccountState.Frozen:
        raise ValueError("Source account is frozen")
    
    # Check balance
    if source_account.amount < amount:
        raise ValueError(f"Insufficient balance: {source_account.amount} < {amount}")
    
    # Check ownership or delegation
    if source_account.owner != owner and source_account.delegate != owner:
        raise ValueError("Not authorized to transfer from this account")
    
    # Check delegate limits
    if source_account.delegate == owner:
        if source_account.delegated_amount < amount:
            raise ValueError("Amount exceeds delegated limit")
    
    return True

# Usage in transaction building
try:
    validate_token_transfer(source_token_account, transfer_amount, sender.pubkey())
    # Proceed with creating transfer instruction
except ValueError as e:
    print(f"Transfer validation failed: {e}")

Account Parsing Errors

def safe_parse_token_account(data: bytes) -> Optional[TokenAccount]:
    """Safely parse token account data with error handling."""
    try:
        if len(data) != TOKEN_ACCOUNT_SIZE:
            print(f"Invalid token account size: {len(data)} bytes")
            return None
        
        return TokenAccount.deserialize(data)
    except ValueError as e:
        print(f"Token account parsing error: {e}")
        return None
    except Exception as e:
        print(f"Unexpected error parsing token account: {e}")
        return None

def safe_parse_mint(data: bytes) -> Optional[Mint]:
    """Safely parse mint account data with error handling."""
    try:
        if len(data) != MINT_ACCOUNT_SIZE:
            print(f"Invalid mint size: {len(data)} bytes")
            return None
        
        return Mint.deserialize(data)
    except ValueError as e:
        print(f"Mint parsing error: {e}")
        return None
    except Exception as e:
        print(f"Unexpected error parsing mint: {e}")
        return None

Install with Tessl CLI

npx tessl i tessl/pypi-solders

docs

account-management.md

cryptographic-primitives.md

error-handling.md

index.md

network-sysvars.md

rpc-functionality.md

system-programs.md

testing-infrastructure.md

token-operations.md

transaction-construction.md

transaction-status.md

tile.json