Skip to main content
This guide walks you through depositing tokens into an Amplify vault by calling the DistributorCodeDepositor smart contract directly. It covers every contract method you need to call, in order, for both the standard approval flow and the single-transaction permit flow.

How Deposits Work

When you deposit tokens (e.g., USDC) into a vault, you receive vault shares in return. These shares represent your proportional ownership of the vault’s assets and appreciate in value as the vault earns yield. There are two ways to deposit:
FlowTransactionsBest For
Standard (Approve + Deposit)2 transactionsAll tokens, all wallets
Permit (Sign + Deposit)1 transactionSupported tokens only (USDC, USDG, pyUSD, USD₮0)

What You’ll Need

RequirementDescription
Contract addressesdistributorCodeDepositorAddress and accountantAddress — see Vault Discovery
ABIProvided below — just copy into your project
RPC endpointAn Ethereum node URL (Alchemy, Infura, QuickNode, etc.)
Private key or walletTo sign and send transactions
Deposit tokensThe token you’re depositing (e.g., USDC) + ETH for gas

ABI Reference

You need three ABI snippets. Copy these into your project as a JSON file or inline constant.

DistributorCodeDepositor ABI

[
  {
    "inputs": [
      { "name": "depositAsset", "type": "address" },
      { "name": "depositAmount", "type": "uint256" },
      { "name": "minimumMint", "type": "uint256" },
      { "name": "to", "type": "address" },
      { "name": "distributorCode", "type": "bytes" }
    ],
    "name": "deposit",
    "outputs": [{ "name": "shares", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "depositAsset", "type": "address" },
      { "name": "depositAmount", "type": "uint256" },
      { "name": "minimumMint", "type": "uint256" },
      { "name": "to", "type": "address" },
      { "name": "distributorCode", "type": "bytes" },
      { "name": "deadline", "type": "uint256" },
      { "name": "v", "type": "uint8" },
      { "name": "r", "type": "bytes32" },
      { "name": "s", "type": "bytes32" }
    ],
    "name": "depositWithPermit",
    "outputs": [{ "name": "shares", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

ERC-20 ABI (for approvals and allowance checks)

[
  {
    "inputs": [
      { "name": "spender", "type": "address" },
      { "name": "amount", "type": "uint256" }
    ],
    "name": "approve",
    "outputs": [{ "name": "", "type": "bool" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "owner", "type": "address" },
      { "name": "spender", "type": "address" }
    ],
    "name": "allowance",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "owner", "type": "address" }],
    "name": "nonces",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  }
]

Accountant ABI (for slippage calculation)

[
  {
    "inputs": [],
    "name": "getRateInQuoteSafe",
    "outputs": [{ "name": "rate", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  }
]

Contract Method Reference

deposit(depositAsset, depositAmount, minimumMint, to, distributorCode)

ParameterTypeDescription
depositAssetaddressThe ERC-20 token address to deposit (e.g., USDC: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)
depositAmountuint256Amount in the token’s smallest unit. USDC has 6 decimals, so 1000000 = 1 USDC.
minimumMintuint256Minimum shares to receive — protects against slippage. Set to 0 to accept any amount.
toaddressAddress that receives the minted vault shares (usually the depositor)
distributorCodebytesTracking code for referrals/attribution. Pass 0x (empty bytes) if not applicable.
Important: depositAmount is in the token’s smallest unit. USDC has 6 decimals, so 1,000 USDC = 1000000000 (1000 × 10^6). Getting this wrong is the most common integration mistake.

depositWithPermit(depositAsset, depositAmount, minimumMint, to, distributorCode, deadline, v, r, s)

Same parameters as deposit(), plus:
ParameterTypeDescription
deadlineuint256Unix timestamp after which the permit expires
vuint8Recovery byte of the EIP-2612 permit signature
rbytes32First 32 bytes of the permit signature
sbytes32Second 32 bytes of the permit signature

Distributor Codes

The distributorCode parameter is used for referral attribution. If Paxos Labs has provided you with a distributor code, encode it as bytes (e.g., toHex('your_code') in JavaScript, b'your_code' in Python, []byte("your_code") in Go). If you don’t have one, pass empty bytes (0x, b"", []byte{}).

Calculating minimumMint (Slippage Protection)

The vault’s Accountant contract publishes the exchange rate between the deposit token and vault shares. Use it to calculate a safe minimumMint.

Contract call

Accountant.getRateInQuoteSafe() → uint256 rate
rate represents the amount of the deposit asset per 1e18 vault shares (i.e., per one full share).

Calculation

expectedShares = (depositAmount × 1e18) / rate
minimumMint    = expectedShares − (expectedShares × SLIPPAGE_BPS) / 10000
Where SLIPPAGE_BPS is your slippage tolerance in basis points (e.g., 50 = 0.5%).
Setting minimumMint to 0 disables slippage protection entirely. This is fine for testing but not recommended for production — a front-running bot could manipulate the rate between your transaction submission and execution.

Standard Deposit Walkthrough

The standard flow requires two transactions: an ERC-20 approval followed by the deposit.
1

Check existing allowance (read)

Call the deposit token’s allowance() to see if the DistributorCodeDepositor already has sufficient spending permission.
DepositToken.allowance(yourAddress, distributorCodeDepositorAddress) → uint256
If the returned value is ≥ your depositAmount, skip to Step 3.
2

Approve the DistributorCodeDepositor (transaction)

Call approve() on the deposit token, granting the DistributorCodeDepositor permission to transfer your tokens.
DepositToken.approve(distributorCodeDepositorAddress, depositAmount) → bool
Wait for the transaction to be mined before proceeding.
USDT special case: USDT requires resetting the allowance to 0 before setting a new value if there’s an existing non-zero allowance. Call approve(spender, 0) first, then approve(spender, amount).
3

Calculate minimumMint (read)

Query the exchange rate and compute slippage protection.
Accountant.getRateInQuoteSafe() → uint256 rate
Then calculate: minimumMint = ((depositAmount × 1e18) / rate) × (10000 − SLIPPAGE_BPS) / 10000
4

Execute the deposit (transaction)

Call deposit() on the DistributorCodeDepositor.
DistributorCodeDepositor.deposit(
  depositAsset,      // e.g., USDC address
  depositAmount,     // e.g., 1000000000 (1,000 USDC)
  minimumMint,       // from Step 3
  to,                // recipient of vault shares (usually your address)
  distributorCode    // 0x if none
) → uint256 shares
The return value is the number of vault shares minted.
5

Confirm the transaction

Wait for the transaction receipt. The deposit is complete once the transaction is included in a block. You can verify by calling BoringVault.balanceOf(yourAddress) to see your new share balance.

Example values (1,000 USDC deposit on Ethereum mainnet)

ParameterValue
depositAsset0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
depositAmount1000000000 (1,000 × 10^6)
minimumMintCalculated from Accountant rate
toYour wallet address
distributorCode0x (empty)

Permit Deposit Walkthrough

For tokens that support EIP-2612 permits, you can combine approval and deposit into a single transaction.
TokenPermit Support
USDCYes
USDGYes
pyUSDYes
USD₮0Yes
USDTNo — use standard approve + deposit
1

Get the permit nonce (read)

Query the token’s current nonce for your address.
DepositToken.nonces(yourAddress) → uint256 nonce
2

Sign the EIP-712 permit message (off-chain)

Construct and sign an EIP-712 typed data message. This is an off-chain signature — no gas required.EIP-712 Domain (varies by token — this example is for USDC on Ethereum):
FieldValue
name"USD Coin"
version"2"
chainId1
verifyingContractUSDC token address
Permit message:
FieldValue
ownerYour wallet address
spenderdistributorCodeDepositorAddress
valuedepositAmount
nonceFrom Step 1
deadlineUnix timestamp (e.g., now + 1 hour)
The EIP-712 type structure:
Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline)
Sign using your wallet’s signTypedData (or equivalent EIP-712 signing method). Parse the resulting signature into v, r, s components.
3

Calculate minimumMint (read)

Same as the standard flow:
Accountant.getRateInQuoteSafe() → uint256 rate
4

Execute depositWithPermit (transaction)

Call depositWithPermit() on the DistributorCodeDepositor with the permit signature.
DistributorCodeDepositor.depositWithPermit(
  depositAsset,      // e.g., USDC address
  depositAmount,     // e.g., 1000000000 (1,000 USDC)
  minimumMint,       // from Step 3
  to,                // recipient of vault shares
  distributorCode,   // 0x if none
  deadline,          // from Step 2
  v,                 // signature recovery byte
  r,                 // signature first 32 bytes
  s                  // signature second 32 bytes
) → uint256 shares
The contract verifies the permit signature on-chain, transfers your tokens, and mints vault shares — all in a single transaction.
Smart contract wallets (like Privy Smart Wallets or Safe) cannot sign permits because they don’t have a private key. Use the standard approval flow instead.

Troubleshooting

The most common cause is that minimumMint is set too high relative to the current exchange rate. Set it to 0 for testing or recalculate from the Accountant’s getRateInQuoteSafe().
The permit signature is invalid and there’s no existing ERC-20 approval. Double-check the permit domain parameters (name, version, verifyingContract) match the token’s EIP-712 domain. Or switch to the standard approve + deposit flow.
You haven’t approved the DistributorCodeDepositor to spend your tokens. Call approve() on the token contract before calling deposit().
Your wallet doesn’t hold enough of the deposit token. Check your balance before depositing.
USDT requires resetting the allowance to 0 before setting a new value if there’s an existing non-zero allowance. Call approve(spender, 0) first, then approve(spender, amount). Other tokens (USDC, USDG, pyUSD) allow overwriting an existing approval directly.

Next Steps