A strongly-typed, asynchronous Rust wrapper for the Canton v2 HTTP JSON API.
This library is a Rust port of the TypeScript c7_ledger repository. It enables Rust applications to interact with Daml/Canton ledgers to submit commands, query the active contract set (ACS), and manage ledger state.
- Async/Await: Built on
tokioandreqwestfor non-blocking I/O. - Strong Typing: Uses Rust enums to strictly define
CreatevsExercisecommands. - JSON Serialization: Seamless integration with
serde_jsonfor dynamic Daml template arguments. - Error Handling: Implements
anyhowfor clean error propagation.
Ensure your file tree looks like this:
.
├── Cargo.toml # Dependencies
├── README.md # This file
└── src
├── client.rs # HTTP Client logic and API endpoints
├── lib.rs # Module exports
├── main.rs # Example workflow (IOU)
└── models.rs # Structs for Commands, Responses, and Ledger State
Add the following dependencies to your Cargo.toml:
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
use crate::client::CantonClient;
// Initialize with the Ledger API URL and a signed JWT
let client = CantonClient::new("http://localhost:7575", "YOUR_JWT_TOKEN");Submit a CreateCommand to the ledger. This function blocks asynchronously until the transaction is committed (Submit and Wait).
use serde_json::json;
let create_args = json!({
"owner": "Alice",
"amount": 100
});
let response = client.create(
"Main:Iou", // Template ID
create_args, // Arguments
vec!["Alice".to_string()] // Act As
).await?;
println!("Contract Created. Update ID: {}", response.update_id);Fetch contracts currently active on the ledger, filtering by Template ID.
let contracts = client.list_active_contracts(
vec!["Main:Iou".to_string()], // Filter by Template ID
None // Optional query filter
).await?;
for contract in contracts {
println!("Found contract: {}", contract.contract_id);
}Exercise a choice on a specific contract ID.
let exercise_args = json!({
"newOwner": "Bob"
});
client.exercise(
"00xx...contract_id...", // Contract ID
"Main:Iou", // Template ID
"Transfer", // Choice Name
exercise_args, // Choice Arguments
vec!["Alice".to_string()] // Act As
).await?;To run the default main.rs workflow, you need a running Canton/Daml ledger with the following setup:
- Daml Model: A template named
Main:Ioumust be deployed. - Parties: Parties named "Alice" and "Bob" must be allocated.
- Auth: You must generate a JWT token for the ledger that has
actAspermissions for "Alice".
cargo run
The library separates the command payload (CommandPayload) from the specific command types (CreateCommand, ExerciseCommand). This ensures that you cannot send a command missing required fields (like sending a Create command without create_arguments).
The client handles HTTP status validation automatically. If the Ledger API returns a non-200 status code, the client will return an Err containing the API error message.
MIT