# Buyer Integration

Use this page when you already have an x402 buyer/client and want Divigent to keep the paying wallet productive without breaking payment liquidity.

Divigent does not replace your x402 client. It attaches hooks around the payment flow:

* before x402 signs, Divigent withdraws enough USDC from the vault if the wallet is short;
* after successful settlement, Divigent deposits wallet USDC above the configured reserve;
* policy controls decide which payments Divigent is allowed to help with.

{% hint style="info" %}
Base mainnet uses real Circle USDC and real x402 settlement. Use small amounts while testing.
{% endhint %}

## Install

```bash
npm install @divigent/sdk viem @x402/core @x402/fetch @x402/evm
```

## Environment

```bash
BASE_MAINNET_RPC_URL="https://..."
BUYER_PRIVATE_KEY="0x..."
X402_PAID_URL="https://api.example.com/paid"
X402_ALLOWED_PAY_TO="0x..."
```

`X402_ALLOWED_PAY_TO` should be the merchant address that the paid resource expects.

## Complete Buyer Code

```ts
import {
  Divigent,
  evmAddress,
  formatUsdc,
  parseUsdc,
} from '@divigent/sdk';
import { x402Client, x402HTTPClient } from '@x402/core/client';
import {
  ExactEvmScheme as ExactEvmClient,
  toClientEvmSigner,
} from '@x402/evm';
import { decodePaymentResponseHeader, wrapFetchWithPayment } from '@x402/fetch';
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';

const account = privateKeyToAccount(process.env.BUYER_PRIVATE_KEY as `0x${string}`);
const rpcUrl = process.env.BASE_MAINNET_RPC_URL!;
const paidUrl = process.env.X402_PAID_URL!;
const merchantPayTo = process.env.X402_ALLOWED_PAY_TO as `0x${string}`;

const publicClient = createPublicClient({
  chain: base,
  transport: http(rpcUrl),
});

const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(rpcUrl),
});

const divigent = Divigent.create({
  publicClient,
  walletClient,
  chain: 'base',
});

const wallet = evmAddress(account.address);
await divigent.verifyAddresses();
await divigent.ensureInitializedAndWait({ wallet });

const maxPaymentAmount = parseUsdc('0.01');
const maxSessionPaymentAmount = parseUsdc('0.10');
const minIdleThreshold = parseUsdc('0.25');

const client = new x402Client()
  .register(
    'eip155:8453',
    new ExactEvmClient(toClientEvmSigner(account, publicClient as never), {
      8453: { rpcUrl },
    }),
  )
  .registerPolicy((_version, requirements) => (
    requirements.filter((req) => {
      if (req.scheme !== 'exact') return false;
      if (req.network !== 'eip155:8453') return false;
      if (req.asset.toLowerCase() !== divigent.addresses.usdc.toLowerCase()) return false;
      if (req.payTo.toLowerCase() !== merchantPayTo.toLowerCase()) return false;
      return BigInt(req.amount) <= maxPaymentAmount;
    })
  ));

const initialDeposit = process.env.DIVIGENT_INITIAL_DEPOSIT_USDC;
if (initialDeposit !== undefined && initialDeposit !== '0') {
  const deposit = await divigent.depositWithPermitAndWait({
    amount: parseUsdc(initialDeposit),
    wallet,
    fallbackOnPermitUnsupported: true,
  });
  console.log(`initial deposit tx: ${deposit.txHash}`);
}

const handle = divigent.attachTo(client, {
  minIdleThreshold,
  maxPaymentAmount,
  maxSessionPaymentAmount,
  allowedPayTo: [merchantPayTo],
  requireAllowedPayTo: true,
  allowedOrigins: [new URL(paidUrl).origin],
  allowedResources: [paidUrl],
  reserveRatio: 0.5,
  reserveMultiplier: 2,
  onBeforePayment: (ctx) => {
    console.log(`payment: ${formatUsdc(ctx.paymentAmount)} USDC`);
    console.log(`wallet balance: ${formatUsdc(ctx.walletBalance)} USDC`);
    console.log(`reserve floor: ${formatUsdc(ctx.reserveFloor)} USDC`);
    console.log(`deficit: ${formatUsdc(ctx.deficit)} USDC`);
    if (ctx.recallTxHash) console.log(`recall tx: ${ctx.recallTxHash}`);
    if (ctx.recallError) console.warn('recall did not fully satisfy reserve policy');
  },
  onAfterPaymentCreation: (ctx) => {
    console.log(`signed x402 payment for ${formatUsdc(ctx.paymentAmount)} USDC`);
  },
  onPaymentFailure: (ctx) => {
    console.warn(`x402 payment creation failed: ${String(ctx.error)}`);
  },
  onNonFatalError: (ctx) => {
    console.warn(`Divigent ${ctx.phase}: ${ctx.error.message}`);
  },
});

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

try {
  const response = await fetchWithYield(paidUrl, {
    headers: { accept: 'application/json' },
  });

  const settlementHeader = response.headers.get('PAYMENT-RESPONSE');
  if (settlementHeader) {
    const settlement = decodePaymentResponseHeader(settlementHeader);
    console.log(settlement);
  }

  console.log(await response.text());
} finally {
  handle.detach();
}
```

## What Happens

Before x402 signs, Divigent 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. If the wallet cannot cover the payment amount after recall, the SDK aborts before signing with `DIVIGENT_X402_RECALL_INSUFFICIENT_LIQUIDITY`.

After successful settlement, `wrapFetchWithYield(...)` deposits wallet USDC above the reserve back into Divigent.

## Reserve Policy

Use a fixed reserve 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('0.01'),
  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('0.01'),
  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`.

## Manual Operations

Withdraw wallet liquidity manually:

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

await divigent.withdrawAndWait({
  shares,
  wallet,
  slippageBps: 50,
});
```

Deposit idle USDC manually with the same reserve policy:

```ts
await handle.depositIdle();
```

Or use the SDK facade directly:

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

## Base Sepolia

For Base Sepolia:

* import `baseSepolia` from `viem/chains`;
* use `BASE_SEPOLIA_RPC_URL`;
* set `chain: 'base-sepolia'`;
* register x402 network `eip155:84532`;
* use `divigent.addresses.usdc` as the payment asset.

Base Sepolia uses Divigent's configured test USDC metadata for x402.

## Next Steps

* [Seller Integration](/divigent-docs/integration/seller-integration.md)
* [SDK Integration](/divigent-docs/integration/sdk-integration.md)
* [API Reference](/divigent-docs/sdk/api-reference.md)


---

# 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/integration/buyer-integration.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.
