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

# SDK Reference

> Constructor, data methods, transaction methods, result shapes, and errors for the Gauntlet SDK.

## Constructor

```typescript theme={null}
import { GauntletClient } from '@gauntlet-xyz/sdk'
import { createPublicClient, createWalletClient, http } from 'viem'
import { base } from 'viem/chains'

const client = new GauntletClient({
  evmClients: {
    [base.id]: createPublicClient({ chain: base, transport: http(process.env.RPC_URL_BASE!) }),
  },
  wallet: createWalletClient({
    account,
    chain: base,
    transport: http(process.env.RPC_URL_BASE!),
  }),
})
```

| Parameter         | Type                                     | Required                | Description                                                                                                                                               |
| ----------------- | ---------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `evmClients`      | `Record<number \| string, PublicClient>` | For transaction methods | Chain ID to viem PublicClient map                                                                                                                         |
| `wallet`          | `WalletClient`                           | For transaction methods | Any viem-compatible WalletClient — used only to read the sender address. The SDK never signs.                                                             |
| `apiKey`          | `string`                                 | No                      | Partner API key from the Developer Portal                                                                                                                 |
| `attributionMode` | `AttributionMode`                        | No                      | Defaults to `AttributionMode.PUBLIC`                                                                                                                      |
| `builderCode`     | `string`                                 | No                      | Builder identifier for attribution — must be requested from Gauntlet, not self-serve. NOT the same as API key. Without it, transactions are unattributed. |

## Discover Vaults

```typescript theme={null}
import { getVaults } from '@gauntlet-xyz/sdk/evm'
// also available from the root: import { getVaults } from '@gauntlet-xyz/sdk'
import { base } from 'viem/chains'

const candidates = await getVaults(client, { chainId: base.id })
// returns:
// [
//   {
//     vaultId: "baseUsdcPrime",
//     name: "...",
//     protocol: "morpho",
//     deployments: [{ chainId: 8453, supplyToken: [{ symbol: "USDC", ... }], ... }]
//   },
//   ...
// ]
```

## Transaction Functions

Import from `@gauntlet-xyz/sdk/evm`. These communicate on-chain via your RPC URLs. Require `wallet` in the client config — the SDK reads the account address from `wallet.account`.

### getDepositTx

The `vaultId` string resolves to a `VaultDeployment` from the manifest. This is how the SDK knows which token to approve (`supplyToken[0].address`), which contract to call (`vaultAddress` or `provisionerAddress`), and whether the vault supports sync or async deposits.

```typescript theme={null}
import { getDepositTx, VaultId } from '@gauntlet-xyz/sdk/evm'
// also available from the root: import { getDepositTx, VaultId } from '@gauntlet-xyz/sdk'

const steps = await getDepositTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 1_000_000n,
  receiver: '0xReceiver', // optional, defaults to wallet account
})
```

| Parameter     | Type      | Required               | Description                                                                                                                                                                                        |
| ------------- | --------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vaultId`     | `string`  | Yes                    | Vault identifier — resolves token, contract, and deposit mode from the manifest                                                                                                                    |
| `amount`      | `bigint`  | Yes                    | Amount in token base units                                                                                                                                                                         |
| `chainId`     | `number`  | For multi-chain vaults | Defaults to Base                                                                                                                                                                                   |
| `assetSymbol` | `string`  | No                     | Required for multi-asset vaults to select the asset token                                                                                                                                          |
| `depositMode` | `string`  | No                     | Override deposit mode: `'async'` (queued) or `'sync'` (instant). When omitted, uses the vault's native mode — async for Aera, sync for Morpho. Vaults with `depositMode: 'both'` default to async. |
| `receiver`    | `Address` | No                     | Defaults to `wallet.account`. Aera vaults require receiver to equal the signer — providing a different address throws an error.                                                                    |
| `slippageBps` | `number`  | No                     | Slippage tolerance in basis points (e.g. `100` = 1%). Defaults to `100`. Must be an integer between 0 and 10000.                                                                                   |

### getWithdrawTx

```typescript theme={null}
import { getWithdrawTx, VaultId } from '@gauntlet-xyz/sdk/evm'
// also available from the root: import { getWithdrawTx, VaultId } from '@gauntlet-xyz/sdk'

// Withdraw all shares
const steps = await getWithdrawTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  entireAmount: true,
  receiver: '0xReceiver', // optional
})

// Withdraw by shares
const steps = await getWithdrawTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  shares: 500_000000000000000000n,
})

