Skip to content

trionlabs/chorus

Repository files navigation

Chorus

Chorus

AI agent committees with FROST threshold consensus and human-delegated authority.

Demo video

FROST (Flexible Round-Optimized Schnorr Threshold) is a protocol where a group of participants can collectively produce a single Schnorr signature without any party ever holding the full private key. If at least t-of-n participants agree, their partial signatures combine into one valid signature. If they don't agree, nothing happens.

  • No agent holds the private key. The key is generated distributedly (DKG) and never exists whole - not during creation, not during signing, not ever. Each agent holds only a share.
  • Constant verification cost. Whether the committee has 3 agents or 500, on-chain verification costs the same ~5,300 gas. The verifier sees one 96-byte signature, not n individual signatures.
  • Policy-constrained by delegation. The committee has zero independent authority. It can only act within the permissions Alice delegates via ERC-7710 - specific contracts, specific methods, specific amounts.
  • Fully automated over XMTP. Agents run the entire FROST protocol (DKG key generation and signing ceremonies) over encrypted XMTP messages. No human in the loop after Alice signs the delegation.

How it works

sequenceDiagram
    participant Alice as Alice (human)
    participant AC as AgentConsensus.sol
    participant Guard
    participant Judge
    participant Steward
    participant Uniswap

    Alice->>AC: ERC-7710 delegation (max 100 USDC, Uniswap only)

    Note over Guard,Steward: Proposal: "Swap 50 USDC for ETH"

    Guard->>Guard: evaluate risk
    Judge->>Judge: check policy
    Steward->>Steward: check feasibility

    Guard--xGuard: REJECT (exceeds risk threshold)
    Judge->>Judge: ACCEPT
    Steward->>Steward: ACCEPT

    Note over Judge,Steward: 2-of-3 threshold met

    Judge->>Steward: FROST round 1 (commitments via XMTP)
    Steward->>Judge: FROST round 2 (signature shares via XMTP)
    Note over Judge,Steward: aggregate into 96-byte Schnorr signature

    Judge->>AC: executeDelegated(sig)
    AC->>AC: FROST.verify() (~5,600 gas)
    AC->>AC: redeem Alice's delegation
    AC->>Uniswap: swap 50 USDC for ETH (from Alice's account)
Loading

Architecture

graph TB
    subgraph "Off-chain (XMTP E2E encrypted)"
        Guard[Guard<br/>risk & security]
        Judge[Judge<br/>policy & compliance]
        Steward[Steward<br/>treasury & ops]
        Guard <-->|FROST round 1+2| Judge
        Judge <-->|FROST round 1+2| Steward
    end

    subgraph "On-chain (Base Sepolia)"
        AC[AgentConsensus.sol]
        FROST[FROST.verify<br/>~5,600 gas, constant]
        DM[DelegationManager<br/>ERC-7710]
        Caveats[Caveat Enforcers<br/>AllowedTargets + AllowedMethods]
        Smart[Alice's Smart Account<br/>HybridDeleGator]
        Uniswap[Uniswap Router]
    end

    Alice[Alice - human] -->|signs ERC-7710 delegation<br/>max 100 USDC, Uniswap only| DM

    Judge -->|submit 96-byte<br/>FROST signature| AC
    AC -->|1. verify signature| FROST
    FROST -->|signer matches<br/>committee pubkey?| AC
    AC -->|2. call redeemDelegations<br/>with signed delegation + swap calldata| DM
    DM -->|3. enforce caveats| Caveats
    Caveats -->|4. execute from<br/>Alice's account| Smart
    Smart -->|5. swap USDC| Uniswap
Loading

The committee has no independent authority. Every action flows through two layers of verification:

  1. FROST consensus - cryptographic proof that 2-of-3 agents agreed (off-chain, ~5,600 gas to verify)
  2. Delegation caveats - on-chain policy enforcement by DelegationManager (target allowlist, method restrictions, amount limits)

Signing ceremony

stateDiagram-v2
    [*] --> EVALUATING: proposal received
    EVALUATING --> ACCEPTED: threshold agents accept
    EVALUATING --> ABORTED: too many rejections
    ACCEPTED --> ROUND1_COMMITTED: commitments collected
    ROUND1_COMMITTED --> ROUND2_SIGNED: signature shares collected
    ROUND2_SIGNED --> EXECUTING: FROST signature aggregated
    EXECUTING --> COMPLETE: on-chain execution confirmed
    ACCEPTED --> ABORTED: timeout (30s)
    ROUND1_COMMITTED --> ABORTED: timeout (30s)
    ROUND2_SIGNED --> ABORTED: timeout (30s)
Loading

Why FROST (not multisig)

FROST Multisig
Proof size 96 bytes (constant) 65 * t bytes
Gas cost ~5,600 (constant) ~3,000 * t
Signer privacy can't tell who signed each signer revealed
On-chain appearance one signer visibly multi-party
Agent rotation share refresh, same address on-chain owner change

FROST implements RFC 9591 (Flexible Round-Optimized Schnorr Threshold signatures). See the FROST Book for how the protocol works, and the DKG tutorial for distributed key generation.

Gas benchmarks

Measured via Foundry gas benchmarks (forge test --match-path test/GasBenchmark.t.sol -vv). FROST verification is constant regardless of committee size.

Operation Gas Notes
FROST.verify() 5,327 constant for any t-of-n (test)
executeDelegated 36,271 verify + delegation redemption
Committee registration 137,887 one-time setup (tx)
executeDelegated + Uniswap 272,614 full pipeline on-chain (tx)

FROST vs multisig (ecrecover per signer)

All numbers measured in Foundry. ecrecover is what Safe's checkNSignatures calls per signer.

Committee size FROST ecrecover (multisig) FROST savings
1 signer 5,327 3,251 -
2-of-3 5,327 10,391 49% cheaper
3-of-5 5,327 11,778 55% cheaper
5-of-10 5,327 19,580 73% cheaper
10-of-20 5,327 39,091 86% cheaper
50-of-100 5,327 195,207 97% cheaper

At 50-of-100, FROST costs the same as a single token transfer. A Safe multisig costs more than a Uniswap swap.

Beyond gas, FROST provides signer privacy - you can't tell which agents signed from the on-chain signature. With Safe multisig, each signer's address is recoverable.

Key generation

Two modes supported:

Distributed Key Generation (DKG) over XMTP - no party ever sees the full key:

pnpm demo:dkg    # 3 agents run DKG over XMTP with DM-only secret shares

Round 1 commitments are broadcast to the group. Round 2 secret shares are sent via XMTP DMs only - the agent enforces this and drops any round 2 message arriving via group chat. All three agents derive their key share independently and agree on the same group public key.

Trusted dealer (quick setup for testing):

safe-frost split --threshold 2 --signers 3

Both produce compatible key material. The signing ceremony runs over XMTP in both cases.

Setup

# install safe-frost cli (requires rust)
cd contracts/lib/safe-frost && cargo install --path . && cd ../../..

# install dependencies
pnpm install

# generate frost keys (2-of-3, trusted dealer)
safe-frost split --threshold 2 --signers 3

# or use --mainnet for mainnet scripts
CHAIN=mainnet npx tsx scripts/deploy/register-committee.ts

# run local demo (no xmtp, no chain)
pnpm demo

# run xmtp multi-agent demo (3 agents, frost ceremony, on-chain execution)
pnpm demo:xmtp

# run DKG over XMTP (distributed key generation + signing)
pnpm demo:dkg

# full lifecycle: DKG -> register committee -> signing -> on-chain execution
pnpm demo:lifecycle

# run a standalone agent
AGENT_ROLE=guard AGENT_WALLET_KEY=0x... COMMITTEE_ID=0x... pnpm agent

# run foundry tests
cd contracts && forge test

# deploy contract
cd contracts && forge script script/Deploy.s.sol --rpc-url $BASE_SEPOLIA_RPC --private-key $DEPLOYER_PRIVATE_KEY --broadcast

# register committee (use CHAIN=mainnet for mainnet)
npx tsx scripts/deploy/register-committee.ts

# deploy alice's smart account
npx tsx scripts/deploy/deploy-alice.ts

# create erc-7710 delegation (alice -> committee, uniswap + 100 usdc cap)
npx tsx scripts/deploy/create-delegation.ts

Project structure

contracts/
  src/AgentConsensus.sol    - FROST-verified delegation-only execution
  test/AgentConsensus.t.sol - 4 passing tests (verify, delegate, replay, reject)
  lib/safe-frost/           - FROST.sol Schnorr verifier (~5,600 gas)

src/
  frost/cli.ts              - safe-frost CLI subprocess wrappers
  frost/executor.ts         - maps ceremony actions to CLI calls
  ceremony/signing.ts       - signing ceremony state machine
  ceremony/dkg.ts           - DKG ceremony state machine (available for production use)
  ceremony/types.ts         - state enums, action types
  xmtp/agent.ts             - XMTP agent with self-delivery + DM enforcement
  xmtp/messages.ts          - protocol message types
  agent/handler.ts          - agent orchestration
  agent/evaluator.ts        - rule-based policy evaluation
  uniswap/client.ts         - Uniswap V3 swap builder (chain-aware)
  frost/dkg.ts              - DKG CLI wrapper (distributed key generation)
  chain/abi.ts              - AgentConsensus ABI
  chain/config.ts           - chain selection (CHAIN=mainnet or sepolia)

scripts/
  deploy/
    register-committee.ts   - on-chain committee registration
    deploy-alice.ts         - deploy Alice's HybridDeleGator
    create-delegation.ts    - ERC-7710 delegation with Uniswap caveats
    register-erc8004.ts     - ERC-8004 identity registration
  test/
    test-onchain.ts         - FROST verification test
    test-redeem.ts          - delegation redemption test
    test-uniswap-delegation.ts - full Uniswap swap via delegation
    test-caveat-rejection.ts   - caveat enforcement demo
    test-mainnet.ts         - mainnet FROST verification
    test-mainnet-swap.ts    - mainnet Uniswap swap

On-chain proof

Base mainnet:

Hackathon tracks

Synthesis Open Track - Agents That Cooperate

FROST is a coordination primitive for AI agents. Three agents (Guard, Judge, Steward) independently evaluate a proposal over XMTP. Each agent's partial signature IS its vote. When 2-of-3 agree, their partial signatures combine into a single Schnorr signature - cryptographic proof of cooperation without a voting protocol, governance token, or multisig contract. The committee acts as one entity on-chain: one address, one signature, one verification. The agents coordinate through encrypted XMTP messages, producing commitments and signature shares across two rounds. No centralized coordinator can forge the result.

Agents With Receipts - ERC-8004

The FROST signature is the receipt. Each on-chain ConsensusReached event records which committee reached consensus, the action hash they signed, and the nonce (preventing replay). The committee is registered on ERC-8004's Identity Registry on Base mainnet (tx) with metadata describing its agents, threshold, group public key, and protocol. The 96-byte signature is verifiable by anyone calling FROST.verify() on-chain - a permanent, tamper-proof receipt that multiple independent agents evaluated and agreed.

Let the Agent Cook - No Humans Required

Once Alice signs the ERC-7710 delegation, no human is in the loop. Agents receive proposals, evaluate independently using their role-specific criteria (risk analysis, policy compliance, operational viability), run the FROST signing ceremony over XMTP, and submit the signed transaction on-chain. The contract verifies the signature, redeems the delegation, enforces caveats, and executes. Alice can walk away. The agents operate autonomously within her bounds. Demonstrated end-to-end: a 5 USDC Uniswap swap executed from Alice's smart account without any human approval after the initial delegation (tx).

Best Use of Delegations - ERC-7710

Alice creates an ERC-7710 delegation from her HybridDeleGator smart account to the AgentConsensus contract with three caveats: AllowedTargets (only Uniswap Router and USDC), AllowedMethods (only exactInputSingle and approve), and ERC20TransferAmount (max 100 USDC). The delegation is signed off-chain and stored as JSON. When the committee FROST-signs an action, AgentConsensus calls DelegationManager.redeemDelegations() with the signed delegation chain. The DelegationManager validates Alice's signature, enforces every caveat, and executes the action from Alice's account. Two independent layers of control: FROST consensus ensures agents agreed, delegation caveats ensure the action is within Alice's policy. Even if all 3 agents are compromised and produce a valid FROST signature for an out-of-bounds action, the DelegationManager rejects it.

Uniswap

The committee executes real token swaps on Uniswap V3 (SwapRouter02) on Base Sepolia. The flow: agents evaluate a swap proposal (5 USDC for WETH), each independently checking risk, compliance, and viability. FROST ceremony produces the signature. AgentConsensus redeems Alice's delegation. Alice's smart account calls exactInputSingle on the Uniswap Router. Real USDC moves, real WETH received. Demonstrated with test USDC on Base Sepolia - Alice started with 20 USDC, the committee swapped 5, leaving 15 (swap tx). The delegation restricts swaps to max 100 USDC on Uniswap only - the committee cannot send tokens elsewhere or call other contracts.

Documentation

  • CONVERSATION.md - human-agent collaboration log (full build transcript)
  • SKILL.md - protocol specification for agents joining the committee
  • agent.json - DevSpot Agent Manifest (committee metadata, tools, contracts)
  • agent_log.json - structured execution logs (agent decisions, tool calls, tx receipts)
  • Demo video - full lifecycle on Base mainnet with real USDC

Built for The Synthesis hackathon.

About

AI agent committees with FROST threshold consensus and human-delegated authority with ERC7710

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors