HOME CONCEPT GRANTS BLOG COMMUNITY

ATP Utils v0.1.0 Release

Introducing the ATP Utils v0.1.0 release, featuring core utilities for blockchain interoperability and Internet Computer development.

By Mycel - 2025-08-06

ATP Utils v0.1.0 Release

We’re excited to announce the release of ATP (Account Transfer Protocol) utility packages! This release includes four core utilities that provide essential functionality for blockchain interoperability and Internet Computer development.

🎯 Overview

Building multi-chain applications has never been easier! The ATP Utils ecosystem provides everything you need to create sophisticated blockchain applications with seamless database management:

  • 🔗 Multi-Chain by Design: Use standardized CAIP identifiers to work with Bitcoin, Ethereum, Solana, and more
  • 💾 Persistent Storage: ic-nosql handles all your database needs with stable memory that survives canister upgrades
  • ⚡ Type-Safe Operations: Full Rust type safety across chains, assets, and database operations

The Power of ATP + ic-nosql: Instead of building complex database schemas and multi-chain logic from scratch, you can focus on your business logic while ATP handles cross-chain standardization and ic-nosql manages persistent data storage. Build everything from simple wallets to sophisticated DEX order books with minimal boilerplate code.

📦 Released Packages

atp-caip

📦 Crates.io: https://crates.io/crates/atp-caip

Chain Agnostic Improvement Proposals (CAIP) implementation for ATP. Provides standardized identifiers and primitives for multi-chain operations:

  • Account ID, Asset ID, and Chain ID implementations
  • Cross-chain validation utilities
  • Money and token pair primitives
  • WebAssembly compatible

Example:

use atp_caip::{ChainId, AssetId, Money, TokenPair};

// Create chain identifiers
let ethereum = ChainId::new("eip155", "1")?;
let solana = ChainId::new("solana", "mainnet")?;

// Create asset identifiers
let eth = AssetId::new(ethereum.clone(), "slip44", "60")?;
let sol = AssetId::new(solana, "slip44", "501")?;

// Create money amounts with proper decimals
let eth_amount = Money::from_decimal_str("1.5", 18)?;
println!("ETH amount: {}", eth_amount); // "1.5"

// Create trading pairs
let cross_chain_pair = TokenPair::new(eth, sol);
println!("Cross-chain pair: {}", cross_chain_pair.is_cross_chain()); // true

atp-chain-registry

📦 Crates.io: https://crates.io/crates/atp-chain-registry

Chain registry for managing blockchain configurations and metadata:

  • Centralized configuration management for supported chains
  • Asset configuration with metadata
  • Query interface for chain discovery
  • Built on top of atp-caip standards

Example:

use atp_chain_registry::{ChainRegistry, ChainId, AssetId};

// Load the default configuration
let registry = ChainRegistry::default()?;

// Get chain ID from name
let chain_id = registry.get_chain_id_from_config("Ethereum Mainnet")?;
println!("Ethereum Mainnet ID: {}", chain_id); // eip155:1

// Get asset ID from symbol and chain
let asset_id = registry.get_asset_id_from_config("ETH", "eip155:1")?;
println!("ETH on Ethereum: {}", asset_id); // eip155:1/slip44:60

// Find cross-chain trading opportunities
let cross_chain_pairs = registry.get_cross_chain_pairs();
println!("Found {} cross-chain trading pairs", cross_chain_pairs.len());

atp-chain-utils

📦 Crates.io: https://crates.io/crates/atp-chain-utils

Chain-specific utilities for address handling and cryptographic operations:

  • Multi-chain address utilities (Bitcoin/BIP122, Ethereum/EIP155, Solana)
  • Cryptographic primitives (ECDSA, SHA, RIPEMD, Base58)
  • Compatible with atp-caip identifiers

Example:

use atp_chain_utils::address::generate_address;
use atp_caip::ChainId;

// Generate Ethereum address
let eth_pubkey = "04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235";
let eth_chain = ChainId::new("eip155", "1")?; // Ethereum mainnet
let eth_address = generate_address(eth_pubkey.to_string(), eth_chain)?;
println!("Ethereum address: {}", eth_address);

// Generate Solana address  
let sol_pubkey = "e258d6e13adfb7b6eb771e0c9e8b1e3d4e3f1a2b3c4d5e6f7a8b9c0d1e2f3a4b";
let sol_chain = ChainId::new("solana", "mainnet")?;
let sol_address = generate_address(sol_pubkey.to_string(), sol_chain)?;
println!("Solana address: {}", sol_address);

