DEV Community

Ycrypto-3
Ycrypto-3

Posted on

Mint APL Token guide

๐Ÿ› ๏ธ Local Validator Setup

Set up a complete local development environment with Bitcoin Core, Titan indexer, and Arch validator running locally.

๐ŸŽฏ What This Setup Provides

  • Bitcoin Core in regtest mode (local Bitcoin network)
  • Titan Indexer for Bitcoin data indexing
  • Arch Validator connected to your local infrastructure
  • Complete isolation from external networks
  • Fast block times and instant transactions

๐Ÿ“‹ Prerequisites

Before starting, ensure you have:


๐Ÿš€ Step-by-Step Setup

Step 1: Start Bitcoin Core in Regtest Mode

Start your local Bitcoin network:

# Start Bitcoin Core in regtest mode
bitcoind -regtest -port=18444 -rpcport=18443 \
    -rpcuser=bitcoin -rpcpassword=bitcoinpass \
    -fallbackfee=0.001
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Creates a local Bitcoin network (regtest)
  • Sets up RPC on port 18443
  • Configures basic authentication
  • Sets a fallback fee for transactions

๐Ÿ’ก Note: Keep this terminal open - Bitcoin Core needs to keep running

Step 2: First-Time Wallet Setup

In a new terminal, set up your Bitcoin wallet:

# Create a wallet called "testwallet"
bitcoin-cli -regtest -rpcuser=bitcoin -rpcpassword=bitcoinpass createwallet testwallet

# Generate an address and mine the first 100 blocks to it
ADDRESS=$(bitcoin-cli -regtest -rpcuser=bitcoin -rpcpassword=bitcoinpass getnewaddress)
bitcoin-cli -regtest -rpcuser=bitcoin -rpcpassword=bitcoinpass generatetoaddress 100 $ADDRESS
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Creates a wallet for managing Bitcoin
  • Generates the first 100 blocks (needed for Bitcoin to be spendable)
  • Funds the wallet with initial Bitcoin

โš ๏ธ Important: This step is only needed the first time you set up Bitcoin Core

Step 3: Clone and Configure Titan Indexer

Set up the Titan indexer (Bitcoin data processor):

# Clone Titan indexer (if not already done)
git clone https://github.com/saturnbtc/Titan.git
cd Titan
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure Titan for Regtest

Important: Configure Titan for proper regtest operation:

# Navigate to the cache configuration file
cd Titan/indexer/src/index/updater/

# Open cache.rs in your editor
# Find lines around 76-80 and ensure this code is commented out:
Enter fullscreen mode Exit fullscreen mode

Open Titan/indexer/src/index/updater/cache.rs and ensure these lines are commented out:

// In regtest, the first block is not accessible via RPC and for testing purposes we
// don't need to start at block 0.
// if block_count == 0 && settings.chain == Chain::Regtest {
//     block_count = 1;
// }
Enter fullscreen mode Exit fullscreen mode

Why this is needed:

  • This prevents Titan from skipping block 0 in regtest mode
  • Ensures proper synchronization with your local Bitcoin node
  • Avoids indexing issues in local development

Step 5: Build Titan Indexer

Build the Titan project:

# Return to Titan root directory
cd Titan

# Build the project (this may take a few minutes)
cargo build --release
Enter fullscreen mode Exit fullscreen mode

Step 6: Start Titan Indexer

In a new terminal, start the Titan indexer:

# Navigate to Titan directory
cd Titan

# Start Titan indexer pointing to your Bitcoin node
cargo run --bin titan -- \
    --bitcoin-rpc-url http://127.0.0.1:18443 \
    --bitcoin-rpc-username bitcoin \
    --bitcoin-rpc-password bitcoinpass \
    --chain regtest \
    --index-addresses \
    --index-bitcoin-transactions \
    --enable-tcp-subscriptions \
    --main-loop-interval 0 \
    --http-listen 127.0.0.1:3030
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Connects to your local Bitcoin node
  • Indexes Bitcoin addresses and transactions
  • Provides HTTP API on port 3030
  • Enables real-time subscriptions