// Withdraw by asset amount
const steps = await getWithdrawTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 1_000_000n,
})
```

| Parameter                              | Type      | Required               | Description                                                                                                                                                                                         |
| -------------------------------------- | --------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vaultId`                              | `string`  | Yes                    | Vault identifier                                                                                                                                                                                    |
| `shares` \| `amount` \| `entireAmount` | —         | Yes (one of)           | Exact shares, exact asset amount, or full position                                                                                                                                                  |
| `chainId`                              | `number`  | For multi-chain vaults | Defaults to Base                                                                                                                                                                                    |
| `assetSymbol`                          | `string`  | No                     | Required for multi-asset vaults to select the withdraw token                                                                                                                                        |
| `depositMode`                          | `string`  | No                     | Override withdraw mode: `'async'` (queued) or `'sync'` (instant). When omitted, uses the vault's native mode — async for Aera, sync for Morpho. Vaults with `depositMode: 'both'` default to async. |
| `receiver`                             | `Address` | No                     | Defaults to `wallet.account`. Aera vaults require receiver to equal the signer — providing a different address throws an error.                                                                     |
| `slippageBps`                          | `number`  | No                     | Slippage tolerance in basis points (e.g. `100` = 1%). Defaults to `100`. Must be an integer between 0 and 10000.                                                                                    |

## User Vault Balance

### Balance lifecycle

An async deposit or withdrawal passes through a **pending state** while the vault solver queues and processes the operation:

* **`pendingDeposit`** — funds are locked in the provisioner contract. They are not yet earning yield. Once the solver settles the request, they move to `balance` and begin earning.
* **`pendingWithdraw`** — vault shares have been redeemed but the underlying assets have not yet been transferred. They are no longer earning yield. Once the solver settles the request, they arrive as ERC-20 tokens in the receiver wallet.

The solver typically processes requests within 2 hours; the maximum window is 12 hours. Funds are safe in both pending states — the delay is operational, not a risk.

How an amount moves through the three states depends on whether the user chose sync or async:

| Path           | Where the balance lands                                                                                                   | Notes                                                               |
| -------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Async deposit  | `pendingDeposit` for \~2–12 hours (usually \~2), then moves to `balance`                                                  | Not earning during pending; best execution price once settled       |
| Sync deposit   | Directly into `balance`                                                                                                   | Slightly worse price; no waiting                                    |
| Sync withdraw  | Removed from `balance` immediately; claimable as ERC-20 in the receiver wallet                                            | Slightly worse price; no waiting                                    |
| Async withdraw | Moves from `balance` to `pendingWithdraw` for \~2–12 hours (usually \~2), then claimable as ERC-20 in the receiver wallet | No longer earning during pending; best execution price once settled |

### getUserCurrentBalance

```typescript theme={null}
import { getUserCurrentBalance, VaultId } from '@gauntlet-xyz/sdk'
// VaultId also available from: import { VaultId } from '@gauntlet-xyz/sdk/evm'

const balance = await getUserCurrentBalance(client, {
  vaultId: VaultId.AeraUsdAlpha,
  address: '0xUser',
})
```

| Parameter | Type      | Required | Description                                                                                                                                                     |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vaultId` | `string`  | Yes      | Must resolve to an Aera multi-depositor vault — throws `UnsupportedProtocolError` otherwise                                                                     |
| `address` | `Address` | Yes      | Account to query                                                                                                                                                |
| `chainId` | `number`  | No       | When omitted, returns one entry per chain the vault is deployed on. When provided, returns only that chain — throws `ChainMismatchError` if not deployed there. |

Returns `Promise<UserCurrentBalance[]>` — one entry per chain the vault is deployed on:

```typescript theme={null}
type UserCurrentBalance = {
  chain: string           // chain identifier, e.g. "base" — included for non-EVM compatibility
  token: Address          // token address
  decimals: number        // token decimals
  pendingDeposit: bigint  // assets locked in provisioner after async deposit — not yet earning yield; 0n if none
  balance: bigint         // assets actively earning in the vault; 0n if no position
  pendingWithdraw: bigint // assets redeemed but not yet claimable after async withdraw — no longer earning; 0n if none
}
```

All numeric fields are always present. If the account has no position on a given chain, all three bigint fields are `0n` — this is not an error.

## Result Shape

Both `getDepositTx` and `getWithdrawTx` return `Promise<PreparedTx[]>`.

```typescript theme={null}
type PreparedTx = {
  payload: {
    type: string    // 'approve' | 'deposit' | 'requestDeposit' | 'redeem' | 'requestRedeem' | 'withdraw'
    to: Address     // contract to call
    data: Hex       // ABI-encoded calldata with attribution suffix already concatenated
    account?: Address
  }
  tx: EvmTxStep    // structured ABI fields + raw attribution bytes — use with writeContract
}

