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

# Examples

> End-to-end SDK examples for vault discovery, live data, deposits, withdrawals, positions, activity, and error handling.

## Initialize

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

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

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

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

const client = new GauntletClient({
  evmClients: { [base.id]: publicClient },
  wallet: walletClient,
  builderCode: 'your-builder-code', // issued by Gauntlet — request from the partnerships team
})
```

## Embedded Wallet (Privy)

The SDK reads only `wallet.account.address` — it never signs. Any viem-compatible wallet works, including embedded wallets from Privy, Dynamic, or similar providers.

For Privy, `@gauntlet-xyz/sdk/privy` sets up the whole client in one call:

```typescript theme={null}
import { createGauntletClientFromPrivy } from '@gauntlet-xyz/sdk/privy'
import { http } from 'viem'
import { base } from 'viem/chains'
import { useWallets } from '@privy-io/react-auth'

const { wallets } = useWallets()
const embeddedWallet = wallets.find(w => w.walletClientType === 'privy')

const client = await createGauntletClientFromPrivy({
  wallet: embeddedWallet,
  chains: [base],
  transports: { [base.id]: http(process.env.RPC_URL_BASE!) }, // optional — defaults to public RPC
  builderCode: 'your-builder-code', // issued by Gauntlet — request from the partnerships team
})
```

For other embedded wallet providers, build the viem clients yourself:

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

const provider = await embeddedWallet.getEthereumProvider()

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

const walletClient = createWalletClient({
  account: embeddedWallet.address as `0x${string}`,
  chain: base,
  transport: custom(provider),
})

const client = new GauntletClient({
  evmClients: { [base.id]: publicClient },
  wallet: walletClient,
  builderCode: 'your-builder-code', // issued by Gauntlet — request from the partnerships team
})
```

## Deposit

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

const steps = await getDepositTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 1_000_000n, // 1 USDC (6 decimals)
  receiver: '0xReceiver', // optional, defaults to wallet account
})
// returns:
// [
//   { payload: { type: 'approve', to: '0x...', data: '0x...', account: '0x...' }, tx: { ... } },
//   { payload: { type: 'requestDeposit', to: '0x...', data: '0x...', account: '0x...' }, tx: { ... } }
// ]

// steps must be executed in order — approve before requestDeposit
for (const step of steps) {
  // Estimate gas — simulates exact calldata before broadcast, catches reverts before spending gas
  const gas = await publicClient.estimateGas({
    to: step.payload.to,
    data: step.payload.data,
    account: step.payload.account,
  })

  const hash = await walletClient.sendTransaction({ ...step.payload, gas })

  // Wait for confirmation before the next step — deposit reverts if preceding approve is not mined
  const receipt = await publicClient.waitForTransactionReceipt({ hash, confirmations: 2 })

  if (receipt.status !== 'success') {
    throw new Error(`Transaction reverted: ${step.payload.type}`)
  }
}
```

## Withdraw

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

// Withdraw entire position
const steps = await getWithdrawTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  entireAmount: true,
  receiver: '0xReceiver', // optional; assets are sent here instead of the wallet account
})
// returns:
// [
//   { payload: { type: 'requestRedeem', to: '0x...', data: '0x...', account: '0x...' }, tx: { ... } }
// ]

for (const step of steps) {
  const gas = await publicClient.estimateGas({
    to: step.payload.to,
    data: step.payload.data,
    account: step.payload.account,
  })

  const hash = await walletClient.sendTransaction({ ...step.payload, gas })

  const receipt = await publicClient.waitForTransactionReceipt({ hash, confirmations: 2 })

  if (receipt.status !== 'success') {
    throw new Error(`Transaction reverted: ${step.payload.type}`)
  }
}
```

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

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

// By asset amount
const steps = await getWithdrawTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 500_000n,
})
```

## Check User Current Balance

`getUserCurrentBalance` returns a unified view of all three balance states for a vault position. Call it on load and after any deposit or withdrawal transaction to keep your UI in sync.

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

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

console.log(balances)
// [
//   {
//     chain: 'base',
//     token: '0xUSDCADDRESS123',
//     decimals: 6
//     pendingDeposit: 0n,
//     balance: 1_000_000n,
//     pendingWithdraw: 0n,
//   },
// ]
```

### After an async deposit

When a user deposits with `depositMode: 'async'`, the amount appears in `pendingDeposit` while the vault solver queues it. During this time the funds are locked in the provisioner contract and are not yet earning yield. They move to `balance` once the solver settles the request — usually within 2 hours.

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

// Submit the async deposit
const steps = await getDepositTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 500_000n,
  depositMode: 'async',
})

