Skip to main content
This guide demonstrates complete deposit flows using the unified deposit API, which automatically detects the optimal authorization method (permit vs approval).
Breaking change in v0.5.0: Transaction functions now use vaultName instead of yieldType. Discover vault names with getVaultsByConfig() before making deposit calls. See the migration guide.
The unified deposit API handles three authorization paths automatically:
MethodDescription
PermitGas-efficient off-chain signature (EIP-2612). Single transaction — no separate approval needed.
ApprovalStandard ERC-20 approve transaction followed by the deposit transaction.
Already ApprovedDirect deposit when sufficient allowance already exists.

Prerequisites

  • Node.js 22+
  • Amplify API key from Paxos Labs
  • SDK initialized with initAmplifySDK()

Installation

pnpm add @paxoslabs/amplify-sdk @privy-io/react-auth viem @tanstack/react-query

Imports

// SDK imports
import {
  getVaultsByConfig,
  prepareDepositAuthorization,
  prepareDeposit,
  isPermitAuth,
  isApprovalAuth,
  isAlreadyApprovedAuth,
  YieldType,
} from "@paxoslabs/amplify-sdk";

// Privy imports
import { usePrivy, useWallets } from "@privy-io/react-auth";

// Viem imports
import { encodeFunctionData } from "viem";
import { mainnet } from "viem/chains";

Step 0: Fetch Available Vaults

Retrieve all vaults accessible with your API key. This is the data you need to build a vault selector for your users.
import { getVaultsByConfig, YieldType } from '@paxoslabs/amplify-sdk'

const vaults = await getVaultsByConfig()

for (const vault of vaults) {
  console.log(`${vault.name}${vault.yieldType} on chain ${vault.chainId}`)
  for (const asset of vault.supportedAssets) {
    console.log(`  ${asset.symbol}: deposit=${asset.depositable}, withdraw=${asset.withdrawable}`)
  }
}
You can also narrow the list with filters:
const coreVaults = await getVaultsByConfig({ yieldType: YieldType.CORE })
const ethVaults = await getVaultsByConfig({ chainId: 1 })
const usdcVaults = await getVaultsByConfig({ depositAssetAddress: USDC_ADDRESS })

Step 1: Select a Vault

Once the user picks a vault from your UI, record its vault.name — all subsequent transaction calls require it.
const selectedVault = vaults[0] // or from user selection

const vaultName = selectedVault.name
const depositAsset = selectedVault.vault.baseTokenAddress
const chainId = selectedVault.chainId

Step 2: Check Authorization

Determine the optimal authorization method for this deposit. The SDK auto-detects permit support.
import {
  prepareDepositAuthorization,
  isPermitAuth,
  isApprovalAuth,
  isAlreadyApprovedAuth,
} from '@paxoslabs/amplify-sdk'

const auth = await prepareDepositAuthorization({
  vaultName,
  depositAsset,
  depositAmount: '1000',
  to: userAddress,
  chainId,
})

if (isPermitAuth(auth)) {
  // Sign EIP-712 typed data off-chain (gasless)
} else if (isApprovalAuth(auth)) {
  // Send an ERC-20 approve transaction first
} else if (isAlreadyApprovedAuth(auth)) {
  // Sufficient allowance exists — skip straight to deposit
}

Step 3: Execute Deposit

After authorization, prepare and submit the deposit transaction.
import { prepareDeposit } from '@paxoslabs/amplify-sdk'

const prepared = await prepareDeposit({
  vaultName,
  depositAsset,
  depositAmount: '1000',
  to: userAddress,
  chainId,
  // For permit path, also pass: signature, deadline
})

// Submit prepared.txData via your wallet provider

Complete Examples

useDeposit Hook

// src/hooks/useDeposit.ts
import { ,  } from "react";
import {  } from "viem";
import {  } from "viem/chains";
import {
  ,
  ,
  ,
  ,
  ,
  ,
} from "@paxoslabs/amplify-sdk";

declare const : () => { : (: any) => <string> };
declare const : () => { : <{ : string; : () => <any> }> };

interface DepositParams {
  : string;
  : `0x${string}`;
  : string;
}

