> ## Documentation Index
> Fetch the complete documentation index at: https://developers.paxoslabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deposits

> Complete deposit integration walkthrough using the Paxos Labs REST API calldata endpoints

This guide walks through the complete deposit flow using REST API calls: discover an account, detect the authorization method, prepare calldata, sign, and submit the transaction.

The unified deposit API handles three authorization paths automatically:

| Method               | Description                                                                                     |
| -------------------- | ----------------------------------------------------------------------------------------------- |
| **Permit**           | Gas-efficient off-chain signature (EIP-2612). Single transaction — no separate approval needed. |
| **Approval**         | Standard ERC-20 `approve` transaction followed by the deposit transaction.                      |
| **Already Approved** | Direct deposit when sufficient allowance already exists.                                        |

## Prerequisites

* A Paxos Labs API key
* An EVM-compatible signer (private key, HSM, or wallet service)
* An HTTP client library for your language

## Step 0: Fetch Available Accounts

Retrieve all accounts accessible with your API key. Accounts are grouped by `name`, with a `deployments[]` array per chain. Record `boringVaultAddress`, `chainId`, and `baseTokenAddress` for the deployment you want to deposit into.

```bash theme={null}
curl "https://api.paxoslabs.com/v2/amplify/vaults?filter=chainId%3D1%20AND%20inDeprecation%3Dfalse" \
  -H "x-api-key: pxl_your_key"
```

From the response, pick an account and note these fields from the matching `deployments[i]` entry:

| Field                | Usage                                                                                  |
| -------------------- | -------------------------------------------------------------------------------------- |
| `boringVaultAddress` | Passed to `GET /v2/core/authorization` and `GET /v2/amplify/deposit` as `vaultAddress` |
| `chainId`            | Passed to all endpoints                                                                |
| `baseTokenAddress`   | The primary deposit asset address                                                      |

## Step 1: Check Authorization

`GET /v2/core/authorization` detects whether the deposit token supports EIP-2612 permits, requires a standard ERC-20 approval, or already has sufficient allowance.

### Parameters

| Parameter      | Type     | Required | Description                                                        |
| -------------- | -------- | -------- | ------------------------------------------------------------------ |
| `vaultAddress` | `string` | Yes      | BoringVault contract address (`boringVaultAddress` from discovery) |
| `tokenAddress` | `string` | Yes      | ERC-20 deposit token address                                       |
| `amount`       | `string` | Yes      | Deposit amount in token base units (decimal string)                |
| `userAddress`  | `string` | Yes      | Depositor's wallet address                                         |
| `chainId`      | `number` | Yes      | EVM chain ID                                                       |

### Response Variants

The response `method` field tells you which path to follow:

**`permit`** — Gas-efficient off-chain signature (EIP-2612). Single transaction, no separate approval needed.

```json theme={null}
{
  "method": "permit",
  "permitData": {
    "domain": {
      "name": "USD Coin",
      "version": "2",
      "chainId": 1,
      "verifyingContract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
    },
    "types": { "Permit": [...] },
    "value": {
      "owner": "0x1234...",
      "spender": "0xcccc...",
      "value": "1000000",
      "nonce": "0",
      "deadline": "9999999999"
    },
    "deadline": "9999999999"
  }
}
```

**`approval`** — Standard ERC-20 `approve()` required before depositing.

```json theme={null}
{
  "method": "approval",
  "approvalTransaction": {
    "encoded": "0x095ea7b3000000000000000000000000..."
  }
}
```

**`already_approved`** — Sufficient allowance exists. Skip directly to the deposit.

```json theme={null}
{
  "method": "already_approved"
}
```

## Step 2: Handle Authorization

### Permit Path

