The same crates the CLI uses are available as Rust libraries. Import them from a path or git and build custom agents, batch tools, or test harnesses.
Example - Buy CC
We take a canonical example β buying CantonCoins. The agent lives atexamples/buy_cc.
It buys CC on CC-USDC.
examples/buy_cc/Cargo.toml:
{
"name": "buy-cc-example",
"version": "0.1.0",
"type": "module",
"dependencies": {
"cloud-agent": "file:../crates/cloud-agent",
"agent-logic": "file:../crates/agent-logic",
"orderbook-proto": "file:../crates/orderbook-proto",
"dotenv": "^0.15.0",
"commander": "^4.0.0",
"pino": "^0.3.0"
}
}
examples/buy_cc/src/main.rs (abridged):
import "dotenv/config";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { BaseConfig } from "agent-logic/config";
import { newConfirmLock } from "agent-logic/confirm";
import { LiquidityManager } from "agent-logic/liquidity";
import { MulticallSettler } from "cloud-agent/accept-settle";
import { CloudSettlementBackend } from "cloud-agent/backend";
import { runFillLoop, FillDirection, type FillParams } from "cloud-agent/fill-loop";
import { populateInstruments } from "cloud-agent";
type Args = {
amount: number;
maxPrice?: number;
pollPeriod: number;
minSettlement: number;
};
async function parseArgs(): Promise<Args> {
const argv = await yargs(hideBin(process.argv))
.option("amount", {
type: "number",
demandOption: true,
describe: "Amount to buy",
})
.option("max_price", {
type: "number",
describe: "Maximum acceptable price",
})
.option("poll_period", {
type: "number",
default: 600,
describe: "Polling period in seconds",
})
.option("min_settlement", {
type: "number",
default: 5.0,
describe: "Minimum settlement amount",
})
.strict()
.parse();
return {
amount: argv.amount,
maxPrice: argv.max_price,
pollPeriod: argv.poll_period,
minSettlement: argv.min_settlement,
};
}
async function main(): Promise<void> {
const args = await parseArgs();
console.info("Starting buy flow");
// 1. Load config from .env + agent.toml
const config = await BaseConfig.loadOrDefaults("agent.toml");
// 2. Fetch instrument registry from orderbook-rpc
await populateInstruments(config);
// 3. Create backend
const confirmLock = newConfirmLock();
const lm = new LiquidityManager(
config.feeReserveCc,
config.liquidityMargin,
config.flowEmaWindowHours,
config.depletionMaxHours,
config.depletionMinHours
);
const backend = new CloudSettlementBackend(
structuredClone(config),
false,
false,
false,
false,
confirmLock,
lm
);
// 4. Create settler for atomic multicall settlement
const settler = new MulticallSettler({
config: structuredClone(config),
amuletCache: backend.amuletCache(),
verbose: false,
dryRun: false,
force: false,
confirm: false,
confirmLock,
});
// 5. Run the fill loop
const params: FillParams = {
direction: FillDirection.Buy,
marketId: "CC-USDC",
totalAmount: args.amount,
priceLimit: args.maxPrice,
minSettlement: args.minSettlement,
maxSettlement: args.amount,
intervalSecs: args.pollPeriod,
};
const _backendGuard = backend;
await runFillLoop(config, settler, params, null, null);
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
})
use std::sync::Arc;
use anyhow::{Context, Result};
use clap::Parser;
use agent_logic::config::BaseConfig;
use agent_logic::confirm::new_confirm_lock;
use agent_logic::liquidity::LiquidityManager;
use cloud_agent::accept_settle::MulticallSettler;
use cloud_agent::backend::CloudSettlementBackend;
use cloud_agent::fill_loop::{self, FillDirection, FillParams};
use cloud_agent::populate_instruments;
#[derive(Parser)]
struct Args {
#[arg(long)] amount: f64,
#[arg(long)] max_price: Option<f64>,
#[arg(long, default_value = "600")] poll_period: u64,
#[arg(long, default_value = "5.0")] min_settlement: f64,
}
#[tokio::main]
async fn main() -> Result<()> {
let _ = dotenvy::dotenv();
let args = Args::parse();
tracing_subscriber::fmt().with_env_filter("info").init();
// 1. Load config from .env + agent.toml
let mut config = BaseConfig::load_or_defaults("agent.toml")?;
// 2. Fetch instrument registry (CC/USDC/β¦) from orderbook-rpc
populate_instruments(&mut config).await?;
// 3. Create backend (spawns ACS worker to keep amulet cache fresh)
let confirm_lock = new_confirm_lock();
let lm = LiquidityManager::new(
config.fee_reserve_cc, config.liquidity_margin,
config.flow_ema_window_hours,
config.depletion_max_hours, config.depletion_min_hours,
);
let backend = CloudSettlementBackend::new(
config.clone(), false, false, false, false, confirm_lock.clone(), lm,
);
// 4. Create settler for atomic multicall settlement
let settler = Arc::new(MulticallSettler {
config: config.clone(),
amulet_cache: backend.amulet_cache().clone(),
verbose: false, dry_run: false, force: false, confirm: false,
confirm_lock,
});
// 5. Run the fill loop
let params = FillParams {
direction: FillDirection::Buy,
market_id: "CC-USDC".to_string(),
total_amount: args.amount,
price_limit: args.max_price,
min_settlement: args.min_settlement,
max_settlement: args.amount,
interval_secs: args.poll_period,
};
let _backend_guard = backend;
fill_loop::run_fill_loop(config, settler, params, None, None).await
}
cargo run -p buy-cc-example -- --amount 10.0 --max-price 0.16 --poll-period 600
CC Buying agent is launched! Great job!