for (const step of steps) {
  const gas = await publicClient.estimateGas({
    to: step.payload.to,
    data: step.payload.data,
    account: step.payload.account,
  })
  const hash = await walletClient.sendTransaction({ ...step.payload, gas })
  const receipt = await publicClient.waitForTransactionReceipt({ hash, confirmations: 2 })
  if (receipt.status !== 'success') {
    throw new Error(`Transaction reverted: ${step.payload.type}`)
  }
}

// funds are locked in pendingDeposit — not yet earning — until solver settles (~2 hours)
const balances = await getUserCurrentBalance(client, {
  vaultId: VaultId.AeraUsdAlpha,
  address: '0xUser',
})
```

### After an async withdrawal

When a user withdraws with `depositMode: 'async'`, the amount moves from `balance` to `pendingWithdraw`. During this time the vault shares have been redeemed and the assets are no longer earning yield, but they have not yet been transferred. Once the solver settles the request (usually within 2 hours), the assets become claimable as ERC-20 tokens in the receiver wallet.

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

// Submit the async withdraw
const steps = await getWithdrawTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  entireAmount: true,
  depositMode: 'async',
})

for (const step of steps) {
  const gas = await publicClient.estimateGas({
    to: step.payload.to,
    data: step.payload.data,
    account: step.payload.account,
  })
  const hash = await walletClient.sendTransaction({ ...step.payload, gas })
  const receipt = await publicClient.waitForTransactionReceipt({ hash, confirmations: 2 })
  if (receipt.status !== 'success') {
    throw new Error(`Transaction reverted: ${step.payload.type}`)
  }
}

// assets are in pendingWithdraw — no longer earning — until solver settles (~2 hours)
const balances = await getUserCurrentBalance(client, {
  vaultId: VaultId.AeraUsdAlpha,
  address: '0xUser',
})
```

### Sync deposit and withdrawal

Sync transactions skip the queue. The balance moves immediately — no `pendingDeposit` or `pendingWithdraw`. Morpho withdrawals are sync. Aera mode support combines token settings with live runtime gates, so read it before presenting an instant action. When the V2 solving gate pauses a provisioner/token pair, both sync modes are unavailable. On Aera vaults, a sync deposit locks all of the depositor's vault units for 1 hour; redeeming or transferring them during that window reverts with `Aera__UnitsLocked`.

```typescript theme={null}
import { getDepositTx, getWithdrawTx, VaultId } from '@gauntlet-xyz/sdk/evm'

// Sync deposit with a Morpho vault: balance goes straight to `balance`
const depositSteps = await getDepositTx(client, {
  vaultId: VaultId.BaseUsdcPrime, // Morpho vault — sync mode
  amount: 500_000n,
})

for (const step of depositSteps) {
  const gas = await publicClient.estimateGas({
    to: step.payload.to,
    data: step.payload.data,
    account: step.payload.account,
  })
  const hash = await walletClient.sendTransaction({ ...step.payload, gas })
  const receipt = await publicClient.waitForTransactionReceipt({ hash, confirmations: 2 })
  if (receipt.status !== 'success') {
    throw new Error(`Transaction reverted: ${step.payload.type}`)
  }
}

// Sync withdraw: balance drops from `balance` immediately;
// tokens appear in the receiver wallet right away
const withdrawSteps = await getWithdrawTx(client, {
  vaultId: VaultId.BaseUsdcPrime, // Morpho vault — sync mode
  entireAmount: true,
})

for (const step of withdrawSteps) {
  const gas = await publicClient.estimateGas({
    to: step.payload.to,
    data: step.payload.data,
    account: step.payload.account,
  })
  const hash = await walletClient.sendTransaction({ ...step.payload, gas })
  const receipt = await publicClient.waitForTransactionReceipt({ hash, confirmations: 2 })
  if (receipt.status !== 'success') {
    throw new Error(`Transaction reverted: ${step.payload.type}`)
  }
}
```

### Quote and submit an Aera instant deposit

Quote expected vault units, then pass the reviewed minimum to the explicit sync deposit.

```typescript theme={null}
import { getDepositTx, getSyncDepositQuote, type PreparedTx, VaultId } from '@gauntlet-xyz/sdk/evm'

const sendAndConfirm = async (step: PreparedTx) => {
  await publicClient.call({
    account: step.payload.account,
    to: step.payload.to,
    data: step.payload.data,
  })
  const hash = await walletClient.sendTransaction({
    to: step.payload.to,
    data: step.payload.data,
  })
  const receipt = await publicClient.waitForTransactionReceipt({ hash })
  if (receipt.status !== 'success') throw new Error(`${step.tx.type} reverted`)
}

const request = {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 1_000_000n,
  slippageBps: 100,
}
let quote = await getSyncDepositQuote(client, request)
const build = () =>
  getDepositTx(client, {
    ...request,
    depositMode: 'sync',
    minUnitsOut: quote.minUnitsOut,
  })
let steps = await build()

if (steps[0]?.tx.type === 'approve') {
  await sendAndConfirm(steps[0])
  quote = await getSyncDepositQuote(client, request)
  steps = await build()
}
await sendAndConfirm(steps[0])
```

