Skip to main content
For AI-friendly documentation, see Para llms.txt and Compass llms.txt.
If you’re using Para for embedded wallets, you can now add yield, lending, borrowing, and portfolio management to your app using the Compass API. Your users get a full DeFi experience without ever managing approvals or interacting with protocols directly.

How It Works

Compass provides a non-custodial DeFi API — you call an endpoint, get back an unsigned payload, and the user signs it via their Para wallet. Users earn yield, borrow against crypto, or rebalance portfolios without touching a DeFi protocol. This walkthrough covers three products: Earn (deposit into Aave, Morpho, Pendle vaults), Credit (borrow stablecoins against collateral), and Portfolio Manager (atomic rebalancing across venues).

Why Combine Para and Compass

Para WalletsCompass API
Email/social login — no seed phrase or browser extensionNon-custodial DeFi across Aave, Morpho, Pendle
EIP-712 signing — users see structured data, not hexGas sponsorship — users never hold or pay ETH
Embedded + external wallet supportAtomic transaction bundling — 50-70% gas savings
Multi-chain (Ethereum, Base, Arbitrum)Embedded fees — monetize from day one

Example Use Cases

  1. Yield Savings Account — Users deposit USDC and earn 4-8% APY across vaults. You embed a 10% performance fee on yield. Users see one balance, one APY.
  2. Crypto-Backed Credit Line — Users borrow USDC against ETH collateral. Display health factor and liquidation risk. Users never interact with Aave directly.
  3. Auto-Rebalancing Portfolio — Monitor rates and rebalance atomically when better opportunities emerge. One signature moves $50K from Aave (4.8%) to Morpho (6.2%).
Want to see what you’d be building? Try Compass Studio — a visual interface for Earn, Credit, and Portfolio products. Explore vaults, simulate transactions, and copy working configurations into your app.

What You Need

With gas sponsorship, your users never need ETH. You provide a gas sponsor wallet — a server-side private key funded with ETH on your target chain (Base, Ethereum, or Arbitrum). Your backend uses this wallet to pay gas and relay transactions on behalf of users. Compass returns EIP-712 typed data, the user signs it off-chain, and your sponsor wallet submits the transaction. Without gas sponsorship, Compass returns a standard unsigned transaction that the user signs and broadcasts directly from their Para wallet. The user pays their own gas, which requires them to hold ETH. This walkthrough uses gas sponsorship for all examples. See Compass Gas Sponsorship docs for more details.
You can also sponsor gas through Account Abstraction integrations like Alchemy, Pimlico, or ZeroDev instead of running your own sponsor wallet.

Step-by-Step Integration

Step 1: Install the Compass SDK

Add the Compass TypeScript SDK alongside your existing Para setup. Para’s ParaProvider already includes wagmi — the useSignTypedData hook used for EIP-712 signing is available out of the box.
npm install @compass-labs/api-sdk
Add these server-side environment variables:
# Server-side only — never expose to the client
COMPASS_API_KEY=your_compass_api_key
GAS_SPONSOR_PK=0x_your_sponsor_wallet_private_key

Step 2: Create a Product Account

Compass uses isolated on-chain accounts (Product Accounts) per product. Create a separate account for each product you plan to use — Earn, Credit, or both. Each account is a smart account controlled by the user’s wallet. Compass never holds custody — it orchestrates creation and transaction routing. Each account is deployed once per user. Calling the create endpoint again safely returns the existing address without a new transaction. Your gas sponsor deploys these accounts so the user never needs ETH.
// app/api/earn-account/create/route.ts
import { CompassApiSDK } from "@compass-labs/api-sdk";
import { createWalletClient, createPublicClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base, mainnet, arbitrum, type Chain } from "viem/chains";

const viemChains: Record<string, Chain> = { base, ethereum: mainnet, arbitrum };

export async function POST(request: Request) {
  const { owner, chain } = await request.json();

  const sdk = new CompassApiSDK({
    apiKeyAuth: process.env.COMPASS_API_KEY,
  });

  const sponsorAccount = privateKeyToAccount(
    process.env.GAS_SPONSOR_PK as `0x${string}`
  );

  const response = await sdk.earn.earnCreateAccount({
    chain,
    owner,                          // User's Para wallet address
    sender: sponsorAccount.address, // Your sponsor pays for deployment
    estimateGas: true,
  });

  // If account already exists, no transaction is returned
  if (!response.transaction) {
    return Response.json({ earnAccountAddress: response.earnAccountAddress });
  }

  const sponsorWallet = createWalletClient({
    account: sponsorAccount, chain: viemChains[chain], transport: http(),
  });

  const tx = response.transaction as any;
  const txHash = await sponsorWallet.sendTransaction({
    to: tx.to, data: tx.data,
    value: BigInt(tx.value || "0"),
    gas: tx.gas ? BigInt(tx.gas) : undefined,
  });

  await createPublicClient({ chain: viemChains[chain], transport: http() })
    .waitForTransactionReceipt({ hash: txHash });

  return Response.json({
    earnAccountAddress: response.earnAccountAddress,
    txHash,
  });
}

Step 3: Fund Your Account

Before depositing into a vault or borrowing, move tokens from the user’s wallet into their Product Account. This step teaches the three-step signing pattern that every gas-sponsored Compass operation follows. Your backend requests EIP-712 typed data from Compass (prepare), the Para wallet signs it via wagmi’s useSignTypedData (sign), and your backend submits the signed payload through your gas sponsor (execute).
All amounts in the Compass API are in human-readable units (e.g., "100" means 100 USDC), not in base units or wei.
// app/api/transfer/prepare/route.ts
import { CompassApiSDK } from "@compass-labs/api-sdk";
import { privateKeyToAccount } from "viem/accounts";