Sign the EIP-712 typed data from `permitData` using `eth_signTypedData_v4` (or your library's equivalent), then include the signature in the deposit request.

### Approval Path

Send the `approvalTransaction.encoded` calldata as a transaction to the deposit token contract address (`tokenAddress` from step 1). Wait for confirmation, then proceed to the deposit.

### Already Approved Path

Skip directly to step 3.

## Step 3: Prepare Deposit Calldata

`GET /v2/amplify/deposit` returns the transaction object for the deposit.

### Parameters

| Parameter         | Type     | Required | Description                                                                                                                                                              |
| ----------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `vaultAddress`    | `string` | Yes      | BoringVault contract address (`0x` + 40 hex chars)                                                                                                                       |
| `depositAsset`    | `string` | Yes      | ERC-20 token address to deposit                                                                                                                                          |
| `depositAmount`   | `string` | Yes      | Amount in token base units (decimal string)                                                                                                                              |
| `userAddress`     | `string` | Yes      | Wallet address signing and submitting the transaction. Also the default share recipient when `to` is omitted.                                                            |
| `chainId`         | `number` | Yes      | EVM chain ID                                                                                                                                                             |
| `to`              | `string` | No       | Destination address that receives the vault shares (maps to the on-chain `to` argument on `DistributorCodeDepositor.deposit()`). Defaults to `userAddress` when omitted. |
| `permitSignature` | `string` | No       | EIP-2612 permit signature (65-byte hex). Required for permit path.                                                                                                       |
| `permitDeadline`  | `number` | No       | Permit deadline as Unix timestamp. Required when `permitSignature` is provided.                                                                                          |
| `responseFormat`  | `string` | No       | `encoded` (default), `full`, or `structured`                                                                                                                             |

### Response

```json theme={null}
{
  "transaction": {
    "to": "0xcccc000000000000000000000000000000000001",
    "data": "0x47e7ef24000000000000000000000000...",
    "value": "0",
    "abi": [{"type": "function", "name": "deposit", "inputs": [...]}],
    "functionName": "deposit",
    "args": ["0xA0b8...", "1000000", "999500", "0x1234..."]
  }
}
```

<Info>
  The `abi`, `functionName`, and `args` fields are only present when
  `responseFormat` is `full` or `structured`. See
  [Authentication](/v1.0.0/intro/products/earn/developers/guides/api-calldata/authentication)
  for details.
</Info>

## Step 4: Sign and Submit

Send the transaction using your signer or wallet infrastructure:

* **`to`** — the contract address to call
* **`data`** — the ABI-encoded calldata (when using `encoded` or `full` format)
* **`value`** — ETH to send (usually `"0"` for ERC-20 deposits)

## Complete Examples

<Tabs>
  <Tab title="Node.js">
    ```ts theme={null}
    import { createWalletClient, createPublicClient, http, custom } from "viem";
    import { mainnet } from "viem/chains";
    import { privateKeyToAccount } from "viem/accounts";

    const API_KEY = process.env.AMPLIFY_API_KEY!;
    const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
    const BASE = "https://api.paxoslabs.com";
    const HEADERS = { "x-api-key": API_KEY };

    const VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001";
    const DEPOSIT_ASSET = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
    const CHAIN_ID = 1;
    const AMOUNT = "1000000"; // 1 USDC (6 decimals)

    async function main() {
      const account = privateKeyToAccount(PRIVATE_KEY);
      const walletClient = createWalletClient({
        account,
        chain: mainnet,
        transport: http(),
      });
      const publicClient = createPublicClient({
        chain: mainnet,
        transport: http(),
      });

      // Step 1: Check authorization
      console.log("Checking authorization method...");
      const permitUrl = new URL(`${BASE}/v2/core/authorization`);
      permitUrl.searchParams.set("vaultAddress", VAULT_ADDRESS);
      permitUrl.searchParams.set("tokenAddress", DEPOSIT_ASSET);
      permitUrl.searchParams.set("amount", AMOUNT);
      permitUrl.searchParams.set("userAddress", account.address);
      permitUrl.searchParams.set("chainId", String(CHAIN_ID));

      const permitResp = await fetch(permitUrl, { headers: HEADERS }).then((r) =>
        r.json()
      );
      console.log(`Authorization method: ${permitResp.method}`);

      // Step 2: Build deposit request params
      const depositUrl = new URL(`${BASE}/v2/amplify/deposit`);
      depositUrl.searchParams.set("vaultAddress", VAULT_ADDRESS);
      depositUrl.searchParams.set("depositAsset", DEPOSIT_ASSET);
      depositUrl.searchParams.set("depositAmount", AMOUNT);
      depositUrl.searchParams.set("userAddress", account.address);
      depositUrl.searchParams.set("chainId", String(CHAIN_ID));

      if (permitResp.method === "permit") {
        const { domain, types, value } = permitResp.permitData;
        const signature = await walletClient.signTypedData({
          account,
          domain,
          types,
          primaryType: "Permit",
          message: value,
        });
        depositUrl.searchParams.set("permitSignature", signature);
        depositUrl.searchParams.set(
          "permitDeadline",
          permitResp.permitData.deadline
        );
      } else if (permitResp.method === "approval") {
        console.log("Sending approval transaction...");
        const approvalHash = await walletClient.sendTransaction({
          to: DEPOSIT_ASSET as `0x${string}`,
          data: permitResp.approvalTransaction.encoded as `0x${string}`,
          chain: mainnet,
          account,
        });
        await publicClient.waitForTransactionReceipt({ hash: approvalHash });
        console.log(`Approval confirmed: ${approvalHash}`);
      }

      // Step 3: Get deposit calldata
      console.log("Fetching deposit calldata...");
      const depositResp = await fetch(depositUrl, { headers: HEADERS }).then(
        (r) => r.json()
      );
      const tx = depositResp.transaction;

      // Step 4: Sign and submit
      console.log("Submitting deposit transaction...");
      const hash = await walletClient.sendTransaction({
        to: tx.to as `0x${string}`,
        data: tx.data as `0x${string}`,
        value: BigInt(tx.value),
        chain: mainnet,
        account,
      });
      const receipt = await publicClient.waitForTransactionReceipt({ hash });
      console.log(`Deposit confirmed in block ${receipt.blockNumber}: ${hash}`);
    }

    main().catch(console.error);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import requests
    from web3 import Web3

    API_KEY = os.environ["AMPLIFY_API_KEY"]
    PRIVATE_KEY = os.environ["PRIVATE_KEY"]
    BASE = "https://api.paxoslabs.com"
    HEADERS = {"x-api-key": API_KEY}

    VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001"
    DEPOSIT_ASSET = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
    CHAIN_ID = 1
    AMOUNT = "1000000"  # 1 USDC (6 decimals)

    w3 = Web3(Web3.HTTPProvider("https://eth.llamarpc.com"))
    account = w3.eth.account.from_key(PRIVATE_KEY)

    # Step 1: Check authorization
    print("Checking authorization method...")
    permit_resp = requests.get(f"{BASE}/v2/core/authorization", headers=HEADERS, params={
        "vaultAddress": VAULT_ADDRESS,
        "tokenAddress": DEPOSIT_ASSET,
        "amount": AMOUNT,
        "userAddress": account.address,
        "chainId": CHAIN_ID,
    }).json()
    print(f"Authorization method: {permit_resp['method']}")

    # Step 2: Handle authorization
    deposit_params = {
        "vaultAddress": VAULT_ADDRESS,
        "depositAsset": DEPOSIT_ASSET,
        "depositAmount": AMOUNT,
        "userAddress": account.address,
        "chainId": CHAIN_ID,
    }

    if permit_resp["method"] == "permit":
        sig = w3.eth.account.sign_typed_data(
            PRIVATE_KEY,
            permit_resp["permitData"]["domain"],
            permit_resp["permitData"]["types"],
            permit_resp["permitData"]["value"],
        )
        deposit_params["permitSignature"] = sig.signature.hex()
        deposit_params["permitDeadline"] = permit_resp["permitData"]["deadline"]

    elif permit_resp["method"] == "approval":
        print("Sending approval transaction...")
        approval_tx = {
            "to": Web3.to_checksum_address(DEPOSIT_ASSET),
            "data": permit_resp["approvalTransaction"]["encoded"],
            "gas": 60_000,
            "nonce": w3.eth.get_transaction_count(account.address),
            "chainId": CHAIN_ID,
        }
        signed = account.sign_transaction(approval_tx)
        tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
        w3.eth.wait_for_transaction_receipt(tx_hash)
        print(f"Approval confirmed: {tx_hash.hex()}")

    # Step 3: Get deposit calldata
    print("Fetching deposit calldata...")
    deposit_resp = requests.get(
        f"{BASE}/v2/amplify/deposit", headers=HEADERS, params=deposit_params
    ).json()
    tx = deposit_resp["transaction"]

    # Step 4: Sign and submit
    print("Submitting deposit transaction...")
    deposit_tx = {
        "to": Web3.to_checksum_address(tx["to"]),
        "data": tx["data"],
        "value": int(tx["value"]),
        "gas": 300_000,
        "nonce": w3.eth.get_transaction_count(account.address),
        "chainId": CHAIN_ID,
    }
    signed = account.sign_transaction(deposit_tx)
    tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f"Deposit confirmed in block {receipt['blockNumber']}: {tx_hash.hex()}")
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "math/big"
        "net/http"
        "net/url"
        "os"

        "github.com/ethereum/go-ethereum/common"
        "github.com/ethereum/go-ethereum/core/types"
        "github.com/ethereum/go-ethereum/crypto"
        "github.com/ethereum/go-ethereum/ethclient"
    )

    const (
        baseURL      = "https://api.paxoslabs.com"
        vaultAddress = "0xbbbb000000000000000000000000000000000001"
        depositAsset = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
        chainID      = 1
        amount       = "1000000"
    )

    type Transaction struct {
        To    string `json:"to"`
        Data  string `json:"data"`
        Value string `json:"value"`
    }

    func apiGet(path string, params url.Values) (map[string]interface{}, error) {
        apiKey := os.Getenv("AMPLIFY_API_KEY")
        u := fmt.Sprintf("%s%s?%s", baseURL, path, params.Encode())
        req, _ := http.NewRequest("GET", u, nil)
        req.Header.Set("x-api-key", apiKey)
        resp, err := http.DefaultClient.Do(req)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        var result map[string]interface{}
        json.Unmarshal(body, &result)
        return result, nil
    }

    func main() {
        pk, _ := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
        fromAddr := crypto.PubkeyToAddress(pk.PublicKey)
        client, _ := ethclient.Dial("https://eth.llamarpc.com")
        ctx := context.Background()

        // Step 1: Check authorization
        fmt.Println("Checking authorization method...")
        permitResp, _ := apiGet("/v2/core/authorization", url.Values{
            "vaultAddress": {vaultAddress},
            "tokenAddress": {depositAsset},
            "amount":       {amount},
            "userAddress":  {fromAddr.Hex()},
            "chainId":      {fmt.Sprint(chainID)},
        })
        method := permitResp["method"].(string)
        fmt.Printf("Authorization method: %s\n", method)

        // Step 2: Handle authorization
        depositParams := url.Values{
            "vaultAddress":  {vaultAddress},
            "depositAsset":  {depositAsset},
            "depositAmount": {amount},
            "userAddress":   {fromAddr.Hex()},
            "chainId":       {fmt.Sprint(chainID)},
        }

        if method == "approval" {
            fmt.Println("Sending approval transaction...")
            approvalTx := permitResp["approvalTransaction"].(map[string]interface{})
            encoded := approvalTx["encoded"].(string)
            nonce, _ := client.PendingNonceAt(ctx, fromAddr)
            gasPrice, _ := client.SuggestGasPrice(ctx)
            tx := types.NewTransaction(
                nonce,
                common.HexToAddress(depositAsset),
                big.NewInt(0),
                60000,
                gasPrice,
                common.FromHex(encoded),
            )
            signer := types.NewEIP155Signer(big.NewInt(chainID))
            signedTx, _ := types.SignTx(tx, signer, pk)
            client.SendTransaction(ctx, signedTx)
            fmt.Printf("Approval submitted: %s\n", signedTx.Hash().Hex())
        }
        // For permit: sign EIP-712 typed data and add to depositParams

        // Step 3: Get deposit calldata
        fmt.Println("Fetching deposit calldata...")
        depositResp, _ := apiGet("/v2/amplify/deposit", depositParams)
        txData := depositResp["transaction"].(map[string]interface{})

        // Step 4: Sign and submit
        fmt.Println("Submitting deposit transaction...")
        nonce, _ := client.PendingNonceAt(ctx, fromAddr)
        gasPrice, _ := client.SuggestGasPrice(ctx)
        depositTx := types.NewTransaction(
            nonce,
            common.HexToAddress(txData["to"].(string)),
            big.NewInt(0),
            300000,
            gasPrice,
            common.FromHex(txData["data"].(string)),
        )
        signedDepositTx, _ := types.SignTx(
            depositTx, types.NewEIP155Signer(big.NewInt(chainID)), pk,
        )
        client.SendTransaction(ctx, signedDepositTx)
        fmt.Printf("Deposit submitted: %s\n", signedDepositTx.Hash().Hex())
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import org.web3j.crypto.Credentials;
    import org.web3j.crypto.RawTransaction;
    import org.web3j.crypto.TransactionEncoder;
    import org.web3j.protocol.Web3j;
    import org.web3j.protocol.http.HttpService;
    import org.web3j.utils.Numeric;

    import java.math.BigInteger;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParser;

    public class AmplifyDeposit {

        static final String BASE = "https://api.paxoslabs.com";
        static final String API_KEY = System.getenv("AMPLIFY_API_KEY");
        static final String VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001";
        static final String DEPOSIT_ASSET = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
        static final int CHAIN_ID = 1;
        static final String AMOUNT = "1000000";

        static HttpClient httpClient = HttpClient.newHttpClient();

        static JsonObject apiGet(String path) throws Exception {
            HttpRequest req = HttpRequest.newBuilder()
                .uri(URI.create(BASE + path))
                .header("x-api-key", API_KEY)
                .GET().build();
            HttpResponse<String> resp = httpClient.send(req,
                HttpResponse.BodyHandlers.ofString());
            return JsonParser.parseString(resp.body()).getAsJsonObject();
        }

        public static void main(String[] args) throws Exception {
            Credentials credentials = Credentials.create(System.getenv("PRIVATE_KEY"));
            String userAddress = credentials.getAddress();
            Web3j web3 = Web3j.build(new HttpService("https://eth.llamarpc.com"));

            // Step 1: Check authorization
            System.out.println("Checking authorization method...");
            String permitPath = String.format(
                "/v2/core/authorization?vaultAddress=%s&tokenAddress=%s&amount=%s&userAddress=%s&chainId=%d",
                VAULT_ADDRESS, DEPOSIT_ASSET, AMOUNT, userAddress, CHAIN_ID);
            JsonObject permitResp = apiGet(permitPath);
            String method = permitResp.get("method").getAsString();
            System.out.println("Authorization method: " + method);

            // Step 2: Handle approval if needed
            if ("approval".equals(method)) {
                System.out.println("Sending approval transaction...");
                String encoded = permitResp.getAsJsonObject("approvalTransaction")
                    .get("encoded").getAsString();
                BigInteger nonce = web3.ethGetTransactionCount(
                    userAddress,
                    org.web3j.protocol.core.DefaultBlockParameterName.PENDING
                ).send().getTransactionCount();
                BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();

                RawTransaction approvalTx = RawTransaction.createTransaction(
                    nonce, gasPrice, BigInteger.valueOf(60000),
                    DEPOSIT_ASSET, BigInteger.ZERO, encoded);
                byte[] signedApproval = TransactionEncoder.signMessage(
                    approvalTx, CHAIN_ID, credentials);
                web3.ethSendRawTransaction(Numeric.toHexString(signedApproval))
                    .send();
                System.out.println("Approval submitted");
            }

            // Step 3: Get deposit calldata
            System.out.println("Fetching deposit calldata...");
            String depositPath = String.format(
                "/v2/amplify/deposit?vaultAddress=%s&depositAsset=%s" +
                "&depositAmount=%s&userAddress=%s&chainId=%d",
                VAULT_ADDRESS, DEPOSIT_ASSET, AMOUNT, userAddress, CHAIN_ID);
            JsonObject depositResp = apiGet(depositPath);
            JsonObject tx = depositResp.getAsJsonObject("transaction");

            // Step 4: Sign and submit
            System.out.println("Submitting deposit transaction...");
            BigInteger nonce = web3.ethGetTransactionCount(
                userAddress,
                org.web3j.protocol.core.DefaultBlockParameterName.PENDING
            ).send().getTransactionCount();
            BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();

            RawTransaction depositTx = RawTransaction.createTransaction(
                nonce, gasPrice, BigInteger.valueOf(300000),
                tx.get("to").getAsString(), BigInteger.ZERO,
                tx.get("data").getAsString());
            byte[] signedDeposit = TransactionEncoder.signMessage(
                depositTx, CHAIN_ID, credentials);
            String txHash = web3.ethSendRawTransaction(
                Numeric.toHexString(signedDeposit)).send().getTransactionHash();
            System.out.println("Deposit submitted: " + txHash);
        }
    }
    ```
  </Tab>
</Tabs>

## Error Responses

| Status | Meaning                                                                                                           |
| ------ | ----------------------------------------------------------------------------------------------------------------- |
| 400    | Invalid parameters (missing field, bad address format, invalid permit signature, deposit amount below fees, etc.) |
| 404    | No account found for the given `vaultAddress` + `chainId`                                                         |
| 503    | Upstream RPC unavailable (share rate, fee module, or supply cap read failed)                                      |
