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

# Deposits

> End-to-end deposit flow using AmplifyClient — authorize, sign or approve, then submit.

A deposit moves an ERC-20 asset from the user's wallet into a BoringVault and mints share tokens back. The flow is the same regardless of vault:

1. Call `client.core.authorization.detect(...)` to discover whether the deposit asset needs a permit signature, a separate `approve` transaction, or already has sufficient allowance.
2. Handle the three response shapes (`permit` / `approval` / `already_approved`).
3. Call `client.amplify.deposit.prepare(...)` to get ABI-encoded calldata.
4. Submit the returned transaction with the user's wallet.

Throughout this guide, `client` refers to a singleton `AmplifyClient` created on the server — see [Project setup](./project-setup) for the wiring. All token amounts are base-units decimal strings; the SDK does no decimal parsing.

<Info>
  See the [AI Coding Reference](../sdk-ai-reference) for the full parameter list on every method shown here.
</Info>

## Step 1: Decide on permit vs. approval

```ts theme={null}
import type { Amplify } from '@paxoslabs/amplify-sdk'

const auth = await client.core.authorization.detect({
  vaultAddress: '0xbbbb000000000000000000000000000000000001',
  tokenAddress: depositAsset, // the ERC-20 you're depositing (e.g. USDC)
  amount: depositAmount,      // base-units decimal string
  userAddress,
  chainId: 1,
})
```

The response is a discriminated union on `method`:

```ts theme={null}
type AuthorizationResponseDto =
  | { method: 'permit'; permitData: Amplify.PermitTypedDataResponseDto }
  | { method: 'approval'; approvalTransaction: Amplify.ApprovalTransactionDto }
  | { method: 'already_approved' }
```

| `method`           | What to do                                                                                             |
| ------------------ | ------------------------------------------------------------------------------------------------------ |
| `permit`           | Sign `auth.permitData` off-chain (gasless) and pass `permitSignature` + `permitDeadline` to `prepare`. |
| `approval`         | Submit `auth.approvalTransaction` first, wait for it to confirm, then call `prepare`.                  |
| `already_approved` | Sufficient allowance already exists. Go straight to `prepare`.                                         |

