| Crates.io | atp-caip |
| lib.rs | atp-caip |
| version | 0.1.3 |
| created_at | 2025-08-05 08:06:12.088993+00 |
| updated_at | 2025-08-28 07:42:54.200486+00 |
| description | Chain Agnostic Improvement Proposals (CAIP) implementation for ATP(Account Transfer Protocol) |
| homepage | |
| repository | https://github.com/mycel-labs/atp |
| max_upload_size | |
| id | 1781700 |
| size | 124,985 |
A Rust implementation of Chain Agnostic Improvement Proposals (CAIP) standards for blockchain interoperability. This crate provides standardized identifiers and primitives for working with multiple blockchain networks in a chain-agnostic manner.
ATP CAIP implements the following CAIP standards:
The crate enables developers to build cross-chain applications by providing:
use atp_caip::{ChainId, AssetId, Money, TokenPair};
use std::str::FromStr;
// 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 usdc = AssetId::new(ethereum, "erc20", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")?;
let sol = AssetId::new(solana, "slip44", "501")?;
// Create money amounts with proper decimals
let eth_amount = Money::from_decimal_str("1.5", 18)?;
let usdc_amount = Money::from_decimal_str("1000.50", 6)?;
// Create trading pairs
let eth_usdc_pair = TokenPair::new(eth.clone(), usdc.clone());
let cross_chain_pair = TokenPair::new(eth, sol);
println!("ETH amount: {}", eth_amount); // "1.5"
println!("Cross-chain pair: {}", cross_chain_pair.is_cross_chain()); // true
Chain IDs follow the format namespace:reference and identify specific blockchain networks:
use atp_caip::ChainId;
// Ethereum mainnet
let ethereum = ChainId::new("eip155", "1")?;
assert_eq!(ethereum.to_string(), "eip155:1");
// Solana mainnet
let solana = ChainId::new("solana", "mainnet")?;
assert_eq!(solana.to_string(), "solana:mainnet");
// Parse from string
let chain = ChainId::from_str("eip155:1")?;
assert_eq!(chain.namespace(), "eip155");
assert_eq!(chain.reference(), "1");
Asset IDs follow the format chain_id/asset_namespace:asset_reference:
use atp_caip::{AssetId, AssetIdBase, ChainId};
let ethereum = ChainId::new("eip155", "1")?;
// Native ETH
let eth = AssetId::new(ethereum.clone(), "slip44", "60")?;
assert_eq!(eth.to_string(), "eip155:1/slip44:60");
// USDC token
let usdc = AssetId::new(
ethereum,
"erc20",
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
)?;
assert_eq!(usdc.to_string(), "eip155:1/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
// Chain-agnostic asset identifier
let asset_base = AssetIdBase::new("slip44", "60")?;
assert_eq!(asset_base.to_string(), "slip44:60");
Account IDs follow the format chain_id:account_address:
use atp_caip::{AccountId, ChainId};
let ethereum = ChainId::new("eip155", "1")?;
let account = AccountId::new(
ethereum,
"0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"
)?;
assert_eq!(account.to_string(), "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb");
assert_eq!(account.account_address(), "0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb");
The Money type provides precise decimal arithmetic for cryptocurrency amounts:
use atp_caip::Money;
use ethers_core::types::U256;
// Create from decimal string (human-readable)
let eth_amount = Money::from_decimal_str("1.5", 18)?;
assert_eq!(eth_amount.to_decimal_string(), "1.5");
// Create from raw amount (base units)
let wei_amount = Money::from_raw("1500000000000000000", 18)?;
assert_eq!(wei_amount.to_decimal_string(), "1.5");
// Arithmetic operations
let amount1 = Money::from_decimal_str("10.5", 6)?;
let amount2 = Money::from_decimal_str("5.25", 6)?;
let sum = amount1.add(&amount2)?;
assert_eq!(sum.to_decimal_string(), "15.75");
let difference = amount1.sub(&amount2)?;
assert_eq!(difference.to_decimal_string(), "5.25");
// Percentage calculations
let fee = amount1.percentage(3); // 3%
let basis_points_fee = amount1.basis_points(250); // 2.5%
// Convert to floating point (use with caution)
let float_value = amount1.to_f64();
// Check if zero
assert!(!amount1.is_zero());
assert!(Money::zero(18)?.is_zero());
Combines an asset identifier with a money amount:
use atp_caip::{Asset, AssetId, Money, ChainId};
let ethereum = ChainId::new("eip155", "1")?;
let eth_id = AssetId::new(ethereum, "slip44", "60")?;
let eth_amount = Money::from_decimal_str("2.5", 18)?;
let asset = Asset::new(eth_id, eth_amount);
// Calculate USD value given price per token
let usd_value = asset.usd_value(3000.0); // $3000 per ETH
assert_eq!(usd_value, 7500.0); // 2.5 * 3000
Represent trading relationships between two assets:
use atp_caip::{TokenPair, AssetId, ChainId};
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")?;
let pair = TokenPair::new(eth.clone(), sol.clone());
// Check if cross-chain
assert!(pair.is_cross_chain());
// Check if pair involves specific asset
assert!(pair.involves_asset(ð));
// Get the other asset in the pair
assert_eq!(pair.get_other_asset(ð), Some(&sol));
// Create reverse pair
let reverse_pair = pair.reverse();
assert_eq!(reverse_pair.from_asset, sol);
assert_eq!(reverse_pair.to_asset, eth);
// String representation
assert_eq!(pair.to_pair_string(), "eip155:1/slip44:60-solana:mainnet/slip44:501");
// Parse from string
let parsed_pair = TokenPair::from_pair_string("eip155:1/slip44:60-solana:mainnet/slip44:501")?;
assert_eq!(parsed_pair, pair);
Predefined enums for common blockchain and asset namespaces:
use atp_caip::{ChainNamespace, AssetNamespace};
// Chain namespaces
let evm_chains = ChainNamespace::Eip155;
let solana_chains = ChainNamespace::Solana;
let cosmos_chains = ChainNamespace::Cosmos;
assert_eq!(evm_chains.as_str(), "eip155");
// Asset namespaces
let native_tokens = AssetNamespace::Slip44;
let erc20_tokens = AssetNamespace::Erc20;
let nfts = AssetNamespace::Erc721;
let solana_tokens = AssetNamespace::Spl;
assert_eq!(erc20_tokens.as_str(), "erc20");
Support for different cryptographic signature algorithms:
use atp_caip::Curve;
let ethereum_curve = Curve::Secp256k1;
let solana_curve = Curve::Ed25519;
Comprehensive error types for validation and parsing failures:
use atp_caip::{CaipError, ChainId, AssetId, Money};
// Invalid chain ID format
match ChainId::new("", "1") {
Err(CaipError::InvalidChainId(msg)) => println!("Invalid chain: {}", msg),
_ => unreachable!(),
}
// Invalid asset ID format
match AssetId::from_str("invalid-format") {
Err(CaipError::InvalidAssetId(msg)) => println!("Invalid asset: {}", msg),
_ => unreachable!(),
}
// Decimal overflow
match Money::new(ethers_core::types::U256::zero(), 100) {
Err(CaipError::DecimalOverflow { max, got }) => {
println!("Too many decimals: {} > {}", got, max);
},
_ => unreachable!(),
}
// Invalid amount format
match Money::from_decimal_str("not-a-number", 18) {
Err(CaipError::InvalidAmount(msg)) => println!("Invalid amount: {}", msg),
_ => unreachable!(),
}
All identifiers are validated against CAIP specification regex patterns:
^([-a-z0-9]{3,8}):([-a-zA-Z0-9]{1,32})$^([-a-z0-9]{3,8}):([-a-zA-Z0-9]{1,32}):([-a-zA-Z0-9]{1,128})$^([-a-z0-9]{3,8}):([-a-zA-Z0-9]{1,32})/([-a-z0-9]{3,8}):([-a-zA-Z0-9]{1,64})$^([-a-z0-9]{3,8}):([-a-zA-Z0-9]{1,64})$This crate is designed to work seamlessly with atp-chain-registry:
// The chain registry uses CAIP types for configuration
use atp_chain_registry::ChainRegistry;
use atp_caip::{AssetId, ChainId};
let registry = ChainRegistry::default()?;
// Find trading routes between assets
let from_asset = AssetId::from_str("eip155:1/slip44:60")?; // ETH
let to_asset = AssetId::from_str("solana:mainnet/slip44:501")?; // SOL
let routes = registry.find_trading_routes(&from_asset, &to_asset, 3);
for route in routes {
println!("Route: {:?}", route);
}
// Get chains supporting specific cryptographic curves
let secp256k1_chains = registry.get_chains_by_curve(&caip::Curve::Secp256k1);
All types support Serde serialization for configuration files and APIs:
use atp_caip::{ChainId, AssetId, TokenPair};
use serde_json;
let chain = ChainId::new("eip155", "1")?;
let json = serde_json::to_string(&chain)?;
println!("{}", json); // "eip155:1"
let asset = AssetId::new(chain, "slip44", "60")?;
let toml = toml::to_string(&asset)?;
// TokenPair with trading configuration
let mut pair = TokenPair::new(asset.clone(), asset.clone());
pair.fee_percentage = Some(0.3);
pair.min_trade_amount = Some("0.01".to_string());
let config_json = serde_json::to_string_pretty(&pair)?;
Run the test suite to verify functionality:
cargo test
The crate includes comprehensive tests covering:
use atp_caip::{ChainId, AssetId, Money, Asset, TokenPair};
// Set up chains
let ethereum = ChainId::new("eip155", "1")?;
let solana = ChainId::new("solana", "mainnet")?;
// Set up assets
let eth = AssetId::new(ethereum, "slip44", "60")?;
let sol = AssetId::new(solana, "slip44", "501")?;
// Create asset holdings
let eth_holding = Asset::new(
eth.clone(),
Money::from_decimal_str("2.5", 18)?
);
let sol_holding = Asset::new(
sol.clone(),
Money::from_decimal_str("100.0", 9)?
);
// Create trading pair
let trading_pair = TokenPair::new(eth, sol);
// Calculate portfolio value
let eth_price = 3000.0; // $3000 per ETH
let sol_price = 150.0; // $150 per SOL
let total_value = eth_holding.usd_value(eth_price) + sol_holding.usd_value(sol_price);
println!("Portfolio value: ${:.2}", total_value); // $22,500.00
println!("Trading pair: {}", trading_pair);
println!("Is cross-chain: {}", trading_pair.is_cross_chain());
use atp_caip::{ChainId, AssetId, AssetIdBase, Money};
use std::collections::HashMap;
// Define supported chains
let chains = vec![
ChainId::new("eip155", "1")?, // Ethereum
ChainId::new("eip155", "137")?, // Polygon
ChainId::new("solana", "mainnet")?, // Solana
];
// Define USDC on different chains
let mut usdc_assets = HashMap::new();
// USDC on Ethereum
usdc_assets.insert(
"ethereum".to_string(),
AssetId::new(
chains[0].clone(),
"erc20",
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
)?
);
// USDC on Polygon
usdc_assets.insert(
"polygon".to_string(),
AssetId::new(
chains[1].clone(),
"erc20",
"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
)?
);
// USDC on Solana
usdc_assets.insert(
"solana".to_string(),
AssetId::new(
chains[2].clone(),
"spl",
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
)?
);
// Create balances
let mut balances = HashMap::new();
balances.insert("ethereum", Money::from_decimal_str("1000.50", 6)?);
balances.insert("polygon", Money::from_decimal_str("500.25", 6)?);
balances.insert("solana", Money::from_decimal_str("750.75", 6)?);
// Calculate total USDC across all chains
let total_usdc = balances.values()
.try_fold(Money::zero(6)?, |acc, amount| acc.add(amount))?;
println!("Total USDC across chains: {}", total_usdc); // 2251.5
ethers-core: For U256 big integer supportserde: For serialization/deserializationregex: For CAIP format validationthiserror: For error handlinglazy_static: For compiled regex patternsThis project is part of the SolverOS ecosystem. See the main repository for license information.