Skip to main content
Now, let’s see how we can build an agent.
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 at examples/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);
})
To launch the agent, run this command:
cargo run -p buy-cc-example -- --amount 10.0 --max-price 0.16 --poll-period 600
CC Buying agent is launched! Great job!