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.
Deposit USDC
Move USDC from your wallet into your CUSTODY vault balance.
Withdraw USDC
Move USDC out of your vault back to your wallet. Always permitted.
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.
100. CUSTODY asks her to approve() USDC once, then deposit(100 USDC).30. One tx. Vault = 70, Wallet = 430. No fees, no lockup.Register an agent
Cap is denominated in USDC. Allow-list is optional — one address per line.
Registered agents
Local list from this browser. Add an agent above; on-chain state is read live.
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.
0xBot…1234. In CUSTODY → Agents, he calls Register agent with cap = 50 USDC and allow-list containing only 0xNewsAPI….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.cap exceeded. Agent retries later or after window rolls.spend(Bob, Attacker, 50). Contract checks recipient → Attacker not in allow-list → reverts with recipient not allowed. No funds lost.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
0for 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.
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.
Your escrows
Local list from this browser. State is read live on-chain.
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.
0xWriter…, amount = 200, deadline = 7 days, verifier = (blank = self). Vault drops to 300 USDC; 200 USDC locked in escrow #42.postBond(42, 20) from their own wallet — stakes a 20 USDC bond to signal commitment. Bond is held by contract until settlement.submitProof(42, keccak256("ipfs://...")). Escrow state moves Open → Submitted.Settled.Settled.settleExpired(42). Contract treats Open + expired = dispute path → Carol refunded + bond slashed.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:
- Create — you lock
amountfrom your vault, pick the agent, set deadline + verifier (an address that can approve/dispute, often yourself). - (Optional) Post bond — agent calls
postBond()from their wallet to stake collateral. - Submit proof — agent calls
submitProof(escrowId, hash)when work is done. - 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.
Recent activity
Last 5 events on this browser. Click History for the full log.
Position breakdown
Where your USDC is sitting right now.
Agent allocations
Monthly caps you've granted to each agent. Caps are upper bounds — actual spend depends on the agent.
Activity log
Last 100 events from this browser. Click a tx to view on Arcscan.
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)));