> ## Documentation Index
> Fetch the complete documentation index at: https://developers.paxoslabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Contract AI Coding Reference

> Condensed smart contract integration reference optimized for AI coding assistants

Copy this page into your AI coding assistant (Cursor, Copilot, Claude, etc.) for accurate Amplify vault direct contract completions.

<Info>
  This page is a condensed, single-file reference for calling Amplify vault
  contracts directly — without the SDK. For full documentation with interactive
  examples, see the [Direct Contract Integration
  guides](/v0.5.2/intro/products/earn/developers/guides/direct-contract/index).
</Info>

## Contract Source

Open-source Solidity: [github.com/paxoslabs/nucleus-boring-vault](https://github.com/paxoslabs/nucleus-boring-vault)

## Architecture

| Contract                     | Role                                                  | GraphQL Field                            |
| ---------------------------- | ----------------------------------------------------- | ---------------------------------------- |
| **DistributorCodeDepositor** | Deposits (standard + permit)                          | `vault.communityCodeDepositorModuleId`   |
| **WithdrawQueue**            | Withdrawal orders + cancellations (ERC-721 per order) | `vault.withdrawQueueModuleId`            |
| **BoringVault**              | ERC-20 vault share token (18 decimals)                | `vault.boringVaultAddress`               |
| **Accountant**               | Exchange rate oracle                                  | `vault.accountantModuleId`               |
| **Teller**                   | Pause state                                           | `vault.tellerModuleId`                   |
| **FeeModule**                | Withdrawal fee calculator                             | Obtained via `WithdrawQueue.feeModule()` |

> `communityCodeDepositorModuleId` is a legacy GraphQL field name. It returns the **DistributorCodeDepositor** address.

## Obtain Addresses — GraphQL API

```
POST https://api.paxoslabs.com/graphql
Headers: Content-Type: application/json, x-api-key: <YOUR_API_KEY>
```

### Query

```graphql theme={null}
query AmplifySdkConfigs($chainId: Int, $yieldType: YieldType) {
  amplifySdkConfigs(chainId: $chainId, yieldType: $yieldType) {
    id
    chainId
    yieldType
    vault {
      id
      name
      chainId
      boringVaultAddress
      tellerModuleId
      accountantModuleId
      withdrawQueueModuleId
      communityCodeDepositorModuleId
      supportedAssets {
        address
        chainId
        depositable
        withdrawable
        symbol
        tokenName
        decimals
      }
    }
  }
}
```

### Example cURL

```bash theme={null}
curl -s -X POST "https://api.paxoslabs.com/graphql" \
  -H "Content-Type: application/json" \
  -H "x-api-key: $AMPLIFY_API_KEY" \
  -d '{
    "query": "query AmplifySdkConfigs($chainId: Int, $yieldType: YieldType) { amplifySdkConfigs(chainId: $chainId, yieldType: $yieldType) { id chainId yieldType vault { id name chainId boringVaultAddress tellerModuleId accountantModuleId withdrawQueueModuleId communityCodeDepositorModuleId supportedAssets { address chainId depositable withdrawable symbol tokenName decimals } } } }",
    "variables": { "chainId": 1 }
  }' | jq
```

### Response Shape

```json theme={null}
{
  "data": {
    "amplifySdkConfigs": [
      {
        "id": "config-id",
        "chainId": 1,
        "yieldType": "CORE",
        "vault": {
          "id": "vault-id",
          "name": "Amplify Core",
          "chainId": 1,
          "boringVaultAddress": "0x...",
          "tellerModuleId": "0x...",
          "accountantModuleId": "0x...",
          "withdrawQueueModuleId": "0x...",
          "communityCodeDepositorModuleId": "0x...",
          "supportedAssets": [
            {
              "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
              "chainId": 1,
              "depositable": true,
              "withdrawable": true,
              "symbol": "USDC",
              "tokenName": "USD Coin",
              "decimals": 6
            }
          ]
        }
      }
    ]
  }
}
```

Multiple configs can reference the same vault. Deduplicate by `vault.id`. `withdrawQueueModuleId` and `communityCodeDepositorModuleId` can be `null` if the vault doesn't support those operations.

### GraphQL Variables

| Variable    | Type        | Description                                                                       |
| ----------- | ----------- | --------------------------------------------------------------------------------- |
| `chainId`   | `Int`       | Filter by chain (e.g., `1` for Ethereum, `8453` for Base)                         |
| `yieldType` | `YieldType` | Filter by yield type: `CORE`, `TREASURY`, `FRONTIER`, `PRIME`, `TBILL`, `LENDING` |

***

## ABIs

### DistributorCodeDepositor

```json theme={null}
[
  {
    "inputs": [
      { "name": "depositAsset", "type": "address" },
      { "name": "depositAmount", "type": "uint256" },
      { "name": "minimumMint", "type": "uint256" },
      { "name": "to", "type": "address" },
      { "name": "distributorCode", "type": "bytes" }
    ],
    "name": "deposit",
    "outputs": [{ "name": "shares", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "depositAsset", "type": "address" },
      { "name": "depositAmount", "type": "uint256" },
      { "name": "minimumMint", "type": "uint256" },
      { "name": "to", "type": "address" },
      { "name": "distributorCode", "type": "bytes" },
      { "name": "deadline", "type": "uint256" },
      { "name": "v", "type": "uint8" },
      { "name": "r", "type": "bytes32" },
      { "name": "s", "type": "bytes32" }
    ],
    "name": "depositWithPermit",
    "outputs": [{ "name": "shares", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]
```

### WithdrawQueue

```json theme={null}
[
  {
    "inputs": [
      {
        "components": [
          { "name": "amountOffer", "type": "uint256" },
          { "name": "wantAsset", "type": "address" },
          { "name": "intendedDepositor", "type": "address" },
          { "name": "receiver", "type": "address" },
          { "name": "refundReceiver", "type": "address" },
          {
            "components": [
              { "name": "approvalMethod", "type": "uint8" },
              { "name": "approvalV", "type": "uint8" },
              { "name": "approvalR", "type": "bytes32" },
              { "name": "approvalS", "type": "bytes32" },
              { "name": "submitWithSignature", "type": "bool" },
              { "name": "deadline", "type": "uint256" },
              { "name": "eip2612Signature", "type": "bytes" }
            ],
            "name": "signatureParams",
            "type": "tuple"
          }
        ],
        "name": "params",
        "type": "tuple"
      }
    ],
    "name": "submitOrder",
    "outputs": [{ "name": "orderIndex", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [{ "name": "orderIndex", "type": "uint256" }],
    "name": "cancelOrder",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "orderIndex", "type": "uint256" },
      { "name": "deadline", "type": "uint256" },
      { "name": "cancelSignature", "type": "bytes" }
    ],
    "name": "cancelOrderWithSignature",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [{ "name": "orderIndex", "type": "uint256" }],
    "name": "getOrderStatus",
    "outputs": [{ "name": "", "type": "uint8" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "minimumOrderSize",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeModule",
    "outputs": [{ "name": "", "type": "address" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "owner", "type": "address" }],
    "name": "balanceOf",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "owner", "type": "address" },
      { "name": "index", "type": "uint256" }
    ],
    "name": "tokenOfOwnerByIndex",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "tokenId", "type": "uint256" }],
    "name": "ownerOf",
    "outputs": [{ "name": "", "type": "address" }],
    "stateMutability": "view",
    "type": "function"
  }
]
```

### ERC-20 (BoringVault shares + deposit tokens)

```json theme={null}
[
  {
    "inputs": [
      { "name": "spender", "type": "address" },
      { "name": "amount", "type": "uint256" }
    ],
    "name": "approve",
    "outputs": [{ "name": "", "type": "bool" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "owner", "type": "address" },
      { "name": "spender", "type": "address" }
    ],
    "name": "allowance",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "account", "type": "address" }],
    "name": "balanceOf",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [{ "name": "", "type": "uint8" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "owner", "type": "address" }],
    "name": "nonces",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  }
]
```

### Accountant

```json theme={null}
[
  {
    "inputs": [{ "name": "quote", "type": "address" }],
    "name": "getRateInQuoteSafe",
    "outputs": [{ "name": "rateInQuote", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  }
]
```

### FeeModule

```json theme={null}
[
  {
    "inputs": [
      { "name": "amount", "type": "uint256" },
      { "name": "offerAsset", "type": "address" },
      { "name": "wantAsset", "type": "address" },
      { "name": "receiver", "type": "address" }
    ],
    "name": "calculateOfferFees",
    "outputs": [{ "name": "feeAmount", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "offerFeePercentage",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  }
]
```

### Teller

```json theme={null}
[
  {
    "inputs": [],
    "name": "isPaused",
    "outputs": [{ "name": "", "type": "bool" }],
    "stateMutability": "view",
    "type": "function"
  }
]
```

***

## Pre-Flight: Pause State Check

Before submitting any deposit or withdrawal transaction, check whether the vault is paused:

```ts theme={null}
const paused = await publicClient.readContract({
  address: TELLER,
  abi: tellerAbi,
  functionName: 'isPaused',
})
if (paused) throw new Error('Vault is paused — transactions will revert with TellerIsPaused')
```

***

## Deposit Flow — Standard (Approve + Deposit)

Two transactions: ERC-20 `approve()` then `deposit()`.

```ts theme={null}
// Step 1: Check allowance
const allowance = await publicClient.readContract({
  address: DEPOSIT_TOKEN,
  abi: erc20Abi,
  functionName: 'allowance',
  args: [account.address, DISTRIBUTOR_CODE_DEPOSITOR],
})

// Step 2: Approve if needed
if (allowance < depositAmount) {
  // USDT QUIRK: if token is USDT and allowance > 0, reset to 0 first
  // await walletClient.writeContract({ address: DEPOSIT_TOKEN, abi: erc20Abi, functionName: 'approve', args: [DISTRIBUTOR_CODE_DEPOSITOR, 0n] })
  const hash = await walletClient.writeContract({
    address: DEPOSIT_TOKEN,
    abi: erc20Abi,
    functionName: 'approve',
    args: [DISTRIBUTOR_CODE_DEPOSITOR, depositAmount],
  })
  await publicClient.waitForTransactionReceipt({ hash })
}

// Step 3: Calculate minimumMint (slippage protection)
const rate = await publicClient.readContract({
  address: ACCOUNTANT,
  abi: accountantAbi,
  functionName: 'getRateInQuoteSafe',
  args: [DEPOSIT_TOKEN],
})
const SLIPPAGE_BPS = 50n // 0.5%
const expectedShares = (depositAmount * 10n ** 18n) / rate
const minimumMint = expectedShares - (expectedShares * SLIPPAGE_BPS) / 10000n

// Step 4: Deposit
const hash = await walletClient.writeContract({
  address: DISTRIBUTOR_CODE_DEPOSITOR,
  abi: depositorAbi,
  functionName: 'deposit',
  args: [DEPOSIT_TOKEN, depositAmount, minimumMint, account.address, '0x'],
})
const receipt = await publicClient.waitForTransactionReceipt({ hash })
```

### Deposit Parameters

| Parameter         | Type      | Description                                                                 |
| ----------------- | --------- | --------------------------------------------------------------------------- |
| `depositAsset`    | `address` | ERC-20 token to deposit (e.g., USDC)                                        |
| `depositAmount`   | `uint256` | Amount in token's smallest unit (USDC 6 decimals: 1000 USDC = `1000000000`) |
| `minimumMint`     | `uint256` | Min shares to receive (slippage protection). `0` to disable.                |
| `to`              | `address` | Recipient of minted vault shares                                            |
| `distributorCode` | `bytes`   | Referral code as bytes. `0x` if none.                                       |

### Distributor Codes

If Paxos Labs provided a distributor code, encode it as bytes: `toHex('your_code')` in JS, `b'your_code'` in Python, `[]byte("your_code")` in Go. Otherwise pass `0x` / `b""` / `[]byte{}`.

***

## Deposit Flow — Permit (Single Transaction)

For tokens supporting EIP-2612 (USDC, USDG, pyUSD, USD₮0). Not available for USDT. Not available for smart contract wallets (Privy Smart Wallets, Safe) — they cannot sign typed data.

```ts theme={null}
// Step 1: Get permit nonce
const nonce = await publicClient.readContract({
  address: DEPOSIT_TOKEN,
  abi: erc20Abi,
  functionName: 'nonces',
  args: [account.address],
})

// Step 2: Sign EIP-712 permit
const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600)
const signature = await walletClient.signTypedData({
  account,
  domain: {
    name: 'USD Coin',        // varies by token — must match token's EIP-712 domain
    version: '2',            // varies by token
    chainId: 1,
    verifyingContract: DEPOSIT_TOKEN,
  },
  types: {
    Permit: [
      { name: 'owner', type: 'address' },
      { name: 'spender', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'nonce', type: 'uint256' },
      { name: 'deadline', type: 'uint256' },
    ],
  },
  primaryType: 'Permit',
  message: {
    owner: account.address,
    spender: DISTRIBUTOR_CODE_DEPOSITOR,
    value: depositAmount,
    nonce,
    deadline,
  },
})

// Step 3: Parse signature into v, r, s
const { v, r, s } = parseSignature(signature) // viem: import { parseSignature } from 'viem'

// Step 4: Calculate minimumMint (same as standard flow)
const rate = await publicClient.readContract({
  address: ACCOUNTANT,
  abi: accountantAbi,
  functionName: 'getRateInQuoteSafe',
  args: [DEPOSIT_TOKEN],
})
const expectedShares = (depositAmount * 10n ** 18n) / rate
const minimumMint = expectedShares - (expectedShares * 50n) / 10000n

// Step 5: depositWithPermit (single transaction — no separate approve needed)
const hash = await walletClient.writeContract({
  address: DISTRIBUTOR_CODE_DEPOSITOR,
  abi: depositorAbi,
  functionName: 'depositWithPermit',
  args: [DEPOSIT_TOKEN, depositAmount, minimumMint, account.address, '0x', deadline, v, r, s],
})
```

### Permit EIP-712 Domain (varies by token)

| Token | `name`       | `version` |
| ----- | ------------ | --------- |
| USDC  | `"USD Coin"` | `"2"`     |
| USDG  | Check token  | Check     |
| pyUSD | Check token  | Check     |
| USD₮0 | Check token  | Check     |

Always read the token's `name()`, `version()` (or `EIP712_VERSION()`) and use those for the domain.

***

## Slippage / minimumMint Formula

```
rate           = Accountant.getRateInQuoteSafe(depositTokenAddress)
expectedShares = (depositAmount × 1e18) / rate
minimumMint    = expectedShares × (10000 − SLIPPAGE_BPS) / 10000
```

Common default: `SLIPPAGE_BPS = 50` (0.5%). Setting `minimumMint = 0` disables slippage protection (fine for testing, not production).

***

## Withdrawal Flow (Complete)

Withdrawals are **order-based, not instant**. The vault operator fulfills orders (typically within 24 hours).

```ts theme={null}
// Step 1: Check share balance
const shareBalance = await publicClient.readContract({
  address: BORING_VAULT,
  abi: erc20Abi,
  functionName: 'balanceOf',
  args: [account.address],
})

// Step 2: Convert want amount to shares
const rate = await publicClient.readContract({
  address: ACCOUNTANT,
  abi: accountantAbi,
  functionName: 'getRateInQuoteSafe',
  args: [WANT_ASSET], // e.g., USDC address
})
const wantAmount = parseUnits('1000', 6) // 1000 USDC
let sharesNeeded = (wantAmount * 10n ** 18n) / rate

// Step 3: Calculate withdrawal fee
const feeModuleAddr = await publicClient.readContract({
  address: WITHDRAW_QUEUE,
  abi: queueAbi,
  functionName: 'feeModule',
})
const feeAmount = await publicClient.readContract({
  address: feeModuleAddr,
  abi: feeModuleAbi,
  functionName: 'calculateOfferFees',
  args: [sharesNeeded, BORING_VAULT, WANT_ASSET, account.address],
})
// Fee is deducted from offered shares. To receive full wantAmount, add fee:
sharesNeeded = sharesNeeded + feeAmount

// Step 4: Check minimum order size
const minimumShares = await publicClient.readContract({
  address: WITHDRAW_QUEUE,
  abi: queueAbi,
  functionName: 'minimumOrderSize',
})
if (sharesNeeded < minimumShares) throw new Error('Below minimum order size')

// Step 5: Approve vault shares to WithdrawQueue
const allowance = await publicClient.readContract({
  address: BORING_VAULT,
  abi: erc20Abi,
  functionName: 'allowance',
  args: [account.address, WITHDRAW_QUEUE],
})
if (allowance < sharesNeeded) {
  const hash = await walletClient.writeContract({
    address: BORING_VAULT,
    abi: erc20Abi,
    functionName: 'approve',
    args: [WITHDRAW_QUEUE, sharesNeeded],
  })
  await publicClient.waitForTransactionReceipt({ hash })
}

// Step 6: Submit withdrawal order
const EMPTY_SIG = {
  approvalMethod: 0,
  approvalV: 0,
  approvalR: ('0x' + '00'.repeat(32)) as `0x${string}`,
  approvalS: ('0x' + '00'.repeat(32)) as `0x${string}`,
  submitWithSignature: false,
  deadline: 0n,
  eip2612Signature: '0x' as `0x${string}`,
}

const hash = await walletClient.writeContract({
  address: WITHDRAW_QUEUE,
  abi: queueAbi,
  functionName: 'submitOrder',
  args: [
    {
      amountOffer: sharesNeeded,
      wantAsset: WANT_ASSET,
      intendedDepositor: account.address, // MUST match msg.sender
      receiver: account.address,
      refundReceiver: account.address,
      signatureParams: EMPTY_SIG,
    },
  ],
})
const receipt = await publicClient.waitForTransactionReceipt({ hash })
// Extract orderIndex from receipt logs or return value

// Step 7: Poll for completion
let status = 1
while (status === 1) {
  await new Promise((r) => setTimeout(r, 60_000))
  status = await publicClient.readContract({
    address: WITHDRAW_QUEUE,
    abi: queueAbi,
    functionName: 'getOrderStatus',
    args: [orderIndex],
  })
}
// status 2 = COMPLETE, 5 = REFUNDED, 6 = FAILED_TRANSFER_REFUNDED
```

### submitOrder Parameters

| Field               | Type      | Description                                             |
| ------------------- | --------- | ------------------------------------------------------- |
| `amountOffer`       | `uint256` | Vault shares to offer (18 decimals)                     |
| `wantAsset`         | `address` | Token to receive (e.g., USDC). Must be `withdrawable`.  |
| `intendedDepositor` | `address` | **Must match `msg.sender`**                             |
| `receiver`          | `address` | Where the want asset is sent when fulfilled             |
| `refundReceiver`    | `address` | Where shares go if order is cancelled                   |
| `signatureParams`   | `tuple`   | Pass all-zeros struct for standard ERC-20 approval flow |

### Share-to-Asset Conversion

```
rate         = Accountant.getRateInQuoteSafe(wantAssetAddress)
sharesNeeded = (wantAmount × 1e18) / rate
```

### Withdrawal Fee Calculation

```
feeModuleAddr = WithdrawQueue.feeModule()
feeAmount     = FeeModule.calculateOfferFees(shareAmount, boringVaultAddress, wantAssetAddress, receiverAddress)
feePercentage = FeeModule.offerFeePercentage()  // 18-decimal precision, e.g., 5000000000000000 = 0.5%
```

Fee is subtracted from offered shares. To receive the full want amount, offer `sharesNeeded + feeAmount`.

***

## Cancellation Flow — Direct

Only orders with `PENDING` status (value `1`) can be cancelled. The WithdrawQueue is an ERC-721 — each order is an NFT.

```ts theme={null}
// Enumerate order NFTs
const count = await publicClient.readContract({
  address: WITHDRAW_QUEUE,
  abi: queueAbi,
  functionName: 'balanceOf',
  args: [account.address],
})
for (let i = 0n; i < count; i++) {
  const orderIndex = await publicClient.readContract({
    address: WITHDRAW_QUEUE,
    abi: queueAbi,
    functionName: 'tokenOfOwnerByIndex',
    args: [account.address, i],
  })
  const status = await publicClient.readContract({
    address: WITHDRAW_QUEUE,
    abi: queueAbi,
    functionName: 'getOrderStatus',
    args: [orderIndex],
  })
  if (status === 1) {
    // PENDING — cancel it
    const hash = await walletClient.writeContract({
      address: WITHDRAW_QUEUE,
      abi: queueAbi,
      functionName: 'cancelOrder',
      args: [orderIndex],
    })
    await publicClient.waitForTransactionReceipt({ hash })
    // Shares returned to refundReceiver set during submitOrder
  }
}
```

If you already have the `orderIndex` (from the `OrderSubmitted` event), skip enumeration and call `cancelOrder(orderIndex)` directly after verifying `getOrderStatus(orderIndex) === 1`.

***

## Cancellation Flow — Meta-Transaction (Gasless)

A relayer submits the cancellation on behalf of the order owner using an EIP-712 signature.

### EIP-712 Cancel Signature

```ts theme={null}
// Order owner signs off-chain
const cancelDeadline = BigInt(Math.floor(Date.now() / 1000) + 3600)
const signature = await walletClient.signTypedData({
  account,
  domain: {
    name: 'WithdrawQueue',
    chainId: 1,
    verifyingContract: WITHDRAW_QUEUE,
  },
  types: {
    Cancel: [
      { name: 'orderIndex', type: 'uint256' },
      { name: 'deadline', type: 'uint256' },
      { name: 'queueAddress', type: 'address' },
      { name: 'chainId', type: 'uint256' },
    ],
  },
  primaryType: 'Cancel',
  message: {
    orderIndex,
    deadline: cancelDeadline,
    queueAddress: WITHDRAW_QUEUE,
    chainId: 1n,
  },
})

// Relayer submits (anyone can call this)
await walletClient.writeContract({
  address: WITHDRAW_QUEUE,
  abi: queueAbi,
  functionName: 'cancelOrderWithSignature',
  args: [orderIndex, cancelDeadline, signature],
})
```

The `deadline` in the signed message and the function call **must match exactly**. Reverts with `SignatureExpired` if the block timestamp exceeds the deadline.

***

## REST API Endpoints

All require `x-api-key: <your_key>` header. Base URL: `https://api.paxoslabs.com`

### Vault APY

```
GET /v2/amplify/vaultApys?filter=vaultAddress%3D{VAULT_ADDRESS}&orderByTimestamp=desc&pageSize=1
```

```json theme={null}
{ "vaultApys": [{ "vaultAddress": "0x...", "chainId": 1, "apy": "0.0523", "timestamp": "2025-01-15T12:00:00Z" }] }
```

`apy` is a decimal string — multiply by 100 for percentage (e.g., `0.0523` = 5.23%).

### Vault TVL

```
GET /v2/amplify/vaultTvls?filter=vaultAddress%3D{VAULT_ADDRESS}&includeCurrent=true&orderByTimestamp=desc&pageSize=1
```

```json theme={null}
{ "vaultTvls": [{ "vaultAddress": "0x...", "chainId": 1, "tvl": "15234567.89", "timestamp": "2025-01-15T12:00:00Z" }] }
```

### Vault Assets (simplified vault discovery)

```
GET /v2/amplify/vaultAssets?pageSize=100
```

```json theme={null}
{
  "vaultAssets": [{ "vaultAddress": "0x...", "chainId": 1, "assetAddress": "0x...", "depositable": true, "withdrawable": true }],
  "nextPageToken": null,
  "tokenMetadata": { "1:0xA0b8...": { "address": "0xA0b8...", "chain_id": "1", "symbol": "USDC", "name": "USD Coin", "decimals": "6" } }
}
```

Only returns `vaultAddress` (BoringVault). Use the GraphQL API for full contract addresses.

### Withdrawal Requests

```
GET /v2/amplify/withdrawalRequests?filter=userAddress%3D{USER_ADDRESS}&pageSize=20
```

```json theme={null}
{
  "withdrawalRequests": [{
    "orderIndex": "42", "userAddress": "0x...", "vaultAddress": "0x...", "chainId": 1,
    "offerAmount": "1000000000000000000", "wantAsset": "0x...", "status": "PENDING",
    "createdAt": "2025-01-15T10:30:00Z", "fulfilledAt": null
  }],
  "nextPageToken": null
}
```

REST status values: `PENDING`, `COMPLETE`, `PENDING_REFUND`, `REFUNDED`.

***

## Order Status Enum (On-Chain)

| Value | Status                     | Cancellable? |
| ----- | -------------------------- | ------------ |
| 0     | NOT\_FOUND                 | No           |
| 1     | PENDING                    | **Yes**      |
| 2     | COMPLETE                   | No           |
| 3     | COMPLETE\_PRE\_FILLED      | No           |
| 4     | PENDING\_REFUND            | No           |
| 5     | COMPLETE\_REFUNDED         | No           |
| 6     | FAILED\_TRANSFER\_REFUNDED | No           |

***

## Token Notes

| Token | Decimals | Permit (EIP-2612) | Notes                                              |
| ----- | -------- | ----------------- | -------------------------------------------------- |
| USDC  | 6        | Yes               | —                                                  |
| USDG  | 6        | Yes               | —                                                  |
| pyUSD | 6        | Yes               | —                                                  |
| USD₮0 | 6        | Yes               | —                                                  |
| USDT  | 6        | **No**            | Must reset allowance to 0 before setting new value |

For USDT: if current allowance > 0, call `approve(spender, 0)` first, then `approve(spender, amount)`.

Smart contract wallets (Privy Smart Wallets, Safe) **cannot sign permits** — use the standard approve + deposit flow.

***

## Common Revert Errors

| Error                                    | Cause                                              | Fix                                                    |
| ---------------------------------------- | -------------------------------------------------- | ------------------------------------------------------ |
| `TellerIsPaused`                         | Vault operations paused                            | Check `Teller.isPaused()` before submitting            |
| `PermitFailedAndAllowanceTooLow`         | Invalid permit sig and no existing ERC-20 approval | Fix EIP-712 domain params or use approve flow          |
| `ERC20: insufficient allowance`          | Spender not approved                               | Call `approve()` on token first                        |
| `ERC20: transfer amount exceeds balance` | Insufficient token balance                         | Check `balanceOf()` before transacting                 |
| `AmountBelowMinimum`                     | Withdrawal order below minimum size                | Check `WithdrawQueue.minimumOrderSize()`               |
| `InvalidDepositor`                       | `intendedDepositor` ≠ `msg.sender`                 | Set `intendedDepositor` to the transaction sender      |
| `AssetNotSupported`                      | Want asset not enabled for this vault              | Check `supportedAssets` with `withdrawable: true`      |
| `OnlyOrderOwnerCanCancel`                | Caller doesn't own the order NFT                   | Only the NFT holder can cancel                         |
| `InvalidOrderType`                       | Order not in PENDING status                        | Check `getOrderStatus()` — only status `1` cancellable |
| `SignatureExpired`                       | Meta-tx cancel deadline passed                     | Re-sign with future deadline                           |

***

## Supported Chains

SDK built-in networks (for parity with `@paxoslabs/amplify-sdk` on-chain reads):

| Chain          | ID       |
| -------------- | -------- |
| Ethereum       | 1        |
| Sepolia        | 11155111 |
| Base           | 8453     |
| HyperEVM       | 999      |
| Stable Testnet | 2201     |
