A Rust library for interacting with the Canton blockchain to manage CBTC (Canton Bitcoin) tokens using the Canton Token Standard (CIP-0056).
- âś… Send CBTC - Transfer tokens to other parties
- âś… Accept CBTC - Accept incoming transfers as a receiver
- âś… Mint CBTC - Deposit BTC and mint CBTC tokens via Bitsafe Attestor network
- âś… Redeem CBTC - Burn CBTC tokens and withdraw BTC
- âś… Batch Distribution - Efficiently distribute tokens to multiple recipients
- âś… UTXO Management - Consolidate and split holdings
- âś… High-Volume Transfers - Optimized for high-volume transfer operations
- âś… Multi-Environment - Support for devnet, testnet, and mainnet
- âś… Token Standard Compliant - Implements Canton Token Standard (CIP-0056)
Important Setup Requirements:
- For Send/Receive Operations: The Digital Asset Registry Utility must be installed on your validator node
- For Mint/Redeem Operations: Review the CBTC Minting App Installation and User Guide to install the required DAR files and configure permissions correctly
- Quick Start - How to Use This Library
- Installation
- Configuration
- Usage Examples
- CBTC Mint & Redeem
- High-Volume Operations
- API Reference
- Direct Canton API Usage (Reference)
- Testing
- Contributing
This library provides a high-level Rust interface for interacting with CBTC (Canton Bitcoin) tokens on the Canton blockchain. Here's how to get started:
Before using this library, you need:
- A Canton Participant Node - Access to a Canton participant node (devnet, testnet, or mainnet)
- DA Registry Utility - For sending and receiving CBTC tokens, the Digital Asset Registry Utility must be installed on your validator node
- Keycloak Credentials - Authentication credentials for your participant node
- A Party ID - Your unique party identifier on the Canton network
- CBTC Holdings - Some CBTC tokens in your account (for sending/distributing)
The quickest way to see the library in action:
# Clone the repository
git clone <your-repo-url>
cd cbtc-lib
# Set up your environment
cp .env.example .env
# Edit .env with your Canton credentials
# Run an example
cargo run --example check_balance
cargo run --example send_cbtcSee Quick Start with Examples for more details.
Add to your Cargo.toml:
[dependencies]
cbtc = { git = "ssh://[email protected]/DLC-link/cbtc-lib", branch = "main" }
keycloak = { git = "ssh://[email protected]/DLC-link/canton-lib", branch = "main" }Or for local development:
[dependencies]
cbtc = { path = "path/to/cbtc-lib" }Then in your code:
use cbtc::transfer;
use keycloak::login;
// Authenticate
let auth = login::password(login::PasswordParams {
client_id: "your-client-id".to_string(),
username: "your-username".to_string(),
password: "your-password".to_string(),
url: login::password_url("https://your-keycloak-host", "your-realm"),
}).await?;
// Send CBTC
transfer::submit(transfer::Params {
// ... see Usage Examples section
}).await?;For advanced users who want direct control, see Direct Canton API Usage to learn how to interact with Canton's REST APIs directly.
| Task | Function | Section |
|---|---|---|
| Check balance | cbtc::active_contracts::get() |
Understanding UTXO Management |
| Send tokens | cbtc::transfer::submit() |
Core Operations |
| Accept tokens | cbtc::accept::submit() |
Core Operations |
| Batch send | cbtc::batch::submit_from_csv() |
High-Volume Operations |
| Consolidate UTXOs | cbtc::consolidate::check_and_consolidate() |
Understanding UTXO Management |
Add this to your Cargo.toml:
[dependencies]
cbtc = { git = "ssh://[email protected]/DLC-link/cbtc-lib", branch = "main" }Or for local development:
[dependencies]
cbtc = { path = "path/to/cbtc-lib" }- Create environment configuration
Copy .env.example to .env and fill in your values:
cp .env.example .env- Configure your environment variables
Edit .env with your Canton participant node details:
# Canton Network
LEDGER_HOST=https://participant.example.com
PARTY_ID=your-party::1220...
DECENTRALIZED_PARTY_ID=cbtc-network::1220... # See environment-specific values below
REGISTRY_URL=https://api.utilities.digitalasset-dev.com # See environment-specific values below
# CBTC Mint/Redeem (optional - only needed for BTC bridging)
ATTESTOR_URL=https://attestor.bitsafe.dev # See environment-specific values below
CANTON_NETWORK=devnet # or testnet/mainnetDECENTRALIZED_PARTY_ID=cbtc-network::12202a83c6f4082217c175e29bc53da5f2703ba2675778ab99217a5a881a949203ff
REGISTRY_URL=https://api.utilities.digitalasset-dev.com
ATTESTOR_URL=https://attestor.bitsafe.dev
CANTON_NETWORK=devnetDECENTRALIZED_PARTY_ID=cbtc-network::12201b1741b63e2494e4214cf0bedc3d5a224da53b3bf4d76dba468f8e97eb15508f
REGISTRY_URL=https://api.utilities.digitalasset-staging.com
ATTESTOR_URL=https://attestor.bitsafe.testnet
CANTON_NETWORK=testnetDECENTRALIZED_PARTY_ID=cbtc-network::12205af3b949a04776fc48cdcc05a060f6bda2e470632935f375d1049a8546a3b262
REGISTRY_URL=https://api.utilities.digitalasset.com
ATTESTOR_URL=https://attestor.bitsafe.com
CANTON_NETWORK=mainnetFor quick experimentation, this library includes ready-to-run example programs. See the examples directory for:
check_balance- Check your CBTC balance and UTXO countsend_cbtc- Send tokens to another partyaccept_transfers- Accept all pending incoming transfersconsolidate_utxos- Consolidate multiple UTXOsbatch_distribute- Distribute tokens to multiple recipients from a CSV file
Run examples from the project root:
cargo run --example check_balanceSee the examples README for detailed instructions.
This library provides several high-level operations for working with CBTC tokens. Below is a quick reference - for complete working examples, see the examples directory.
| Operation | Example File | Run Command | Description |
|---|---|---|---|
| Mint CBTC | mint_cbtc_flow.rs |
cargo run --example mint_cbtc_flow |
Set up deposit account for BTC deposits |
| Redeem CBTC | redeem_cbtc_flow.rs |
cargo run --example redeem_cbtc_flow |
Burn CBTC and withdraw BTC |
| Check Balance | check_balance.rs |
cargo run --example check_balance |
View your CBTC balance and UTXO count |
| Send CBTC | send_cbtc.rs |
cargo run --example send_cbtc |
Transfer tokens to another party |
| Accept CBTC | accept_transfers.rs |
cargo run --example accept_transfers |
Accept incoming transfers |
| List Incoming Offers | list_incoming_offers.rs |
cargo run --example list_incoming_offers |
List pending transfers where you're the receiver |
| List Outgoing Offers | list_outgoing_offers.rs |
cargo run --example list_outgoing_offers |
List pending transfers where you're the sender |
| Cancel Offers | cancel_offers.rs |
cargo run --example cancel_offers |
Cancel all pending outgoing transfers |
| Stream CBTC | stream.rs |
cargo run --example stream_cbtc |
Stream CBTC to a single receiver multiple times |
| Batch Distribution | batch_distribute.rs |
cargo run --example batch_distribute |
Distribute to multiple recipients from CSV |
| Consolidate UTXOs | consolidate_utxos.rs |
cargo run --example consolidate_utxos |
Merge multiple UTXOs into one |
Authentication: All operations require Keycloak/OIDC authentication. The library handles token management - you just provide credentials.
UTXO Model: CBTC uses a UTXO (Unspent Transaction Output) model similar to Bitcoin. Each holding is a separate UTXO that can be split or combined.
Two-Phase Transfers:
- Sender creates a transfer offer
- Receiver must accept the transfer to complete it
BTC ↔ CBTC Bridge: The mint/redeem flow allows you to bridge between native Bitcoin and CBTC tokens:
- Minting: Create deposit account → Get BTC address → Send BTC → Attestor network confirms (6+ blocks) → CBTC automatically minted
- Redeeming: Burn CBTC → Create withdraw request → Attestor network sends BTC
See the examples README for detailed usage instructions.
Important: Before using mint/redeem operations, please review the CBTC Minting App Installation and User Guide. This guide covers the required DAR file installation and permission configuration needed on your Canton participant node for mint/redeem functionality to work properly.
The mint/redeem functionality allows you to bridge between native Bitcoin and CBTC tokens on the Canton network. This is powered by the Bitsafe Attestor Network, a decentralized network that monitors Bitcoin transactions and confirms deposits/withdrawals.
Flow:
- Create a deposit account with the
mint_redeemmodule - Receive a unique BTC deposit address from the attestor
- Send BTC to that address (external to this library)
- Attestor network monitors and confirms your deposit (6+ blocks)
- Attestors automatically mint CBTC tokens to your party
Example:
# Run the mint flow example to set up deposit account
cargo run --example mint_cbtc_flow
# After sending BTC, monitor for minted CBTC by checking your balance
cargo run --example check_balanceSee mint_cbtc_flow.rs for complete code.
Note: This library does NOT monitor Bitcoin transactions. The attestor network handles all monitoring and automatically mints CBTC after confirmation. You can periodically run check_balance to see when CBTC has been minted to your account.
Flow:
- Create a withdraw account with your destination BTC address
- Burn CBTC tokens to create a withdraw request
- Attestor network processes the request
- Receive BTC at your destination address
Example:
# Run the redeem flow example
cargo run --example redeem_cbtc_flow
# Test burning CBTC with an existing withdraw account
cargo run --example test_burn_cbtcSee redeem_cbtc_flow.rs for complete code.
To use mint/redeem functionality, add these environment variables:
ATTESTOR_URL=https://attestor.bitsafe.dev # Bitsafe Attestor API
CANTON_NETWORK=devnet # or testnet/mainnetWhat are UTXOs?
Every CBTC holding is a UTXO (Unspent Transaction Output), similar to Bitcoin. Each transfer can create new UTXOs, and over time you may accumulate many small ones.
Why Consolidate?
- Performance: Canton has a soft limit of 10 UTXOs per party per token type
- Node Efficiency: Fewer UTXOs reduce database and memory usage
- Network Load: Smaller transactions with fewer inputs
Best Practice: Consolidate regularly, especially for high-volume operations. See consolidate_utxos.rs for example code.
For applications running high-volume CBTC transfers (e.g., payment processors, exchanges, automated trading):
- Monitor UTXOs: Consolidate when approaching 10 UTXOs per party
- Use Batch Operations:
batch::submit_from_csv()for efficient multi-recipient transfers - Consolidate Proactively: Check and consolidate before large distributions
- Handle Both Parties: If you control sender and receiver, consolidate both
# 1. Check UTXO count and consolidate if needed
cargo run --example consolidate_utxos
# 2. Run batch distribution
cargo run --example batch_distributeSee batch_distribute.rs and batch_with_callback.rs for complete examples with callbacks and logging.
submit(Params)- Send CBTC to a single recipientsubmit_multi(MultiParams)- Send CBTC to multiple recipients in one transaction
submit(Params)- Accept an incoming CBTC transfer
withdraw_all(WithdrawAllParams)- Withdraw all pending outgoing transferssubmit(Params)- Withdraw a specific transfer offer
submit(Params)- Distribute CBTC to multiple recipients
submit_from_csv(Params)- Batch distribution from CSV file
check_and_consolidate(CheckConsolidateParams)- Check and consolidate if neededget_utxo_count(GetUtxoCountParams)- Get UTXO countconsolidate_utxos(ConsolidateParams)- Force consolidation
submit(Params)- Split holdings into specific amounts
get(Params)- Get active CBTC holdings
list_deposit_accounts(Params)- Get all deposit accounts for your partycreate_deposit_account(Params)- Create new deposit account for receiving BTCget_bitcoin_address(Params)- Get Bitcoin address for a deposit accountget_deposit_account_status(Params)- Get full status including Bitcoin address and last processed block
list_withdraw_accounts(Params)- Get all withdraw accountscreate_withdraw_account(Params)- Create withdraw account with BTC destinationlist_holdings(Params)- Get CBTC holdings for burningrequest_withdraw(Params)- Burn CBTC and request BTC withdrawallist_withdraw_requests(Params)- Monitor withdrawal status
password(PasswordParams)- Authenticate with username/passwordclient_credentials(ClientCredentialsParams)- Service account authentication
- Low-level ledger API operations
- WebSocket streaming for real-time updates
- Registry service integration
- Factory contract queries
For teams who want to understand the underlying protocol or implement custom workflows, here's how to interact with Canton APIs directly.
- Access to a Canton participant node
- Valid OIDC authentication token
- Understanding of Canton's UTXO model
LEDGER_OFFSET=$(curl -X GET "$LEDGER_HOST/v2/state/ledger-end" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq -r '.offset')
curl -X POST $LEDGER_HOST/v2/state/active-contracts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"filter": {
"filtersByParty": {
"$SENDER_PARTY_ID": {
"cumulative": [{
"identifierFilter": {
"InterfaceFilter": {
"value": {
"interfaceId": "#splice-api-token-holding-v1:Splice.Api.Token.HoldingV1:Holding",
"includeInterfaceView": true,
"includeCreatedEventBlob": true
}
}
}
}]
}
}
},
"verbose": false,
"activeAtOffset": '$(echo $LEDGER_OFFSET | jq -R 'tonumber')'
}' | jqcurl -X POST $REGISTRY_URL/api/token-standard/v0/registrars/$DECENTRALIZED_PARTY_ID/registry/transfer-instruction/v1/transfer-factory \
-H "Content-Type: application/json" \
-d '{
"choiceArguments": {
"expectedAdmin": "'$DECENTRALIZED_PARTY_ID'",
"transfer": {
"sender": "'$SENDER_PARTY_ID'",
"receiver": "'$RECEIVER_PARTY_ID'",
"amount": 0.5,
"instrumentId": {
"admin": "'$DECENTRALIZED_PARTY_ID'",
"id": "CBTC"
},
"requestedAt": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",
"executeBefore": "'$(date -u -d "+1 days" +"%Y-%m-%dT%H:%M:%SZ")'",
"inputHoldingCids": ["'$HOLDING_CID'"]
},
"extraArgs": {
"context": {"values": {}},
"meta": {"values": {}}
}
},
"excludeDebugFields": true
}' | jqSee example_transfer.sh for a complete example.
# Get accept context
curl -X POST $REGISTRY_URL/api/token-standard/v0/registrars/$DECENTRALIZED_PARTY_ID/registry/transfer-instruction/v1/$TRANSFER_OFFER_CID/choice-contexts/accept \
-H "Content-Type: application/json" \
-d '{"meta":{}}' | jq
# Submit acceptance (use disclosed contracts from above)
curl -X POST $LEDGER_HOST/v2/commands/submit-and-wait-for-transaction-tree \
-H "Authorization: Bearer $RECEIVER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"commands": [{
"ExerciseCommand": {
"templateId": "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferInstruction",
"contractId": "'$TRANSFER_OFFER_CID'",
"choice": "TransferInstruction_Accept",
"choiceArgument": {
"extraArgs": {
"context": {"values": '$CHOICE_CONTEXT_VALUES'},
"meta": {"values": {}}
}
}
}
}],
"commandId": "'$(uuidgen)'",
"actAs": ["'$RECEIVER_PARTY'"],
"disclosedContracts": '$DISCLOSED_CONTRACTS'
}' | jqWe welcome contributions from the Canton ecosystem! This library is designed to help developers build on Canton's CBTC token standard.
Found a bug or have a feature request?
- Check existing issues to avoid duplicates
- Open a new issue with:
- Clear description of the problem or feature
- Steps to reproduce (for bugs)
- Expected vs actual behavior
- Your environment (Canton network, Rust version, OS)
-
Fork the repository and create a feature branch
git checkout -b feature/your-feature-name
-
Make your changes
- Follow Rust best practices and naming conventions
- Keep library code free of environment variable dependencies
- Add tests for new functionality (when applicable)
- Update documentation and examples
-
Test your changes
# Build the library cargo build --release # Build examples cargo build --examples --release # Run clippy for linting cargo clippy --all-targets --all-features # Format code cargo fmt --all
-
Submit a pull request
- Provide a clear description of your changes
- Reference any related issues
- Ensure CI checks pass
-
Library code (
src/) should:- Accept all configuration as function parameters (no
env::var()calls) - Be environment-agnostic and testable
- Follow dependency injection patterns
- Accept all configuration as function parameters (no
-
Example code (
examples/) can:- Read from environment variables
- Demonstrate practical usage patterns
- Show best practices for error handling
- Integration tests require live Canton network access and credentials
- Set required environment variables (see
.env.example) - Tests expect to connect to real endpoints - they will fail without proper setup
- This is intentional: tests validate real-world behavior
- Add doc comments to public functions using
/// - Include usage examples in doc comments
- Update README.md for significant changes
- Keep examples up-to-date with API changes
- Review the examples directory for usage patterns
- Check the Canton documentation for protocol details
- Open a discussion for questions about the library
- Join the Canton community for broader ecosystem questions
By contributing, you agree that your contributions will be licensed under the MIT License.
This library includes integration tests that validate real-world interactions with Canton networks.
Tests require:
- Access to a Canton participant node
- Valid Keycloak credentials
- Network connectivity
Set up your environment:
cp .env.example .env
# Edit .env with your Canton credentialsRun tests:
# Build the library (always works)
cargo build --release
# Run integration tests (requires credentials)
cargo test --lib
# Note: Tests will fail without proper environment variables
# This is expected and intentional - they validate real network behaviorUnlike unit tests, these are integration tests that:
- Connect to actual Canton participant nodes
- Perform real ledger operations
- Validate end-to-end workflows
This ensures the library works correctly with real Canton infrastructure, not just in isolation.
MIT License - see LICENSE file for details