npm package: @gasfree-kit/intent
Description: Chain-abstracted stablecoin payment intents — powered by gasfree-kit
Latest version: 0.2.0

# @gasfree-kit/intent

Chain-abstracted stablecoin payment intents for EVM chains.

Declare what you want ("send 50 USDT to this address") and the SDK handles multi-chain balance discovery, route planning, cross-chain bridging, and gasless execution automatically.

## How It Works

```
 ┌──────────────────────────────────────────────────────────┐
 │                        Your app                          │
 └────────────────────────────┬─────────────────────────────┘
                              │
                              v
 ┌──────────────────────────────────────────────────────────┐
 │               @gasfree-kit/intent                        │
 │                                                          │
 │   discoverBalances ─> planIntent ─> executeIntent        │
 │                                                          │
 │   Routes:  direct  |  bridge  |  aggregate               │
 └────────┬──────────────────┬──────────────────┬───────────┘
          │                  │                  │
          v                  v                  v
 ┌────────────────┐ ┌────────────────┐ ┌────────────────────┐
 │ @gasfree-kit/  │ │ WDK USDT0      │ │  Multi-chain RPC   │
 │ evm-4337       │ │ bridge         │ │  (balance queries)  │
 │                │ │ (LayerZero)    │ │                    │
 │ ERC-4337       │ │                │ │  eth_call per chain │
 │ UserOps        │ │ Cross-chain    │ │                    │
 └────────────────┘ └────────────────┘ └────────────────────┘
```

## Supported Chains

| Chain    | Chain ID | Direct transfer | Bridge source |
| -------- | -------- | --------------- | ------------- |
| Ethereum | 1        | Yes             | Yes           |
| Base     | 8453     | Yes             | Yes           |
| Arbitrum | 42161    | Yes             | Yes           |
| Optimism | 10       | Yes             | Yes           |
| Polygon  | 137      | Yes             | Yes           |
| Celo     | 42220    | Yes             | Yes           |
| Plasma   | 9745     | Yes             | Yes           |

## Prerequisites

This package depends on:

- [`@gasfree-kit/core`](../core/README.md) for chain metadata, types, validation, and errors
- [`@gasfree-kit/evm-4337`](../evm-4337/README.md) for ERC-4337 gasless transfers and wallet setup

Both are installed automatically as dependencies.

## Installation

```bash
npm install @gasfree-kit/intent
```

Required peer dependency:

```bash
npm install @tetherto/wdk-wallet-evm-erc-4337
```

Optional peer dependency (for cross-chain bridging):

```bash
npm install @tetherto/wdk-protocol-bridge-usdt0-evm
```

## Quick Start

### 5-line integration

```ts
import { GasfreeIntent } from '@gasfree-kit/intent';

const intent = GasfreeIntent.create({
  chains: ['base', 'arbitrum', 'optimism'],
  signer: { seedPhrase },
  bundlerUrl: 'https://your-bundler.example.com',
  paymasterUrl: 'https://your-paymaster.example.com',
  isSponsored: true,
  sponsorshipPolicyId: 'your-policy-id',
});

const receipt = await intent.sendIntent({
  from: senderAddress,
  to: merchantAddress,
  amount: '50.00',
});

console.log(receipt.status); // "completed"
```

### Step by step

#### 1. Create a config

```ts
import type { IntentConfig } from '@gasfree-kit/intent';

const config: IntentConfig = {
  chains: ['base', 'arbitrum', 'optimism', 'polygon', 'celo'],
  signer: { seedPhrase: 'your twelve word seed phrase ...' },
  bundlerUrl: 'https://your-bundler.example.com',
  paymasterUrl: 'https://your-paymaster.example.com',
  isSponsored: true,
  sponsorshipPolicyId: 'your-policy-id',
  // Enable cross-chain bridging (requires wdk-protocol-bridge-usdt0-evm):
  // bridgeConfig: { enabled: true },
};
```

#### 2. Discover balances

```ts
import { GasfreeIntent } from '@gasfree-kit/intent';

const intent = GasfreeIntent.create(config);

const balances = await intent.discoverBalances(senderAddress);

console.log(balances.total); // "83.50"
console.log(balances.chains); // [{ chain: 'base', balance: '50.00', ... }, ...]
```

#### 3. Plan the intent

```ts
const plan = await intent.planIntent({
  from: senderAddress,
  to: merchantAddress,
  amount: '50.00',
});

console.log(plan.route); // "direct" | "bridge" | "aggregate"
console.log(plan.steps); // ordered execution steps
console.log(plan.estimatedFee); // "0" (sponsored) or "0.15" etc.
```

#### 4. Execute the plan

```ts
const receipt = await intent.executeIntent(plan);

console.log(receipt.status); // "completed" | "partial" | "failed"
console.log(receipt.totalFee); // actual fee paid
console.log(receipt.executionTime); // ms
```

## Route Types

The planner selects the simplest feasible route:

| Route     | When                                       | Steps                  |
| --------- | ------------------------------------------ | ---------------------- |
| Direct    | Sender has enough on the destination chain | 1 transfer             |
| Bridge    | One other chain has enough                 | 1 bridge + 1 transfer  |
| Aggregate | Balance split across multiple chains       | N bridges + 1 transfer |