<Steps>
  <Step title="Branch on auth.method">
    ```ts theme={null}
    import type { Address, Hex } from 'viem'

    let permitSignature: Hex | undefined
    let permitDeadline: number | undefined

    if (auth.method === 'permit') {
      // see Step 2a
    } else if (auth.method === 'approval') {
      // see Step 2b
    } else if (auth.method === 'already_approved') {
      // skip to Step 3
    }
    ```
  </Step>

  <Step title="2a — Permit path">
    For permit-supporting tokens (USDC mainnet, DAI, most modern ERC-20s with EIP-2612), sign the typed data with viem and forward the signature to the backend. The `primaryType` is always `'Permit'`.

    ```ts theme={null}
    import { createWalletClient, custom } from 'viem'
    import { mainnet } from 'viem/chains'

    if (auth.method === 'permit') {
      const walletClient = createWalletClient({
        account: userAddress,
        chain: mainnet,
        transport: custom(window.ethereum),
      })

      permitSignature = await walletClient.signTypedData({
        account: userAddress,
        domain: auth.permitData.domain,
        types: auth.permitData.types,
        primaryType: 'Permit',
        message: auth.permitData.value,
      })
      permitDeadline = Number(auth.permitData.deadline)
    }
    ```

    With wagmi:

    ```ts theme={null}
    import { useSignTypedData } from 'wagmi'

    const { signTypedDataAsync } = useSignTypedData()

    if (auth.method === 'permit') {
      permitSignature = await signTypedDataAsync({
        domain: auth.permitData.domain,
        types: auth.permitData.types,
        primaryType: 'Permit',
        message: auth.permitData.value,
      })
      permitDeadline = Number(auth.permitData.deadline)
    }
    ```
  </Step>

  <Step title="2b — Approval path">
    When the token does not support permit (or you want to force a standard ERC-20 flow), the backend returns a ready-to-submit `approvalTransaction` containing ABI-encoded `approve()` calldata. Submit it to the deposit asset address and wait for the receipt before calling `prepare`.

    ```ts theme={null}
    import { useSendTransaction, usePublicClient } from 'wagmi'

    const { sendTransactionAsync } = useSendTransaction()
    const publicClient = usePublicClient()

    if (auth.method === 'approval') {
      const approvalHash = await sendTransactionAsync({
        to: depositAsset, // the token being approved
        data: auth.approvalTransaction.encoded as Hex,
        chainId,
      })

      await publicClient!.waitForTransactionReceipt({ hash: approvalHash })
    }
    ```

    <Warning>
      Always wait for the approval receipt before submitting the deposit. If the deposit lands before the approval is mined, the deposit transaction will revert with an `ERC20: insufficient allowance` style error.
    </Warning>
  </Step>

  <Step title="2c — Already approved">
    ```ts theme={null}
    if (auth.method === 'already_approved') {
      // Nothing to do — fall through to Step 3.
    }
    ```
  </Step>

  <Step title="Step 3 — Prepare the deposit">
    ```ts theme={null}
    const prepared = await client.amplify.deposit.prepare({
      vaultAddress: '0xbbbb000000000000000000000000000000000001',
      depositAsset, // e.g. '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC
      depositAmount, // base-units decimal string
      userAddress,
      // Optional: send shares to a different address than the signer
      // to: receiverAddress,
      chainId: 1,
      // Permit fields — pass BOTH or NEITHER
      ...(permitSignature && permitDeadline ? { permitSignature, permitDeadline } : {}),
    })

    // prepared.transaction = { to, data, value, ... }
    ```

    Required fields:

    * `vaultAddress` — BoringVault contract address.
    * `depositAsset` — ERC-20 token address you're depositing.
    * `depositAmount` — amount in base units (decimal string).
    * `userAddress` — wallet that signs and submits the deposit. Also the default share recipient when `to` is omitted.
    * `chainId` — EVM chain ID.

    Optional fields:

    * `to` — destination address that receives the vault shares. Defaults to `userAddress`.
    * `permitSignature` + `permitDeadline` — required together when you came through the permit branch in Step 2a.
    * `responseFormat` — `'encoded'` (default), `'full'`, or `'structured'`. Pass `'full'` to also receive `abi`, `functionName`, and `args`.
  </Step>

  <Step title="Step 4 — Submit the transaction">
    ```ts theme={null}
    const tx = prepared.transaction
    const depositHash = await sendTransactionAsync({
      to: tx.to as Address,
      data: tx.data as Hex,
      value: BigInt(tx.value),
      chainId,
    })

    await publicClient!.waitForTransactionReceipt({ hash: depositHash })
    ```

    `tx.value` is a decimal string (usually `"0"` for ERC-20 deposits); cast to `BigInt` before passing to viem/wagmi.
  </Step>
</Steps>

## End-to-end example