The sync approval spender is the vault. Requote after approval and review a lower
`minUnitsOut` before requesting the deposit signature.

### Quote and submit an Aera instant withdrawal

Use the same sizing input for the quote and transaction. Passing `syncWithdrawQuote` makes the transaction sync and pins the quoted on-chain bound. When transaction slippage is omitted, the builder uses the quote's slippage; an explicitly supplied value must match.

```typescript theme={null}
import {
  getAeraTokenModeSupport,
  getSyncWithdrawQuote,
  getWithdrawTx,
  VaultId,
} from '@gauntlet-xyz/sdk/evm'

const vaultId = VaultId.AeraUsdAlpha
const support = await getAeraTokenModeSupport(client, { vaultId })

if (support.syncRedeem) {
  const quote = await getSyncWithdrawQuote(client, {
    vaultId,
    amount: 500_000n, // exact USDC output
    account: account.address, // optional here; includes this account's lock state
    slippageBps: 50,
  })

  if (quote.capacity.exceedsCapacity) {
    throw new Error('Requested amount exceeds the current sync-withdraw epoch capacity')
  }

  const steps = await getWithdrawTx(client, {
    vaultId,
    amount: 500_000n,
    syncWithdrawQuote: quote,
  })

  for (const step of steps) {
    const hash = await walletClient.sendTransaction({
      to: step.payload.to,
      data: step.payload.data,
      account: step.payload.account,
    })
    const receipt = await publicClient.waitForTransactionReceipt({ hash })
    if (receipt.status !== 'success') throw new Error('Sync withdrawal reverted')
  }
}
```

For a full-position instant exit, the quote requires the account whose shares it reads. The transaction always uses the configured `wallet.account`; an optional `account` must match it. The builder rereads the current share balance and rejects a stale full-position quote.

`shares` and `entireAmount` quotes with `slippageBps: 10000` are rejected because they would produce `minTokensOut: 0`.

```typescript theme={null}
const quote = await getSyncWithdrawQuote(client, {
  vaultId: VaultId.AeraUsdAlpha,
  account: account.address,
  entireAmount: true,
})

const steps = await getWithdrawTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  account: account.address,
  entireAmount: true,
  syncWithdrawQuote: quote,
})
```

## Read Live Vault Data

`client.api` exposes every REST API endpoint with generated types — live metrics, timeseries, positions, activity, TVL, and prices. No RPC needed; a data-only client is just `new GauntletClient({ apiKey })`.

```typescript theme={null}
import { apiVaultIdFromVaultId } from '@gauntlet-xyz/sdk'
import { VaultId } from '@gauntlet-xyz/sdk/evm'

// All vaults with live TVL / APY / unit price
const { data: vaults } = await client.api.vaults()

// One vault — the API identifies vaults by "{chainId}:{address}"
const apiVaultId = await apiVaultIdFromVaultId(client, VaultId.AeraUsdAlpha)
const { data: vault } = await client.api.vault(apiVaultId)

// 30 days of daily TVL / unit-price / APY history
const { data: points } = await client.api.vaultTimeseries(apiVaultId, {
  start: '2026-06-01',
  end: '2026-07-01',
  granularity: 'day',
})
```

User positions and PnL:

```typescript theme={null}
// All positions for a wallet
const { data: positions } = await client.api.positions('0xUser')

// One position's value / cost-basis / PnL / ROI history
const { data: history } = await client.api.positionTimeseries('0xUser', apiVaultId, {
  granularity: 'day',
})
```

Amounts are human-unit decimal strings (e.g. `"1250.5"`). Convert to base units exactly with `decimalToBigInt(value, token.decimals)` — it throws instead of rounding.

## Track Activity and Wait for Settlement

`getActivityFlows` turns the wallet's raw activity log into one flow per user action, pairing async request/settle rows automatically:

```typescript theme={null}
import { getActivityFlows } from '@gauntlet-xyz/sdk'

const flows = await getActivityFlows(client.api, '0xUser')

for (const flow of flows) {
  console.log(flow.kind, flow.status, flow.assets.decimal, flow.txHashes)
}
// deposit  settled  1000    ['0xrequest...', '0xsettle...']
// withdraw pending  250.5   ['0xrequest...']
```

After submitting an async deposit or withdrawal, block until the solver settles it:

