# x402 with SDK

## x402 with SDK

This guide adds Divigent liquidity management to x402 flows you already have.

Divigent does not replace your x402 client or seller server. It adds vault deposits, pre-payment withdrawals, and post-settlement deposits around your existing payment flow.

{% hint style="info" %}
The SDK supports Base mainnet (`chain: 'base'`) and Base Sepolia (`chain: 'base-sepolia'`). Base mainnet uses real Circle USDC and real transactions.
{% endhint %}

### What Divigent Adds

| Flow                                    | SDK API                                                   | What Happens                                                                            |
| --------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Initial deposit                         | `divigent.depositWithPermitAndWait(...)`                  | Move wallet USDC into Divigent before x402 usage starts                                 |
| Buyer automatic withdrawal              | `divigent.attachTo(x402Client, config)`                   | Before x402 signs, withdraw the deficit if wallet USDC is below `payment + reserve`     |
| Buyer automatic post-settlement deposit | `handle.wrapFetchWithYield(...)`                          | After successful x402 settlement, deposit wallet USDC above the reserve                 |
| Seller automatic income deposit         | `divigent.attachToResourceServer(resourceServer, config)` | After successful x402 settlement, deposit merchant wallet USDC above the seller reserve |
| Manual idle deposit                     | `handle.depositIdle(...)` or `divigent.depositIdle(...)`  | Sweep wallet USDC above the reserve without making an x402 request                      |
| Manual withdrawal                       | `previewWithdrawNet(...)` + `withdrawAndWait(...)`        | Pull a target amount of USDC back to the wallet                                         |

### 1. Attach Divigent To Your Existing Buyer x402 Client

```ts
import { Divigent, parseUsdc } from '@divigent/sdk';
import { x402HTTPClient, type x402Client } from '@x402/core/client';
import { wrapFetchWithPayment } from '@x402/fetch';

declare const divigent: Divigent;
declare const client: x402Client;
declare const merchantPayTo: `0x${string}`;

const handle = divigent.attachTo(client, {
  minIdleThreshold: parseUsdc('0.25'),
  maxPaymentAmount: parseUsdc('5'),
  maxSessionPaymentAmount: parseUsdc('25'),
  allowedPayTo: [merchantPayTo],
  requireAllowedPayTo: true,
  allowedOrigins: ['https://api.example.com'],
  allowedResources: ['https://api.example.com/paid'],
});

const http = new x402HTTPClient(client);
const fetchWithPayment = wrapFetchWithPayment(fetch, http);
const fetchWithYield = handle.wrapFetchWithYield(fetchWithPayment, http);
```

`minIdleThreshold` is the minimum USDC left liquid in the wallet. In this example, Divigent tries to keep `0.25 USDC` available after x402 settlement.

For production, set `allowedPayTo`, `allowedOrigins`, `allowedResources`, and payment caps. `requireAllowedPayTo: true` makes the SDK fail fast if no payee allowlist is configured.

### 2. Optional Initial Deposit

Use this when the wallet already has USDC and you want most of it in Divigent before the agent starts making x402 requests.

```ts
import { type EvmAddress, formatUsdc, parseUsdc } from '@divigent/sdk';

declare const wallet: EvmAddress;

const deposit = await divigent.depositWithPermitAndWait({
  amount: parseUsdc('100'),
  wallet,
  fallbackOnPermitUnsupported: true,
});

console.log(`deposit tx: ${deposit.txHash}`);
console.log(`minted: ${formatUsdc(deposit.sharesMinted)} dvUSDC`);
```

Base mainnet Circle USDC supports the permit fields used by the SDK. When the SDK has to fall back, it uses `approveUsdc + deposit` only for the signer wallet path.

### 3. Automatic Withdrawal Before x402 Payment

Once attached, Divigent can withdraw before payment creation.

When an x402 request needs payment, the hook checks:

```
wallet USDC >= payment amount + reserve
```

If the wallet is short, Divigent previews the shares required and withdraws the deficit before x402 signs the payment payload.

```ts
const response = await fetchWithYield('https://api.example.com/paid');
console.log(await response.text());
```

You can observe the pre-payment withdrawal decision:

