Skip to main content
This example demonstrates a complete deposit flow using Privy for wallet management and the unified deposit API.
This example uses the unified deposit API which automatically detects the optimal authorization method (permit vs approval).

Prerequisites

  • Node.js 18+
  • Privy App ID from Privy Dashboard
  • Amplify API key from Paxos Labs

Installation

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

Complete Example

useDeposit Hook

// src/hooks/useDeposit.ts
import { useState, useCallback } from "react";
import { encodeFunctionData } from "viem";
import { mainnet } from "viem/chains";
import { usePrivy, useWallets } from "@privy-io/react-auth";
import {
  prepareDepositAuthorization,
  prepareDepositTxData,
  prepareDepositWithPermitTxData,
  DepositAuthMethod,
  YieldType,
} from "@paxoslabs/amplify-sdk";

interface DepositParams {
  amount: string;
  depositToken: `0x${string}`;
  yieldType: YieldType;
}

export function useDeposit() {
  const { sendTransaction } = usePrivy();
  const { wallets } = useWallets();
  const [status, setStatus] = useState<string>("");
  const [isLoading, setIsLoading] = useState(false);

  const deposit = useCallback(
    async ({ amount, depositToken, yieldType }: DepositParams) => {
      const wallet = wallets[0];
      if (!wallet) {
        throw new Error("Connect a wallet first");
      }

      setIsLoading(true);
      const owner = wallet.address as `0x${string}`;

      try {
        const params = {
          yieldType,
          depositToken,
          depositAmount: amount,
          recipientAddress: owner,
          chainId: mainnet.id,
        };

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

        // Step 2: Handle based on method
        switch (auth.method) {
          case DepositAuthMethod.PERMIT: {
            setStatus("Please sign permit in your wallet...");
            const provider = await wallet.getEthereumProvider();
            const signature = (await provider.request({
              method: "eth_signTypedData_v4",
              params: [
                owner,
                JSON.stringify({
                  domain: auth.permitData.domain,
                  types: auth.permitData.types,
                  primaryType: auth.permitData.primaryType,
                  message: auth.permitData.message,
                }),
              ],
            })) as `0x${string}`;

            setStatus("Submitting deposit...");
            const permitTx = await prepareDepositWithPermitTxData({
              ...params,
              signature,
              deadline: BigInt(auth.permitData.message.deadline),
            });

            const { abi: permitAbi, functionName: permitFn, args: permitArgs } = permitTx.data;
            await sendTransaction({
              chainId: permitTx.chainId,
              to: permitTx.address,
              data: encodeFunctionData({
                abi: permitAbi,
                functionName: permitFn,
                args: permitArgs,
              }),
            });
            break;
          }

          case DepositAuthMethod.APPROVAL: {
            setStatus("Approving token spend...");
            const { abi: approvalAbi, functionName: approvalFn, args: approvalArgs } = auth.txData;
            await sendTransaction({
              chainId: mainnet.id,
              to: auth.txData.address,
              data: encodeFunctionData({
                abi: approvalAbi,
                functionName: approvalFn,
                args: approvalArgs,
              }),
            });

            setStatus("Submitting deposit...");
            const depositTx = await prepareDepositTxData(params);
            const { abi: depositAbi, functionName: depositFn, args: depositArgs } = depositTx;
            await sendTransaction({
              chainId: depositTx.chainId,
              to: depositTx.address,
              data: encodeFunctionData({
                abi: depositAbi,
                functionName: depositFn,
                args: depositArgs,
              }),
            });
            break;
          }

          case DepositAuthMethod.ALREADY_APPROVED: {
            setStatus("Submitting deposit...");
            const directTx = await prepareDepositTxData(params);
            const { abi, functionName, args } = directTx;
            await sendTransaction({
              chainId: directTx.chainId,
              to: directTx.address,
              data: encodeFunctionData({ abi, functionName, args }),
            });
            break;
          }
        }

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

  return { deposit, status, isLoading };
}

Deposit Component

// src/components/DepositForm.tsx
import { useState } from "react";
import { YieldType } from "@paxoslabs/amplify-sdk";
import { useDeposit } from "../hooks/useDeposit";

const USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as const;

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

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await deposit({
      amount,
      depositToken: USDC_ADDRESS,
      yieldType: YieldType.PRIME,
    });
  };

  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>
  );
}

What This Code Does

  1. Checks authorization method - Determines if permit, approval, or already approved
  2. Handles permit flow - Signs typed data off-chain for gas-efficient authorization
  3. Handles approval flow - Sends approval transaction then deposit
  4. Handles pre-approved - Deposits directly if allowance exists
  5. Reports status - Updates UI with current operation status

Key Features

  • Automatic method detection - SDK chooses optimal authorization path
  • Type-safe results - TypeScript discriminated unions for each case
  • Status updates - Real-time feedback during multi-step flows
  • Error handling - Catches and reports errors with context

Troubleshooting

IssueSolution
”SDK not initialized”Call initAmplifySDK() before using hooks
”Connect a wallet first”Ensure user is authenticated with Privy
Permit signature failsToken may not support EIP-2612, falls back to approval
For more details, see Deposits.