// Generate Bitcoin address
let btc_chain = ChainId::new("bip122", "000000000019d6689c085ae165831e93")?;
let btc_address = generate_address(eth_pubkey.to_string(), btc_chain)?;
println!("Bitcoin address: {}", btc_address);

ic-nosql

📦 Crates.io: https://crates.io/crates/ic-nosql

Flexible NoSQL database library for Internet Computer canisters:

  • Stable memory management for persistent storage
  • Database abstraction with repository pattern
  • Serialization utilities for Candid types
  • Model macros for simplified data structures

Example:

use ic_nosql::{define_model, DatabaseManager};
use candid::CandidType;
use serde::{Deserialize, Serialize};

// Define your model
define_model! {
    #[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
    pub struct User {
        pub id: String,
        pub username: String,
        pub email: String,
        pub created_at: u64,
    }
    primary_key: id -> String,
}

// Initialize database
let mut db_manager = DatabaseManager::new();
db_manager.register_model("users", Some(10), None)?;

// Create and insert user
let user = User {
    id: "user_123".to_string(),
    username: "alice".to_string(),
    email: "alice@example.com".to_string(),
    created_at: ic_cdk::api::time(),
};

db_manager.insert("users", &user.id, &user)?;

// Query user
let retrieved_user = db_manager.get::<User>("users", "user_123")?;
println!("Retrieved user: {:?}", retrieved_user);

🔧 Key Features

  • Multi-chain Support: Comprehensive utilities for Bitcoin, Ethereum, and Solana ecosystems
  • Internet Computer Native: Optimized for IC canister development with stable storage
  • WebAssembly Ready: All packages compile to WASM for web and IC deployment
  • Type Safety: Full Rust type safety with comprehensive error handling
  • Standardized: Built on CAIP standards for blockchain interoperability

🚀 Getting Started

Add to your Cargo.toml:

[dependencies]
atp-caip = "0.1.1"
atp-chain-registry = "0.1.1" 
atp-chain-utils = "0.1.1"
ic-nosql = "0.1.1"

💡 Use Cases

Cross-Chain Portfolio Management

use atp_caip::{ChainId, AssetId, Money, Asset};

// Set up chains and assets
let ethereum = ChainId::new("eip155", "1")?;
let solana = ChainId::new("solana", "mainnet")?;
let eth = AssetId::new(ethereum, "slip44", "60")?;
let sol = AssetId::new(solana, "slip44", "501")?;

// Create portfolio holdings
let eth_holding = Asset::new(eth, Money::from_decimal_str("2.5", 18)?);
let sol_holding = Asset::new(sol, Money::from_decimal_str("100.0", 9)?);

// Calculate total value
let total_value = eth_holding.usd_value(3000.0) + sol_holding.usd_value(150.0);
println!("Portfolio value: ${:.2}", total_value); // $22,500.00

Address Generation for Multiple Chains

use atp_chain_utils::address::generate_address;
use atp_caip::ChainId;

let pubkey = "04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235";

// Generate addresses for multiple chains
let chains = vec![
    ChainId::new("eip155", "1")?,      // Ethereum
    ChainId::new("eip155", "137")?,    // Polygon
    ChainId::new("bip122", "000000000019d6689c085ae165831e93")?, // Bitcoin
];

for chain in chains {
    let address = generate_address(pubkey.to_string(), chain.clone())?;
    println!("{}: {}", chain, address);
}

Cross-Chain Order Matchmaking Canister

use ic_nosql::{define_model, DatabaseManager};
use atp_caip::{ChainId, AssetId, Money, TokenPair};
use candid::CandidType;
use serde::{Deserialize, Serialize};

// Define order model for cross-chain trading
define_model! {
    #[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
    pub struct Order {
        pub id: String,
        pub user_id: String,
        pub from_asset: String,      // e.g., "eip155:1/slip44:60" (ETH)
        pub to_asset: String,        // e.g., "solana:mainnet/slip44:501" (SOL)
        pub from_amount: String,     // Decimal string format
        pub to_amount: String,       // Expected amount
        pub status: OrderStatus,
        pub created_at: u64,
        pub expires_at: u64,
    }
    primary_key: id -> String,
    secondary_key: from_asset -> String,
}

// Define matched trade model
define_model! {
    #[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
    pub struct Trade {
        pub id: String,
        pub maker_order_id: String,
        pub taker_order_id: String,
        pub asset_pair: String,      // e.g., "eip155:1/slip44:60-solana:mainnet/slip44:501"
        pub executed_amount: String,
        pub execution_rate: f64,
        pub status: TradeStatus,
        pub created_at: u64,
    }
    primary_key: id -> String,
    secondary_key: asset_pair -> String,
}