```ts
const handle = divigent.attachTo(client, {
  minIdleThreshold: parseUsdc('0.25'),
  maxPaymentAmount: parseUsdc('5'),
  maxSessionPaymentAmount: parseUsdc('25'),
  allowedPayTo: [merchantPayTo],
  requireAllowedPayTo: true,
  onBeforePayment: (ctx) => {
    console.log(`payment: ${formatUsdc(ctx.paymentAmount)} USDC`);
    console.log(`wallet balance: ${formatUsdc(ctx.walletBalance)} USDC`);
    console.log(`reserve: ${formatUsdc(ctx.reserveFloor)} USDC`);
    console.log(`deficit: ${formatUsdc(ctx.deficit)} USDC`);
    if (ctx.recallShares !== undefined) {
      console.log(`withdrew shares: ${ctx.recallShares}`);
      console.log(`withdraw tx: ${ctx.recallTxHash}`);
    }
    if (ctx.recallError !== undefined) {
      console.warn('Divigent recall did not fully satisfy the reserve policy');
    }
  },
});
```

No withdrawal happens when the wallet already has enough USDC for the payment and reserve.

#### Recall Failure Behavior

Divigent is fail-closed for actual payment liquidity. Before x402 signs a payment, Divigent tries to recall enough USDC for `payment + reserve`.

If recall fails but the wallet still has enough USDC to cover the payment, the payment can proceed and `recallError` is surfaced for telemetry. If the wallet cannot cover the payment amount, the SDK aborts before signing and throws a `DivigentError` with code `DIVIGENT_X402_RECALL_INSUFFICIENT_LIQUIDITY`. The merchant never receives a settleable x402 authorization.

### 4. Automatic Deposit After x402 Settlement

Use `wrapFetchWithYield(...)` to redeposit idle buyer USDC after the x402 payment settles.

```ts
const fetchWithYield = handle.wrapFetchWithYield(fetchWithPayment, http, {
  waitForIdleDeposit: true,
  onIdleDeposit: (ctx) => {
    console.log(`auto-deposited: ${formatUsdc(ctx.idleAmount)} USDC`);
    console.log(`kept liquid: ${formatUsdc(ctx.reserveFloor)} USDC`);
    console.log(`deposit tx: ${ctx.txHash}`);
  },
});

await fetchWithYield('https://api.example.com/paid');
```

The wrapper deposits only after a successful x402 settlement response. It also keeps the just-settled payment amount liquid while the settlement debit propagates, so it does not sweep funds that were already paid.

### 5. Seller-Side Income Deposit

Sellers use `attachToResourceServer(...)`, not buyer `attachTo(...)`.

```ts
import { formatUsdc, parseUsdc, x402UsdcPrice } from '@divigent/sdk';
import { x402ResourceServer } from '@x402/express';

declare const divigent: Divigent;
declare const resourceServer: x402ResourceServer;
declare const merchantWallet: `0x${string}`;

const incomeHandle = divigent.attachToResourceServer(resourceServer, {
  minIdleThreshold: parseUsdc('1'),
  onIdleDeposit: (ctx) => {
    console.log(`seller deposited ${formatUsdc(ctx.idleAmount)} USDC`);
    console.log(`kept liquid ${formatUsdc(ctx.reserveFloor)} USDC`);
    console.log(`deposit tx ${ctx.txHash}`);
  },
});

const price = x402UsdcPrice({
  chain: 'base',
  amount: parseUsdc('0.001'),
  asset: divigent.addresses.usdc,
});
```

Use `price` in the seller x402 payment requirement. On Base mainnet, `x402UsdcPrice(...)` includes Circle USDC EIP-3009 metadata. On Base Sepolia, it uses Divigent's configured test USDC metadata.

Call `incomeHandle.detach()` when the seller no longer wants the hook attached.

### 6. Manual Idle Deposit

Use `depositIdle(...)` when your app handles settlement itself or when you want to sweep idle wallet USDC without making a paid request.

```ts
const txHash = await handle.depositIdle({
  onIdleDeposit: (ctx) => {
    console.log(`deposited: ${formatUsdc(ctx.idleAmount)} USDC`);
    console.log(`reserve kept: ${formatUsdc(ctx.reserveFloor)} USDC`);
  },
});

console.log(txHash ?? 'nothing to deposit');
```

You can also call the facade directly:

