Skip to main content
The Unified Deposit API simplifies deposits by automatically detecting the optimal authorization method. Use prepareDepositAuthorization() to determine the method, then prepareDepositTxData() or prepareDepositWithPermitTxData() to execute.

DepositAuthMethod

export const DepositAuthMethod = {
  PERMIT: "permit",           // EIP-2612 off-chain signature (gas efficient)
  APPROVAL: "approval",       // Traditional ERC20 approval (two transactions)
  ALREADY_APPROVED: "already_approved", // Existing allowance sufficient
} as const;

export type DepositAuthMethod =
  (typeof DepositAuthMethod)[keyof typeof DepositAuthMethod];

prepareDepositAuthorization()

Determines the optimal authorization method for a deposit operation.

Function Signature

export async function prepareDepositAuthorization(
  params: PrepareDepositAuthorizationParams
): Promise<DepositAuthorizationResult>;

Parameters

export interface PrepareDepositAuthorizationParams {
  /** Yield strategy type (e.g., YieldType.CORE) */
  yieldType: YieldType;

  /** Token contract address to deposit */
  depositToken: Address;

  /** Amount of assets to deposit as decimal string (e.g., "100.25") */
  depositAmount: string;

  /** User's wallet address (permit owner / approval sender) */
  recipientAddress: Address;

  /** Blockchain network ID */
  chainId: ChainId;

  /** Optional deadline for permit signature (defaults to 1 hour from now) */
  deadline?: bigint;

  /** Force specific authorization method (bypasses automatic detection) */
  forceMethod?: "permit" | "approval";
}

Return Type (Discriminated Union)

export type DepositAuthorizationResult =
  | PermitAuthorizationResult
  | ApprovalAuthorizationResult
  | AlreadyApprovedAuthorizationResult;

/** Returned when token supports EIP-2612 permit */
export interface PermitAuthorizationResult {
  method: DepositAuthMethod.PERMIT;
  /** EIP-712 typed data ready for wallet signing */
  permitData: PermitSignatureData;
}

/** Returned when an ERC20 approval transaction is needed */
export interface ApprovalAuthorizationResult {
  method: DepositAuthMethod.APPROVAL;
  /** Transaction data for ERC20 approve() call */
  txData: ApproveDepositTokenTxData;
  /** Current allowance (human-readable) */
  currentAllowance: string;
  /** Current allowance as BigInt string */
  currentAllowanceAsBigInt: string;
}

/** Returned when existing allowance is sufficient */
export interface AlreadyApprovedAuthorizationResult {
  method: DepositAuthMethod.ALREADY_APPROVED;
  /** Current allowance (human-readable) */
  allowance: string;
  /** Current allowance as BigInt string */
  allowanceAsBigInt: string;
}

Decision Logic

The function follows this priority order:
  1. Check forceMethod - If specified, use that method
  2. Check permit support - If token is in permit allowlist, return permit data
  3. Check existing allowance - If sufficient, return ALREADY_APPROVED
  4. Default to approval - Return approval transaction data

prepareDepositTxData()

Prepares transaction data for a standard deposit (after approval).

Function Signature

export async function prepareDepositTxData(
  params: PrepareDepositParams
): Promise<DepositTxData>;

Parameters

export interface PrepareDepositParams {
  /** Yield strategy type (e.g., YieldType.PRIME) */
  yieldType: YieldType;

  /** Token contract address to deposit */
  depositToken: Address;

  /** Amount of assets to deposit as decimal string (e.g., "100.25") */
  depositAmount: string;

  /** Recipient address for vault shares */
  recipientAddress: Address;

  /** Blockchain network ID */
  chainId: ChainId;

  /** Optional slippage in basis points (default: 50 = 0.5%) */
  slippage?: number;

  /** Optional partner code for fee attribution */
  partnerCode?: string;
}

Return Type

export interface DepositTxData {
  abi: Abi;
  address: Address;
  functionName: string;
  args: unknown[];
  chainId: number;
}