export async function POST(request: Request) {
  const { owner, chain, token, amount } = await request.json();

  const sdk = new CompassApiSDK({
    apiKeyAuth: process.env.COMPASS_API_KEY,
  });

  const sponsorAccount = privateKeyToAccount(
    process.env.GAS_SPONSOR_PK as `0x${string}`
  );

  // Request EIP-712 typed data with gas sponsorship
  const transfer = await sdk.earn.earnTransfer({
    owner, chain,
    token,
    amount,
    action: "DEPOSIT",
    spender: sponsorAccount.address,
    gasSponsorship: true,
  });

  // For Credit accounts, use the same pattern:
  // const transfer = await sdk.credit.creditTransfer({
  //   owner, chain, token, amount,
  //   action: "DEPOSIT", spender: sponsorAccount.address,
  //   gasSponsorship: true,
  // });

  const eip712 = transfer.eip712!;

  // Gas-sponsored transfers use Permit2 — returns PermitTransferFrom types.
  // Product operations (earnManage, creditBorrow) return SafeTx types instead.
  // Normalize camelCase keys to PascalCase for wagmi.
  const normalizedTypes = {
    EIP712Domain: (eip712.types as any).eip712Domain,
    PermitTransferFrom: (eip712.types as any).permitTransferFrom,
    TokenPermissions: (eip712.types as any).tokenPermissions,
  };

  return Response.json({
    eip712, normalizedTypes,
    primaryType: eip712.primaryType,
    domain: eip712.domain,
    message: eip712.message,
  });
}
Every Compass operation — deposit, withdraw, swap, borrow, repay, bundle — follows this same three-step pattern. Only the SDK call in the Prepare step changes. The Execute route is reused by all operations.
Before your first gas-sponsored transfer, each token needs a one-time Permit2 approval. Without it, earnTransfer and creditTransfer will return an “Insufficient allowance” error.
// app/api/approve-transfer/route.ts
import { CompassApiSDK } from "@compass-labs/api-sdk";

export async function POST(request: Request) {
  const { owner, chain, token } = await request.json();

  const sdk = new CompassApiSDK({
    apiKeyAuth: process.env.COMPASS_API_KEY,
  });

  const approval = await sdk.gasSponsorship.gasSponsorshipApproveTransfer({
    owner, chain, token,
    gasSponsorship: true,
  });

  const eip712 = approval.eip712;

  // If no EIP-712 data, the token is already approved
  if (!eip712) {
    return Response.json({ approved: true });
  }

  const normalizedTypes = {
    EIP712Domain: (eip712.types as any).eip712Domain,
    Permit: (eip712.types as any).permit,
  };

  return Response.json({
    eip712, normalizedTypes,
    primaryType: eip712.primaryType,
    domain: eip712.domain,
    message: eip712.message,
  });
}
The frontend signing and execution flow is identical — use the same signTypedDataAsync and /api/execute calls shown above.
Some tokens like USDT and WETH don’t support EIP-2612 permits. For those, set gasSponsorship: false in the approve call — the user signs and pays gas for the one-time approval directly.

Product Operations

The signing pattern is identical across all products. Only the SDK call in the Prepare step changes — swap it in and use the same Execute route and frontend signing flow from Step 3. Product operations return SafeTx types (normalize with EIP712Domain + SafeTx), unlike transfers which use PermitTransferFrom. The backend returns primaryType so the frontend handles both automatically.

Deposit into a Vault

const deposit = await sdk.earn.earnManage({
  owner, chain,
  venue: { type: "VAULT", vaultAddress },
  action: "DEPOSIT",
  amount: "100",
  gasSponsorship: true,
});
// → returns eip712 → sign → execute

Withdraw with Fee

// You collect fees atomically — no separate payment flow
const withdraw = await sdk.earn.earnManage({
  owner, chain,
  venue: { type: "VAULT", vaultAddress },
  action: "WITHDRAW",
  amount: "ALL",
  gasSponsorship: true,
  fee: {
    recipient: "0xYOUR_FEE_ADDRESS",
    amount: "10",
    denomination: "PERFORMANCE", // 10% of realized profit
  },
});
// → returns eip712 → sign → execute

Check Positions

// Read positions — no signing required
const positions = await sdk.earn.earnPositions({
  owner: walletAddress,
  chain: "base",
});
// Returns: balance, PnL, yield earned, fee history

Querying Data

All read endpoints are free, require no signing, and return instantly. Use these to populate your UI with available markets and rates.
const sdk = new CompassApiSDK({ apiKeyAuth: process.env.COMPASS_API_KEY });

// List top vaults by TVL on Base
const vaults = await sdk.earn.earnVaults({
  orderBy: "tvl_usd", direction: "desc",
  chain: "base", assetSymbol: "USDC", limit: 10,
});
// Returns: vault address, APY, TVL, underlying asset, protocol

// List Aave lending markets with supply/borrow APYs
const aaveMarkets = await sdk.earn.earnAaveMarkets({ chain: "base" });

// List Pendle markets with implied APY and expiry
const pendleMarkets = await sdk.earn.earnPendleMarkets({
  orderBy: "tvl_usd", direction: "desc",
  chain: "base", limit: 10,
});
See the full Compass API reference for all available endpoints.

Compass Earn Docs

Vaults, APYs, and yield strategies across Morpho, Aave, and Pendle

Gas Sponsorship

Gasless transactions with EIP-712 signing and sponsor wallets

EIP-712 Typed Data Signing

Sign structured data using the EIP-712 standard with Para wallets