```ts
await divigent.depositIdle({
  wallet,
  minIdleThreshold: parseUsdc('0.25'),
});
```

### 7. Manual Withdrawal

Use manual withdrawal when your app needs wallet USDC outside the x402 payment flow.

```ts
import { type EvmAddress, formatUsdc, parseUsdc } from '@divigent/sdk';

declare const wallet: EvmAddress;

const desired = parseUsdc('10');
const shares = await divigent.previewWithdrawNet(desired, wallet);

const withdrawal = await divigent.withdrawAndWait({
  shares,
  wallet,
  slippageBps: 50,
});

console.log(`withdraw tx: ${withdrawal.txHash}`);
console.log(`received: ${formatUsdc(withdrawal.usdcReturned)} USDC`);
```

### 8. Fixed Buffer vs Adaptive Reserve

Use a fixed buffer when the agent should always keep one predictable amount liquid.

```ts
const handle = divigent.attachTo(client, {
  minIdleThreshold: parseUsdc('0.25'),
  reserveRatio: 0,
  reserveMultiplier: 0,
  maxPaymentAmount: parseUsdc('1'),
  allowedPayTo: [merchantPayTo],
  requireAllowedPayTo: true,
});
```

Use an adaptive reserve when payment sizes vary.

```ts
const handle = divigent.attachTo(client, {
  minIdleThreshold: parseUsdc('0.25'),
  reserveRatio: 0.5,
  reserveMultiplier: 2,
  maxPaymentAmount: parseUsdc('25'),
  allowedPayTo: [merchantPayTo],
  requireAllowedPayTo: true,
});
```

The adaptive reserve is:

```
max(minIdleThreshold, recent-payment-EMA * reserveRatio * reserveMultiplier)
```

The EMA records successful x402 payment creation with a 20% update weight. Samples are clamped by `maxPaymentAmount`.

### Complete Buyer Pattern

```ts
import {
  Divigent,
  type EvmAddress,
  formatUsdc,
  parseUsdc,
} from '@divigent/sdk';
import { x402HTTPClient, type x402Client } from '@x402/core/client';
import { wrapFetchWithPayment } from '@x402/fetch';

declare const divigent: Divigent;
declare const client: x402Client;
declare const wallet: EvmAddress;
declare const merchantPayTo: `0x${string}`;

const handle = divigent.attachTo(client, {
  minIdleThreshold: parseUsdc('0.25'),
  maxPaymentAmount: parseUsdc('5'),
  maxSessionPaymentAmount: parseUsdc('25'),
  allowedPayTo: [merchantPayTo],
  requireAllowedPayTo: true,
  allowedOrigins: ['https://api.example.com'],
  allowedResources: ['https://api.example.com/paid'],
});

await divigent.depositWithPermitAndWait({
  amount: parseUsdc('100'),
  wallet,
  fallbackOnPermitUnsupported: true,
});

const http = new x402HTTPClient(client);
const fetchWithPayment = wrapFetchWithPayment(fetch, http);
const fetchWithYield = handle.wrapFetchWithYield(fetchWithPayment, http, {
  waitForIdleDeposit: true,
  onIdleDeposit: (ctx) => {
    console.log(`redeposited ${formatUsdc(ctx.idleAmount)} USDC`);
  },
});

const response = await fetchWithYield('https://api.example.com/paid');
console.log(await response.text());

handle.detach();
```

This pattern gives the agent:

1. an optional starting deposit into Divigent;
2. automatic withdrawal before x402 payment if wallet liquidity is short;
3. automatic redeposit after x402 settlement;
4. manual withdrawal when your app needs liquid USDC outside x402.

### Next Steps

* [SDK Integration](/divigent-docs/integration/sdk-integration.md) for the full buyer/seller setup.
* [SDK Reference](/divigent-docs/sdk/api-reference.md) for the full attach configuration and callback list.
* [Payment Lifecycle](/divigent-docs/quick-start/payment-lifecycle.md) for the conceptual model behind reserve-aware payments.

***

## Agent Instructions: Querying This Documentation

If you need additional information that is not directly available on this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://divigent.gitbook.io/divigent-docs/quick-start/x402-with-sdk.md?ask=<question>
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://divigent.gitbook.io/divigent-docs/quick-start/x402-with-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