#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
pub enum OrderStatus {
    Open,
    PartiallyFilled,
    Filled,
    Cancelled,
    Expired,
}

#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
pub enum TradeStatus {
    Pending,
    Executed,
    Failed,
}

// Example usage: Cross-chain order matchmaking
#[ic_cdk::update]
async fn create_order(
    from_asset: String,
    to_asset: String,
    from_amount: String,
    to_amount: String,
    expires_in: u64,
) -> Result<String, String> {
    let order_id = generate_order_id();
    let user_id = ic_cdk::caller().to_string();
    
    let order = Order {
        id: order_id.clone(),
        user_id,
        from_asset: from_asset.clone(),
        to_asset: to_asset.clone(),
        from_amount: from_amount.clone(),
        to_amount,
        status: OrderStatus::Open,
        created_at: ic_cdk::api::time(),
        expires_at: ic_cdk::api::time() + expires_in,
    };

    DB_MANAGER.with(|db_manager| {
        let mut db = db_manager.borrow_mut();
        let db = db.as_mut().ok_or("Database not initialized")?;
        db.insert("orders", &order.id, &order)
    })?;

    // Try to find matching orders
    let matches = find_matching_orders(&from_asset, &to_asset, &from_amount)?;
    for matching_order in matches {
        execute_trade(&order_id, &matching_order.id).await?;
    }

    Ok(order_id)
}

#[ic_cdk::query]
fn get_cross_chain_pairs() -> Vec<String> {
    let registry = ChainRegistry::default().unwrap();
    registry.get_cross_chain_pairs()
        .into_iter()
        .map(|pair| pair.to_pair_string())
        .collect()
}

// Find orders that can match with the given order parameters
fn find_matching_orders(
    from_asset: &str,
    to_asset: &str,
    amount: &str,
) -> Result<Vec<Order>, String> {
    DB_MANAGER.with(|db_manager| {
        let db = db_manager.borrow();
        let db = db.as_ref().ok_or("Database not initialized")?;
        
        // Query orders with reverse asset pair (to_asset as from_asset)
        let response = db.query::<Order>("orders", 100, 1)?;
        let matching_orders: Vec<Order> = response.data
            .into_iter()
            .filter(|order| {
                order.from_asset == to_asset && 
                order.to_asset == from_asset &&
                order.status == OrderStatus::Open &&
                order.expires_at > ic_cdk::api::time()
            })
            .collect();
            
        Ok(matching_orders)
    })
}

async fn execute_trade(maker_id: &str, taker_id: &str) -> Result<String, String> {
    let trade_id = generate_trade_id();
    
    // Get both orders
    let (maker_order, taker_order) = DB_MANAGER.with(|db_manager| {
        let db = db_manager.borrow();
        let db = db.as_ref().ok_or("Database not initialized")?;
        let maker = db.get::<Order>("orders", maker_id)?.ok_or("Maker order not found")?;
        let taker = db.get::<Order>("orders", taker_id)?.ok_or("Taker order not found")?;
        Ok::<_, String>((maker, taker))
    })?;

    // Create trading pair for rate calculation
    let from_asset = AssetId::from_str(&maker_order.from_asset)?;
    let to_asset = AssetId::from_str(&maker_order.to_asset)?;
    let pair = TokenPair::new(from_asset, to_asset);

    let trade = Trade {
        id: trade_id.clone(),
        maker_order_id: maker_id.to_string(),
        taker_order_id: taker_id.to_string(),
        asset_pair: pair.to_pair_string(),
        executed_amount: maker_order.from_amount.clone(),
        execution_rate: calculate_exchange_rate(&maker_order, &taker_order)?,
        status: TradeStatus::Pending,
        created_at: ic_cdk::api::time(),
    };

    DB_MANAGER.with(|db_manager| {
        let mut db = db_manager.borrow_mut();
        let db = db.as_mut().ok_or("Database not initialized")?;
        db.insert("trades", &trade.id, &trade)
    })?;

    // Update order statuses
    update_order_status(maker_id, OrderStatus::Filled)?;
    update_order_status(taker_id, OrderStatus::Filled)?;

    Ok(trade_id)
}

📚 Repository

https://github.com/mycel-labs/atp

📄 License

MIT License - See individual package licenses for details.


Built by Mycel Labs for the ATP ecosystem