```ts theme={null}
import { AmplifyClient } from '@paxoslabs/amplify-sdk'
import type { Address, Hex } from 'viem'
import { createPublicClient, createWalletClient, custom, http, parseUnits } from 'viem'
import { mainnet } from 'viem/chains'

const client = new AmplifyClient({
  apiKey: process.env.PAXOS_LABS_API_KEY!,
})

async function deposit({
  userAddress,
  depositAsset,
  userInput,
  chainId,
}: {
  userAddress: Address
  depositAsset: Address
  userInput: string // e.g. "10" for 10 USDC
  chainId: number
}) {
  const publicClient = createPublicClient({ chain: mainnet, transport: http() })
  const walletClient = createWalletClient({
    account: userAddress,
    chain: mainnet,
    transport: custom(window.ethereum),
  })

  // 1. Discover a vault that accepts the deposit asset
  const { vaults } = await client.amplify.vaults.list({
    filter: `chainId=${chainId}`,
  })

  const vault = vaults
    .flatMap((v) => v.deployments)
    .find((d) =>
      d.assets.some(
        (a) => a.assetAddress.toLowerCase() === depositAsset.toLowerCase() && a.depositable,
      ),
    )
  if (!vault) {
    throw new Error(`No vault on chain ${chainId} accepts ${depositAsset}`)
  }

  // 2. Read decimals from the deposit asset
  const decimals = await publicClient.readContract({
    address: depositAsset,
    abi: [{ type: 'function', name: 'decimals', inputs: [], outputs: [{ type: 'uint8' }], stateMutability: 'view' }],
    functionName: 'decimals',
  })
  const depositAmount = parseUnits(userInput, decimals).toString()

  // 3. Authorize
  const auth = await client.core.authorization.detect({
    vaultAddress: vault.boringVaultAddress as Address,
    tokenAddress: depositAsset,
    amount: depositAmount,
    userAddress,
    chainId,
  })

  let permitSignature: Hex | undefined
  let permitDeadline: number | undefined

  // 4. Handle the three branches
  if (auth.method === 'permit') {
    permitSignature = await walletClient.signTypedData({
      account: userAddress,
      domain: auth.permitData.domain,
      types: auth.permitData.types,
      primaryType: 'Permit',
      message: auth.permitData.value,
    })
    permitDeadline = Number(auth.permitData.deadline)
  } else if (auth.method === 'approval') {
    const hash = await walletClient.sendTransaction({
      to: depositAsset,
      data: auth.approvalTransaction.encoded as Hex,
    })
    await publicClient.waitForTransactionReceipt({ hash })
  }

  // 5. Prepare
  const prepared = await client.amplify.deposit.prepare({
    vaultAddress: vault.boringVaultAddress as Address,
    depositAsset,
    depositAmount,
    userAddress,
    chainId,
    ...(permitSignature && permitDeadline ? { permitSignature, permitDeadline } : {}),
  })

  // 6. Submit
  const tx = prepared.transaction
  const depositHash = await walletClient.sendTransaction({
    to: tx.to as Address,
    data: tx.data as Hex,
    value: BigInt(tx.value),
  })
  await publicClient.waitForTransactionReceipt({ hash: depositHash })

  return depositHash
}
```

## Converting user input to base units

```ts theme={null}
import { parseUnits } from 'viem'

// Read decimals live from the contract (do not hardcode).
const decimals = await publicClient.readContract({
  address: depositAsset,
  abi: [{ type: 'function', name: 'decimals', inputs: [], outputs: [{ type: 'uint8' }], stateMutability: 'view' }],
  functionName: 'decimals',
})

const depositAmount = parseUnits(userInput, decimals).toString()
```

## Error handling

```ts theme={null}
import { AmplifyError, AmplifyTimeoutError } from '@paxoslabs/amplify-sdk'

try {
  await client.amplify.deposit.prepare({ /* ... */ })
} catch (err) {
  if (err instanceof AmplifyTimeoutError) {
    // Retry, surface a timeout UI, etc.
  } else if (err instanceof AmplifyError) {
    // err.statusCode  number  — HTTP status (e.g. 400, 401, 500)
    // err.body        unknown — parsed backend error body (when JSON)
    // err.message     string  — human-readable summary
    // err.rawResponse RawResponse — the original Response
    console.error('Amplify error', err.statusCode, err.message)
  } else {
    throw err
  }
}
```

When surfacing errors to the browser, log `err.body` and `err.rawResponse` server-side and return a generic message to the client.

## Next steps

* [Withdrawals guide](./withdrawals) — redeem shares for the underlying asset.
* [AI Coding Reference](../sdk-ai-reference) — all parameters and response shapes.
* [Migrating from 0.5.x](../migrating-from-0-5) — field-by-field rename table.