Bridge and aggregate routes require `bridgeConfig: { enabled: true }` in the config. Without it, only direct routes are planned.

## Events

Subscribe to lifecycle events for real-time UI feedback:

```ts
intent.on('balances:discovered', (event) => {
  console.log('Balances:', event.data);
});

intent.on('step:started', (event) => {
  console.log('Starting step:', event.data);
});

intent.on('step:completed', (event) => {
  console.log('Step done:', event.data);
});

intent.on('intent:completed', (event) => {
  console.log('Payment complete:', event.data);
});

intent.on('recovery:needed', (event) => {
  console.log('Recovery info:', event.data);
});
```

All event types: `balances:discovered`, `plan:created`, `step:started`, `step:completed`, `step:failed`, `intent:completed`, `intent:failed`, `recovery:needed`.

Event handler exceptions are swallowed and never affect execution.

## Configuration Reference

| Parameter             | Type                                   | Default  | Description                                   |
| --------------------- | -------------------------------------- | -------- | --------------------------------------------- |
| `chains`              | `EvmChain[]`                           | Required | EVM chains to discover balances on            |
| `signer`              | `IntentSigner`                         | Required | `{ seedPhrase, accountIndex? }`               |
| `bundlerUrl`          | `string`                               | Required | Candide ERC-4337 bundler endpoint             |
| `paymasterUrl`        | `string`                               | Required | Candide paymaster endpoint                    |
| `isSponsored`         | `boolean`                              | Required | Whether gas is sponsored                      |
| `sponsorshipPolicyId` | `string`                               | -        | Required when `isSponsored` is `true`         |
| `paymasterAddress`    | `string`                               | -        | Paymaster contract (non-sponsored mode)       |
| `chainOverrides`      | `Partial<Record<EvmChain, overrides>>` | -        | Per-chain RPC, bundler, paymaster overrides   |
| `bridgeConfig`        | `{ enabled: boolean, ... }`            | disabled | Must be `{ enabled: true }` for bridge routes |
| `balanceCacheTTL`     | `number`                               | `15000`  | Balance cache TTL in ms                       |
| `bridgeTimeout`       | `number`                               | `300000` | Bridge step timeout in ms (5 min)             |
| `transferTimeout`     | `number`                               | `120000` | Transfer step timeout in ms (2 min)           |
| `maxRetries`          | `number`                               | `3`      | Auto-retries per step on transient failure    |
| `rpcFallbacks`        | `Partial<Record<EvmChain, string[]>>`  | -        | Fallback RPC URLs per chain                   |
| `onStatusChange`      | `(event: IntentEvent) => void`         | -        | Shorthand callback for all lifecycle events   |

## Error Handling

All errors extend `GasfreeError` from `@gasfree-kit/core`:

```ts
import {
  IntentValidationError,
  RoutePlanningError,
  InsufficientCrossChainBalanceError,
  BridgeError,
  IntentTimeoutError,
  IntentExecutionError,
} from '@gasfree-kit/intent';

try {
  await intent.sendIntent({ from, to, amount: '50.00' });
} catch (error) {
  if (error instanceof InsufficientCrossChainBalanceError) {
    console.log('Not enough USDT across all chains');
  } else if (error instanceof IntentTimeoutError) {
    console.log('Step timed out:', error.message);
  } else if (error instanceof RoutePlanningError) {
    console.log('No viable route:', error.message);
  }
}
```

## Recovery

When a step fails, the receipt includes a `recovery` object explaining where funds are and what to do:

```ts
const receipt = await intent.executeIntent(plan);

if (receipt.recovery) {
  console.log(receipt.recovery.type); // "bridge_pending" | "transfer_failed" | "timeout"
  console.log(receipt.recovery.stuckChain); // which chain funds are on
  console.log(receipt.recovery.stuckAmount); // how much
  console.log(receipt.recovery.recoveryAction); // human-readable next step
  console.log(receipt.recovery.retryable); // can the SDK retry automatically?
}
```

Funds are never custodied by the SDK. If a bridge succeeds but the transfer fails, funds sit in the sender's own address on the destination chain.

## Fund Safety

- The SDK never holds, pools, or controls user funds
- Every on-chain action requires the user's wallet signature
- Bridge uses Tether's USDT0 protocol (LayerZero) which guarantees delivery or revert
- If any step fails, funds remain in the sender's address on whichever chain they're on
- All failures are reflected in the `IntentReceipt` with recovery guidance

## Main Exports

| Export                  | What it does                                            |
| ----------------------- | ------------------------------------------------------- |
| `GasfreeIntent`         | Main class: discover, plan, execute, one-shot send      |
| `discoverBalances`      | Standalone multi-chain balance discovery                |
| `planIntent`            | Standalone route planning from a balance map            |
| `validateIntentRequest` | Validate an intent request before planning              |
| `buildEvm4337Config`    | Build per-chain EVM4337 config from IntentConfig        |
| `IntentEventEmitter`    | Typed event emitter for lifecycle events                |
| `clearBalanceCache`     | Clear the in-memory balance cache                       |
| Error classes           | `IntentValidationError`, `RoutePlanningError`, etc.     |
| Types                   | `IntentRequest`, `ExecutionPlan`, `IntentReceipt`, etc. |

## License

MIT