```typescript theme={null}
import { waitForRequestSettlement, SettlementTimeoutError } from '@gauntlet-xyz/sdk'

try {
  const flow = await waitForRequestSettlement(client.api, '0xUser', requestHash)
  console.log(flow.status) // 'settled' or 'refunded'
} catch (e) {
  if (e instanceof SettlementTimeoutError) { /* still pending after 10 minutes — poll again later */ }
}
```

## Position History

Replay a wallet's full event history for one vault into a chronological timeline — share balance, escrowed pending amounts, and net asset flows after every event:

```typescript theme={null}
import { getPositionHistory, apiVaultIdFromVaultId } from '@gauntlet-xyz/sdk'
import { VaultId } from '@gauntlet-xyz/sdk/evm'

const apiVaultId = await apiVaultIdFromVaultId(client, VaultId.AeraUsdAlpha)
const { points } = await getPositionHistory(client.api, '0xUser', apiVaultId)

const latest = points.at(-1)
console.log(latest?.sharesBalance, latest?.netAssetsIn)
```

## Wagmi / writeContract

When integrating with wagmi, use `step.tx` fields with `writeContractAsync`. Pass `step.tx.attribution` as `dataSuffix` — wagmi appends it to the calldata before sending. **Omitting `dataSuffix` silently drops attribution: the transaction succeeds but volume is not tracked.**

```typescript theme={null}
import { getDepositTx, VaultId } from '@gauntlet-xyz/sdk/evm'
import { useWriteContract } from 'wagmi'

const { writeContractAsync } = useWriteContract()

const steps = await getDepositTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 1_000_000n,
})

for (const step of steps) {
  await writeContractAsync({
    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
  })
}
```

## Slippage

Both `getDepositTx` and `getWithdrawTx` accept a `slippageBps` parameter (integer basis points, e.g. `50` = 0.5%). Defaults to `100` (1%).

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

const steps = await getDepositTx(client, {
  vaultId: VaultId.AeraUsdAlpha,
  amount: 1_000_000n,
  slippageBps: 50, // 0.5% slippage tolerance
})
```

## Error Handling

```typescript theme={null}
import { getDepositTx, VaultId } from '@gauntlet-xyz/sdk/evm'
import {
  VaultNotFoundError,
  AccountRequiredError,
  RpcNotConfiguredError,
  UnsupportedDepositModeError,
  InvalidSlippageBPSError,
  UnimplementedFeatureError,
  UnitConversionError,
} from '@gauntlet-xyz/sdk'

try {
  await getDepositTx(client, {
    vaultId: VaultId.AeraUsdAlpha,
    amount: 1_000_000n,
  })
} catch (e) {
  if (e instanceof VaultNotFoundError)          { /* e.vaultId, e.chainId */ }
  if (e instanceof AccountRequiredError)        { /* add wallet to GauntletClient config */ }
  if (e instanceof RpcNotConfiguredError)       { /* e.chainId — add RPC URL for this chain */ }
  if (e instanceof UnsupportedDepositModeError) { /* e.vaultId, e.requested, e.available */ }
  if (e instanceof InvalidSlippageBPSError)     { /* e.slippage — must be integer 0–10000 */ }
  if (e instanceof UnimplementedFeatureError)   { /* e.feature */ }
  if (e instanceof UnitConversionError)         { /* e.vaultAddress */ }
}
```

Aera instant quote and transaction flows can throw `UnsupportedFeatureError` when the runtime is not sync capable. A zero or non-sync `minUnitsOut` throws `InvalidSyncDepositBoundError`. Instant withdrawals can also throw `StalePriceError`, `InvalidSyncWithdrawBoundError`, or `InvalidWithdrawParamsError` for invalid sizing or quote context. Full-position flows use `AccountRequiredError` when the quote omits `account`, and `AccountMismatchError` when the `account` passed with `getWithdrawTx({ entireAmount: true })` differs from the configured wallet.

Data-path calls throw `GauntletApiError` on failed requests:

```typescript theme={null}
import { GauntletApiError } from '@gauntlet-xyz/sdk'

try {
  await client.api.position('0xUser', apiVaultId)
} catch (e) {
  if (e instanceof GauntletApiError) {
    console.error(e.status, e.code, e.path) // e.g. 404 NOT_FOUND /v1/users/0xUser/positions/...
  }
}
```

## Go Deeper

<CardGroup cols={2}>
  <Card title="SDK Reference" icon="code" href="/sdk/reference">
    Full constructor, methods, result shapes, and errors.
  </Card>

  <Card title="Deposit Your First Dollar" icon="arrow-right-arrow-left" href="/guides/developer/earn/deposits-and-withdrawals">
    The full integration guide with confirmation and fallback guidance.
  </Card>
</CardGroup>
