Skip to main content
This example demonstrates how to combine the Amplify SDK with the Privy React SDK to prepare approvals, submit deposits, and display transaction status inside a React app.

Overview

  • Access the embedded or external wallet provided by Privy.
  • Prepare approval and deposit calldata with the Amplify helpers.
  • Submit both transactions with sendTransaction.
  • Track optimistic UI state and surface errors.

Prerequisites

  • React 18+
  • @privy-io/react-auth >= 3
  • @paxoslabs/amplify-sdk (initialized via initAmplifySDK)
  • viem >= 2.0.0 for calldata encoding and allowance checks
  • Privy provider configured at the app root (<PrivyProvider appId="...">)

Deposit Hook

import { useCallback, useMemo, useState } from "react";
import { encodeFunctionData } from "viem";
import {
  isDepositSpendApproved,
  prepareApproveDepositTokenTxData,
  prepareDepositTxData,
  type YieldType,
} from "@paxoslabs/amplify-sdk";
import { usePrivy, useWallets } from "@privy-io/react-auth";

interface DepositParams {
  yieldType: YieldType;
  depositToken: `0x${string}`;
  recipientAddress: `0x${string}`;
  depositAmount: string;
  chainId: number;
  slippage?: number;
}

export function usePrivyDeposit() {
  const { sendTransaction } = usePrivy();
  const { wallets } = useWallets();
  const wallet = useMemo(() => wallets.find(Boolean), [wallets]);

  const [txHash, setTxHash] = useState<`0x${string}` | null>(null);
  const [status, setStatus] = useState<
    "idle" | "checking" | "approving" | "depositing" | "success"
  >("idle");
  const [error, setError] = useState<Error | null>(null);

  const run = useCallback(
    async ({
      yieldType,
      depositToken,
      recipientAddress,
      depositAmount,
      chainId,
      slippage = 100,
    }: DepositParams) => {
      if (!wallet) {
        throw new Error("Connect a Privy wallet before depositing.");
      }

      setStatus("checking");
      setError(null);
      setTxHash(null);

      try {
        const approved = await isDepositSpendApproved({
          yieldType,
          depositToken,
          recipientAddress,
          chainId,
        });

        if (!approved) {
          setStatus("approving");
          const approval = await prepareApproveDepositTokenTxData({
            yieldType,
            depositToken,
            depositAmount,
            chainId,
          });

          await sendTransaction({
            chainId: approval.chainId,
            to: approval.address,
            data: encodeFunctionData({
              abi: approval.abi,
              functionName: approval.functionName,
              args: approval.args,
            }),
          });
        }

        setStatus("depositing");
        const deposit = await prepareDepositTxData({
          yieldType,
          recipientAddress,
          depositToken,
          depositAmount,
          chainId,
          slippage,
        });

        const hash = await sendTransaction({
          chainId: deposit.chainId,
          to: deposit.address,
          data: encodeFunctionData({
            abi: deposit.abi,
            functionName: deposit.functionName,
            args: deposit.args,
          }),
        });

        setTxHash(hash as `0x${string}`);
        setStatus("success");
      } catch (err) {
        const message = err instanceof Error ? err : new Error(String(err));
        setError(message);
        setStatus("idle");
        throw err;
      }
    },
    [wallet, sendTransaction]
  );

  return { run, status, error, txHash };
}

Component Usage

import { usePrivyDeposit } from "./usePrivyDeposit";
import { useWallets } from "@privy-io/react-auth";
import { useMemo } from "react";
import { YieldType } from "@paxoslabs/amplify-sdk";

const CORE_DEPOSIT_TOKEN = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // USDC

export function PrivyDepositButton() {
  const { wallets } = useWallets();
  const wallet = useMemo(() => wallets.find(Boolean), [wallets]);
  const { run, status, txHash, error } = usePrivyDeposit();

  if (!wallet) {
    return <p>Connect with Privy to deposit.</p>;
  }

  async function handleClick() {
    await run({
      yieldType: YieldType.CORE,
      depositToken: CORE_DEPOSIT_TOKEN,
      recipientAddress: wallet.address as `0x${string}`,
      depositAmount: "100",
      chainId: 1,
    });
  }

  return (
    <section>
      <button
        onClick={handleClick}
        disabled={status !== "idle" && status !== "success"}
      >
        {status === "checking" && "Checking allowance…"}
        {status === "approving" && "Approving…"}
        {status === "depositing" && "Depositing…"}
        {status === "success" && "Deposit Again"}
        {(status === "idle" || status === "success") && "Deposit with Privy"}
      </button>

      {txHash && (
        <p>
          Deposit sent:{" "}
          <a
            href={`https://etherscan.io/tx/${txHash}`}
            target="_blank"
            rel="noreferrer"
          >
            {txHash}
          </a>
        </p>
      )}

      {error && <p role="alert">Deposit failed: {error.message}</p>}
    </section>
  );
}
sendTransaction resolves with the transaction hash. Add receipt polling, analytics, and user notifications to fit your UX requirements.