🛠️ 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:
- ✅ Bitcoin Core installed - Download Bitcoin Core
- ✅ Rust 1.70+ - Install Rust
- ✅ Solana CLI 1.16+ (NOT 2.x) - Install Guide
- ✅ Git - Install Git
- ✅ Arch CLI - Download Latest
🚀 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
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
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
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:
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;
// }
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
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
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
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
Check Titan Indexer
# Check Titan is running
curl http://127.0.0.1:3030/health
Check Arch Validator
# Check validator is running
cli get-block-height
🎮 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>
Mine New Bitcoin Blocks (When Needed)
# Generate 10 new blocks
bitcoin-cli -regtest -rpcuser=bitcoin -rpcpassword=bitcoinpass -generate 10
🔧 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:
-
Stop Arch Validator:
Ctrl+C
in validator terminal -
Stop Titan Indexer:
Ctrl+C
in Titan terminal -
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
🔍 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:
- Deploy the test token program: Follow the Token Guide
- Try the examples: Explore the arch-examples repository
- 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:
- ✅ Rust 1.70+ - Install Rust
- ✅ Solana CLI 1.16+ (NOT 2.x) - Install Guide
- ✅ Arch Network CLI - Download Latest
- ✅ Running Arch validator - See Quick Start Guide
- ✅ Basic Rust knowledge - Understanding of structs and functions
APL Token Program ID
apl-token00000000000000000000000
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
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"
🏗️ 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))
}
🏦 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);
}
💰 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(())
}
💸 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(())
}
🔥 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);
}
🔥 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(())
}
🎯 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() -> Result<(), Box> {
// 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(&client)?;
println!("🎉 Token lifecycle completed successfully!");
Ok(())
}
fn run_token_lifecycle(client: &ArchRpcClient) -> Result<(), Box> {
// 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(&user1_keypair, BITCOIN_NETWORK);
create_and_fund_account_with_faucet(&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(
&authority_keypair.x_only_public_key().0.serialize()
);
mint_tokens(client, &token_mint_pubkey, &user1_token_account, &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, &user1_token_account, &user2_token_account, &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, &user2_token_account, &token_mint_pubkey, &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: &ArchRpcClient) -> Result<(bitcoin::key::Keypair, arch_program::pubkey::Pubkey), Box> {
// 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))
}
fn create_token_account(
client: &ArchRpcClient,
token_mint_pubkey: arch_program::pubkey::Pubkey,
owner_keypair: bitcoin::key::Keypair,
) -> Result> {
let owner_pubkey = arch_program::pubkey::Pubkey::from_slice(
&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(
&owner_pubkey,
&token_account_pubkey,
arch_program::account::MIN_ACCOUNT_LAMPORTS,
apl_token::state::Account::LEN as u64,
&apl_token::id(),
);
// 3. Initialize token account
let initialize_account_ix = apl_token::instruction::initialize_account(
&apl_token::id(),
&token_account_pubkey,
&token_mint_pubkey, // Which token this account holds
&owner_pubkey, // Who owns this account
)?;
// 4. Send transaction
let transaction = build_and_sign_transaction(
ArchMessage::new(
&[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: &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> {
// 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(())
}
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> {
// 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(())
}
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> {
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(())
}
fn get_token_balance(token_account: arch_program::pubkey::Pubkey) -> Result> {
let account_info = read_account_info(token_account);
let account_data = Account::unpack(&account_info.data)?;
Ok(account_data.amount)
}
📋 Running Your Token Program
# Run your token program
cargo run
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!
# Confirm it works
cli show <mint_address>
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
📚 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
- Associated Token Accounts - Simplified account management
- Multisig Authorities - Enhanced security patterns
- Token Metadata - Rich token information
🔧 Advanced Features
- Conditional Transfers - Smart transfer logic
- Token Swaps - Build decentralized exchanges
- Staking Programs - Token reward mechanisms
🌐 Deploy to Production
- Mainnet Deployment - Go live safely
- Security Audits - Protect your users
- Token Economics - Design sustainable systems
🆘 Need Help?
- 💬 Discord Community - Real-time support
- 📖 Complete Documentation - Detailed guides
- 🔍 APL Token Reference - API documentation
- 🐛 Report Issues - Bug reports and feature requests
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)