๐Ÿ’ก Tip: You should see logs showing Titan connecting to Bitcoin and processing blocks

Step 7: Start Arch Validator

In a new terminal, start the Arch validator:

# Start validator pointing to your local Titan
cli validator-start \
    --network-mode devnet \
    --data-dir ./.arch_data \
    --rpc-bind-ip 127.0.0.1 \
    --rpc-bind-port 9002 \
    --titan-endpoint http://127.0.0.1:3030 \
    --titan-socket-endpoint 127.0.0.1:3030
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Starts Arch validator in devnet mode
  • Connects to your local Titan indexer
  • Provides RPC API on port 9002
  • Stores data in ./.arch_data directory

โœ… Verify Your Setup

Check Bitcoin Core

# Check Bitcoin Core is running
bitcoin-cli -regtest -rpcuser=bitcoin -rpcpassword=bitcoinpass getblockchaininfo
Enter fullscreen mode Exit fullscreen mode

Check Titan Indexer

# Check Titan is running
curl http://127.0.0.1:3030/health
Enter fullscreen mode Exit fullscreen mode

Check Arch Validator

# Check validator is running
cli get-block-height
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฎ Using Your Local Environment

Create and Fund Accounts

# Create a new account
cli account create --keypair-path ./dev-wallet.json --airdrop 1000000000

# Check account balance
cli show <ACCOUNT_ADDRESS>
Enter fullscreen mode Exit fullscreen mode

Mine New Bitcoin Blocks (When Needed)

# Generate 10 new blocks
bitcoin-cli -regtest -rpcuser=bitcoin -rpcpassword=bitcoinpass -generate 10
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ง Configuration Reference

Port Usage

Service Port Purpose
Bitcoin Core 18443 RPC API
Bitcoin Core 18444 P2P networking
Titan Indexer 3030 HTTP API
Arch Validator 9002 RPC API

๐Ÿ›‘ Stopping the Environment

To cleanly shut down your local environment:

  1. Stop Arch Validator: Ctrl+C in validator terminal
  2. Stop Titan Indexer: Ctrl+C in Titan terminal
  3. Stop Bitcoin Core: Ctrl+C in Bitcoin terminal

Clean Reset (If Needed)

# Remove all data and start fresh
rm -rf ./.arch_data
rm -rf ~/.bitcoin/regtest

# Then repeat the setup process
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” Troubleshooting

Bitcoin Core Won't Start

  • Check if port 18443 is already in use: lsof -i :18443
  • Try different ports or kill existing processes

Titan Can't Connect to Bitcoin

  • Ensure Bitcoin Core is running first
  • Check the RPC credentials match
  • Verify Bitcoin Core is accepting connections
  • Make sure you commented out the regtest block skip code in cache.rs

Validator Won't Start

  • Ensure Titan is running and responding on port 3030
  • Check that no other validator is using port 9002
  • Verify the data directory permissions

Titan Indexing Issues

  • Verify the regtest configuration in cache.rs is properly commented out
  • Check Bitcoin Core has generated at least 100 blocks
  • Restart Titan if you made configuration changes

Performance Issues

  • Allocate more CPU/RAM if running in a VM
  • Use SSD storage for better performance
  • Close unnecessary applications

๐ŸŽฏ Next Steps

Once your local environment is running:

  1. Deploy the test token program: Follow the Token Guide
  2. Try the examples: Explore the arch-examples repository
  3. Build your own program: Start with the Program Development Guide

๐Ÿ’ก Pro Tips

  • Keep terminals organized: Use terminal tabs or tmux for easier management
  • Monitor logs: Watch for errors in all three services
  • Backup important data: Save keypairs and important addresses
  • Use scripts: Create shell scripts to automate the startup process
  • Double-check Titan config: Always verify the regtest configuration is properly set

Your local Arch Network development environment is now ready! ๐Ÿš€

