What You’ll Learn
- Create a Para-backed viem account and wallet client
- Deposit stablecoins into an Amplify vault
- Withdraw vault shares through a Para-powered wallet
Why Combine Para and Paxos Amplify
| Para Wallets | Paxos Amplify |
|---|---|
| Instant onboarding, MPC-secure, white-label | Stablecoin yield across multiple strategies |
| Embedded + external wallet support | Multi-chain support, unified deposit API |
| Email/social login — no seed phrase or browser extension | Audited smart contracts |
Example Use Cases
- Fintech App Integration — Embed stablecoin deposit and yield flows directly in your fintech application with Para’s white-label wallet onboarding.
- Stablecoin Yield Product — Build a consumer-facing earn product where users deposit stablecoins into Amplify vaults through a Para-powered wallet — no browser extension required.
- Treasury Management — Automate treasury yield strategies from Para wallets with programmable deposit and withdrawal rules.
- Multi-Language Backend — Call Amplify contracts directly from Python, Go, Swift, or any language with an Ethereum JSON-RPC library — no JavaScript SDK required.
What You Need
- Para SDK for creating and managing wallets
- EVM-compatible chain such as Ethereum Mainnet
- Amplify API Credentials (
pxl_your_api_key) from support@paxoslabs.com - Amplify SDK (SDK path only) —
@paxoslabs/amplify-sdk
Step-by-Step Integration
- SDK Integration
- Direct Contracts
Step 1: Install Dependencies
npm install @getpara/viem-v2-integration @getpara/react-sdk @paxoslabs/amplify-sdk viem
Step 2: Initialize Para and Amplify
Complete authentication before signing any transactions.import { createParaViemAccount, createParaViemClient } from "@getpara/viem-v2-integration";
import { ParaWeb } from "@getpara/react-sdk";
import { http, createPublicClient } from "viem";
import { mainnet } from "viem/chains";
import { initAmplifySDK } from "@paxoslabs/amplify-sdk";
// Initialize Para
const para = new ParaWeb("YOUR_PARA_API_KEY");
// Initialize Amplify SDK
await initAmplifySDK("pxl_your_api_key", {
rpcUrls: { [mainnet.id]: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY" },
});
Step 3: Create Para Account and Clients
const account = createParaViemAccount({ para });
const walletClient = createParaViemClient({
para,
walletClientConfig: {
account,
chain: mainnet,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
},
});
const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
});
Step 4: Discover a Vault and Deposit
The SDK auto-detects the optimal authorization method (gasless permit, standard approval, or existing allowance) and handles each path for you.import {
getVaultsByConfig,
prepareDepositAuthorization,
prepareDeposit,
isPermitAuth,
isApprovalAuth,
isAlreadyApprovedAuth,
YieldType,
} from "@paxoslabs/amplify-sdk";
// Find a vault by yield strategy, chain, and asset
const [vault] = await getVaultsByConfig({
yieldType: YieldType.CORE, // "CORE", "TREASURY", or "FRONTIER"
chainId: mainnet.id,
depositAssetAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
});
const params = {
vaultName: vault.name,
depositAsset: vault.vault.baseTokenAddress,
depositAmount: "1000", // 1000 USDC
to: account.address,
chainId: mainnet.id,
};
// Auto-detect optimal authorization method
const auth = await prepareDepositAuthorization(params);
if (isPermitAuth(auth)) {
// Para signs the EIP-712 permit off-chain (gasless)
const signature = await walletClient.signTypedData({
account,
...auth.permitData,
});
const prepared = await prepareDeposit({
...params,
signature,
deadline: auth.permitData.message.deadline,
});
const hash = await walletClient.writeContract({ ...prepared.txData, account });
await publicClient.waitForTransactionReceipt({ hash });
} else if (isApprovalAuth(auth)) {
// Standard ERC-20 approve followed by deposit
const approvalHash = await walletClient.writeContract({ ...auth.txData, account });
await publicClient.waitForTransactionReceipt({ hash: approvalHash });
const prepared = await prepareDeposit(params);
const depositHash = await walletClient.writeContract({ ...prepared.txData, account });
await publicClient.waitForTransactionReceipt({ hash: depositHash });
} else if (isAlreadyApprovedAuth(auth)) {
// Sufficient allowance — deposit directly
const prepared = await prepareDeposit(params);
const hash = await walletClient.writeContract({ ...prepared.txData, account });
await publicClient.waitForTransactionReceipt({ hash });
}
Step 5: Withdraw from a Vault
import {
prepareWithdrawalAuthorization,
prepareWithdrawal,
isWithdrawApprovalAuth,
} from "@paxoslabs/amplify-sdk";
const withdrawParams = {
vaultName: vault.name,
wantAsset: vault.vault.baseTokenAddress,
withdrawAmount: "500", // 500 Shares
userAddress: account.address,
chainId: mainnet.id,
};
const withdrawAuth = await prepareWithdrawalAuthorization(withdrawParams);
if (isWithdrawApprovalAuth(withdrawAuth)) {
const approvalHash = await walletClient.writeContract({ ...withdrawAuth.txData, account });
await publicClient.waitForTransactionReceipt({ hash: approvalHash });
}
const withdrawTxData = await prepareWithdrawal(withdrawParams);
const withdrawHash = await walletClient.writeContract({ ...withdrawTxData, account });
await publicClient.waitForTransactionReceipt({ hash: withdrawHash });
Step 1: Install Dependencies
npm install @getpara/viem-v2-integration @getpara/react-sdk viem
Step 2: Initialize Para
Complete authentication before signing any transactions.import { createParaViemAccount, createParaViemClient } from "@getpara/viem-v2-integration";
import { ParaWeb } from "@getpara/react-sdk";
import { http, createPublicClient } from "viem";
import { mainnet } from "viem/chains";
const para = new ParaWeb("YOUR_PARA_API_KEY");
Step 3: Create Para Account and Clients
const account = createParaViemAccount({ para });
const walletClient = createParaViemClient({
para,
walletClientConfig: {
account,
chain: mainnet,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
},
});
const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
});
Step 4: Discover Contract Addresses
Query the Amplify GraphQL API to get vault and module addresses for your target chain and yield type.const response = await fetch("https://api.paxoslabs.com/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "YOUR_AMPLIFY_API_KEY",
},
body: JSON.stringify({
query: `query {
amplifySdkConfigs(chainId: 1, yieldType: CORE) {
vault {
boringVaultAddress
communityCodeDepositorModuleId
withdrawQueueModuleId
accountantModuleId
supportedAssets { address symbol decimals depositable withdrawable }
}
}
}`,
}),
});
const { data } = await response.json();
const vault = data.amplifySdkConfigs[0].vault;
const depositorAddress = vault.communityCodeDepositorModuleId;
const withdrawQueueAddress = vault.withdrawQueueModuleId;
const accountantAddress = vault.accountantModuleId;
const boringVaultAddress = vault.boringVaultAddress;
const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
Step 5: Approve and Deposit
const erc20Abi = [
{
inputs: [
{ name: "spender", type: "address" },
{ name: "amount", type: "uint256" },
],
name: "approve",
outputs: [{ name: "", type: "bool" }],
stateMutability: "nonpayable",
type: "function",
},
];
const depositorAbi = [
{
inputs: [
{ name: "depositAsset", type: "address" },
{ name: "depositAmount", type: "uint256" },
{ name: "minimumMint", type: "uint256" },
{ name: "to", type: "address" },
{ name: "distributorCode", type: "bytes" },
{
components: [
{ name: "uuid", type: "string" },
{ name: "expiration", type: "uint256" },
{ name: "attester", type: "address" },
{ name: "signature", type: "bytes" },
],
name: "_attestation",
type: "tuple",
},
],
name: "deposit",
outputs: [{ name: "shares", type: "uint256" }],
stateMutability: "nonpayable",
type: "function",
},
];
const depositAmount = 1000000000n; // 1,000 USDC (6 decimals)
// Approve the depositor to spend your USDC
const approvalHash = await walletClient.writeContract({
address: usdcAddress,
abi: erc20Abi,
functionName: "approve",
args: [depositorAddress, depositAmount],
account,
});
await publicClient.waitForTransactionReceipt({ hash: approvalHash });
// Deposit into the vault
const depositHash = await walletClient.writeContract({
address: depositorAddress,
abi: depositorAbi,
functionName: "deposit",
args: [
usdcAddress,
depositAmount,
0n, // minimumMint — see production note below
account.address,
"0x", // distributorCode
{
uuid: "",
expiration: 0n,
attester: "0x0000000000000000000000000000000000000000",
signature: "0x",
},
],
account,
});
await publicClient.waitForTransactionReceipt({ hash: depositHash });
In production, query
Accountant.getRateInQuoteSafe(usdcAddress) to calculate a safe minimumMint with slippage protection instead of passing 0n. See the slippage calculation below.const WAD = 10n ** 18n;
const SLIPPAGE_BPS = 50n; // 0.5%
const accountantAbi = [
{
inputs: [{ name: "quote", type: "address" }],
name: "getRateInQuoteSafe",
outputs: [{ name: "rateInQuote", type: "uint256" }],
stateMutability: "view",
type: "function",
},
] as const;
const rateInQuote = await publicClient.readContract({
address: accountantAddress,
abi: accountantAbi,
functionName: "getRateInQuoteSafe",
args: [usdcAddress],
});
const vaultTokenDecimals = await publicClient.readContract({
address: boringVaultAddress,
abi: [{ inputs: [], name: "decimals", outputs: [{ type: "uint8" }], stateMutability: "view", type: "function" }],
functionName: "decimals",
});
const idealMint = (depositAmount * WAD) / rateInQuote;
const slippageWad = (SLIPPAGE_BPS * WAD) / 10_000n;
const slippageAmount = (idealMint * slippageWad) / WAD;
const minimumMint = vaultTokenDecimals > 18
? (idealMint - slippageAmount) * 10n ** (BigInt(vaultTokenDecimals) - 18n)
: (idealMint - slippageAmount) / 10n ** (18n - BigInt(vaultTokenDecimals));
Step 6: Submit a Withdrawal Order
Withdrawal orders are fulfilled by the Amplify account operator, typically within 24 hours.const withdrawQueueAbi = [
{
inputs: [
{
components: [
{ name: "amountOffer", type: "uint256" },
{ name: "wantAsset", type: "address" },
{ name: "intendedDepositor", type: "address" },
{ name: "receiver", type: "address" },
{ name: "refundReceiver", type: "address" },
{
components: [
{ name: "approvalMethod", type: "uint8" },
{ name: "approvalV", type: "uint8" },
{ name: "approvalR", type: "bytes32" },
{ name: "approvalS", type: "bytes32" },
{ name: "submitWithSignature", type: "bool" },
{ name: "deadline", type: "uint256" },
{ name: "eip2612Signature", type: "bytes" },
],
name: "signatureParams",
type: "tuple",
},
],
name: "params",
type: "tuple",
},
],
name: "submitOrder",
outputs: [{ name: "orderIndex", type: "uint256" }],
stateMutability: "nonpayable",
type: "function",
},
];
const sharesToWithdraw = 500000000000000000000n; // example share amount
// Approve vault shares to the WithdrawQueue
const shareApprovalHash = await walletClient.writeContract({
address: boringVaultAddress,
abi: erc20Abi,
functionName: "approve",
args: [withdrawQueueAddress, sharesToWithdraw],
account,
});
await publicClient.waitForTransactionReceipt({ hash: shareApprovalHash });
// Submit the withdrawal order
const withdrawHash = await walletClient.writeContract({
address: withdrawQueueAddress,
abi: withdrawQueueAbi,
functionName: "submitOrder",
args: [
{
amountOffer: sharesToWithdraw,
wantAsset: usdcAddress,
intendedDepositor: account.address,
receiver: account.address,
refundReceiver: account.address,
signatureParams: {
approvalMethod: 0,
approvalV: 0,
approvalR: "0x0000000000000000000000000000000000000000000000000000000000000000",
approvalS: "0x0000000000000000000000000000000000000000000000000000000000000000",
submitWithSignature: false,
deadline: 0n,
eip2612Signature: "0x",
},
},
],
account,
});
await publicClient.waitForTransactionReceipt({ hash: withdrawHash });
Use
WithdrawQueue.getOrderStatus(orderIndex) to poll for withdrawal completion.