CUSTODY
● Yield + Custody Vault

USDC Custody Vault
Earn yield. Constrain agents. One vault.

Anyone (you or your agents) can deposit USDC. Idle funds earn pro-rata yield via the strategy. On top, you can register agents with spending rules and create escrows. Withdraw anytime — no lockups.

Total Value Locked
$USDC
Vault buffer + strategy position
Current APY (net)
%
Loading strategy…
Your Position
$USDC
Your share value (principal + yield earned)

Deposit USDC

Move USDC from your wallet into your CUSTODY vault balance.

USDC

Withdraw USDC

Move USDC out of your vault back to your wallet. Always permitted.

USDC

Example workflow

Alice is testing an AI shopping assistant. She wants to give it a fixed budget that's isolated from her main wallet — so even if the agent is compromised, the blast radius is capped.

1
Alice's wallet holds 500 USDC. She opens CUSTODY, clicks Deposit, enters 100. CUSTODY asks her to approve() USDC once, then deposit(100 USDC).
2
After 2 transactions: Vault balance = 100 USDC, Wallet balance = 400 USDC. The 100 USDC in vault is now the agent's budget ceiling.
3
A week later Alice decides to scale down. She clicks Withdraw, enters 30. One tx. Vault = 70, Wallet = 430. No fees, no lockup.
Outcome — The vault is a programmable sub-wallet Alice can fund or defund anytime. Only she can move money in or out at this layer.

Register an agent

Cap is denominated in USDC. Allow-list is optional — one address per line.

USDC

Registered agents

Local list from this browser. Add an agent above; on-chain state is read live.

No agents registered yet on this browser.

Example workflow

Bob runs an autonomous trading-news monitor agent. The agent fetches data from a paid API and pays per call. Bob wants to bound the agent's monthly spend and restrict who it can pay.

1
Bob's vault holds 200 USDC. He deploys his agent at address 0xBot…1234. In CUSTODY → Agents, he calls Register agent with cap = 50 USDC and allow-list containing only 0xNewsAPI….
2
Agent's own script (off-chain, using agent's private key) calls vault.spend(Bob, NewsAPI, 5_000_000) for a $5 data fetch. Contract checks: agent active ✓, cap not exceeded (5 ≤ 50) ✓, recipient allowed ✓. Tx succeeds. Spent window: $5 / 50.
3
Over 25 days, agent makes 9 more calls totaling $48. On call #11 trying to spend $5: cap remaining is $2. Contract reverts with cap exceeded. Agent retries later or after window rolls.
4
Hypothetical: agent is compromised, attacker tries spend(Bob, Attacker, 50). Contract checks recipient → Attacker not in allow-list → reverts with recipient not allowed. No funds lost.
Outcome — Bob never shared his private key. Maximum monthly loss to compromise = $50, capped to one recipient. Pause anytime with one tx.

What's an agent?

An agent is any wallet address (an AI agent, a script, a person) you authorise to spend USDC from your vault — under on-chain constraints the contract enforces every transaction. You never share your private key; agents use their own wallet to call spend().

Two constraints you set per agent:

  • Monthly cap — maximum USDC the agent can spend in any rolling 30-day window. Set to 0 for unlimited.
  • Allow-list — only these recipient addresses can receive payments (optional; leave blank to allow any recipient).

You stay in control: pause, resume, or remove an agent at any time. Changes are instant on-chain.

Two roles, one vault — The vault has two independent layers. (1) Yield layer: anyone (you OR an AI agent with idle USDC) can deposit/withdraw their own funds and earn pro-rata yield. Each depositor owns only their own shares. (2) Custody layer: on top of your own balance, you authorise agents with caps + allow-lists; an agent's spend() burns shares from YOUR balance (not its own). A compromised agent's blast radius is bounded by the cap and allow-list you set.

Create escrow

Pick a registered agent. Amount is debited from your vault and held until settlement.

USDC

Your escrows

Local list from this browser. State is read live on-chain.

No escrows created yet on this browser.

Example workflow

Carol hires an AI writer agent for a $200 article with a 7-day deadline. She wants the money locked so the agent knows it's real, but she wants leverage if the work is bad.

