Skip to content

Latest commit

 

History

History
140 lines (93 loc) · 3.79 KB

File metadata and controls

140 lines (93 loc) · 3.79 KB

Canton Ledger API Client (Rust)

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.

Features

  • Async/Await: Built on tokio and reqwest for non-blocking I/O.
  • Strong Typing: Uses Rust enums to strictly define Create vs Exercise commands.
  • JSON Serialization: Seamless integration with serde_json for dynamic Daml template arguments.
  • Error Handling: Implements anyhow for clean error propagation.

Project Structure

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

Installation

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"

Usage

1. Initialize the Client

use crate::client::CantonClient;

// Initialize with the Ledger API URL and a signed JWT
let client = CantonClient::new("http://localhost:7575", "YOUR_JWT_TOKEN");

2. Create a Contract

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);

3. Query Active Contracts

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);
}

4. Exercise a Choice

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?;

Prerequisites for Running the Example

To run the default main.rs workflow, you need a running Canton/Daml ledger with the following setup:

  1. Daml Model: A template named Main:Iou must be deployed.
  2. Parties: Parties named "Alice" and "Bob" must be allocated.
  3. Auth: You must generate a JWT token for the ledger that has actAs permissions for "Alice".

Running the Project

cargo run

Implementation Details

Models (src/models.rs)

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).

Client (src/client.rs)

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.

License

MIT