TypeScript SDK for A2A resource discovery and x402 payment protocol on Cronos networks
npm install @qilinxlabs/a2a-cronosTypeScript SDK for A2A resource discovery and x402 payment protocol on Cronos networks.
> Disclaimer: This project is built on Cronos Labs x402 Examples and uses the @crypto.com/facilitator-client SDK for x402 payment processing.
``bash`
pnpm add a2a-cronosor
npm install a2a-cronos
- A2AClient: Discover and access x402-protected resources
- A2AServer: Expose and monetize resources with x402 payment
- A2ABroker: DeFi swap broker with x402 payment (VVS Finance integration)
- LLM Support: Google, Anthropic, and OpenAI for intelligent resource selection
- System Prompts: Customizable LLM guardrails for service acceptance
- Stateless Design: No internal database - developer provides storage callbacks
- Framework Agnostic: Works with Express or any HTTP framework
- Network Support: Cronos mainnet and testnet
`typescript
import { A2AClient } from 'a2a-cronos';
const client = new A2AClient({
privateKey: '0x...', // Required - your wallet private key
network: 'cronos-testnet',
llm: {
provider: 'google',
apiKey: 'your-google-api-key',
model: 'gemini-3-pro-preview',
},
});
// Discover + LLM select + fetch in one call
const result = await client.fetchWithQuery(
'get me the paywalled resource',
['http://localhost:8787']
);
console.log(result);
// { ok: true, paid: true, paymentId: 'pay_xxx', txHash: '0x...', data: {...} }
`
`typescript
import express from 'express';
import { A2AServer } from 'a2a-cronos';
// In-memory store (use database in production)
const entitlements = new Map();
const server = new A2AServer({
name: 'my-resource-server',
url: 'http://localhost:8787',
network: 'cronos-testnet',
merchantAddress: '0x...', // Required - address to receive payments
resources: [
{
id: 'premium-data',
title: 'Premium Data',
path: '/api/data',
price: '1000000', // 1 USDC (6 decimals)
},
],
onEntitlementCreated: async (entitlement) => {
entitlements.set(entitlement.paymentId, entitlement);
},
checkEntitlement: async (paymentId) => {
return entitlements.has(paymentId);
},
});
const app = express();
app.use(express.json());
const middleware = server.expressMiddleware();
app.get('/.well-known/agent-card.json', middleware.agentCard);
app.get('/api/data', middleware.requirePayment('premium-data'), (req, res) => {
res.json({ secret: 'premium content' });
});
app.post('/api/pay', middleware.handleSettlement);
app.listen(8787);
`
---
`typescript
interface A2AClientConfig {
/* Wallet private key for signing payments (REQUIRED) /
privateKey: string;
/* Target network. Default: 'cronos-testnet' /
network?: 'cronos' | 'cronos-testnet';
/* Custom RPC URL. Uses default if not specified /
rpcUrl?: string;
/* LLM configuration for AI-based resource selection /
llm?: LLMConfig;
/**
* System prompt for LLM (optional).
* Used to set guardrails and instructions for agent behavior.
*/
systemPrompt?: string;
}
interface LLMConfig {
/* LLM provider /
provider: 'google' | 'anthropic' | 'openai';
/* API key for the provider (REQUIRED) /
apiKey: string;
/* Model name (e.g., 'gemini-3-pro-preview', 'claude-3-opus', 'gpt-4o') /
model: string;
}
`
`typescriptYou are a cautious AI agent.
const client = new A2AClient({
privateKey: '0x...',
network: 'cronos-testnet',
llm: {
provider: 'google',
apiKey: 'your-api-key',
model: 'gemini-2.0-flash',
},
// Custom guardrails for service acceptance
systemPrompt:
Rules:
- Never accept swap quotes with price impact > 5%
- Reject any service charging more than 0.5 USDC
- Always verify the broker fee is disclosed
- For VVS swaps, ensure minimum output is at least 95% of quoted amount,`
});
---
When using broker services for DeFi swaps, you can configure guardrails to automatically evaluate quotes before accepting them. The LLM evaluates each guardrail and decides whether to accept or reject the quote.
`typescript
interface QuoteGuardrail {
/* Rule identifier /
id: string;
/* Human-readable description /
description: string;
/**
* Condition in natural language that the LLM will evaluate.
* The LLM checks this condition against the quote data.
*/
condition: string;
/* 'error' rejects quote, 'warning' logs but accepts /
severity: 'error' | 'warning';
}
interface QuoteAcceptanceCriteria {
/* Guardrail rules for LLM to evaluate /
guardrails?: QuoteGuardrail[];
/* Additional context for the LLM /
evaluationContext?: string;
/* Use LLM evaluation (default: true if LLM configured) /
useLLMEvaluation?: boolean;
// Programmatic fallbacks (used if LLM not available)
minOutputAmount?: string;
maxPriceImpact?: string;
maxBrokerFee?: string;
maxSlippage?: string;
customCheck?: (quote: BrokerQuote) => boolean | Promise
}
`
`typescript
import { requestQuote, acceptQuote, type QuoteAcceptanceCriteria } from 'a2a-cronos';
// Define guardrails
const criteria: QuoteAcceptanceCriteria = {
guardrails: [
{
id: 'min_output',
description: 'Ensure we receive tokens',
condition: 'Output amount must be greater than 0',
severity: 'error',
},
{
id: 'max_slippage',
description: 'Limit slippage tolerance',
condition: 'Slippage tolerance must be less than 5%',
severity: 'error',
},
{
id: 'check_provider',
description: 'Only accept trusted providers',
condition: 'Provider must be VVS Finance or VVS Finance (Hybrid)',
severity: 'error',
},
{
id: 'warn_high_fee',
description: 'Warn about high fees',
condition: 'LP fee should be less than 1%',
severity: 'warning', // Warns but doesn't reject
},
],
evaluationContext: 'Be conservative with large swaps over 100 USDC',
};
// Request and evaluate quote
const quote = await requestQuote(selection);
const evaluation = await acceptQuote(quote, criteria, llmConfig);
if (evaluation.accepted) {
console.log('Quote accepted:', evaluation.reason);
// Proceed with payment
} else {
console.log('Quote rejected:', evaluation.reason);
console.log('Failed guardrails:', evaluation.guardrailResults);
}
`
`typescript`
interface QuoteEvaluationResult {
accepted: boolean;
reason: string;
guardrailResults?: Array<{
id: string;
passed: boolean;
message: string;
}>;
warnings?: string[];
}
1. LLM Evaluation: When guardrails are configured and LLM is available, the quote is formatted and sent to the LLM along with the guardrail conditions
2. The LLM analyzes the quote data (amounts, fees, slippage, metadata) against each guardrail condition
3. Error severity guardrails that fail cause the quote to be rejected
4. Warning severity guardrails that fail are logged but don't reject the quote
5. Fallback: If LLM is unavailable, programmatic checks (minOutputAmount, maxSlippage, etc.) are used
---
#### discover(url: string): Promise
Discover an agent from a single URL by fetching its agent card.
`typescript`
const agent = await client.discover('http://localhost:8787');
if (agent) {
console.log(agent.name, agent.resources);
}
#### discoverAll(urls: string[]): Promise
Discover agents from multiple URLs concurrently.
`typescript`
const agents = await client.discoverAll([
'http://localhost:8787',
'http://localhost:8788',
]);
#### selectResource(agent: DiscoveredAgent, resourceIndex: number): ResourceTarget
Manually select a resource from a discovered agent by index.
`typescript`
const agent = await client.discover('http://localhost:8787');
const target = client.selectResource(agent, 0); // First resource
#### selectResourceWithLLM(query: string, agents: DiscoveredAgent[]): Promise
Use LLM to intelligently select the best resource for a query. Requires llm config.
`typescript`
const agents = await client.discoverAll(['http://localhost:8787']);
const target = await client.selectResourceWithLLM(
'get premium data',
agents
);
#### fetchResource(target: ResourceTarget): Promise
Fetch a resource with automatic x402 payment handling.
`typescript
const target = client.selectResource(agent, 0);
const result = await client.fetchResource(target);
if (result.ok) {
console.log('Data:', result.data);
if (result.paid) {
console.log('Paid:', result.paymentId, result.txHash);
}
}
`
#### fetchWithQuery(query: string, discoveryUrls: string[]): Promise
Convenience method combining discover + LLM select + fetch. Requires llm config.
`typescript`
const result = await client.fetchWithQuery(
'get me the paywalled resource',
['http://localhost:8787']
);
`typescript`
interface FetchResult {
ok: boolean; // Whether the fetch succeeded
paid: boolean; // Whether payment was made
paymentId?: string; // Payment ID if paid
txHash?: string; // Transaction hash if paid
data?: unknown; // Response data
error?: string; // Error message if failed
}
---
`typescript
interface A2AServerConfig {
/* Server name for AgentCard (REQUIRED) /
name: string;
/* Server description /
description?: string;
/* Public URL of the server (REQUIRED) /
url: string;
/* Target network (REQUIRED) /
network: 'cronos' | 'cronos-testnet';
/* Merchant address to receive payments (REQUIRED) /
merchantAddress: string;
/* Resource definitions (REQUIRED, at least one) /
resources: ResourceDefinition[];
/* Custom RPC URL /
rpcUrl?: string;
/* Asset symbol or contract address. Default: 'USDC' /
asset?: string;
/* Callback when payment is settled (REQUIRED) /
onEntitlementCreated: (entitlement: Entitlement) => Promise
/* Callback to check if paymentId is entitled (REQUIRED) /
checkEntitlement: (paymentId: string) => Promise
}
interface ResourceDefinition {
/* Unique resource ID (REQUIRED) /
id: string;
/* Display title /
title?: string;
/* Description /
description?: string;
/* URL path (REQUIRED, e.g., '/api/data') /
path: string;
/* Price in base units (REQUIRED, e.g., '1000000' for 1 USDC) /
price: string;
/* Settlement endpoint path. Default: '/api/pay' /
settlementPath?: string;
}
`
#### getAgentCard(): AgentCard
Get the AgentCard JSON for this server.
`typescript`
const card = server.getAgentCard();
// Returns: { name, description, url, network, resources: [...] }
#### expressMiddleware(): ExpressMiddleware
Get Express middleware handlers.
`typescript
const middleware = server.expressMiddleware();
// Serve agent card
app.get('/.well-known/agent-card.json', middleware.agentCard);
// Protect a resource
app.get('/api/data', middleware.requirePayment('resource-id'), handler);
// Handle settlement
app.post('/api/pay', middleware.handleSettlement);
`
#### handlers(): Handlers
Get generic handlers for framework-agnostic usage.
`typescript
const handlers = server.handlers();
// Get agent card
const card = handlers.getAgentCard();
// Check access (returns { allowed: boolean, challenge?: X402Challenge })
const access = await handlers.checkAccess('resource-id', paymentId);
// Generate challenge manually
const challenge = handlers.generateChallenge('resource-id');
// Settle payment
const result = await handlers.settlePayment({
paymentId,
paymentHeader,
paymentRequirements,
});
`
Passed to onEntitlementCreated callback after successful payment:
`typescript`
interface Entitlement {
paymentId: string; // Unique payment ID
resourceId: string; // Resource that was paid for
txHash: string; // Settlement transaction hash
amount: string; // Amount paid
payer?: string; // Payer address (if available)
settledAt: Date; // Settlement timestamp
}
---
The broker module enables DeFi token swaps with x402 payment. Currently supports VVS Finance on Cronos.
`bash`VVS Finance SDK is an optional peer dependency
pnpm add @vvs-finance/swap-sdk
`typescript
interface A2ABrokerConfig {
/* Broker name for AgentCard (REQUIRED) /
name: string;
/* Broker description /
description?: string;
/* Public URL of the broker (REQUIRED) /
url: string;
/* Target network (REQUIRED) /
network: 'cronos' | 'cronos-testnet';
/* Broker wallet private key for executing swaps (REQUIRED) /
brokerPrivateKey: string;
/* Broker wallet address to receive x402 payments (REQUIRED) /
brokerAddress: string;
/* DeFi providers to use (REQUIRED) /
providers: DeFiProvider[];
/* Broker fee configuration (default: no fee) /
fee?: BrokerFeeConfig;
/* Custom RPC URL /
rpcUrl?: string;
/* Callback when swap is completed (REQUIRED) /
onSwapCompleted: (swap: SwapRecord) => Promise
/* Callback to check swap status (REQUIRED) /
checkSwapStatus: (swapId: string) => Promise
}
interface BrokerFeeConfig {
/* Fee type: 'fixed' for USDC, 'percentage' for output tokens /
type: 'fixed' | 'percentage';
/* Amount: USDC for fixed (e.g., "0.01"), or % for percentage (e.g., "1.5") /
amount: string;
}
`
`typescript
import express from 'express';
import { A2ABroker, VVSFinanceProvider, type SwapRecord } from 'a2a-cronos';
const swaps = new Map
const broker = new A2ABroker({
name: 'vvs-swap-broker',
description: 'Token swap broker via VVS Finance',
url: 'http://localhost:8787',
network: 'cronos-testnet',
brokerPrivateKey: process.env.BROKER_PRIVATE_KEY!,
brokerAddress: process.env.BROKER_ADDRESS!,
providers: [
new VVSFinanceProvider({
quoteApiClientId: process.env.VVS_QUOTE_API_CLIENT_ID!,
}),
],
// Optional: 1% fee on output tokens
fee: { type: 'percentage', amount: '1.0' },
onSwapCompleted: async (swap) => {
swaps.set(swap.swapId, swap);
console.log('Swap completed:', swap.swapTxHash);
},
checkSwapStatus: async (swapId) => {
return swaps.get(swapId)?.status || null;
},
});
const app = express();
app.use(express.json());
// Get swap quote
app.get('/api/broker/quote', async (req, res) => {
const { input, output, amount } = req.query;
const quote = await broker.getQuote(
input as string,
output as string,
amount as string
);
const { trade, ...publicQuote } = quote;
res.json(publicQuote);
});
// Execute swap (paywalled)
app.post('/api/broker/swap', async (req, res) => {
const { quoteId } = req.body;
const paymentHeader = req.headers['x-payment'];
if (!paymentHeader) {
const challenge = broker.generateChallenge(quoteId);
return res.status(402).json(challenge);
}
// Verify payment and execute swap...
});
app.listen(8787);
`
`typescript
interface SwapQuote {
quoteId: string; // Quote ID for reference
provider: string; // "VVS Finance"
inputToken: string; // Input token address
inputTokenSymbol: string; // "USDC"
outputToken: string; // Output token address
outputTokenSymbol: string; // "VVS"
inputAmount: string; // "0.1"
outputAmount: string; // "125.5"
minimumOutput: string; // "124.25" (after slippage)
priceImpact?: string; // "0.05"
brokerFee?: BrokerFeeInfo; // Fee details if configured
routes: SwapRoute[]; // Routing/pool details
validUntil: number; // Quote expiry timestamp
}
interface SwapRoute {
path: string[]; // Token addresses in route
pools: SwapPoolInfo[]; // Pool details
portion?: string; // "100%" or split percentage
}
interface SwapPoolInfo {
poolType: string; // "V2", "V3_3000", etc.
poolAddress?: string; // Pool contract address
fee?: string; // "0.30%"
liquidity?: string; // Available liquidity
}
`
| Token | Mainnet | Testnet |
|-------|---------|---------|
| USDC | 0xc01efAaF7C5C61bEbFAeb358E1161b537b8bC0e0 | 0xf951eC28187D9E5Ca673Da8FE6757E6f0Be5F77C |0x2D03bECE6747ADC00E1a131BBA1469C15fD11e03
| VVS | | 0x2D03bECE6747ADC00E1a131BBA1469C15fD11e03 |NATIVE
| CRO | | NATIVE |0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23
| WCRO | | 0x6a3173618859C7cd40fAF6921b5E9eB6A76f1fD4 |
You can use either token symbols or contract addresses:
`typescript
// Using symbols
const quote = await broker.getQuote('USDC', 'VVS', '0.1');
// Using addresses
const quote = await broker.getQuote(
'0xf951eC28187D9E5Ca673Da8FE6757E6f0Be5F77C',
'0x2D03bECE6747ADC00E1a131BBA1469C15fD11e03',
'0.1'
);
`
---
| Network | RPC URL |
|---------|---------|
| cronos | https://evm.cronos.org |cronos-testnet
| | https://evm-t3.cronos.org |
Currently only USDC supports EIP-3009 and x402 protocol:
| Asset | Mainnet Address | Testnet Address |
|-------|-----------------|-----------------|
| USDC | 0xc01efAaF7C5C61bEbFAeb358E1161b537b8bC0e0 | 0xf951eC28187D9E5Ca673Da8FE6757E6f0Be5F77C |
You can pass either the symbol 'USDC' or the contract address directly:
`typescript
// Using symbol (recommended)
const server = new A2AServer({
asset: 'USDC', // Resolves to correct address per network
// ...
});
// Using address directly
const server = new A2AServer({
asset: '0xf951eC28187D9E5Ca673Da8FE6757E6f0Be5F77C',
// ...
});
`
---
`typescript
import {
A2AError,
ConfigurationError,
NetworkError,
PaymentError,
ValidationError
} from 'a2a-cronos';
try {
await client.fetchResource(target);
} catch (error) {
if (error instanceof ConfigurationError) {
console.log('Config issue:', error.message);
} else if (error instanceof NetworkError) {
console.log('Network issue:', error.status, error.message);
} else if (error instanceof PaymentError) {
console.log('Payment failed:', error.phase, error.details);
} else if (error instanceof ValidationError) {
console.log('Validation failed:', error.field, error.message);
}
}
`
---
For displaying transaction links:
| Network | Explorer URL |
|---------|--------------|
| cronos | https://explorer.cronos.org/tx/{txHash} |cronos-testnet
| | https://explorer.cronos.org/testnet/tx/{txHash} |
---
`typescript
// app/api/agent/route.ts
import { A2AClient } from 'a2a-cronos';
export async function POST(request: Request) {
const { query } = await request.json();
const client = new A2AClient({
privateKey: process.env.PRIVATE_KEY!,
network: 'cronos-testnet',
llm: {
provider: 'google',
apiKey: process.env.GOOGLE_API_KEY!,
model: 'gemini-3-pro-preview',
},
});
const result = await client.fetchWithQuery(query, [
'http://localhost:8787',
]);
return Response.json(result);
}
`
`typescript
// server.ts
import 'dotenv/config';
import express from 'express';
import { A2AServer, type Entitlement } from 'a2a-cronos';
const entitlements = new Map
const a2aServer = new A2AServer({
name: 'my-api',
description: 'Premium API with x402 paywall',
url: process.env.PUBLIC_URL || 'http://localhost:8787',
network: 'cronos-testnet',
merchantAddress: process.env.MERCHANT_ADDRESS!,
resources: [
{
id: 'premium-endpoint',
title: 'Premium Endpoint',
description: 'Access to premium data',
path: '/api/premium',
price: '1000000', // 1 USDC
},
{
id: 'basic-endpoint',
title: 'Basic Endpoint',
path: '/api/basic',
price: '100000', // 0.1 USDC
},
],
onEntitlementCreated: async (entitlement) => {
entitlements.set(entitlement.paymentId, entitlement);
console.log('Payment received:', entitlement.txHash);
},
checkEntitlement: async (paymentId) => {
return entitlements.has(paymentId);
},
});
const app = express();
app.use(express.json());
const middleware = a2aServer.expressMiddleware();
// Discovery endpoint
app.get('/.well-known/agent-card.json', middleware.agentCard);
// Protected endpoints
app.get('/api/premium', middleware.requirePayment('premium-endpoint'), (req, res) => {
res.json({ data: 'premium content' });
});
app.get('/api/basic', middleware.requirePayment('basic-endpoint'), (req, res) => {
res.json({ data: 'basic content' });
});
// Settlement endpoint
app.post('/api/pay', middleware.handleSettlement);
app.listen(8787, () => {
console.log('Server running on http://localhost:8787');
});
``
---
MIT