# Vault Integration Guide

1. [Overview](#overview)
2. [Installation](#installation)
3. [Core Concepts](#core-concepts)
4. [Quick Start](#quick-start)
5. [SDK guide repository](#sdk-guide-repository)
6. [Operations](#operations)
7. [Input Parameter Reference](#input-parameter-reference)
8. [Slippage and Fee Buffer](#slippage-and-fee-buffer)
9. [Tracking Transactions](#tracking-transactions)
10. [Error Reference](#error-reference)

### Overview

* Quoting vault outputs (`previewDeposit` / `previewRedeem`)
* Building LayerZero send parameters including compose messages
* Calculating cross-chain messaging fees
* Determining required token approvals (including EIP-2612 permit support)
* Resolving all contract addresses, EIDs, and decimals from a built-in registry
* Tracking deposits & redeems

### Installation

```bash
npm install @zircuit/ovault-evm
# or
pnpm add @zircuit/ovault-evm
```

### Core Concepts

#### Hub and Spoke Architecture

OVaults use a hub-and-spoke model:

* **Hub chain** — the chain where the ERC-4626 vault contract lives. All deposits and redeems ultimately execute here. The hub chain is always **Base**.
* **Spoke chains** — other supported chains where users hold the underlying asset or vault shares (as OFTs). example: **Ethereum**.
* **Source chain** — the chain the user is initiating the transaction from.
* **Destination chain** — the chain where the user wants to receive tokens after the operation completes.

#### Chain Topology Shorthand

The SDK internally handles four routing scenarios, named by the relationship between source, hub, and destination:

| Scenario | Source = Hub | Destination = Hub | Description                                                                     |
| -------- | ------------ | ----------------- | ------------------------------------------------------------------------------- |
| **BBB**  | Yes          | Yes               | All on the hub chain. Single transaction, no cross-chain messaging.             |
| **BBA**  | Yes          | No                | User is on the hub chain, tokens go to a spoke chain after vault operation.     |
| **ABB**  | No           | Yes               | User is on a spoke chain, tokens arrive on the hub chain after vault operation. |
| **ABA**  | No           | No                | Fully cross-chain: spoke → hub → spoke (or another chain).                      |

#### Simplified Inputs

The SDK provides a built-in address registry for all deployed contracts. You only need to specify chain names, a token name, and the operation.

### Quick Start

```typescript
import { createWalletClient, http, publicActions } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import {
  OVaultSyncMessageBuilder,
  OVaultSyncOperations,
  CHAINS,
} from "@zircuit/ovault-evm";

const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");

const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
}).extend(publicActions);

// 1. Build the inputs
const inputs = await OVaultSyncMessageBuilder.generateOVaultInputs({
  sourceChain: "ethereum",
  destinationChain: "ethereum",
  token: "usdc",
  operation: OVaultSyncOperations.DEPOSIT,
  amount: "1",
  walletAddress: account.address,
  slippage: 0.01,
  buffer: 0.3,
  referralCode: "ZIRCUIT",
});

// 2. Approve if needed
if (inputs.approval && !inputs.approval.usePermit) {
  const approveTx = await walletClient.writeContract({
    address: inputs.approval.tokenAddress,
    abi: [{
      name: "approve", type: "function", stateMutability: "nonpayable",
      inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
      outputs: [{ type: "bool" }],
    }],
    functionName: "approve",
    args: [inputs.approval.spender, inputs.approval.amount],
  });
  await walletClient.waitForTransactionReceipt({ hash: approveTx });
}

// 3. Send the main transaction
const txRequest = OVaultSyncMessageBuilder.encodeTransactionRequest(inputs);
const txHash = await walletClient.sendTransaction(txRequest);

console.log("Submitted:", txHash);
```

### SDK Guide Repository

<https://github.com/zircuit-labs/vault-integration-guide>

### Operations

#### Deposit

Set `operation: OVaultSyncOperations.DEPOSIT`.

* The user provides the **underlying asset** amount.
* The vault mints **shares** in exchange.
* Shares are sent cross-chain to the destination chain if different from the hub.

**What `generateOVaultInputs` selects internally:**

| Source = Hub? | EIP-2612? | Contract           | Function                   |
| ------------- | --------- | ------------------ | -------------------------- |
| No            | No        | OFT (`oftAddress`) | `send`                     |
| No            | Yes       | OFT (`oftAddress`) | `sendWithPermit`           |
| Yes           | No        | OVaultComposerSync | `depositAndSend`           |
| Yes           | Yes       | OVaultComposerSync | `depositAndSendWithPermit` |

#### Redeem

Set `operation: OVaultSyncOperations.REDEEM`.

* The user provides the **share** amount to burn.
* The vault returns the **underlying asset** in exchange.
* Assets are sent cross-chain to the destination chain if different from the hub.

**What `generateOVaultInputs` selects internally:**

| Source = Hub? | Contract           | Function        |
| ------------- | ------------------ | --------------- |
| No            | VaultToken OFT     | `send`          |
| Yes           | OVaultComposerSync | `redeemAndSend` |

**Withdrawal queue**

Redemptions are **not executed immediately**. When the user calls `redeemAndSend` (either directly on the hub or via a cross-chain compose), the VaultComposer enqueues the request into a **withdrawal queue** instead of redeeming from the vault in the same transaction.

**How it works:**

1. The user's shares are transferred into the VaultComposer and a `WithdrawalRequest` event is emitted with a queue `index`.
2. A **keeper** (an account with the `WITHDRAWAL_MANAGER` role) calls `processWithdrawals(ids, extraFees)` to batch-fulfill one or more queued entries. This is the transaction that actually calls `vault.redeem()` and sends the resulting underlying assets to the user's destination chain.
3. The user may **cancel** a pending withdrawal by calling `cancelWithdrawal(id)`, which returns their shares and prepaid fees. Cancellation is only possible before the keeper has processed the entry.

### Input Parameter Reference

All parameters are passed as a single `OVaultCoreInputs` object to `generateOVaultInputs`.

#### Required

| Parameter          | Type                   | Description                                                                         |
| ------------------ | ---------------------- | ----------------------------------------------------------------------------------- |
| `sourceChain`      | `"ethereum" \| "base"` | Chain where the user submits the transaction.                                       |
| `destinationChain` | `"ethereum" \| "base"` | Chain where the user wants to receive tokens.                                       |
| `token`            | `"usdc" \| "usdt"`     | Token to deposit or redeem.                                                         |
| `operation`        | `OVaultSyncOperations` | `DEPOSIT` or `REDEEM`.                                                              |
| `amount`           | `string`               | Human-readable unscaled amount (e.g. `"1.23"`).                                     |
| `walletAddress`    | `` `0x${string}` ``    | The user's wallet address. Also used as the default refund and destination address. |

#### Optional

| Parameter              | Type                                          | Default         | Description                                                                                              |
| ---------------------- | --------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------- |
| `dstAddress`           | `` `0x${string}` ``                           | `walletAddress` | Where tokens are delivered on the destination chain.                                                     |
| `slippage`             | `number`                                      | `0.01`          | Fractional slippage tolerance. Minimum `0.001` (0.1%). Example: `0.01` = 1%.                             |
| `buffer`               | `number`                                      | `0.3`           | Additional fractional buffer on the LayerZero messaging fee. `0.3` = +30%.                               |
| `supportsEip2612`      | `boolean`                                     | `false`         | Attempt gasless permit instead of a separate `approve` tx. Only applies to deposits.                     |
| `hubLzComposeGasLimit` | `bigint`                                      | `300_000n`      | Gas limit for the `lzCompose` call on the hub chain.                                                     |
| `referralCode`         | `string`                                      | —               | Optional referral code forwarded via `oftCmd` (max 32 bytes).                                            |
| `rpcUrls`              | `Partial<Record<SupportedChainName, string>>` | —               | Custom RPC URLs keyed by chain name (e.g. `{ ethereum: "https://..." }`). Avoids public RPC rate limits. |

### Tracking Transactions

After submitting a transaction, use `trackOVaultSyncTransaction` to poll the cross-chain status:

```typescript
import { trackOVaultSyncTransaction, CHAINS } from "@zircuit/ovault-evm";

const status = await trackOVaultSyncTransaction(
  txHash,
  {
    sourceChain: CHAINS.ethereum.viemChain,
    hubChain:    CHAINS.base.viemChain,
    dstChain:    CHAINS.ethereum.viemChain,
  },
);

console.log(status.step);
```

#### Transaction Steps

The returned `step` is an `OVaultTransactionStep` enum value:

| Step                           | Description                                                           |
| ------------------------------ | --------------------------------------------------------------------- |
| `SOURCE_CHAIN_TRANSACTION`     | The source-chain transaction is pending or failed.                    |
| `SOURCE_TO_HUB_LZ_TRANSACTION` | Waiting for LayerZero to deliver the message to the hub.              |
| `HUB_CHAIN_TRANSACTION`        | The hub-chain compose is pending, executing, or failed.               |
| `HUB_TO_DST_LZ_TRANSACTION`    | Waiting for LayerZero to deliver the output to the destination chain. |
| `DST_CHAIN_TRANSACTION`        | The destination-chain receive is pending.                             |
| `COMPLETED`                    | The full operation is complete.                                       |

#### Returned Fields

```typescript
{
  step: OVaultTransactionStep;
  failureReason?: OVaultFailureReason; // "unknown" — the only value currently
  refunded?: boolean;                  // true if the hub refunded the user's tokens
  destinationTxHash?: `0x${string}`;   // final tx hash on the destination chain
  hubTxHash?: `0x${string}`;           // compose tx hash on the hub chain
}
```

### Slippage and Fee Buffer

#### Slippage

`slippage` controls the minimum acceptable output from the vault operation. It applies to the vault's `previewDeposit` / `previewRedeem` quote:

```
minDstAmount = previewOutput - (previewOutput × slippage)
```

The contract reverts with `SlippageExceeded` if the actual output is below `minDstAmount`.

* Minimum allowed: `0.001` (0.1%)
* Default: `0.01` (1%)

#### Fee Buffer

`buffer` adds a safety margin on top of the quoted LayerZero messaging fee. This protects against fee fluctuations between quote time and execution time:

```
effectiveFee = quotedFee + (quotedFee × buffer)
```

* Default: `0.3` (30%)
* Any excess native fee is refunded to `walletAddress` by LayerZero.

#### Tracking Withdrawals

When multiple redemptions are processed in a single transaction, pass `withdrawalInfo` so the tracker selects the correct LayerZero message:

```typescript
const status = await trackOVaultSyncTransaction(
  txHash,
  { sourceChain: CHAINS.base.viemChain, hubChain: CHAINS.base.viemChain, dstChain: CHAINS.ethereum.viemChain },
  {
    index:     withdrawalIndex,   // bigint | number | string
    initiator: walletAddress,     // address that initiated the redeem
  },
);
```

### Error Reference

#### SDK-level errors (thrown before transaction submission)

| Error message                                         | Cause                                                                                      |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `"Unsupported token: <token>"`                        | The token is not in the built-in registry.                                                 |
| `"Token <token> is not deployed on <chain>"`          | The token/chain combination does not exist in the registry.                                |
| `"Amount is too small"`                               | After LayerZero dust removal, the scaled amount rounded down to zero. Increase the amount. |
| `"Output amount is too small"`                        | `previewDeposit` / `previewRedeem` returned zero. The amount is below the vault's minimum. |
| `"Slippage must be greater or equal to 0.001 (0.1%)"` | `slippage` was set below the minimum.                                                      |
| `"inputs cannot be null"`                             | `amount` or `tokenHubDecimals` was `null` / `undefined`.                                   |


---

# 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://docs.zircuit.com/zircuit-finance/zircuit-finance-vaults/vault-integration-guide.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.
