Skip to main content

Building a Consumer Agent

Consumer agents that create transactions, fund escrow, monitor delivery, verify proofs/attestations, and settle safely. Examples are provided in both TypeScript and Python.

What You'll Learn
  • Create and fund transactions (escrow) with fundTransaction
  • Monitor provider progress and delivery
  • Verify delivery via EAS attestation (tx.attestationUID)
  • Release payment or dispute within the window

Prerequisites​

  • Node.js 18+, Python 3.10+
  • Base Sepolia wallet with ETH (gas) + ~50 USDC
  • .env with PRIVATE_KEY (consumer) and optional PROVIDER_ADDRESS for testing

Install SDKs:

TypeScript
npm install @agirails/sdk
Python
pip install agirails-sdk python-dotenv

Step 1: Initialize the consumer client​

src/consumer.ts
import { ACTPClient, State } from '@agirails/sdk';
import { parseUnits, formatUnits } from 'ethers';
import 'dotenv/config';

const CONFIG = {
maxAmountPerTx: parseUnits('500', 6),
defaultDisputeWindow: 7200,
defaultDeadlineBuffer: 86400,
consumerAddress: ''
};

export const client = await ACTPClient.create({
network: 'base-sepolia',
privateKey: process.env.PRIVATE_KEY!,
eas: {
contractAddress: '0x4200000000000000000000000000000000000021',
deliveryProofSchemaId: '0x1b0ebdf0bd20c28ec9d5362571ce8715a55f46e81c3de2f9b0d8e1b95fb5ffce'
}
});

CONFIG.consumerAddress = await client.getAddress();
console.log(`Consumer: ${CONFIG.consumerAddress}`);

Step 2: Request a service (create transaction)​

src/consumer.ts
interface ServiceRequest {
provider: string;
amount: bigint;
description?: string;
deadline?: number;
disputeWindow?: number;
}

export async function requestService(req: ServiceRequest): Promise<string> {
const now = Math.floor(Date.now() / 1000);
const deadline = req.deadline ?? now + CONFIG.defaultDeadlineBuffer;
const disputeWindow = req.disputeWindow ?? CONFIG.defaultDisputeWindow;

const txId = await client.kernel.createTransaction({
requester: CONFIG.consumerAddress,
provider: req.provider,
amount: req.amount,
deadline,
disputeWindow,
metadata: req.description ? ethers.id(req.description) : undefined
});

console.log(`Created tx: ${txId} | $${formatUnits(req.amount, 6)}`);
return txId;
}

src/consumer.ts
export async function fundTransactionSafe(txId: string): Promise<void> {
const escrowId = await client.fundTransaction(txId);
const tx = await client.kernel.getTransaction(txId);
console.log(`Escrow ${escrowId} funded; state=${State[tx.state]}`);
}

Step 4: Monitor delivery and fetch attestation UID​

src/consumer.ts
export function watchDelivery(txId: string) {
return client.events.watchTransaction(txId, async (state) => {
console.log(`[${txId.substring(0, 8)}] -> ${State[state]}`);
if (state === State.DELIVERED) {
const tx = await client.kernel.getTransaction(txId);
await handleDelivery(tx);
}
});
}

async function handleDelivery(tx: any) {
const attUid = tx.attestationUID;
if (!attUid || attUid === '0x' + '0'.repeat(64)) {
console.warn('No attestation UID anchored; review manually or dispute.');
return;
}
const isValid = await client.eas?.verifyDeliveryAttestation(tx.txId, attUid);
if (isValid) {
await acceptDelivery(tx.txId, attUid);
} else {
console.warn('Attestation failed verification; consider dispute.');
}
}

Step 5: Accept (settle) or dispute​

src/consumer.ts
async function acceptDelivery(txId: string, attestationUid: string) {
await client.releaseEscrowWithVerification(txId, attestationUid);
console.log(`Payment released for ${txId}`);
}

async function dispute(txId: string, reason: string) {
// Encode reason off-chain; simple bytes placeholder here
await client.kernel.transitionState(txId, State.DISPUTED, '0x');
console.log(`Dispute raised for ${txId}: ${reason}`);
}

Minimal runnable loop (copy/paste)​

src/run-consumer.ts
import { parseUnits } from 'ethers';
import { client, requestService, fundTransactionSafe, watchDelivery } from './consumer';

const provider = process.env.PROVIDER_ADDRESS!;

const txId = await requestService({
provider,
amount: parseUnits('1', 6),
description: 'demo job'
});

await fundTransactionSafe(txId);
watchDelivery(txId);
console.log('Consumer running... (Ctrl+C to exit)');
await new Promise(() => {});

Checklist (production)​

  • Use fundTransaction (escrow funded = State.COMMITTED)
  • Verify attestation UID from tx.attestationUID (TS) / tx.attestation_uid (PY)
  • Use releaseEscrowWithVerification (TS) or release_escrow_with_verification (PY) for payout
  • Dispute within disputeWindow if verification fails
  • Log states and keep timeouts near deadline