Skip to main content

Escrow

The EscrowVault module manages USDC funds during the transaction lifecycle.


Overview

EscrowVault provides:

  • USDC approval helpers
  • Escrow balance queries
  • Fund status tracking

Important: Escrow creation happens atomically inside ACTPKernel.linkEscrow(). This module provides helper methods for approvals and read-only access to escrow state.


Workflow

Per AIP-3, the escrow workflow is:

  1. Approve: Consumer approves USDC to EscrowVault address
  2. Link: Consumer calls ACTPKernel.linkEscrow()
  3. Lock: Kernel creates escrow, pulls USDC from consumer
  4. Release: After delivery + dispute window, funds release to provider

Methods

approveToken()

Approve USDC for escrow creation. Must be called before linkEscrow().

// Level 2: Advanced API - Direct protocol control
import { parseUnits } from 'ethers';

// Approve 100 USDC for escrow
const amount = parseUnits('100', 6); // USDC has 6 decimals
await client.escrow.approveToken(USDC_ADDRESS, amount);

// Now link escrow via advanced API
await client.advanced.linkEscrow(txId);

Parameters:

ParameterTypeDescription
tokenAddressstringUSDC contract address
amountbigintAmount to approve (wei)

getEscrow()

Get escrow details by ID.

// Level 2: Advanced API - Direct protocol control
const escrow = await client.escrow.getEscrow(escrowId);

console.log('Requester:', escrow.requester);
console.log('Provider:', escrow.provider);
console.log('Amount:', escrow.amount);
console.log('Remaining:', escrow.remaining);
console.log('Released:', escrow.released);

Returns:

// Level 2: Advanced API - Direct protocol control
interface Escrow {
escrowId: string;
requester: string;
provider: string;
amount: bigint;
remaining: bigint;
released: boolean;
createdAt: number;
}

getRemaining()

Get remaining balance in escrow.

// Level 2: Advanced API - Direct protocol control
const remaining = await client.escrow.getRemaining(escrowId);
console.log('Remaining in escrow:', remaining, 'wei');

getAddress()

Get the EscrowVault contract address.

// Level 2: Advanced API - Direct protocol control
const vaultAddress = client.escrow.getAddress();
console.log('EscrowVault:', vaultAddress);

USDC Addresses

NetworkUSDC Address
Base Sepolia0x036CbD53842c5426634e7929541eC2318f3dCF7e
Base Mainnet0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Mock ModeAuto-deployed mock token

Example: Complete Escrow Flow

// Level 2: Advanced API - Direct protocol control
import { ACTPClient, parseUnits } from '@agirails/sdk';
import { BASE_SEPOLIA } from '@agirails/sdk/config';

async function completeEscrowFlow() {
const client = await ACTPClient.create({
mode: 'mock',
requesterAddress: '0x1111111111111111111111111111111111111111',
});

// 1. Create transaction
const txId = await client.advanced.createTransaction({
provider: '0x2222222222222222222222222222222222222222',
requester: '0x1111111111111111111111111111111111111111',
amount: parseUnits('100', 6), // $100 USDC
deadline: Math.floor(Date.now() / 1000) + 86400,
disputeWindow: 172800,
});

// 2. Approve USDC
const amount = parseUnits('100', 6);
await client.escrow.approveToken(BASE_SEPOLIA.contracts.usdc, amount);

// 3. Link escrow (auto-transitions to COMMITTED)
const escrowId = await client.advanced.linkEscrow(txId);
console.log('Escrow linked:', escrowId);

// 4. Check escrow balance
const remaining = await client.escrow.getRemaining(escrowId);
console.log('Funds locked:', remaining);

// 5. After delivery and dispute window...
await client.advanced.releaseEscrow(escrowId);
console.log('Funds released to provider');
}

Security Notes

  • Non-custodial: Funds are held by smart contract, not by AGIRAILS
  • 2-of-3 pattern: Release requires either:
    • Requester approval (explicit release)
    • Dispute window expiry (automatic release)
    • Mediator decision (dispute resolution)
  • Emergency withdrawal: 7-day timelock for admin access

Next Steps

  • Kernel - Transaction lifecycle
  • Events - Monitor escrow events
  • EAS - Delivery attestations