# SDK Integration

This page shows how to integrate Divigent into an existing TypeScript x402 stack.

Divigent can sit on either side of x402:

* **Buyer side:** keep an agent wallet liquid, recall USDC before payment, and redeposit idle USDC after settlement.
* **Seller side:** deposit merchant income after successful x402 settlement, while keeping a seller reserve liquid.

The SDK is viem-native. Your app owns the wallet, RPC, x402 client, facilitator choice, and policy. Divigent adds the yield and liquidity-management layer.

## Install

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

For seller examples using Express:

```bash
npm install @x402/express @x402/evm express
```

## Create Divigent

```ts
import { Divigent, evmAddress } from '@divigent/sdk';
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const publicClient = createPublicClient({
  chain: base,
  transport: http(process.env.BASE_MAINNET_RPC_URL!),
});

const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(process.env.BASE_MAINNET_RPC_URL!),
});

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

await divigent.verifyAddresses();

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

For Base Sepolia, use `baseSepolia`, `BASE_SEPOLIA_RPC_URL`, and `chain: 'base-sepolia'`.

## Buyer-Side Integration

Start with an x402 client you already created.

```ts
import { x402Client, x402HTTPClient } from '@x402/core/client';
import {
  ExactEvmScheme as ExactEvmClient,
  toClientEvmSigner,
} from '@x402/evm';
import { wrapFetchWithPayment } from '@x402/fetch';
import { formatUsdc, parseUsdc } from '@divigent/sdk';

const client = new x402Client()
  .register(
    'eip155:8453',
    new ExactEvmClient(toClientEvmSigner(account, publicClient as never), {
      8453: { rpcUrl: process.env.BASE_MAINNET_RPC_URL! },
    }),
  )
  .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() !== process.env.X402_ALLOWED_PAY_TO!.toLowerCase()) return false;
      return BigInt(req.amount) <= parseUsdc('0.01');
    })
  ));
```

Then attach Divigent:

```ts
const merchantPayTo = process.env.X402_ALLOWED_PAY_TO as `0x${string}`;

const handle = divigent.attachTo(client, {
  minIdleThreshold: parseUsdc('0.25'),
  maxPaymentAmount: parseUsdc('0.01'),
  maxSessionPaymentAmount: parseUsdc('0.10'),
  allowedPayTo: [merchantPayTo],
  requireAllowedPayTo: true,
  allowedOrigins: ['https://api.example.com'],
  allowedResources: ['https://api.example.com/*'],
  reserveRatio: 0.5,
  reserveMultiplier: 2,
  onBeforePayment: (ctx) => {
    console.log(`payment ${formatUsdc(ctx.paymentAmount)} USDC`);
    console.log(`deficit ${formatUsdc(ctx.deficit)} USDC`);
    if (ctx.recallTxHash) console.log(`recall tx ${ctx.recallTxHash}`);
  },
  onNonFatalError: (ctx) => {
    console.warn(ctx.phase, ctx.error.message);
  },
});

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

### Buyer Behavior

Before x402 signs, Divigent checks:

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

If the wallet is short, the SDK withdraws from Divigent first. If the wallet still cannot cover the payment amount, the SDK aborts before signing and throws `DIVIGENT_X402_RECALL_INSUFFICIENT_LIQUIDITY`.

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

## Seller-Side Integration

Use seller-side integration when you operate the paid resource server and want merchant income swept into Divigent after settlement.

```ts
import express from 'express';
import { formatUsdc, parseUsdc, x402UsdcPrice } from '@divigent/sdk';
import { HTTPFacilitatorClient } from '@x402/core/server';
import { ExactEvmScheme as ExactEvmServer } from '@x402/evm/exact/server';
import { paymentMiddleware, x402ResourceServer } from '@x402/express';

const app = express();
app.use(express.json());

const resourceServer = new x402ResourceServer(
  new HTTPFacilitatorClient({ url: process.env.X402_FACILITATOR_URL! }),
)
  .register('eip155:8453', new ExactEvmServer())
  .onAfterSettle(async (ctx) => {
    console.log(`settled x402 tx ${ctx.result.transaction}`);
  });

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}`);
  },
  onNonFatalError: (ctx) => {
    console.warn(ctx.phase, ctx.error.message);
  },
});

app.use(paymentMiddleware(
  {
    'GET /paid': {
      accepts: {
        scheme: 'exact',
        network: 'eip155:8453',
        payTo: wallet,
        price: x402UsdcPrice({
          chain: 'base',
          amount: parseUsdc('0.001'),
          asset: divigent.addresses.usdc,
        }),
        maxTimeoutSeconds: 300,
      },
      description: 'Paid endpoint',
      mimeType: 'application/json',
    },
  },
  resourceServer,
  {
    appName: 'Divigent seller',
    testnet: false,
  },
));

app.get('/paid', (_req, res) => {
  res.json({ ok: true });
});
```

Call `incomeHandle.detach()` when the server should stop sweeping merchant income.

## Initial Deposits

You can seed a Divigent position before starting x402.

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

Or use explicit approval:

```ts
await divigent.approveUsdc(parseUsdc('100'));
await divigent.depositAndWait({
  amount: parseUsdc('100'),
  wallet,
});
```

## Manual Withdrawals

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

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

console.log(result.txHash);
```

## Manual Idle Deposits

For buyer handles:

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

For direct SDK usage:

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

## Policy Checklist

Use these controls in production:

| Control                      | Purpose                                         |
| ---------------------------- | ----------------------------------------------- |
| `maxPaymentAmount`           | Per-payment cap for Divigent automation         |
| `maxSessionPaymentAmount`    | Cumulative cap for the attached client session  |
| `allowedPayTo`               | Merchant/payee allowlist                        |
| `requireAllowedPayTo`        | Fail fast if no payee allowlist is configured   |
| `allowedOrigins`             | Origin allowlist                                |
| `allowedResources`           | URL/resource allowlist, with `*` wildcards      |
| `maxPaymentAmountByResource` | Different caps for different paid resources     |
| `shouldHandlePayment`        | Last-mile custom policy function                |
| `redact`                     | Redact addresses and tx hashes in callbacks     |
| `onNonFatalError`            | Observe degraded but non-breaking hook failures |

## x402 USDC Metadata

Use `x402UsdcPrice(...)` on the seller side.

```ts
const price = x402UsdcPrice({
  chain: 'base',
  amount: parseUsdc('0.001'),
});
```

Base mainnet metadata uses Circle USDC EIP-3009:

```ts
{
  name: 'USD Coin',
  version: '2',
  assetTransferMethod: 'eip3009',
}
```

Base Sepolia metadata uses Divigent's configured test USDC with Permit2.


---

# 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/sdk-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.