1
Carol's vault has 500 USDC. In CUSTODY → Escrow, she creates: agent = 0xWriter…, amount = 200, deadline = 7 days, verifier = (blank = self). Vault drops to 300 USDC; 200 USDC locked in escrow #42.
2
Optional: agent calls postBond(42, 20) from their own wallet — stakes a 20 USDC bond to signal commitment. Bond is held by contract until settlement.
3
Day 5: agent finishes article, uploads to IPFS, calls submitProof(42, keccak256("ipfs://...")). Escrow state moves Open → Submitted.
4a
Happy path: Carol reads the article — good. Clicks Approve on escrow #42. Contract releases 200 USDC to agent + 20 USDC bond back to agent. Escrow Settled.
Approved — Agent paid $200, gets bond back. Carol got her article.
4b
Sad path: Article is plagiarised. Carol clicks Dispute. Contract refunds 200 USDC to Carol's vault + sends 20 USDC bond to Carol as compensation. Escrow Settled.
Disputed — Carol gets $200 back + $20 from agent's bond. Agent loses bond.
4c
Timeout: Day 8 arrives, agent never submitted. Anyone (including Carol) calls settleExpired(42). Contract treats Open + expired = dispute path → Carol refunded + bond slashed.
Expired — Agent ghosted. Carol auto-refunded after deadline.

What's an escrow?

Lock USDC against a specific task for a specific agent until a deadline. The agent optionally posts a performance bond (from their own wallet) to signal commitment. Funds release only when work is approved — or refund + slash bond on dispute.

Lifecycle:

  1. Create — you lock amount from your vault, pick the agent, set deadline + verifier (an address that can approve/dispute, often yourself).
  2. (Optional) Post bond — agent calls postBond() from their wallet to stake collateral.
  3. Submit proof — agent calls submitProof(escrowId, hash) when work is done.
  4. Settle — you or the verifier approve (agent paid + bond returned) or dispute (you refunded + bond slashed to you). After deadline, anyone can settle: Open → dispute path, Submitted → approve path.

Best for tasks with measurable completion criteria — purchases, deliverables, scheduled payouts.

Portfolio

Your activity across deposits, agents, and escrows on CUSTODY.

Vault USDC
$
Locked in escrow
$
Active agents
Wallet USDC
$

Recent activity

Last 5 events on this browser. Click History for the full log.

No activity yet.

Position breakdown

Where your USDC is sitting right now.

Available in vault
Locked in escrows (Open + Submitted)
Total controlled by CUSTODY
USDC in your wallet (outside vault)

Agent allocations

Monthly caps you've granted to each agent. Caps are upper bounds — actual spend depends on the agent.

No agents registered yet.

Activity log

Last 100 events from this browser. Click a tx to view on Arcscan.

No history yet.

Integrate your agent

Wire your agent's wallet to call spend, postBond, and submitProof directly. On-chain constraints are enforced.

Have your agent spend

The agent's own wallet calls spend. Cap and allow-list are enforced on-chain.

// agent-side (node + ethers v6)
import { ethers } from "ethers";

const VAULT  = "0x…";
const USDC   = "0x3600000000000000000000000000000000000000";
const RPC    = "https://rpc.testnet.arc.network";
const ABI    = ["function spend(address owner, address recipient, uint256 amount)"];

const provider = new ethers.JsonRpcProvider(RPC);
const wallet   = new ethers.Wallet(process.env.AGENT_PRIVATE_KEY, provider);
const vault    = new ethers.Contract(VAULT, ABI, wallet);

// pay 1 USDC from `ownerAddress` to `recipient`
await vault.spend(ownerAddress, recipient, 1_000_000n);

Post a bond + submit proof for an escrow

Agent-side only. Posts a USDC bond and a proof hash for an open escrow.

const ABI = [
  "function postBond(uint256 id, uint256 bondAmount)",
  "function submitProof(uint256 id, bytes32 proofHash)"
];
const vault = new ethers.Contract(VAULT, ABI, agentWallet);

await usdc.approve(VAULT, bondAmount);
await vault.postBond(escrowId, bondAmount);
await vault.submitProof(escrowId, ethers.keccak256(ethers.toUtf8Bytes(proofString)));

Contract

USDC: 0x36…000000 · Chain: Arc Testnet (5042002)

Download ABI