Agent Budget Vault
Give AI agents a capped USDC budget — no private keys, no wallet access
The Agent Budget Vault is an HTLC-style on-chain spending vault deployed on SKALE Base Sepolia. An owner deposits USDC and commits a batch of hashlocks (one per spending unit). Each hashlock locks a fixed USDC amount. The agent is given the corresponding secrets — when it needs to pay for an API call, it reveals a secret to the RelAI facilitator, which calls vault.redeem(secret, payee) on-chain.
The agent never holds a private key. If compromised, an attacker can only spend the pre-committed budget — not drain the owner's wallet.
cancelBatch() to reclaim unspent USDC at any time before redemption.How It Works
POST /facilitator/agent-budget/preparevault.deposit(usdc, items[]) via MetaMaskvault.redeem(secret, payeeAddress)cancelBatch(hashlocks[]) to reclaim unspent budgetWhy SKALE Base Sepolia? Near-zero gas costs for the facilitator. Each redeem() call is a on-chain transaction paid by the relayer in CREDIT — not the agent or the owner.
Secrets are generated client-side and never stored on the server. Save them immediately after preparation — if lost, funds remain locked until expiry.
Using the Dashboard UI
The Agent Budget section is available in /dashboard/payment-codes under the Agent Budget tab.
- 1Set amount per secret (e.g. 1.00 USDC), number of secrets, and expiry hours.
- 2Click "Prepare budget" — secrets are generated and shown. Copy all secrets now.
- 3Click "Deposit to vault" — MetaMask will prompt to switch to SKALE Base Sepolia, approve USDC, then deposit.
- 4Hand the secrets to your AI agent (env var, system prompt, API call).
- 5To reclaim unused funds: click "Withdraw all" (calls cancelBatch in one tx).
- 6After expiry: "Reclaim expired" button appears — recovers USDC without needing secrets.
localStorage — secrets and hashlocks survive page refreshes until you withdraw or reclaim.API Reference
/facilitator/agent-budget/prepareGenerate N (secret, hashlock) pairs. Returns secrets + deposit calldata. Requires auth.
/facilitator/agent-budget/redeemReveal a secret to redeem a commitment to a payee address. Called by the agent.
/facilitator/agent-budget/cancelCancel an unused commitment by hashlock (owner only). Returns USDC to owner.
curl -X POST https://api.relai.fi/facilitator/agent-budget/prepare \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "amount": 1000000, "expiry": 1735000000 },
{ "amount": 1000000, "expiry": 1735000000 },
{ "amount": 1000000, "expiry": 1735000000 }
]
}'
# Response
{
"secrets": ["0xabc123...", "0xdef456...", "0x789xyz..."],
"depositCalldata": [
{ "hashlock": "0x...", "amount": 1000000, "expiry": 1735000000 },
...
],
"totalAmount": 3000000,
"totalHuman": "3.00",
"vaultAddress": "0xA570faFC8128792fD71503026cC683A3d54369Eb",
"usdc": "0x2e08028E3C4c2356572E096d8EF835cD5C6030bD"
}curl -X POST https://api.relai.fi/facilitator/agent-budget/redeem \
-H "Content-Type: application/json" \
-d '{
"secret": "0xabc123...",
"payee": "0xApiProviderAddress"
}'
# Response
{
"success": true,
"txHash": "0x...",
"amount": 1000000
}Contract Reference
deposit(address usdc, Item[] items)Lock USDC for each item. Item = { hashlock, amount, expiry }. Caller becomes owner of each commitment.
redeem(bytes32 secret, address payee)Reveal preimage. If hash(secret) matches a commitment, transfers amount to payee. Anyone can call.
cancel(bytes32 hashlock)Owner cancels a single unused commitment and recovers USDC.
cancelBatch(bytes32[] hashlocks)Owner cancels multiple commitments in one transaction. Skips already-used ones.
reclaimExpired(bytes32[] hashlocks)Reclaim expired, unused commitments. Anyone can call after expiry — funds returned to owner.
Security Model
If the agent leaks or misuses secrets, the attacker can only spend committed amounts. The owner's main wallet is untouched.
Secrets are generated client-side and never stored on the RelAI server. The server only knows hashlocks (SHA-256 hashes). Treat secrets like API keys.
Every commitment has an expiry timestamp. After expiry, reclaimExpired() returns all unspent USDC to the owner even without knowing the secrets.
Each secret unlocks exactly one fixed-amount commitment. There is no way to spend a fraction of a commitment — choose denomination granularity at deposit time.