๐Ÿช™ Creating Fungible Tokens on Arch Network

Build Arch Network tokens using the powerful APL Token Program - your gateway to tokenization on Arch Network!

๐ŸŽฏ What You'll Build

By following this guide, you'll create a complete fungible token system with:

  • โœ… Custom token mint with your own supply and decimals
  • โœ… Token accounts for holding and managing tokens
  • โœ… Minting capabilities to create new tokens
  • โœ… Transfer system between accounts
  • โœ… Advanced features like delegation, burning, and freezing

๐ŸŒŸ Why APL Tokens?

The Arch Program Library (APL) Token Program brings proven token standards to Bitcoin:

Feature Benefit
SPL Compatible Based on Solana's battle-tested token standard
Bitcoin Security All operations recorded on Bitcoin blockchain
Full-Featured Minting, transfers, burning, delegation, multisig
Developer Friendly Familiar APIs and comprehensive tooling

๐Ÿ“‹ Prerequisites

Ensure you have these tools installed:

APL Token Program ID

apl-token00000000000000000000000
Enter fullscreen mode Exit fullscreen mode

This is the fixed program ID for all APL token operations.


๐Ÿš€ Project Setup

Step 1: Create Your Project

# Create project directory
mkdir my-arch-token
cd my-arch-token

# Initialize Rust project
cargo init --bin
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure Dependencies

Update your Cargo.toml:

[package]
name = "my_arch_token"
version = "0.1.0"
edition = "2021"

[dependencies]
# Core Arch Network dependencies
arch_sdk = "0.5.2"
arch_program = "0.5.2"
arch_test_sdk = "0.5.2"

# APL Token Programs
apl-token = { version = "0.5.2", features = ["no-entrypoint"] }


# Serialization and utilities
borsh = { version = "1.5.1", features = ["derive"] }
bitcoincore-rpc = "0.18.0"
bitcoin = { version = "0.32.3", features = ["serde", "rand"] }
hex = "0.4.3"
log = "0.4"
env_logger = "0.10"

[dev-dependencies]
serial_test = "3.1.1"
Enter fullscreen mode Exit fullscreen mode

๐Ÿ—๏ธ Core Token Operations

๐ŸŽฏ Step 1: Create Your Token Mint

The mint is the master account that defines your token's properties:

use apl_token::state::{Mint, Account};
use arch_program::{program_pack::Pack, sanitized::ArchMessage};
use arch_sdk::{build_and_sign_transaction, generate_new_keypair, ArchRpcClient, Status};
use arch_test_sdk::{
    constants::{ BITCOIN_NETWORK,NODE1_ADDRESS},
    helper::{create_and_fund_account_with_faucet, read_account_info, send_transactions_and_wait},
};
use log::info;


