npm package: @qilinxlabs/a2a-cronos
Description: TypeScript SDK for A2A resource discovery and x402 payment protocol on Cronos networks
Latest version: 0.3.1

# a2a-cronos

TypeScript SDK for A2A resource discovery and x402 payment protocol on Cronos networks.

> **Disclaimer**: This project is built on [Cronos Labs x402 Examples](https://github.com/cronos-labs/x402-examples) and uses the `@crypto.com/facilitator-client` SDK for x402 payment processing.

## Installation

```bash
pnpm add a2a-cronos
# or
npm install a2a-cronos
```

## Features

- **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

## Quick Start

### Client Usage

```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: {...} }
```

### Server Usage (Express)

```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);
```

---

## Client API Reference

### A2AClientConfig

```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;
}
```

### System Prompt Example

```typescript
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: `You are a cautious AI agent.
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`,
});
```

---

## Quote Guardrails (Broker Services)

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.

### QuoteGuardrail Interface

```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<boolean>;
}
```

### Guardrail Example

```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);
}
```

### Evaluation Result

```typescript
interface QuoteEvaluationResult {
  accepted: boolean;
  reason: string;
  guardrailResults?: Array<{
    id: string;
    passed: boolean;
    message: string;
  }>;
  warnings?: string[];
}
```

### How It Works

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

---

### A2AClient Methods

#### `discover(url: string): Promise<DiscoveredAgent | null>`

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<DiscoveredAgent[]>`

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<ResourceTarget | null>`

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<FetchResult>`

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<FetchResult>`

Convenience method combining discover + LLM select + fetch. Requires `llm` config.

```typescript
const result = await client.fetchWithQuery(
  'get me the paywalled resource',
  ['http://localhost:8787']
);
```

### FetchResult

```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
}
```

---

## Server API Reference

### A2AServerConfig

```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<void>;
  
  /** Callback to check if paymentId is entitled (REQUIRED) */
  checkEntitlement: (paymentId: string) => Promise<boolean>;
}

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;
}
```

### A2AServer Methods

#### `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,
});
```

### Entitlement

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
}
```

---

## Broker API Reference (DeFi Swaps)

The broker module enables DeFi token swaps with x402 payment. Currently supports VVS Finance on Cronos.

### Installation

```bash
# VVS Finance SDK is an optional peer dependency
pnpm add @vvs-finance/swap-sdk
```

### A2ABrokerConfig

```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<void>;
  
  /** Callback to check swap status (REQUIRED) */
  checkSwapStatus: (swapId: string) => Promise<SwapStatus | null>;
}

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;
}
```

### Broker Usage Example

```typescript
import express from 'express';
import { A2ABroker, VVSFinanceProvider, type SwapRecord } from 'a2a-cronos';

const swaps = new Map<string, SwapRecord>();

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);
```

### SwapQuote Response

```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
}
```

### Supported Tokens

| Token | Mainnet | Testnet |
|-------|---------|---------|
| USDC | `0xc01efAaF7C5C61bEbFAeb358E1161b537b8bC0e0` | `0xf951eC28187D9E5Ca673Da8FE6757E6f0Be5F77C` |
| VVS | `0x2D03bECE6747ADC00E1a131BBA1469C15fD11e03` | `0x2D03bECE6747ADC00E1a131BBA1469C15fD11e03` |
| CRO | `NATIVE` | `NATIVE` |
| WCRO | `0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23` | `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'
);
```

---

## Networks & Assets

### Default RPC URLs

| Network | RPC URL |
|---------|---------|
| `cronos` | `https://evm.cronos.org` |
| `cronos-testnet` | `https://evm-t3.cronos.org` |

### Supported Assets

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',
  // ...
});
```

---

## Error Handling

```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);
  }
}
```

---

## Explorer URLs

For displaying transaction links:

| Network | Explorer URL |
|---------|--------------|
| `cronos` | `https://explorer.cronos.org/tx/{txHash}` |
| `cronos-testnet` | `https://explorer.cronos.org/testnet/tx/{txHash}` |

---

## Complete Examples

### Client Example (Next.js API Route)

```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);
}
```

### Server Example (Express)

```typescript
// server.ts
import 'dotenv/config';
import express from 'express';
import { A2AServer, type Entitlement } from 'a2a-cronos';

const entitlements = new Map<string, Entitlement>();

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');
});
```

---

## License

MIT