# Compass API Documentation for LLMs > Complete reference for building DeFi applications with Compass API > Version: 2.0 > Last Updated: January 9, 2026 > Base URL: https://docs.compasslabs.ai/v2/get_started/overview ## Table of Contents 1. Introduction & Overview 2. Core Concepts 3. Getting Started 4. API Endpoints Reference 5. Common Workflows 6. Error Handling 7. Chain-Specific Considerations 8. SDK Reference 9. Best Practices 10. Complete Examples 11. Appendix --- # 1. Introduction & Overview ## What is Compass API Compass is DeFi infrastructure for fintech and crypto applications. It provides a complete API for building DeFi products without managing protocols, smart contracts, or blockchain complexity. Compass enables developers to: - Access dozens of DeFi yield venues (Aave, Morpho, Pendle) through one unified interface - Generate unsigned transaction payloads for any DeFi operation - Create isolated Product Accounts for yield, borrowing, and trading - Bundle multiple operations into single atomic transactions - Sponsor gas for users so they never need ETH - Embed fees directly in transactions for instant monetization - Bridge USDC cross-chain via Circle's CCTP protocol - Track positions with complete PnL and performance data ## Key Capabilities ### Protocol Access Layer Access dozens of DeFi venues through one interface. Compass handles all protocol integrations, upgrades, and maintenance. **Supported protocols:** - **ERC-4626 vaults** (Morpho and others): Standardized yield-bearing vaults - **Aave V3**: Decentralized lending markets across Ethereum, Base, and Arbitrum - **Pendle**: Fixed-yield and yield trading markets ### Transaction Generation Compass generates unsigned transaction payloads for any DeFi operation. Users sign with their wallet (MetaMask, Dynamic, Privy, Fireblocks, or any wallet). Transactions execute directly on-chain into protocols. Compass never holds funds or private keys. Users sign. Users execute (or relayers execute on their behalf). Compass only generates transaction payloads. ### Product Accounts Isolated smart contract wallets created per product type. Each user gets one Earn Account per chain for yield products. Product Accounts enable: - **Bundled transactions** without re-approval: Execute multi-step flows (transfer + swap + deposit) in one atomic transaction - **Isolated accounting**: Earn positions don't affect future Credit or Trading positions - **Gas sponsorship**: All Product Account transactions support gasless execution - **Future expansion**: Add new products without refactoring existing integrations ### Transaction Bundling Combine multiple operations into single atomic transactions. All operations execute or all revert, no partial execution. **Example bundle**: Transfer USDC from wallet → Swap USDC to USDT → Deposit USDT into yield vault → Collect 10 bps fee **Benefits**: - 50-70% gas savings: One transaction instead of four - Better UX: One signature instead of four - Atomic execution: If vault deposit fails, swap and transfer also revert ### Embedded Fees Monetize DeFi integrations from day one. Add fees to deposits, withdrawals, or any transaction. Fees are collected automatically when transactions execute, bundled atomically with the primary operation. **Revenue examples**: - **Yield fees**: $20M in deposits earning 5% APY = $1M yield/year. At 10% platform fee = $100K/year passive revenue - **Gas markup**: Charge users $0.50, actual cost $0.25 = $0.25 margin. 100,000 transactions/month = $25K/month ### Gas Sponsorship Sponsor gas for users so they never need ETH. Users sign transactions off-chain. Your relayer executes and pays gas. 95% of new users don't hold ETH. Without gas sponsorship, users must acquire ETH before their first transaction—losing 9 out of 10 conversions. With gas sponsorship, users transact with only USDC. ### Bridging Move USDC cross-chain via Circle's CCTP (Cross-Chain Transfer Protocol). Burn USDC on source chain → Wait for attestation → Mint USDC on destination chain. Bridging completes in minutes (standard mode) or seconds (fast mode) with low fees. ## Non-Custodial Architecture Compass never holds funds or private keys. The flow is: 1. User calls Compass API with an intent (deposit into yield vault, bridge USDC, swap tokens) 2. Compass generates transaction payload including Product Account creation/management, transaction bundling, fee embedding, and gas sponsorship data 3. User signs the transaction with their wallet (standard signing for normal transactions, off-chain signature for gas-sponsored transactions) 4. Transaction executes on-chain directly into DeFi protocols (user executes, or relayer executes for gas-sponsored transactions) 5. Compass provides ongoing data for position tracking, balances, yields, and analytics ## Supported Chains - **Ethereum** (chain_id: 1, chain name: "ethereum") - **Base** (chain_id: 8453, chain name: "base") - **Arbitrum** (chain_id: 42161, chain name: "arbitrum") ## Authentication All API requests require authentication via API key passed in the `Authorization` header. **Header format:** ``` Authorization: Bearer YOUR_API_KEY ``` ### Getting an API Key 1. **Sign up** at https://www.compasslabs.ai/login 2. **Verify your email** (check spam folder if needed) 3. **Navigate to Dashboard** → API Keys section 4. **Generate new key** - copy immediately (shown only once) 5. **Free tier**: 1,000 calls/month included ### API Key Security **DO**: - Store API keys in environment variables, never in code - Use different keys for development, staging, and production - Rotate keys periodically (every 90 days recommended) - Restrict key permissions if available - Monitor usage via dashboard for unusual activity **DON'T**: - Commit keys to version control (.env files should be in .gitignore) - Share keys in Slack, Discord, or public channels - Use production keys in client-side code (browsers, mobile apps) - Hard-code keys in scripts or applications **Environment Variable Example**: ```bash # .env file (add to .gitignore) COMPASS_API_KEY=your_api_key_here BASE_RPC_URL=https://base.llamarpc.com PRIVATE_KEY=0x... ``` ```python # Python: load from environment import os from compass_api_sdk import CompassAPI api_key = os.environ["COMPASS_API_KEY"] compass_api = CompassAPI(api_key_auth=api_key) ``` ```typescript // TypeScript: load from environment import { CompassApiSDK } from "@compass-labs/api-sdk"; const compassApiSDK = new CompassApiSDK({ apiKeyAuth: process.env.COMPASS_API_KEY!, }); ``` ### Key Rotation To rotate your API key: 1. **Generate new key** in dashboard 2. **Update applications** with new key (test in staging first) 3. **Verify functionality** with new key 4. **Revoke old key** only after confirming new key works **Zero-downtime rotation**: Keep both keys active temporarily during migration, then revoke the old key. ### Troubleshooting Authentication **401 Unauthorized Error**: ```json { "detail": "Invalid authentication credentials" } ``` Common causes: - Missing `Bearer` prefix in header - Expired or revoked key - Typo in key (keys are case-sensitive) - Key for wrong environment (dev key used in prod) ## Rate Limits Compass API enforces rate limits to ensure fair usage and system stability. **Free Tier**: - 1,000 API calls per month - No per-second/per-minute limits - Suitable for development, testing, and small-scale applications **Rate Limit Behavior**: - When you exceed your monthly quota, API returns `429 Too Many Requests` - Rate limit resets monthly on your signup anniversary date - No automatic tier upgrades - contact support for higher limits **Response Headers** (included in all responses): - `X-RateLimit-Limit`: Your total monthly quota - `X-RateLimit-Remaining`: Calls remaining this month - `X-RateLimit-Reset`: Unix timestamp when quota resets **429 Too Many Requests Response**: ```json { "detail": "Rate limit exceeded. You have used 1,000 of 1,000 calls this month. Limit resets on 2026-02-01." } ``` **Best Practices**: - Check `X-RateLimit-Remaining` header to monitor usage - Implement exponential backoff when receiving 429 errors - Cache frequently accessed data (vault lists, APYs) locally - Batch operations using `/v2/earn/bundle` to reduce call count - Use webhooks or polling intervals appropriate for your use case **Upgrading Limits**: - Email contact@compasslabs.ai with: - Expected monthly call volume - Use case description - Production launch timeline - Custom enterprise plans available for high-volume integrations ## Base URL All API endpoints are relative to: ``` https://api.compasslabs.ai ``` --- # 2. Core Concepts ## Product Accounts ### What They Are Product Accounts are isolated on-chain smart contract wallets for each DeFi product (Earn, Borrow, Trade, etc.). When a user first uses Earn, Compass creates an Earn Product Account. If they later use Borrow, they get a separate Borrow Product Account. Both accounts are controlled by the user's main wallet. Compass never holds custody of the funds. Compass only orchestrates creation and transaction routing. **Key characteristics:** - One Product Account per product type per chain per user - Controlled by user's wallet (EOA or smart wallet like Safe or ZeroDev) - Created via API call to `/v2/earn/create_account` - Deterministic address based on owner and product type - Non-custodial: User retains full control ### Why Use Them 1. **Transaction Bundling**: Bundle any workflow, such as approvals, swap, and deposit in a single transaction. Save gas and improve UX. 2. **Gas Sponsorship**: All transactions within Product Accounts can be gas-sponsored. Users never need ETH. You can sponsor their gas across deposits, withdrawals, swaps, and borrows. 3. **No Fund Intermingling**: Your Earn positions can't get liquidated because of your Leverage positions. Each product operates in isolation. 4. **Clean Accounting**: Separate on-chain addresses per product simplify compliance and auditing. 5. **Independent Development**: Add new products without refactoring existing integrations. ### Account Lifecycle **Creation**: Call `/v2/earn/create_account` with owner address and chain. Returns unsigned transaction that creates the account contract. **Funding**: Call `/v2/earn/transfer` with `action=DEPOSIT` to move tokens from owner's wallet into the Product Account. **Usage**: Call `/v2/earn/manage` to deposit into venues or withdraw from venues. **Withdrawal**: Call `/v2/earn/transfer` with `action=WITHDRAW` to move tokens from Product Account back to owner's wallet. ### Account Address Derivation Product Account addresses are deterministic. Given an owner address and product type, the account address is always the same. This allows you to: - Check if an account exists before creating it - Compute the account address client-side - Know the account address before creation ## Supported Chains ### Ethereum Mainnet - **Chain ID**: 1 - **Chain name** (in API): `"ethereum"` - **Block time**: ~12 seconds - **Gas costs**: Highest among supported chains - **CCTP support**: Yes (domain 0) - **Supported venues**: Aave V3, Morpho vaults, Pendle markets, Sky (sUSDS) ### Arbitrum - **Chain ID**: 42161 - **Chain name** (in API): `"arbitrum"` - **Block time**: ~0.25 seconds - **Gas costs**: Low (Layer 2) - **CCTP support**: Yes (domain 3) - **CCTP fast mode**: Available - **Supported venues**: Aave V3, Morpho vaults, Pendle markets ### Base - **Chain ID**: 8453 - **Chain name** (in API): `"base"` - **Block time**: ~2 seconds - **Gas costs**: Low (Layer 2) - **CCTP support**: Yes (domain 6) - **CCTP fast mode**: Available - **Supported venues**: Aave V3, Morpho vaults, Pendle markets ## Authentication All API requests require authentication via API key in the `Authorization` header. **Python SDK:** ```python from compass_api_sdk import CompassAPI with CompassAPI(api_key_auth="YOUR_API_KEY") as compass_api: # Make API calls pass ``` **TypeScript SDK:** ```typescript import { CompassApiSDK } from "@compass-labs/api-sdk"; const compassApiSDK = new CompassApiSDK({ apiKeyAuth: "YOUR_API_KEY", }); ``` **cURL:** ```bash curl -X GET "https://api.compasslabs.ai/v2/earn/vaults?order_by=tvl_usd&limit=10" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Transaction Flow ### Standard Flow (User Pays Gas) 1. **API Call**: Call Compass endpoint (e.g., `/v2/earn/manage`) with `gas_sponsorship=false` 2. **Response**: Receive unsigned transaction object with `to`, `data`, `value`, `gas`, `gasPrice`/`maxFeePerGas`/`maxPriorityFeePerGas` 3. **Sign**: User signs transaction with their private key using web3.py (Python) or viem (TypeScript) 4. **Broadcast**: Submit signed transaction to the blockchain via RPC provider 5. **Confirm**: Wait for transaction confirmation **Python example:** ```python # Get unsigned transaction response = compass_api.earn.earn_transfer( owner=WALLET_ADDRESS, chain=models.Chain.BASE, token="USDC", amount="10", action=models.EarnTransferRequestAction.DEPOSIT, gas_sponsorship=False, ) # Sign and broadcast w3 = Web3(Web3.HTTPProvider(BASE_RPC_URL)) tx_dict = response.model_dump(by_alias=True)["transaction"] signed_tx = w3.eth.account.sign_transaction(tx_dict, PRIVATE_KEY) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) receipt = w3.eth.wait_for_transaction_receipt(tx_hash) ``` ### Gas-Sponsored Flow (Relayer Pays Gas) 1. **API Call**: Call Compass endpoint with `gas_sponsorship=true` 2. **Response**: Receive EIP-712 typed data structure 3. **Owner Sign**: Owner signs typed data off-chain (no gas cost) 4. **Prepare**: Submit signature + typed data to `/v2/gas_sponsorship/prepare` 5. **Sender Sign**: Sender (relayer) signs the returned transaction 6. **Broadcast**: Sender broadcasts transaction and pays gas 7. **Confirm**: Wait for transaction confirmation **Python example:** ```python from eth_account.messages import encode_typed_data # Step 1: Get EIP-712 typed data response = compass_api.earn.earn_transfer( owner=OWNER_ADDRESS, chain=models.Chain.BASE, token="USDC", amount="10", action=models.EarnTransferRequestAction.DEPOSIT, gas_sponsorship=True, spender=SENDER_ADDRESS, ) # Step 2: Owner signs off-chain typed_data = response.eip_712.model_dump(by_alias=True) encoded = encode_typed_data(full_message=typed_data) signature = owner_account.sign_message(encoded).signature.hex() # Step 3: Prepare gas-sponsored transaction prepare_response = compass_api.gas_sponsorship.gas_sponsorship_prepare( owner=OWNER_ADDRESS, chain=models.Chain.BASE, eip_712=typed_data, signature=signature, sender=SENDER_ADDRESS, ) # Step 4: Sender signs and broadcasts tx_dict = prepare_response.model_dump(by_alias=True)["transaction"] signed_tx = sender_account.sign_transaction(tx_dict) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) receipt = w3.eth.wait_for_transaction_receipt(tx_hash) ``` ## Transaction Bundling Transaction bundling combines multiple DeFi operations into a single atomic transaction. All operations execute together or all revert. **Common bundled operations:** - Token transfer + approval + deposit - Withdraw + swap + deposit (rebalancing) - Deposit + fee collection - Multiple venue deposits in one transaction **Benefits:** - **Gas savings**: 50-70% reduction by eliminating multiple transaction overhead - **Better UX**: One signature instead of multiple - **Atomicity**: If any operation fails, entire transaction reverts - **Simplified error handling**: Either everything succeeds or nothing happens **How it works:** Product Accounts enable bundling because tokens are already in the account. No repeated approvals needed. The Compass API constructs a single transaction that calls multiple protocol functions in sequence. **Example**: Depositing from wallet into vault with embedded fee ``` 1. Transfer USDC from owner wallet to Product Account 2. Deposit USDC from Product Account into Morpho vault 3. Transfer fee from Product Account to fee recipient ``` All three operations execute in one transaction atomically. ## Gas Sponsorship Gas sponsorship allows applications to pay gas fees on behalf of users. Users never need to hold ETH. ### How It Works **Standard Pattern (Owner Pays Gas):** - Owner = Sender - Owner signs transaction - Owner pays gas - Owner broadcasts transaction **Gas Sponsored Pattern (Relayer Pays Gas):** - Owner ≠ Sender - Owner signs EIP-712 typed data off-chain (free) - Relayer (Sender) prepares transaction via `/v2/gas_sponsorship/prepare` - Relayer signs transaction - Relayer pays gas - Relayer broadcasts transaction ### EIP-712 Typed Data EIP-712 is a standard for typed structured data hashing and signing. It provides: - Human-readable signature prompts in wallets - Type safety for signed data - Protection against signature replay attacks - Domain separation (signatures valid only for specific contracts/chains) **Structure:** ```json { "domain": { "name": "Product Account", "version": "1", "chainId": 8453, "verifyingContract": "0x..." }, "types": { "EIP712Domain": [...], "ExecuteCall": [...] }, "primaryType": "ExecuteCall", "message": { "to": "0x...", "value": "0", "data": "0x...", "nonce": "1" } } ``` ### Gas-Sponsored Deposits Require Approval For gas-sponsored deposits via `/v2/earn/transfer`, the owner must first set up a Permit2 allowance using `/v2/gas_sponsorship/approve_transfer`. This is a one-time setup per token. **Why?** To transfer tokens from the owner's wallet to their Product Account without the owner paying gas, Compass uses Permit2 (EIP-2612). This requires a one-time approval. **Workflow:** 1. **First time per token**: Call `/v2/gas_sponsorship/approve_transfer` to set up Permit2 allowance 2. **Every deposit**: Call `/v2/earn/transfer` with `gas_sponsorship=true` and `spender` set to relayer address **Tokens requiring approval**: USDC, DAI, USDT (if it supports Permit2), WETH ### When Gas Sponsorship is NOT Needed Gas sponsorship is optional for: - `/v2/earn/create_account`: Owner can pay their own gas to create their account - `/v2/earn/transfer` with `action=WITHDRAW`: Withdrawing from Product Account to wallet - `/v2/earn/manage`: Deposits and withdrawals within the Product Account - `/v2/cctp/burn` and `/v2/cctp/mint`: Bridge operations ## Embedded Fees Embedded fees allow you to monetize DeFi integrations by collecting fees atomically with the primary operation. ### Fee Structure Fees are configured using a `fee` object with three parameters: ```json { "recipient": "0xYourFeeAddress", "amount": 20, "denomination": "PERFORMANCE" } ``` **Parameters:** 1. **recipient** (string, required): The wallet address that receives the fee - Example: `"0xb8340945eBc917D2Aa0368a5e4E79C849c461511"` 2. **amount** (number, required): The fee amount (meaning depends on denomination) - For PERCENTAGE: fee as % of transaction (e.g., `1.5` = 1.5%) - For FIXED: fixed amount in token units (e.g., `0.1` = 0.1 USDC) - For PERFORMANCE: fee as % of realized profit (e.g., `20` = 20% of profit) 3. **denomination** (string, required): The unit type for the fee amount - Options: `"PERCENTAGE"`, `"FIXED"`, `"PERFORMANCE"` ### Fee Types **PERCENTAGE**: Percentage of the transaction amount - **Use case**: Platform fees, transaction fees - **Example**: `amount=1.5` means 1.5% of the deposit/withdrawal - **Calculation**: For $100 deposit with 1.5% fee, fee is $1.50 **FIXED**: Fixed token amount - **Use case**: Flat fees, minimum fees - **Example**: `amount=0.1` means 0.1 USDC per transaction - **Calculation**: For any size deposit, fee is 0.1 USDC **PERFORMANCE**: Percentage of realized profit (withdrawals only) - **Use case**: Performance fees, success fees - **Example**: `amount=20` means 20% of profit - **Calculation**: User deposits $10,000, withdraws $10,500 → profit is $500 → fee is $100 (20% of $500) - **Important**: No fee charged if withdrawal results in a loss - **Cost basis**: Calculated using FIFO (First In, First Out) ### Fee Timing - **Deposits**: Fee deducted BEFORE venue deposit - Order: User amount → Fee extracted → Remaining amount deposited - **Withdrawals**: Fee deducted AFTER venue withdrawal - Order: Venue withdrawal → Fee extracted → Remaining amount to user - **Performance fees**: Only on withdrawals, only when profit exists ### Atomicity Fees are bundled with the primary operation. If the operation fails, the fee transfer also reverts. No partial execution. ### Supported Endpoints Fees are supported on: - `/v2/earn/manage` with `action=DEPOSIT` or `action=WITHDRAW` - NOT supported for Pendle PT venues - NOT supported for `/v2/earn/transfer` (use manage instead) ## CCTP Bridging Circle's Cross-Chain Transfer Protocol (CCTP) enables native USDC transfers between chains without wrapped tokens or liquidity pools. ### How CCTP Works 1. **Burn**: USDC is burned on the source chain 2. **Attest**: Circle's attestation service validates the burn 3. **Mint**: Native USDC is minted on the destination chain **Key benefits:** - Native USDC on both chains (not wrapped) - No liquidity pools required - No slippage - Secure and audited by Circle ### Transfer Modes **Standard Mode:** - Speed: 15-19 minutes - Fee: Free - Use case: Non-urgent transfers, treasury operations **Fast Mode:** - Speed: ~30 seconds - Fee: ~0.01% (calculated by Circle) - Use case: Time-sensitive transfers, user-facing operations ### CCTP Domains Each chain has a CCTP domain identifier: - Ethereum: Domain 0 - Arbitrum: Domain 3 - Base: Domain 6 ### Workflow **Step 1: Burn USDC** Call `/v2/cctp/burn` to get a transaction that burns USDC from the Product Account on the source chain. **Response includes:** - `bridge_id`: Unique identifier for tracking the transfer - `transaction` or `eip_712`: Transaction data to sign and broadcast **Step 2: Wait for Attestation** Circle's attestation service validates the burn. This takes: - Standard mode: 15-19 minutes - Fast mode: 30 seconds **Step 3: Mint USDC** Call `/v2/cctp/mint` with the `bridge_id` and `burn_tx_hash` to get the mint transaction. **Response codes:** - **200 OK**: Attestation ready, mint transaction included - **202 Accepted**: Attestation pending, poll again - **404 Not Found**: Bridge ID not found ### Polling Pattern ```python import time POLL_INTERVAL_SECONDS = 10 MAX_ATTEMPTS = 120 # 20 minutes max attempts = 0 while attempts < MAX_ATTEMPTS: attempts += 1 mint_response = compass_api.bridge.cctp_mint( bridge_id=bridge_id, burn_tx_hash=burn_tx_hash, sender=WALLET_ADDRESS, ) # Check if transaction is ready if hasattr(mint_response, 'transaction') and mint_response.transaction: # Attestation ready, proceed with mint break # Wait before retrying time.sleep(POLL_INTERVAL_SECONDS) ``` ## Venue Types ### ERC-4626 Vaults **What they are**: Standardized yield-bearing vaults implementing the ERC-4626 standard. **Examples**: Morpho vaults, yield aggregators **How they work**: - Deposit underlying asset (e.g., USDC) - Receive vault shares (e.g., morphoUSDC) - Shares accrue value as vault earns yield - Redeem shares for underlying asset + yield **Risk profile**: Varies by vault curator and strategy - Conservative: Blue-chip lending (Aave, Compound) - Moderate: Diversified lending strategies - Aggressive: Leverage, exotic collateral **Supported chains**: Ethereum, Arbitrum, Base **API endpoint**: `/v2/earn/vaults` to list vaults, `/v2/earn/manage` with `venue.type=VAULT` to deposit/withdraw ### Aave V3 **What it is**: Decentralized lending protocol allowing users to supply assets and earn interest. **How it works**: - Supply assets to lending pools - Earn variable interest from borrowers - Receive aTokens (e.g., aUSDC) representing supplied amount + interest - Interest accrues in real-time - Withdraw anytime (subject to utilization) **Risk profile**: Low to moderate - Battle-tested protocol with billions in TVL - Over-collateralized loans - Risk of smart contract exploits (rare) **Supported chains**: Ethereum, Arbitrum, Base **Typical APY**: 2-8% for stablecoins, varies by chain and utilization **API endpoint**: `/v2/earn/aave_markets` to list markets, `/v2/earn/manage` with `venue.type=AAVE` to deposit/withdraw ### Pendle Markets (Fixed Yield) **What they are**: Fixed-yield trading markets that split yield-bearing tokens into Principal Tokens (PT) and Yield Tokens (YT). **How they work**: - Deposit underlying asset (e.g., USDC) - Receive Principal Token (PT) representing the principal - PT redeems to underlying at maturity - Buy PT at discount to lock in fixed yield **Example**: - Buy PT-USDC at $0.95 (5% discount) - Hold until maturity - Redeem for $1.00 USDC - Fixed yield: 5.26% APR **Maturity dates**: Markets have fixed maturity dates. Check `expiry` field (Unix timestamp). **Risk profile**: Low to moderate - Fixed yield eliminates rate uncertainty - Market risk on PT price before maturity - Smart contract risk **Supported chains**: Ethereum, Arbitrum, Base **API endpoint**: `/v2/earn/pendle_markets` to list markets, `/v2/earn/manage` with `venue.type=PENDLE_PT` to deposit/withdraw **Python Example - List Pendle Markets:** ```python from compass_api_sdk import CompassAPI, models with CompassAPI(api_key_auth=COMPASS_API_KEY) as compass_api: result = compass_api.earn.earn_pendle_markets( chain=models.Chain.ARBITRUM, order_by="implied_apy", direction=models.V2EarnPendleMarketsDirection.DESC, limit=10 ) for market in result.markets: apy = float(market.implied_apy or 0) * 100 print(f"{market.pt_name}") print(f" Underlying: {market.underlying_symbol}") print(f" Implied APY: {apy:.2f}%") print(f" Market Address: {market.market_address}") print(f" Expiry: {market.expiry}") ``` **Python Example - Deposit into Pendle PT:** ```python from compass_api_sdk import CompassAPI, models with CompassAPI(api_key_auth=COMPASS_API_KEY) as compass_api: # Deposit 100 USDC into a Pendle PT market response = compass_api.earn.earn_manage( owner=WALLET_ADDRESS, chain=models.Chain.ARBITRUM, venue={ "type": "PENDLE_PT", "market_address": "0x...", # From earn_pendle_markets() "token": "USDC", "max_slippage_percent": 1.0 }, action=models.EarnManageRequestAction.DEPOSIT, amount="100", gas_sponsorship=False, ) # Sign and broadcast transaction tx_dict = response.model_dump(by_alias=True)["transaction"] signed_tx = w3.eth.account.sign_transaction(tx_dict, PRIVATE_KEY) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) ``` **TypeScript Example - List Pendle Markets:** ```typescript import { CompassApiSDK } from "@compass-labs/api-sdk"; const compass = new CompassApiSDK({ apiKeyAuth: process.env.COMPASS_API_KEY! }); async function listPendleMarkets() { const result = await compass.earn.earnPendleMarkets({ chain: "arbitrum", orderBy: "implied_apy", direction: "desc", limit: 10, }); for (const market of result.markets) { const apy = Number(market.impliedApy || 0) * 100; console.log(`${market.ptName}`); console.log(` Underlying: ${market.underlyingSymbol}`); console.log(` Implied APY: ${apy.toFixed(2)}%`); console.log(` Market Address: ${market.marketAddress}`); } } listPendleMarkets(); ``` ### Sky (formerly MakerDAO) **What it is**: Dai Savings Rate (DSR) offered by Sky (formerly MakerDAO). Deposit DAI or USDS, earn fixed yield. **How it works**: - Deposit USDS or DAI - Receive sUSDS (savings USDS) - sUSDS accrues value at the DSR - Redeem for USDS + yield **Risk profile**: Low - Backed by MakerDAO's over-collateralized system - No lock-up period - Stable, predictable yield **Supported chains**: Ethereum only **Typical APY**: 4-6% (DSR set by MakerDAO governance) **API endpoint**: `/v2/earn/manage` with specific vault address for sUSDS --- # 3. Getting Started ## Prerequisites Before you start, you'll need: 1. **Compass API Key**: Sign up at https://www.compasslabs.ai/login for a free API key (1,000 calls/month) 2. **Wallet with Funds**: - A wallet address (EOA or smart wallet) - Private key for signing transactions - Tokens to deposit (e.g., USDC) - ETH for gas (if not using gas sponsorship) 3. **RPC Provider**: - Ethereum RPC URL (e.g., Alchemy, Infura, Ankr) - Arbitrum RPC URL - Base RPC URL 4. **Development Environment**: - Python 3.8+ with pip or uv - OR Node.js 18+ with npm or yarn ## Install SDKs ### Python SDK ```bash pip install compass_api_sdk ``` **Dependencies:** ```bash pip install web3 python-dotenv eth-account ``` **Important SDK Parameter Notes:** - `earn_positions()` uses `owner=` parameter - `earn_transfer()`, `earn_manage()` use `owner=` parameter - For web3.py transaction signing, use `signed_tx.rawTransaction` (camelCase) **SDK Field Reference (Python SDK v2.0.31+):** | Method | Response Field | Type | Description | |--------|---------------|------|-------------| | `earn_vaults()` | `vault.vault_address` | str | Vault contract address | | `earn_vaults()` | `vault.name` | str | Vault display name | | `earn_vaults()` | `vault.symbol` | str | Vault token symbol | | `earn_vaults()` | `vault.asset_symbol` | str | Underlying token symbol (e.g., "USDC") | | `earn_vaults()` | `vault.asset_name` | str | Underlying token name | | `earn_vaults()` | `vault.asset` | str | Underlying token address | | `earn_vaults()` | `vault.chain` | str | Chain name (e.g., "base") | | `earn_vaults()` | `vault.apy_7d`, `apy_30d`, `apy_90d` | str | APY over different periods | | `earn_vaults()` | `vault.tvl_usd` | str | Total value locked in USD | | `earn_vaults()` | `vault.owner` | str | Vault owner address | | `earn_aave_markets()` | `markets` | dict | Dict keyed by token symbol | | `earn_aave_markets()` | `markets[token].chains[chain].supply_apy` | str | Supply APY for chain | | `earn_aave_markets()` | `markets[token].chains[chain].borrow_apy` | str | Borrow APY for chain | | `earn_aave_markets()` | `markets[token].chains[chain].address` | str | Token address | | `earn_pendle_markets()` | `market.pt_name` | str | Principal Token name (e.g., "PT Staked USDai 5MAR2026") | | `earn_pendle_markets()` | `market.market_address` | str | Pendle market contract address | | `earn_pendle_markets()` | `market.underlying_symbol` | str | Underlying token symbol | | `earn_pendle_markets()` | `market.underlying_asset` | str | Underlying token address | | `earn_pendle_markets()` | `market.implied_apy` | str | Implied fixed APY | | `earn_pendle_markets()` | `market.expiry` | int | Maturity date (Unix timestamp) | | `earn_pendle_markets()` | `market.tvl_usd` | str | Total value locked in USD | | `earn_pendle_markets()` | `market.pt_address` | str | PT token address | | `earn_positions(owner=...)` | `positions.user_positions` | list | List of position objects | | `earn_positions()` | `position.TYPE` | str | Position type: "VAULT", "AAVE", "PENDLE_PT" | | `earn_positions()` | `position.vault_name` | str | Vault name (for VAULT type only) | | `earn_positions()` | `position.vault_address` | str | Vault address (for VAULT type only) | | `earn_positions()` | `position.token_name` | str | Token name (for VAULT/AAVE types) | | `earn_positions()` | `position.amount_in_underlying_token` | str | Amount deposited (for VAULT/AAVE types) | | `earn_positions()` | `position.market_address` | str | Pendle market address (PENDLE_PT type only) | | `earn_positions()` | `position.pt_address` | str | PT token address (PENDLE_PT type only) | | `earn_positions()` | `position.amount_in_pt` | str | Amount in PT tokens (PENDLE_PT type only) | | `earn_positions()` | `position.underlying_symbol` | str | Underlying token symbol (PENDLE_PT type only) | | `earn_positions()` | `position.underlying_asset` | str | Underlying token address (PENDLE_PT type only) | | `earn_positions()` | `position.expiry` | int | Maturity timestamp (PENDLE_PT type only) | **Note: Position fields vary by TYPE. VAULT and AAVE positions use `token_name`/`amount_in_underlying_token`. PENDLE_PT uses `underlying_symbol`/`amount_in_pt`.** **Note: `earn_vaults()` does NOT have a `protocol` field. Use `vault.name` which includes the protocol name.** **Direction Enums:** - For `earn_vaults()`: use `models.V2EarnVaultsDirection.DESC` or `.ASC` - For `earn_pendle_markets()`: use `models.V2EarnPendleMarketsDirection.DESC` or `.ASC` **Recommended pyproject.toml:** ```toml [project] dependencies = [ "compass_api_sdk>=1.0.0", "web3>=6.0.0", "python-dotenv>=1.0.0", "eth-account>=0.10.0", ] ``` ### TypeScript SDK ```bash npm install @compass-labs/api-sdk viem dotenv ``` **TypeScript SDK Field Reference (@compass-labs/api-sdk):** | Method | Parameter/Field | Type | Notes | |--------|----------------|------|-------| | `earnVaults()` | `chain` | string | `"ethereum"`, `"arbitrum"`, `"base"` | | `earnVaults()` | `orderBy` | string | `"apy"`, `"tvl_usd"`, `"lifetime_return"`, `"one_month_cagr_net"` | | `earnVaults()` | `direction` | string | `"asc"` or `"desc"` | | `earnVaults()` | `vault.vaultAddress` | string | Vault contract address | | `earnVaults()` | `vault.name` | string | Vault display name | | `earnVaults()` | `vault.assetSymbol` | string | Underlying token (e.g., "USDC") | | `earnVaults()` | `vault.oneMonthCagrNet` | string | 30-day annualized return | | `earnVaults()` | `vault.lifetimeReturn` | string | Total lifetime return | | `earnVaults()` | `vault.tvlUsd` | string | Total value locked | | `earnPositions()` | `userAddress` | string | **NOT `owner`** - wallet address | | `earnPositions()` | `position.type` | string | **lowercase**: `"VAULT"`, `"AAVE"`, `"PENDLE_PT"` | | `earnPositions()` | `position.vaultName` | string | Vault name (camelCase) | | `earnPositions()` | `position.vaultAddress` | string | Vault address | | `earnPositions()` | `position.tokenName` | string | Token name | | `earnPositions()` | `position.amountInUnderlyingToken` | string | Deposited amount | | `earnPendleMarkets()` | `market.ptName` | string | PT name (camelCase) | | `earnPendleMarkets()` | `market.marketAddress` | string | Market address | | `earnPendleMarkets()` | `market.underlyingSymbol` | string | Underlying token | | `earnPendleMarkets()` | `market.impliedApy` | string | Fixed APY | | `earnPendleMarkets()` | `market.expiry` | number | Maturity (Unix timestamp) | **IMPORTANT: Python vs TypeScript SDK differences:** - Python: `earn_positions(owner=...)` with `position.TYPE` (uppercase) - TypeScript: `earnPositions({ userAddress: ... })` with `position.type` (lowercase) - Python uses snake_case: `vault.vault_address`, `vault.asset_symbol` - TypeScript uses camelCase: `vault.vaultAddress`, `vault.assetSymbol` **Dependencies:** ```bash npm install viem dotenv ``` **Recommended package.json:** ```json { "dependencies": { "@compass-labs/api-sdk": "^1.0.0", "viem": "^2.0.0", "dotenv": "^16.0.0" } } ``` ## Environment Setup Create a `.env` file in your project root: ```bash # API Key COMPASS_API_KEY=your_api_key_here # Wallet Configuration WALLET_ADDRESS=0xYourWalletAddress PRIVATE_KEY=0xYourPrivateKey # RPC URLs ETHEREUM_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/your-key ARBITRUM_RPC_URL=https://arb-mainnet.g.alchemy.com/v2/your-key BASE_RPC_URL=https://base-mainnet.g.alchemy.com/v2/your-key # Optional: For gas sponsorship examples OWNER_ADDRESS=0xOwnerAddress OWNER_PRIVATE_KEY=0xOwnerPrivateKey SENDER_ADDRESS=0xRelayerAddress SENDER_PRIVATE_KEY=0xRelayerPrivateKey # Optional: For embedded fees FEE_RECIPIENT=0xFeeRecipientAddress ``` **Security note**: Never commit `.env` files to version control. Add `.env` to `.gitignore`. ## First API Call: Explore Yield Opportunities Let's make your first API call to fetch vault data. ### Python ```python from compass_api_sdk import CompassAPI, models import os from dotenv import load_dotenv load_dotenv() COMPASS_API_KEY = os.getenv("COMPASS_API_KEY") with CompassAPI(api_key_auth=COMPASS_API_KEY) as compass_api: # Get USDC vaults on Base ordered by APY result = compass_api.earn.earn_vaults( chain=models.Chain.BASE, order_by="apy", direction=models.V2EarnVaultsDirection.DESC, limit=20 ) # Filter for USDC vaults using asset_symbol field usdc_vaults = [v for v in result.vaults if v.asset_symbol == "USDC"][:5] for vault in usdc_vaults: apy = float(vault.apy_90d or vault.apy_30d or vault.apy_7d or 0) * 100 tvl = float(vault.tvl_usd or 0) print(f"{vault.name}: {apy:.2f}% APY, ${tvl:,.0f} TVL") print(f" Address: {vault.vault_address}") ``` ### TypeScript ```typescript import { CompassApiSDK } from "@compass-labs/api-sdk"; import * as dotenv from "dotenv"; dotenv.config(); const compassApiSDK = new CompassApiSDK({ apiKeyAuth: process.env.COMPASS_API_KEY!, }); async function run() { const result = await compassApiSDK.earn.earnVaults({ chain: "ethereum", orderBy: "lifetime_return", direction: "desc", limit: 50, }); const morphoVaults = result.vaults.filter(v => v.protocol === "Morpho").slice(0, 3); for (const vault of morphoVaults) { const lifetimeReturn = Number(vault.lifetimeReturn ?? 0) * 100; console.log(`${vault.name}: ${lifetimeReturn.toFixed(2)}% lifetime return`); } } run(); ``` **Expected output:** ``` Gauntlet USDC Core: 12.96% lifetime return Smokehouse USDC: 8.29% lifetime return IMF USDS: 7.64% lifetime return ``` ## Complete First Deposit Flow This example demonstrates the complete flow: create account, fund account, deposit into vault. ### Python ```python from compass_api_sdk import CompassAPI, models import os from dotenv import load_dotenv from web3 import Web3 load_dotenv() COMPASS_API_KEY = os.getenv("COMPASS_API_KEY") WALLET_ADDRESS = os.getenv("WALLET_ADDRESS") PRIVATE_KEY = os.getenv("PRIVATE_KEY") BASE_RPC_URL = os.getenv("BASE_RPC_URL") w3 = Web3(Web3.HTTPProvider(BASE_RPC_URL)) with CompassAPI(api_key_auth=COMPASS_API_KEY) as compass_api: # Step 1: Create Earn Account print("Step 1: Creating Earn Account...") create_response = compass_api.earn.earn_create_account( chain=models.CreateAccountRequestChain.BASE, sender=WALLET_ADDRESS, owner=WALLET_ADDRESS, estimate_gas=True, ) tx_dict = create_response.model_dump(by_alias=True)["transaction"] signed_tx = w3.eth.account.sign_transaction(tx_dict, PRIVATE_KEY) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Earn Account created: {create_response.earn_account_address}") # Step 2: Fund Earn Account print("\nStep 2: Funding Earn Account with 10 USDC...") transfer_response = compass_api.earn.earn_transfer( owner=WALLET_ADDRESS, chain=models.Chain.BASE, token="USDC", amount="10", action=models.EarnTransferRequestAction.DEPOSIT, gas_sponsorship=False, ) tx_dict = transfer_response.model_dump(by_alias=True)["transaction"] signed_tx = w3.eth.account.sign_transaction(tx_dict, PRIVATE_KEY) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Account funded. Tx: {tx_hash.hex()}") # Step 3: Deposit into Morpho Vault print("\nStep 3: Depositing 5 USDC into Morpho vault...") manage_response = compass_api.earn.earn_manage( owner=WALLET_ADDRESS, chain=models.Chain.BASE, venue={ "type": "VAULT", "vault_address": "0xbeeF010f9cb27031ad51e3333f9aF9C6B1228183", }, action=models.EarnManageRequestAction.DEPOSIT, amount="5", gas_sponsorship=False, fee=None, ) tx_dict = manage_response.model_dump(by_alias=True)["transaction"] signed_tx = w3.eth.account.sign_transaction(tx_dict, PRIVATE_KEY) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Deposit complete. Tx: {tx_hash.hex()}") # Step 4: Check positions print("\nStep 4: Checking positions...") positions = compass_api.earn.earn_positions( chain=models.Chain.BASE, owner=WALLET_ADDRESS, ) for position in positions.user_positions: if position.TYPE == "VAULT": print(f"{position.vault_name}: {position.amount_in_underlying_token} {position.token_name}") ``` ### TypeScript ```typescript import { CompassApiSDK } from "@compass-labs/api-sdk"; import { createWalletClient, http, publicActions } from "viem"; import { base } from "viem/chains"; import { privateKeyToAccount } from "viem/accounts"; import * as dotenv from "dotenv"; dotenv.config(); const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); const walletClient = createWalletClient({ account, chain: base, transport: http(process.env.BASE_RPC_URL), }).extend(publicActions); const compassApiSDK = new CompassApiSDK({ apiKeyAuth: process.env.COMPASS_API_KEY!, }); async function run() { // Step 1: Create Earn Account console.log("Step 1: Creating Earn Account..."); const createResponse = await compassApiSDK.earn.earnCreateAccount({ chain: "base", sender: account.address, owner: account.address, estimateGas: true, }); let txHash = await walletClient.sendTransaction(createResponse.transaction as any); await walletClient.waitForTransactionReceipt({ hash: txHash }); console.log(`Earn Account created: ${createResponse.earnAccountAddress}`); // Step 2: Fund Earn Account console.log("\nStep 2: Funding Earn Account with 10 USDC..."); const transferResponse = await compassApiSDK.earn.earnTransfer({ owner: account.address, chain: "base", token: "USDC", amount: "10", action: "DEPOSIT", gasSponsorship: false, }); txHash = await walletClient.sendTransaction(transferResponse.transaction as any); await walletClient.waitForTransactionReceipt({ hash: txHash }); console.log(`Account funded. Tx: ${txHash}`); // Step 3: Deposit into Morpho Vault console.log("\nStep 3: Depositing 5 USDC into Morpho vault..."); const manageResponse = await compassApiSDK.earn.earnManage({ owner: account.address, chain: "base", venue: { type: "VAULT", vaultAddress: "0xbeeF010f9cb27031ad51e3333f9aF9C6B1228183", }, action: "DEPOSIT", amount: "5", gasSponsorship: false, }); txHash = await walletClient.sendTransaction(manageResponse.transaction as any); await walletClient.waitForTransactionReceipt({ hash: txHash }); console.log(`Deposit complete. Tx: ${txHash}`); // Step 4: Check positions console.log("\nStep 4: Checking positions..."); const positions = await compassApiSDK.earn.earnPositions({ chain: "base", userAddress: account.address, // Note: TypeScript SDK uses userAddress, not owner }); for (const position of positions.userPositions) { if (position.type === "VAULT") { // Note: TypeScript uses lowercase "type" console.log(`${position.vaultName}: ${position.amountInUnderlyingToken} ${position.tokenName}`); } } } run(); ``` ## Next Steps Now that you've completed your first deposit, explore: 1. **Gas Sponsorship**: Enable gasless transactions for your users 2. **Embedded Fees**: Monetize your integration 3. **Cross-Chain Bridging**: Move USDC between chains 4. **Position Management**: Track yields and rebalance 5. **Multiple Venues**: Diversify across Aave, Morpho, and Pendle --- # 4. API Endpoints Reference This section provides comprehensive documentation for all V2 API endpoints. ## Gas Sponsorship Endpoints ### POST /v2/gas_sponsorship/prepare **Purpose**: Prepare a gas-sponsored transaction from signed EIP-712 typed data. **Use Case**: After the owner signs EIP-712 typed data off-chain, submit the signature to this endpoint to get an unsigned transaction for the sender (relayer) to sign and broadcast. **How gas sponsorship works:** 1. Call an endpoint with `gas_sponsorship=true` (e.g., `/earn/transfer`, `/earn/manage`) to get EIP-712 typed data 2. Owner signs the typed data off-chain 3. Submit signature + typed data to this endpoint 4. Sender signs and broadcasts the returned transaction, paying gas on behalf of the owner **Request Body:** ```json { "owner": "0xCE1A77F0abff993d6d3D04d44b70831c6924fb40", "chain": "arbitrum", "eip_712": { "domain": { "name": "Product Account", "version": "1", "chainId": 42161, "verifyingContract": "0x..." }, "types": { "EIP712Domain": [ {"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "chainId", "type": "uint256"}, {"name": "verifyingContract", "type": "address"} ], "ExecuteCall": [ {"name": "to", "type": "address"}, {"name": "value", "type": "uint256"}, {"name": "data", "type": "bytes"}, {"name": "nonce", "type": "uint256"} ] }, "primaryType": "ExecuteCall", "message": { "to": "0x...", "value": "0", "data": "0x...", "nonce": "1" } }, "signature": "0x160d2709ae195f591daa33ad6ab1fb18b8762a39d8c4466c4cbe95cf6881fc3d54d469710ef0e7fd64ecff47c1ba5741d7254903bfaebdacea5aa8289f81ba9a1c", "sender": "0x02122Ac49b0Be2e0eAD957F2D080805A0127Aa9d" } ``` **Request Parameters:** - `owner` (string, required): The address that signed the EIP-712 typed data - `chain` (string, required): Chain name (`"ethereum"`, `"arbitrum"`, `"base"`) - `eip_712` (object, required): The EIP-712 typed data that was signed - `signature` (string, required): The hex-encoded signature from the owner - `sender` (string, required): The address that will sign and broadcast the transaction (pays gas) **Response (Success):** ```json { "transaction": { "to": "0x...", "data": "0x...", "value": "0", "gas": "200000", "maxFeePerGas": "1000000000", "maxPriorityFeePerGas": "1000000000", "nonce": 5, "chainId": 42161 } } ``` **Response Fields:** - `transaction.to`: Contract address to call - `transaction.data`: Encoded function call data - `transaction.value`: ETH value to send (usually "0") - `transaction.gas`: Estimated gas limit - `transaction.maxFeePerGas`: Maximum fee per gas (EIP-1559) - `transaction.maxPriorityFeePerGas`: Maximum priority fee (EIP-1559) - `transaction.nonce`: Transaction nonce for the sender - `transaction.chainId`: Chain ID **Common Patterns:** - Validate owner signature before calling this endpoint - Sender must have ETH to pay for gas - Transaction must be broadcast immediately after preparation **Python Example:** ```python from compass_api_sdk import CompassAPI, models from eth_account.messages import encode_typed_data from web3 import Web3 w3 = Web3(Web3.HTTPProvider(ARBITRUM_RPC_URL)) owner_account = w3.eth.account.from_key(OWNER_PRIVATE_KEY) sender_account = w3.eth.account.from_key(SENDER_PRIVATE_KEY) with CompassAPI(api_key_auth=COMPASS_API_KEY) as compass_api: # Step 1: Get EIP-712 typed data (example: transfer) transfer_response = compass_api.earn.earn_transfer( owner=OWNER_ADDRESS, chain=models.Chain.ARBITRUM, token="USDC", amount="10", action=models.EarnTransferRequestAction.DEPOSIT, gas_sponsorship=True, spender=SENDER_ADDRESS, ) # Step 2: Owner signs off-chain typed_data = transfer_response.eip_712.model_dump(by_alias=True) encoded = encode_typed_data(full_message=typed_data) signature = owner_account.sign_message(encoded).signature.hex() # Step 3: Prepare gas-sponsored transaction prepare_response = compass_api.gas_sponsorship.gas_sponsorship_prepare( owner=OWNER_ADDRESS, chain=models.Chain.ARBITRUM, eip_712=typed_data, signature=signature, sender=SENDER_ADDRESS, ) # Step 4: Sender signs and broadcasts tx_dict = prepare_response.model_dump(by_alias=True)["transaction"] signed_tx = sender_account.sign_transaction(tx_dict) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Transaction confirmed: {tx_hash.hex()}") ``` **TypeScript Example:** ```typescript import { CompassApiSDK } from "@compass-labs/api-sdk"; import { createWalletClient, http, publicActions } from "viem"; import { arbitrum } from "viem/chains"; import { privateKeyToAccount } from "viem/accounts"; const ownerAccount = privateKeyToAccount(process.env.OWNER_PRIVATE_KEY as `0x${string}`); const senderAccount = privateKeyToAccount(process.env.SENDER_PRIVATE_KEY as `0x${string}`); const senderClient = createWalletClient({ account: senderAccount, chain: arbitrum, transport: http(process.env.ARBITRUM_RPC_URL), }).extend(publicActions); const compassApiSDK = new CompassApiSDK({ apiKeyAuth: process.env.COMPASS_API_KEY!, }); async function run() { // Step 1: Get EIP-712 typed data const transferResponse = await compassApiSDK.earn.earnTransfer({ owner: ownerAccount.address, chain: "arbitrum", token: "USDC", amount: "10", action: "DEPOSIT", gasSponsorship: true, spender: senderAccount.address, }); // Step 2: Owner signs off-chain const signature = await ownerAccount.signTypedData(transferResponse.eip712 as any); // Step 3: Prepare gas-sponsored transaction const prepareResponse = await compassApiSDK.gasSponsorship.gasSponsorshipPrepare({ owner: ownerAccount.address, chain: "arbitrum", eip712: transferResponse.eip712, signature: signature, sender: senderAccount.address, }); // Step 4: Sender signs and broadcasts const txHash = await senderClient.sendTransaction(prepareResponse.transaction as any); const receipt = await senderClient.waitForTransactionReceipt({ hash: txHash }); console.log(`Transaction confirmed: ${txHash}`); } run(); ``` **Related Endpoints:** - `/v2/gas_sponsorship/approve_transfer`: Set up Permit2 allowance (required once per token for gas-sponsored deposits) - `/v2/earn/transfer`: Get EIP-712 typed data for gas-sponsored transfers - `/v2/earn/manage`: Get EIP-712 typed data for gas-sponsored deposits/withdrawals **Important Notes:** - EIP-712 typed data must match exactly what the owner signed - Signature must be valid for the owner address - Sender must have sufficient ETH to pay gas - Transaction nonce is managed by Compass for the Product Account, but sender's nonce is standard - TypeScript: EIP-712 type field names use camelCase (e.g., `chainId`), Python uses snake_case (e.g., `chain_id`) --- ### POST /v2/gas_sponsorship/approve_transfer **Purpose**: Set up a one-time Permit2 allowance for gas-sponsored token transfers. **Use Case**: Required when using `/earn/transfer` with `gas_sponsorship=true` for the first time per token. This allowance only needs to be set up once per token. **When to use:** - First gas-sponsored deposit for a specific token (e.g., USDC) - Only for tokens that support EIP-2612 permit (USDC, DAI, etc.) - Not needed for tokens like USDT or WETH that don't support permit **Request Body (gas_sponsorship=true):** ```json { "owner": "0x01E62835dd7F52173546A325294762143eE4a882", "chain": "base", "token": "USDC", "gas_sponsorship": true, "spender": "0x02122Ac49b0Be2e0eAD957F2D080805A0127Aa9d" } ``` **Request Body (gas_sponsorship=false):** ```json { "owner": "0x01E62835dd7F52173546A325294762143eE4a882", "chain": "base", "token": "USDT", "gas_sponsorship": false } ``` **Request Parameters:** - `owner` (string, required): Wallet address that owns the tokens - `chain` (string, required): Chain name (`"ethereum"`, `"arbitrum"`, `"base"`) - `token` (string, required): Token symbol (`"USDC"`, `"USDT"`, `"DAI"`, `"WETH"`, etc.) - `gas_sponsorship` (boolean, optional, default=true): Whether to use gas sponsorship - `spender` (string, optional): Required if `gas_sponsorship=true`. Address of the relayer that will pay gas. **Response (gas_sponsorship=true):** ```json { "eip_712": { "domain": { "name": "Permit2", "chainId": 8453, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "types": { "PermitSingle": [ {"name": "details", "type": "PermitDetails"}, {"name": "spender", "type": "address"}, {"name": "sigDeadline", "type": "uint256"} ], "PermitDetails": [ {"name": "token", "type": "address"}, {"name": "amount", "type": "uint160"}, {"name": "expiration", "type": "uint48"}, {"name": "nonce", "type": "uint48"} ] }, "primaryType": "PermitSingle", "message": { "details": { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "1461501637330902918203684832716283019655932542975", "expiration": "1735689600", "nonce": "0" }, "spender": "0x...", "sigDeadline": "1735689600" } } } ``` **Response (gas_sponsorship=false):** ```json { "transaction": { "to": "0x000000000022D473030F116dDEE9F6B43aC78BA3", "data": "0x...", "value": "0", "gas": "100000", "maxFeePerGas": "1000000", "maxPriorityFeePerGas": "1000000", "nonce": 5, "chainId": 8453 } } ``` **Workflow for gas_sponsorship=true:** 1. Call this endpoint with `gas_sponsorship=true` to get EIP-712 typed data 2. Owner signs the typed data off-chain 3. Submit signature + typed data to `/gas_sponsorship/prepare` 4. Sender signs and broadcasts the returned transaction **Workflow for gas_sponsorship=false:** 1. Call this endpoint with `gas_sponsorship=false` to get unsigned transaction 2. Owner signs and broadcasts the transaction 3. Owner pays gas --- ## Earn Endpoints ### POST /v2/earn/create_account **Purpose**: Create a new Earn Product Account for a user. **Request Body:** ```json { "chain": "base", "sender": "0x01E62835dd7F52173546A325294762143eE4a882", "owner": "0x01E62835dd7F52173546A325294762143eE4a882", "estimate_gas": true } ``` **Response:** ```json { "earn_account_address": "0x...", "transaction": { "to": "0x...", "data": "0x...", "value": "0", "gas": "200000" } } ``` ### POST /v2/earn/transfer **Purpose**: Transfer tokens between owner wallet and Earn Account. **Request Body:** ```json { "owner": "0x01E62835dd7F52173546A325294762143eE4a882", "chain": "base", "token": "USDC", "amount": "10", "action": "DEPOSIT", "gas_sponsorship": false } ``` **Parameters:** - `action`: `"DEPOSIT"` (wallet → account) or `"WITHDRAW"` (account → wallet) - `gas_sponsorship`: If `true`, requires `spender` parameter and returns EIP-712 typed data ### POST /v2/earn/manage **Purpose**: Deposit or withdraw from yield venues (vaults, Aave, Pendle). **Request Body (Vault):** ```json { "owner": "0x01E62835dd7F52173546A325294762143eE4a882", "chain": "base", "venue": { "type": "VAULT", "vault_address": "0xbeeF010f9cb27031ad51e3333f9aF9C6B1228183" }, "action": "DEPOSIT", "amount": "5", "gas_sponsorship": false, "fee": { "recipient": "0xFeeRecipient", "amount": 1.5, "denomination": "PERCENTAGE" } } ``` **Request Body (Aave):** ```json { "owner": "0x...", "chain": "base", "venue": { "type": "AAVE", "token": "USDC" }, "action": "DEPOSIT", "amount": "100", "gas_sponsorship": false } ``` **Request Body (Pendle PT):** ```json { "owner": "0x...", "chain": "arbitrum", "venue": { "type": "PENDLE_PT", "market_address": "0x...", "token": "USDC", "max_slippage_percent": 1.0 }, "action": "DEPOSIT", "amount": "1000", "gas_sponsorship": false } ``` **Fee Denominations:** - `"PERCENTAGE"`: Fee as % of amount (e.g., 1.5 = 1.5%) - `"FIXED"`: Fixed token amount (e.g., 0.1 = 0.1 USDC) - `"PERFORMANCE"`: % of profit on withdrawals (e.g., 20 = 20% of gains) ### GET /v2/earn/positions **Purpose**: Get all positions for a user. **Query Parameters:** - `chain` (required): Chain name - `owner` (required): Owner address **Response:** ```json { "user_positions": [ { "TYPE": "VAULT", "vault_address": "0x...", "vault_name": "Morpho USDC", "token_name": "USDC", "amount_in_underlying_token": "1050.5", "apy": "0.05" }, { "TYPE": "AAVE", "token": "USDC", "supplied_amount": "500", "apy": "0.03" } ] } ``` ### GET /v2/earn/vaults **Purpose**: List available ERC-4626 vaults. **Query Parameters:** - `chain` (optional): Filter by chain - `order_by` (optional): `"tvl_usd"`, `"apy"`, `"lifetime_return"` - `direction` (optional): `"asc"` or `"desc"` - `limit` (optional): Max results **Response:** ```json { "vaults": [ { "address": "0x...", "name": "Morpho USDC Vault", "protocol": "Morpho", "chain": "base", "underlying_token": "USDC", "tvl_usd": "50000000", "apy": "0.05", "lifetime_return": "0.12" } ] } ``` ### GET /v2/earn/aave_markets **Purpose**: List available Aave V3 markets. ### GET /v2/earn/pendle_markets **Purpose**: List available Pendle markets. --- ## Bridge Endpoints ### POST /v2/cctp/burn **Purpose**: Initiate a cross-chain USDC transfer by burning USDC on source chain. **Request Body:** ```json { "owner": "0x...", "source_chain": "ethereum", "destination_chain": "arbitrum", "amount": "1000", "fast_mode": false, "gas_sponsorship": false } ``` **Response:** ```json { "bridge_id": "uuid-xxx", "transaction": { "to": "0x...", "data": "0x...", "value": "0" } } ``` ### GET /v2/cctp/mint **Purpose**: Complete a cross-chain transfer by minting USDC on destination chain. **Query Parameters:** - `bridge_id` (required): Bridge ID from burn response - `burn_tx_hash` (required): Transaction hash of the burn - `sender` (required): Address that will execute mint **Response Codes:** - `200`: Attestation ready, transaction included - `202`: Attestation pending, poll again - `404`: Bridge not found --- ## Bundle Endpoint ### POST /v2/earn/bundle **Purpose**: Execute multiple actions in a single atomic transaction. **Request Body:** ```json { "owner": "0x...", "chain": "base", "gas_sponsorship": false, "actions": [ { "action_type": "V2_SWAP", "token_in": "WETH", "token_out": "USDC", "amount_in": "1", "max_slippage_percent": 0.5 }, { "action_type": "V2_MANAGE", "venue": { "type": "VAULT", "vault_address": "0x..." }, "action": "DEPOSIT", "amount": "ALL" } ] } ``` **Action Types:** - `V2_SWAP`: Swap tokens using 1inch - `V2_MANAGE`: Deposit/withdraw from venues **Special Values:** - `amount: "ALL"`: Use entire balance --- # 5. Common Workflows ## Deposit with Embedded Fee ```python manage_response = compass_api.earn.earn_manage( owner=WALLET_ADDRESS, chain=models.Chain.BASE, venue={ "type": "VAULT", "vault_address": "0xbeeF010f9cb27031ad51e3333f9aF9C6B1228183", }, action=models.EarnManageRequestAction.DEPOSIT, amount="100", gas_sponsorship=False, fee={ "recipient": FEE_RECIPIENT, "amount": 1.5, "denomination": "PERCENTAGE", }, ) ``` ## Withdraw with Performance Fee ```python manage_response = compass_api.earn.earn_manage( owner=WALLET_ADDRESS, chain=models.Chain.BASE, venue={ "type": "VAULT", "vault_address": "0xbeeF010f9cb27031ad51e3333f9aF9C6B1228183", }, action=models.EarnManageRequestAction.WITHDRAW, amount="ALL", gas_sponsorship=False, fee={ "recipient": FEE_RECIPIENT, "amount": 20, "denomination": "PERFORMANCE", }, ) ``` ## Gas-Sponsored Deposit ```python # 1. One-time: Set up Permit2 approval approve_response = compass_api.gas_sponsorship.gas_sponsorship_approve_transfer( owner=OWNER_ADDRESS, chain=models.Chain.BASE, token="USDC", gas_sponsorship=True, spender=SENDER_ADDRESS, ) # Owner signs, sender broadcasts # 2. Transfer to Earn Account transfer_response = compass_api.earn.earn_transfer( owner=OWNER_ADDRESS, chain=models.Chain.BASE, token="USDC", amount="100", action=models.EarnTransferRequestAction.DEPOSIT, gas_sponsorship=True, spender=SENDER_ADDRESS, ) # Owner signs typed data, call /gas_sponsorship/prepare, sender broadcasts # 3. Deposit into vault manage_response = compass_api.earn.earn_manage( owner=OWNER_ADDRESS, chain=models.Chain.BASE, venue={"type": "VAULT", "vault_address": "0x..."}, action=models.EarnManageRequestAction.DEPOSIT, amount="100", gas_sponsorship=True, spender=SENDER_ADDRESS, ) # Owner signs typed data, call /gas_sponsorship/prepare, sender broadcasts ``` ## Cross-Chain Bridge ```python # 1. Burn on source chain burn_response = compass_api.bridge.cctp_burn( owner=WALLET_ADDRESS, source_chain=models.SourceChain.ETHEREUM, destination_chain=models.DestinationChain.ARBITRUM, amount="1000", fast_mode=False, gas_sponsorship=False, ) # Sign and broadcast burn transaction # ... # 2. Poll for attestation and mint import time while True: mint_response = compass_api.bridge.cctp_mint( bridge_id=burn_response.bridge_id, burn_tx_hash=burn_tx_hash, sender=WALLET_ADDRESS, ) if hasattr(mint_response, 'transaction') and mint_response.transaction: break time.sleep(10) # Sign and broadcast mint transaction ``` --- # 6. Error Handling ## Common Error Responses **400 Bad Request:** ```json { "detail": "Invalid venue type. Expected VAULT, AAVE, or PENDLE_PT" } ``` **401 Unauthorized:** ```json { "detail": "Invalid authentication credentials" } ``` **404 Not Found:** ```json { "detail": "Vault not found: 0x..." } ``` **422 Validation Error:** ```json { "detail": [ { "loc": ["body", "amount"], "msg": "Amount must be positive", "type": "value_error" } ] } ``` **429 Rate Limited:** ```json { "detail": "Rate limit exceeded" } ``` --- # 7. Support - **Documentation**: https://docs.compasslabs.ai - **Discord**: https://discord.com/invite/ujetyJJPYr - **Email**: contact@compasslabs.ai - **Dashboard**: https://www.compasslabs.ai/login