fn create_token_mint(client: &ArchRpcClient) -> Result<(bitcoin::key::Keypair, arch_program::pubkey::Pubkey), Box<dyn std::error::Error>> {
    // 1. Create mint authority (you control the token supply)
    let (authority_keypair, authority_pubkey, _) = generate_new_keypair(BITCOIN_NETWORK);
    create_and_fund_account_with_faucet(&authority_keypair, BITCOIN_NETWORK);

    // 2. Create mint account
    let (token_mint_keypair, token_mint_pubkey, _) = generate_new_keypair(BITCOIN_NETWORK);

    // 3. Create the mint account on-chain
    let create_account_ix = arch_program::system_instruction::create_account(
        &authority_pubkey,       // Payer
        &token_mint_pubkey,      // New account
        arch_program::account::MIN_ACCOUNT_LAMPORTS, // Rent
        Mint::LEN as u64,        // Space needed
        &apl_token::id(),        // Owner program
    );

    // 4. Initialize the mint with your token parameters
    let initialize_mint_ix = apl_token::instruction::initialize_mint(
        &apl_token::id(),
        &token_mint_pubkey,
        &authority_pubkey,       // Mint authority (can create tokens)
        None,                   // No freeze authority (optional)
        9,                      // Decimals (9 = like USDC, 0 = whole numbers only)
    )?;

    // 5. Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &[create_account_ix, initialize_mint_ix],
            Some(authority_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![authority_keypair, token_mint_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to create token mint".into());
    }

    println!("๐ŸŽ‰ Token mint created: {}", token_mint_pubkey);

    Ok((authority_keypair, token_mint_pubkey))
}

Enter fullscreen mode Exit fullscreen mode

๐Ÿฆ Step 2: Create Token Accounts

Token accounts hold the actual token balances for users:

fn mint_tokens(
    client: &ArchRpcClient,
    mint_pubkey: &arch_program::pubkey::Pubkey,
    account_pubkey: &arch_program::pubkey::Pubkey,
    authority_keypair: bitcoin::key::Keypair,
    amount: u64,
) {
    let authority_pubkey = arch_program::pubkey::Pubkey::from_slice(
        &authority_keypair.x_only_public_key().0.serialize()
    );

    // Create mint instruction
    let mint_ix = apl_token::instruction::mint_to(
        &apl_token::id(),
        mint_pubkey,
        account_pubkey,
        &authority_pubkey,
        &[],                    // No additional signers for single authority
        amount,                 // Amount to mint (in smallest units)
    ).unwrap();

    // Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &[mint_ix],
            Some(authority_pubkey),
            client.get_best_block_hash().unwrap(),
        ),
        vec![authority_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    assert_eq!(processed_txs[0].status, Status::Processed);

    println!("๐Ÿช™ Minted {} tokens", amount);
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ฐ Step 3: Mint Tokens

Create new tokens and add them to an account:

fn mint_tokens(
    client: &ArchRpcClient,
    mint_pubkey: &arch_program::pubkey::Pubkey,
    account_pubkey: &arch_program::pubkey::Pubkey,
    authority_pubkey: &arch_program::pubkey::Pubkey,
    authority_keypair: bitcoin::key::Keypair,
    amount: u64,
) -> Result<(), Box<dyn std::error::Error>> {

    // Create mint instruction
    let mint_ix = apl_token::instruction::mint_to(
        &apl_token::id(),
        mint_pubkey,
        account_pubkey,
        authority_pubkey,
        &[],                    // No additional signers for single authority
        amount,                 // Amount to mint (in smallest units)
    )?;

    // Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &[mint_ix],
            Some(*authority_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![authority_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to mint tokens".into());
    }

    println!("๐Ÿช™ Minted {} tokens", amount);
    Ok(())
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ธ Step 4: Transfer Tokens

Send tokens between accounts:

fn transfer_tokens(
    client: &ArchRpcClient,
    from_account: &arch_program::pubkey::Pubkey,
    to_account: &arch_program::pubkey::Pubkey,
    owner_pubkey: &arch_program::pubkey::Pubkey,
    owner_keypair: bitcoin::key::Keypair,
    amount: u64,
) -> Result<(), Box<dyn std::error::Error>> {

    // Create transfer instruction
    let transfer_ix = apl_token::instruction::transfer(
        &apl_token::id(),
        from_account,
        to_account,
        owner_pubkey,
        &[],                    // No additional signers
        amount,
    )?;

    // Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &[transfer_ix],
            Some(*owner_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![owner_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to transfer tokens".into());
    }

    println!("๐Ÿ“ค Transferred {} tokens", amount);
    Ok(())
}

Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ฅ Advanced Token Features

๐Ÿค Delegate Token Spending

Allow another account to spend tokens on your behalf:

fn approve_delegate(
    client: &ArchRpcClient,
    token_account: &arch_program::pubkey::Pubkey,
    delegate_pubkey: &arch_program::pubkey::Pubkey,
    owner_keypair: bitcoin::key::Keypair,
    amount: u64,
) {
    let owner_pubkey = arch_program::pubkey::Pubkey::from_slice(
        &owner_keypair.x_only_public_key().0.serialize()
    );

    let approve_ix = apl_token::instruction::approve(
        &apl_token::id(),
        token_account,
        delegate_pubkey,
        &owner_pubkey,
        &[&owner_pubkey],
        amount,
    ).unwrap();

    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &[approve_ix],
            Some(owner_pubkey),
            client.get_best_block_hash().unwrap(),
        ),
        vec![owner_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    assert_eq!(processed_txs[0].status, Status::Processed);

    println!("โœ… Approved {} tokens for delegation", amount);
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ฅ Burn Tokens

Permanently remove tokens from circulation:

fn burn_tokens(
    client: &ArchRpcClient,
    token_account: &arch_program::pubkey::Pubkey,
    mint_pubkey: &arch_program::pubkey::Pubkey,
    owner_pubkey: &arch_program::pubkey::Pubkey,
    owner_keypair: bitcoin::key::Keypair,
    amount: u64,
) -> Result<(), Box<dyn std::error::Error>> {

    let burn_ix = apl_token::instruction::burn(
        &apl_token::id(),
        token_account,
        mint_pubkey,
        owner_pubkey,
        &[],
        amount,
    )?;

    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &[burn_ix],
            Some(*owner_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![owner_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to burn tokens".into());
    }

    println!("๐Ÿ”ฅ Burned {} tokens", amount);
    Ok(())
}

Enter fullscreen mode Exit fullscreen mode

๐ŸŽฏ Complete Example

Here's a full implementation showing the complete token lifecycle:

๐Ÿ’ป Click to view complete example

use apl_token::state::{Mint, Account};
use arch_program::{program_pack::Pack, sanitized::ArchMessage};
use arch_sdk::{build_and_sign_transaction, generate_new_keypair, ArchRpcClient, Status};
use arch_test_sdk::{
    constants::{ BITCOIN_NETWORK,NODE1_ADDRESS},
    helper::{create_and_fund_account_with_faucet, read_account_info, send_transactions_and_wait},
};
use log::info;
// const BITCOIN_NETWORK: Network = Network::Testnet;

fn main() -&gt; Result&lt;(), Box&gt; {
    // Initialize logging
    env_logger::init();
    info!("๐Ÿš€ Starting test_token program...");

    let client = ArchRpcClient::new(NODE1_ADDRESS);
    println!("๐Ÿ“ก Connected to node: {}", NODE1_ADDRESS);
    println!("๐ŸŒ Using network: {:?}", BITCOIN_NETWORK);

    // Run the complete token lifecycle
    run_token_lifecycle(&amp;client)?;

    println!("๐ŸŽ‰ Token lifecycle completed successfully!");
    Ok(())
}

fn run_token_lifecycle(client: &amp;ArchRpcClient) -&gt; Result&lt;(), Box&gt; {
    // Step 1: Create token mint
    println!("\n๐Ÿ“‹ Step 1: Creating token mint...");
    let (authority_keypair, token_mint_pubkey) = create_token_mint(client)?;

    // Step 2: Create user accounts
    println!("\n๐Ÿ‘ฅ Step 2: Creating user accounts...");
    let (user1_keypair, user1_pubkey, _) = generate_new_keypair(BITCOIN_NETWORK);
    let (user2_keypair, user2_pubkey, _) = generate_new_keypair(BITCOIN_NETWORK);

    create_and_fund_account_with_faucet(&amp;user1_keypair, BITCOIN_NETWORK);
    create_and_fund_account_with_faucet(&amp;user2_keypair, BITCOIN_NETWORK);

    // Step 3: Create token accounts
    println!("\n๐Ÿ’ณ Step 3: Creating token accounts...");
    let user1_token_account = create_token_account(client, token_mint_pubkey, user1_keypair)?;
    let user2_token_account = create_token_account(client, token_mint_pubkey, user2_keypair)?;

    // Step 4: Mint initial tokens
    println!("\n๐Ÿช™ Step 4: Minting initial supply...");
    let authority_pubkey = arch_program::pubkey::Pubkey::from_slice(
        &amp;authority_keypair.x_only_public_key().0.serialize()
    );
    mint_tokens(client, &amp;token_mint_pubkey, &amp;user1_token_account, &amp;authority_pubkey, authority_keypair, 1_000_000_000)?; // 1,000 tokens (9 decimals)

    // Step 5: Check balance
    println!("\n๐Ÿ’ฐ Step 5: Checking balances...");
    let user1_balance = get_token_balance(user1_token_account)?;
    println!("User1 balance: {} tokens", user1_balance as f64 / 1_000_000_000.0);

    // Step 6: Transfer tokens
    println!("\n๐Ÿ“ค Step 6: Transferring tokens...");
    transfer_tokens(client, &amp;user1_token_account, &amp;user2_token_account, &amp;user1_pubkey, user1_keypair, 500_000_000)?; // 500 tokens

    // Step 7: Check final balances
    println!("\n๐Ÿ Step 7: Final balances...");
    let user1_final = get_token_balance(user1_token_account)?;
    let user2_final = get_token_balance(user2_token_account)?;

    println!("User1 final balance: {} tokens", user1_final as f64 / 1_000_000_000.0);
    println!("User2 final balance: {} tokens", user2_final as f64 / 1_000_000_000.0);

    // Step 8: Demonstrate burning tokens
    println!("\n๐Ÿ”ฅ Step 8: Burning some tokens...");
    burn_tokens(client, &amp;user2_token_account, &amp;token_mint_pubkey, &amp;user2_pubkey, user2_keypair, 100_000_000)?; // Burn 100 tokens

    let user2_after_burn = get_token_balance(user2_token_account)?;
    println!("User2 balance after burn: {} tokens", user2_after_burn as f64 / 1_000_000_000.0);

    Ok(())
}

fn create_token_mint(client: &amp;ArchRpcClient) -&gt; Result&lt;(bitcoin::key::Keypair, arch_program::pubkey::Pubkey), Box&gt; {
    // 1. Create mint authority (you control the token supply)
    let (authority_keypair, authority_pubkey, _) = generate_new_keypair(BITCOIN_NETWORK);
    create_and_fund_account_with_faucet(&amp;authority_keypair, BITCOIN_NETWORK);

    // 2. Create mint account
    let (token_mint_keypair, token_mint_pubkey, _) = generate_new_keypair(BITCOIN_NETWORK);

    // 3. Create the mint account on-chain
    let create_account_ix = arch_program::system_instruction::create_account(
        &amp;authority_pubkey,       // Payer
        &amp;token_mint_pubkey,      // New account
        arch_program::account::MIN_ACCOUNT_LAMPORTS, // Rent
        Mint::LEN as u64,        // Space needed
        &amp;apl_token::id(),        // Owner program
    );

    // 4. Initialize the mint with your token parameters
    let initialize_mint_ix = apl_token::instruction::initialize_mint(
        &amp;apl_token::id(),
        &amp;token_mint_pubkey,
        &amp;authority_pubkey,       // Mint authority (can create tokens)
        None,                   // No freeze authority (optional)
        9,                      // Decimals (9 = like USDC, 0 = whole numbers only)
    )?;

    // 5. Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &amp;[create_account_ix, initialize_mint_ix],
            Some(authority_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![authority_keypair, token_mint_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to create token mint".into());
    }

    println!("๐ŸŽ‰ Token mint created: {}", token_mint_pubkey);

    Ok((authority_keypair, token_mint_pubkey))
}

fn create_token_account(
    client: &amp;ArchRpcClient,
    token_mint_pubkey: arch_program::pubkey::Pubkey,
    owner_keypair: bitcoin::key::Keypair,
) -&gt; Result&gt; {

    let owner_pubkey = arch_program::pubkey::Pubkey::from_slice(
        &amp;owner_keypair.x_only_public_key().0.serialize()
    );

    // 1. Create account keypair
    let (token_account_keypair, token_account_pubkey, _) = generate_new_keypair(BITCOIN_NETWORK);

    // 2. Create account on-chain
    let create_account_ix = arch_program::system_instruction::create_account(
        &amp;owner_pubkey,
        &amp;token_account_pubkey,
        arch_program::account::MIN_ACCOUNT_LAMPORTS,
        apl_token::state::Account::LEN as u64,
        &amp;apl_token::id(),
    );

    // 3. Initialize token account
    let initialize_account_ix = apl_token::instruction::initialize_account(
        &amp;apl_token::id(),
        &amp;token_account_pubkey,
        &amp;token_mint_pubkey,      // Which token this account holds
        &amp;owner_pubkey,           // Who owns this account
    )?;

    // 4. Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &amp;[create_account_ix, initialize_account_ix],
            Some(owner_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![owner_keypair, token_account_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to create token account".into());
    }

    println!("๐Ÿ’ณ Token account created: {}", token_account_pubkey);
    Ok(token_account_pubkey)
}

fn mint_tokens(
    client: &amp;ArchRpcClient,
    mint_pubkey: &amp;arch_program::pubkey::Pubkey,
    account_pubkey: &amp;arch_program::pubkey::Pubkey,
    authority_pubkey: &amp;arch_program::pubkey::Pubkey,
    authority_keypair: bitcoin::key::Keypair,
    amount: u64,
) -&gt; Result&lt;(), Box&gt; {

    // Create mint instruction
    let mint_ix = apl_token::instruction::mint_to(
        &amp;apl_token::id(),
        mint_pubkey,
        account_pubkey,
        authority_pubkey,
        &amp;[],                    // No additional signers for single authority
        amount,                 // Amount to mint (in smallest units)
    )?;

    // Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &amp;[mint_ix],
            Some(*authority_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![authority_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to mint tokens".into());
    }

    println!("๐Ÿช™ Minted {} tokens", amount);
    Ok(())
}

fn transfer_tokens(
    client: &amp;ArchRpcClient,
    from_account: &amp;arch_program::pubkey::Pubkey,
    to_account: &amp;arch_program::pubkey::Pubkey,
    owner_pubkey: &amp;arch_program::pubkey::Pubkey,
    owner_keypair: bitcoin::key::Keypair,
    amount: u64,
) -&gt; Result&lt;(), Box&gt; {

    // Create transfer instruction
    let transfer_ix = apl_token::instruction::transfer(
        &amp;apl_token::id(),
        from_account,
        to_account,
        owner_pubkey,
        &amp;[],                    // No additional signers
        amount,
    )?;

    // Send transaction
    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &amp;[transfer_ix],
            Some(*owner_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![owner_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to transfer tokens".into());
    }

    println!("๐Ÿ“ค Transferred {} tokens", amount);
    Ok(())
}

fn burn_tokens(
    client: &amp;ArchRpcClient,
    token_account: &amp;arch_program::pubkey::Pubkey,
    mint_pubkey: &amp;arch_program::pubkey::Pubkey,
    owner_pubkey: &amp;arch_program::pubkey::Pubkey,
    owner_keypair: bitcoin::key::Keypair,
    amount: u64,
) -&gt; Result&lt;(), Box&gt; {

    let burn_ix = apl_token::instruction::burn(
        &amp;apl_token::id(),
        token_account,
        mint_pubkey,
        owner_pubkey,
        &amp;[],
        amount,
    )?;

    let transaction = build_and_sign_transaction(
        ArchMessage::new(
            &amp;[burn_ix],
            Some(*owner_pubkey),
            client.get_best_block_hash()?,
        ),
        vec![owner_keypair],
        BITCOIN_NETWORK,
    );

    let processed_txs = send_transactions_and_wait(vec![transaction]);
    if processed_txs[0].status != Status::Processed {
        return Err("Failed to burn tokens".into());
    }

    println!("๐Ÿ”ฅ Burned {} tokens", amount);
    Ok(())
}

fn get_token_balance(token_account: arch_program::pubkey::Pubkey) -&gt; Result&gt; {
    let account_info = read_account_info(token_account);
    let account_data = Account::unpack(&amp;account_info.data)?;
    Ok(account_data.amount)
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“‹ Running Your Token Program


# Run your token program
cargo run
Enter fullscreen mode Exit fullscreen mode
Compiling my_arch_token v0.1.0 
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
     Running `target/debug/my_arch_token`
๐Ÿ“ก Connected to node: http://localhost:9002/
๐ŸŒ Using network: Regtest

๐Ÿ“‹ Step 1: Creating token mint...
๐ŸŽ‰ Token mint created: fd00247fdcf868d0df9663a898456094754ee77d64ebb73c5207fc11b7e0af98

๐Ÿ‘ฅ Step 2: Creating user accounts...

๐Ÿ’ณ Step 3: Creating token accounts...
๐Ÿ’ณ Token account created: 3b99cd1877aae73f14a7c544cbb205867be3be068a509cd38dff970e2ea7b10b
๐Ÿ’ณ Token account created: 447e9bdc9c4092be7ff9e23e6dd2e9a2803b4b4503197bbf1ca1e7cabed68960

๐Ÿช™ Step 4: Minting initial supply...
๐Ÿช™ Minted 1000000000 tokens

๐Ÿ’ฐ Step 5: Checking balances...
User1 balance: 1 tokens

๐Ÿ“ค Step 6: Transferring tokens...
๐Ÿ“ค Transferred 500000000 tokens

๐Ÿ Step 7: Final balances...
User1 final balance: 0.5 tokens
User2 final balance: 0.5 tokens

๐Ÿ”ฅ Step 8: Burning some tokens...
๐Ÿ”ฅ Burned 100000000 tokens
User2 balance after burn: 0.4 tokens
๐ŸŽ‰ Token lifecycle completed successfully!
Enter fullscreen mode Exit fullscreen mode
# Confirm it works
cli show <mint_address>
Enter fullscreen mode Exit fullscreen mode
Welcome to the Arch Network CLI
Public Key: fd00247fdcf868d0df9663a898456094754ee77d64ebb73c5207fc11b7e0af98
Balance: 0.000001024 ARCH
Owner: 61706c2d746f6b656e3030303030303030303030303030303030303030303030
Executable: false
UTXO: 0000000000000000000000000000000000000000000000000000000000000000:0
Data:
  01 00 00 00 4b 86 8a 25 ab 80 05 eb 19 d9 ce bf
  06 54 43 f7 1d 5d 45 f8 c2 bc 2a a3 6e 20 09 81
  d1 3e 58 37 00 e9 a4 35 00 00 00 00 09 01 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“š Key Concepts Reference

๐Ÿ—๏ธ Account Types

Account Type Purpose Size
Mint Account Token metadata and authorities Mint::LEN
Token Account Holds token balances Account::LEN
Multisig Account Shared authority management Variable

๐Ÿ”‘ Authority Types

Authority Powers Notes
Mint Authority Create new tokens Required for minting
Freeze Authority Freeze/thaw accounts Optional
Owner Control token account Can transfer, approve
Delegate Spend approved amounts Temporary permission

๐Ÿ’ก Best Practices

  • โœ… Use appropriate decimals (9 for most tokens, 0 for NFTs)
  • โœ… Implement proper error handling for all operations
  • โœ… Test thoroughly before mainnet deployment
  • โœ… Consider multisig for important authorities
  • โœ… Document your token specification and usage

๐ŸŽฏ Next Steps

๐Ÿ“– Learn More

๐Ÿ”ง Advanced Features

๐ŸŒ Deploy to Production


๐Ÿ†˜ Need Help?

Ready to tokenize on Bitcoin? Start building! ๐Ÿš€


The APL Token Program provides a robust, battle-tested foundation for tokenization on Arch Network, leveraging Bitcoin's security while maintaining compatibility with proven token standards.

Top comments (0)