prepareDepositWithPermitTxData()

Prepares transaction data for a permit-based deposit (single transaction).

Function Signature

export async function prepareDepositWithPermitTxData(
  params: PrepareDepositWithPermitParams
): Promise<PermitDepositTxData>;

Parameters

export interface PrepareDepositWithPermitParams {
  /** Yield strategy type (e.g., YieldType.PRIME) */
  yieldType: YieldType;

  /** Token contract address to deposit */
  depositToken: Address;

  /** Amount of assets to deposit as decimal string (e.g., "100.25") */
  depositAmount: string;

  /** Recipient address for vault shares */
  recipientAddress: Address;

  /** Blockchain network ID */
  chainId: ChainId;

  /** Permit signature (hex string from wallet signing) */
  signature: Hex;

  /** Permit deadline (must match signature deadline) */
  deadline: bigint;

  /** Optional slippage in basis points (default: 50 = 0.5%) */
  slippage?: number;

  /** Optional partner code for fee attribution */
  partnerCode?: string;
}

Return Type

export interface PermitDepositTxData {
  address: Address;
  chainId: number;
  data: {
    abi: Abi;
    functionName: string;
    args: unknown[];
  };
}

Complete Flow Examples

React with Privy

import { encodeFunctionData } from "viem";
import { mainnet } from "viem/chains";
import {
  initAmplifySDK,
  prepareDepositAuthorization,
  prepareDepositTxData,
  prepareDepositWithPermitTxData,
  DepositAuthMethod,
  YieldType,
} from "@paxoslabs/amplify-sdk";
import { usePrivy, useWallets } from "@privy-io/react-auth";

// Initialize SDK (call once at app startup)
await initAmplifySDK("pxl_your_api_key");

const { sendTransaction } = usePrivy();
const { wallets } = useWallets();
const wallet = wallets[0];
if (!wallet) {
  throw new Error("Connect a wallet before preparing transactions.");
}

const owner = wallet.address as `0x${string}`;

const params = {
  yieldType: YieldType.PRIME,
  depositToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as `0x${string}`, // USDC
  depositAmount: "1000",
  recipientAddress: owner,
  chainId: mainnet.id,
};

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