type EvmTxStep = {
  type: 'approve' | 'deposit' | 'requestDeposit' | 'redeem' | 'requestRedeem' | 'withdraw'
  address: Address        // contract to call
  abi: Abi                // ABI fragment for this call
  functionName: string
  args: readonly unknown[]
  account: Address        // sender address
  attribution?: Hex       // raw attribution bytes — must pass as dataSuffix to writeContract
}
```

Steps must be executed in order. An `approve` step, when present, always comes first.

Each step exposes two submission paths with different trade-offs:

### Path 1 — `step.payload` + `sendTransaction`

**Use for:** backend scripts, embedded wallets (Privy, Dynamic), server-side signing, EVM pre-simulation (`eth_call` on the exact bytes to be broadcast).

Attribution is pre-concatenated into `payload.data` — it cannot be lost regardless of wallet or provider.

```typescript theme={null}
for (const step of steps) {
  await walletClient.sendTransaction(step.payload)
}
```

### Path 2 — `step.tx` + `writeContract`

**Use for:** browser wallets via wagmi (MetaMask, Coinbase Wallet, WalletConnect), or when you need wagmi simulation hooks.

Pass `step.tx.attribution` as `dataSuffix` — wagmi appends it to the ABI-encoded calldata before sending. **If `dataSuffix` is omitted or the EIP-1193 provider strips it, the transaction succeeds but volume is not attributed.**

```typescript theme={null}
for (const step of steps) {
  await walletClient.writeContract({
    address: step.tx.address,
    abi: step.tx.abi,
    functionName: step.tx.functionName,
    args: step.tx.args,
    account: step.tx.account,
    dataSuffix: step.tx.attribution, // required — omitting silently drops attribution
  })
}
```

<Card title="Attribution" icon="tag" href="/sdk/attribution">
  How ERC-8021 builder codes work, why `dataSuffix` matters, and how to verify attribution is tracked.
</Card>

## Types

### VaultInfo

```typescript theme={null}
type VaultInfo = {
  vaultId: string
  name: string
  protocol: 'aera' | 'morpho'
  strategy: string
  deployments: VaultDeployment[]
}
```

### VaultDeployment

The object `vaultId` resolves to. Carries all metadata the SDK needs to construct deposit and withdraw transactions — you never supply these directly.

```typescript theme={null}
type VaultDeployment = {
  chain: 'evm'
  chainId: number
  vaultAddress: Address           // ERC4626 vault contract
  provisionerAddress?: Address    // multi-depositor vaults: deposit routes through this instead
  vaultType: 'single-depositor' | 'multi-depositor'
  depositMode: 'sync' | 'async' | 'both'  // validates the depositMode param in getDepositTx / getWithdrawTx
  supplyToken: TokenInfo[]        // tokens accepted by this vault; provides address and decimals
}
```

### TokenInfo

```typescript theme={null}
type TokenInfo = {
  symbol: string
  address: Address
  decimals: number
}
```

### VaultFilter

```typescript theme={null}
type VaultFilter = {
  chainId?: number
  protocol?: string
}
```

### AttributionMode

```typescript theme={null}
enum AttributionMode {
  PUBLIC  = 'public',
  ENCODED = 'encoded',  // not yet implemented
  PRIVATE = 'private',  // not yet implemented
}
```

## Errors

All errors extend `GauntletSDKError`, which extends `Error`.

| Error                         | Description                                                                                                                                 |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `VaultNotFoundError`          | Vault ID doesn't exist or isn't deployed on the requested chain. Has `.vaultId` and optional `.chainId` properties.                         |
| `UnsupportedAssetError`       | Token not accepted by this vault. Has `.asset` and `.vaultId` properties.                                                                   |
| `ChainMismatchError`          | Chain parameter doesn't match vault deployment. Has `.expected` and `.received` properties.                                                 |
| `UnsupportedDepositModeError` | Requested sync/async mode not supported by this vault. Has `.vaultId`, `.requested`, and `.available` properties.                           |
| `RpcNotConfiguredError`       | No `evmClients` entry provided for the required chain ID. Has `.chainId` property.                                                          |
| `AccountRequiredError`        | No `wallet` provided in client config.                                                                                                      |
| `UnsupportedProtocolError`    | Vault protocol is not supported by this method (e.g. `getUserCurrentBalance` only supports Aera multi-depositor). Has `.protocol` property. |
| `InvalidWithdrawParamsError`  | `getWithdrawTx` called without exactly one of `shares`, `amount`, or `entireAmount`.                                                        |
| `InvalidSlippageBPSError`     | `slippageBps` is not an integer in the range 0–10000. Has `.slippage` property.                                                             |
| `UnimplementedFeatureError`   | Feature exists in the API but is not yet implemented (e.g. `AttributionMode.ENCODED`). Has `.feature` property.                             |
| `UnsupportedFeatureError`     | Feature is not supported at all (distinct from unimplemented). Has `.feature` property.                                                     |
| `UnitConversionError`         | Failed to convert token units for a vault — fee calculator unavailable on-chain. Has `.vaultAddress` property.                              |

## Go Deeper

<CardGroup cols={2}>
  <Card title="Examples" icon="play" href="/sdk/examples">
    End-to-end code for deposits, withdrawals, and error handling.
  </Card>

  <Card title="API Reference" icon="database" href="/onboarding/credentials">
    Use the raw API directly if you need more control than the SDK provides.
  </Card>
</CardGroup>