export function () {
  const {  } = ();
  const {  } = ();
  const [, ] = <string>("");
  const [, ] = (false);

  const  = (
    async ({ , ,  }: DepositParams) => {
      const  = [0];
      if (!) {
        throw new ("Connect a wallet first");
      }

      (true);
      const  = . as `0x${string}`;

      try {
        const  = {
          ,
          ,
          : ,
          ,
          : .id,
        };

        // Step 1: Get authorization method
        ("Checking authorization...");
        const  = await ();

        // Step 2: Handle based on method using type guards
        if (()) {
          ("Please sign permit in your wallet...");
          const  = await .();
          const  = (await .request({
            : "eth_signTypedData_v4",
            : [
              ,
              .({
                : .permitData.domain,
                : .permitData.types,
                : .permitData.primaryType,
                : .permitData.message,
              }),
            ],
          })) as `0x${string}`;

          ("Submitting deposit...");
          const  = await ({
            ...,
            ,
            : .permitData.message.deadline,
          });

          // Permit path: txData has nested .data structure
          await ({
            : .txData.chainId,
            : .txData.address,
            : ({
              : .txData.data.abi,
              : .txData.data.functionName,
              : .txData.data.args,
            }),
          });
        } else if (()) {
          ("Approving token spend...");
          await ({
            : .id,
            : .txData.address,
            : ({
              : .txData.abi,
              : .txData.functionName,
              : .txData.args,
            }),
          });

          // Important: In production, wait for the approval transaction
          // to be confirmed before proceeding with the deposit.
          // See the Node.js example for the full pattern using
          // publicClient.waitForTransactionReceipt().

          ("Submitting deposit...");
          const  = await ();
          await ({
            : .txData.chainId,
            : .txData.address,
            : ({
              : .txData.abi,
              : .txData.functionName,
              : .txData.args,
            }),
          });
        } else if (()) {
          ("Submitting deposit...");
          const  = await ();
          await ({
            : .txData.chainId,
            : .txData.address,
            : ({
              : .txData.abi,
              : .txData.functionName,
              : .txData.args,
            }),
          });
        }

        ("Deposit successful!");
      } catch () {
        (`Error: ${ instanceof  ? . : "Unknown"}`);
        throw ;
      } finally {
        (false);
      }
    },
    [, ]
  );

  return { , ,  };
}

Deposit Component

// src/components/DepositForm.tsx
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { getVaultsByConfig, YieldType } from "@paxoslabs/amplify-sdk";
import { useDeposit } from "../hooks/useDeposit";
import { mainnet } from "viem/chains";

export function DepositForm() {
  const [amount, setAmount] = useState("100");
  const { deposit, status, isLoading } = useDeposit();

  const { data: vaults } = useQuery({
    queryKey: ["vaults", YieldType.CORE, mainnet.id],
    queryFn: () => getVaultsByConfig({ yieldType: YieldType.CORE, chainId: mainnet.id }),
  });

  const vault = vaults?.[0];

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!vault) return;
    await deposit({
      amount,
      depositAsset: vault.vault.baseTokenAddress as `0x${string}`,
      vaultName: vault.name,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Amount (USDC):
        <input
          type="number"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          disabled={isLoading}
          min="0"
          step="0.01"
        />
      </label>
      <button type="submit" disabled={isLoading}>
        {isLoading ? "Processing..." : "Deposit"}
      </button>
      {status && <p>{status}</p>}
    </form>
  );
}

Customization

Custom Slippage

const  = await ({
  ...,
  : 100, // 1% instead of default 0.5%
})

Distributor Code

const  = await ({
  ...,
  : 'your_distributor_code',
})

Force Approval Flow

const  = await ({
  ...,
  : 'approval', // Skip permit detection
})

Troubleshooting

IssueSolution
”SDK not initialized”Call initAmplifySDK() before using hooks
”Connect a wallet first”Ensure user has connected wallet
Permit signature failsToken may not support EIP-2612, falls back to approval
Transaction revertsCheck token balance and allowance
For more details on the deposit API, see Deposit Workflow.