// Step 2: Handle based on method
switch (auth.method) {
  case DepositAuthMethod.PERMIT: {
    // Sign permit off-chain
    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}`;

    // Deposit with permit (single transaction)
    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: {
    // Send approval transaction first
    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,
      }),
    });

    // Then 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: {
    // Deposit directly
    const directTx = await prepareDepositTxData(params);
    const { abi, functionName, args } = directTx;
    await sendTransaction({
      chainId: directTx.chainId,
      to: directTx.address,
      data: encodeFunctionData({ abi, functionName, args }),
    });
    break;
  }
}

React with wagmi

import {
  useAccount,
  useWriteContract,
  useSignTypedData,
} from "wagmi";
import { encodeFunctionData } from "viem";
import {
  initAmplifySDK,
  prepareDepositAuthorization,
  prepareDepositTxData,
  prepareDepositWithPermitTxData,
  DepositAuthMethod,
  YieldType,
} from "@paxoslabs/amplify-sdk";

// Initialize SDK
await initAmplifySDK("pxl_your_api_key");

const { address, chainId } = useAccount();
const { writeContractAsync } = useWriteContract();
const { signTypedDataAsync } = useSignTypedData();

if (!address || !chainId) {
  throw new Error("Connect a wallet before preparing transactions.");
}

const params = {
  yieldType: YieldType.PRIME,
  depositToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as `0x${string}`,
  depositAmount: "1000",
  recipientAddress: address,
  chainId,
};

const auth = await prepareDepositAuthorization(params);

switch (auth.method) {
  case DepositAuthMethod.PERMIT: {
    const signature = await signTypedDataAsync(auth.permitData);
    const permitTx = await prepareDepositWithPermitTxData({
      ...params,
      signature,
      deadline: BigInt(auth.permitData.message.deadline),
    });
    await writeContractAsync({
      address: permitTx.address,
      abi: permitTx.data.abi,
      functionName: permitTx.data.functionName,
      args: permitTx.data.args,
      account: address,
    });
    break;
  }

  case DepositAuthMethod.APPROVAL: {
    await writeContractAsync({ ...auth.txData, account: address });
    const depositTx = await prepareDepositTxData(params);
    await writeContractAsync({ ...depositTx, account: address });
    break;
  }

  case DepositAuthMethod.ALREADY_APPROVED: {
    const directTx = await prepareDepositTxData(params);
    await writeContractAsync({ ...directTx, account: address });
    break;
  }
}

React with viem

import { createWalletClient, custom } from "viem";
import { mainnet } from "viem/chains";
import {
  initAmplifySDK,
  prepareDepositAuthorization,
  prepareDepositTxData,
  prepareDepositWithPermitTxData,
  DepositAuthMethod,
  YieldType,
} from "@paxoslabs/amplify-sdk";

// Initialize SDK
await initAmplifySDK("pxl_your_api_key");

const [account] = (await ethereum.request({
  method: "eth_requestAccounts",
})) as `0x${string}`[];

const client = createWalletClient({
  account,
  chain: mainnet,
  transport: custom(ethereum),
});

const params = {
  yieldType: YieldType.PRIME,
  depositToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as `0x${string}`,
  depositAmount: "1000",
  recipientAddress: account,
  chainId: mainnet.id,
};

const auth = await prepareDepositAuthorization(params);

switch (auth.method) {
  case DepositAuthMethod.PERMIT: {
    const signature = await client.signTypedData({
      account,
      ...auth.permitData,
    });
    const permitTx = await prepareDepositWithPermitTxData({
      ...params,
      signature,
      deadline: BigInt(auth.permitData.message.deadline),
    });
    await client.writeContract({
      address: permitTx.address,
      abi: permitTx.data.abi,
      functionName: permitTx.data.functionName,
      args: permitTx.data.args,
      account,
    });
    break;
  }

  case DepositAuthMethod.APPROVAL: {
    await client.writeContract({ ...auth.txData, account });
    const depositTx = await prepareDepositTxData(params);
    await client.writeContract({ ...depositTx, account });
    break;
  }

  case DepositAuthMethod.ALREADY_APPROVED: {
    const directTx = await prepareDepositTxData(params);
    await client.writeContract({ ...directTx, account });
    break;
  }
}

Force Specific Method

Override automatic detection when needed:
// Force permit even if automatic detection would choose approval
const authForcePermit = await prepareDepositAuthorization({
  ...params,
  forceMethod: "permit",
});
// Always returns DepositAuthMethod.PERMIT

// Force approval even for permit-supporting tokens
const authForceApproval = await prepareDepositAuthorization({
  ...params,
  forceMethod: "approval",
});
// Returns APPROVAL or ALREADY_APPROVED

Slippage Configuration

The default slippage is 50 basis points (0.5%). Override when needed:
const depositResult = await prepareDeposit({
  ...params,
  slippage: 100, // 1% slippage
});

Error Handling

Both helpers throw APIError with actionable metadata:
try {
  const auth = await prepareDepositAuthorization(params);
} catch (error) {
  if (error.code === "VAULT_NOT_FOUND") {
    console.error("No vault matches parameters");
  }
  if (error.code === "PERMIT_NOT_SUPPORTED") {
    // Fall back to approval flow
  }
}
Error CodeDescriptionResolution
SDK_NOT_INITIALIZEDSDK used before initCall initAmplifySDK() first
VAULT_NOT_FOUNDNo vault matches paramsVerify yieldType, token, chainId
PERMIT_NOT_SUPPORTEDToken doesn’t support EIP-2612Use approval flow instead
INSUFFICIENT_ALLOWANCEApproval amount too lowIncrease approval or use permit
For withdrawal operations, see Withdraw.