# Analytics Source: https://docs.leokit.dev/api-reference/analytics /api-reference/openapi.json GET /analytics Get per-client usage analytics — quotes, deposits, pair breakdowns, and timelines ## GET /leokit/analytics Returns aggregated analytics for the authenticated client, including quote/deposit counts, top trading pairs, protocol usage, and a time-bucketed activity timeline. ### Authentication Requires `Api-Key` header (or `api_key` query parameter). ### Request Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------ | | `period` | string | No | Time window: `1h`, `24h` (default), `7d`, `30d`, `all` | ### Response Format **Status Code:** `200 OK` ```json theme={null} { "period": "24h", "generated_at": "2026-02-07T12:00:00.000Z", "client": { "name": "My App" }, "summary": { "total_quotes": 150, "total_deposits": 45, "conversion_rate": "30%", "avg_response_time_ms": 340, "near_ecosystem_fees_usd": 12.45 }, "top_pairs": [ { "from_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "to_asset": "BTC.BTC", "quotes": 25, "deposits": 10 } ], "protocols": [ { "name": "chainflip", "quotes_offered": 80 }, { "name": "thorchain", "quotes_offered": 60 } ], "timeline": [ { "timestamp": "2026-02-07T00:00:00.000Z", "quotes": 12, "deposits": 4 }, { "timestamp": "2026-02-07T01:00:00.000Z", "quotes": 8, "deposits": 2 } ] } ``` ### Fields Reference #### Summary | Field | Description | | ------------------------- | ------------------------------------------------------------- | | `total_quotes` | Number of quote requests in the period | | `total_deposits` | Number of deposit (swap initiation) calls | | `conversion_rate` | Percentage of quotes that converted to deposits | | `avg_response_time_ms` | Average quote response time in milliseconds | | `near_ecosystem_fees_usd` | NEAR-ecosystem affiliate fees attributed to your client (USD) | #### Top Pairs Returns up to 20 most-quoted asset pairs, sorted by quote count. Each pair shows how many quotes and deposits it received. #### Protocols Shows how many times each protocol returned a quote offer. A single quote request may produce offers from multiple protocols. #### Timeline Time-bucketed activity data. Bucket size varies by period: | Period | Bucket Size | | ------ | ----------- | | `1h` | 5 minutes | | `24h` | 1 hour | | `7d` | 1 day | | `30d` | 1 day | | `all` | 1 day | ### Caching Responses are cached for 30 seconds (`Cache-Control: private, max-age=30`). ### Errors | Status | Code | Description | | ------ | ----------------- | ------------------------------------------------------- | | `401` | `INVALID_API_KEY` | Missing or invalid `Api-Key` header | | `400` | `INVALID_PERIOD` | `period` must be one of `1h`, `24h`, `7d`, `30d`, `all` | | `500` | `INTERNAL_ERROR` | Server-side error while aggregating analytics | See the [error codes catalog](/reference/error-codes) for the full list. ### Examples #### Get 24-hour analytics (default) ```bash theme={null} curl -H "Api-Key: YOUR_API_KEY" https://api.leokit.dev/leokit/analytics ``` #### Get 7-day analytics ```bash theme={null} curl -H "Api-Key: YOUR_API_KEY" "https://api.leokit.dev/leokit/analytics?period=7d" ``` #### Get all-time analytics ```bash theme={null} curl -H "Api-Key: YOUR_API_KEY" "https://api.leokit.dev/leokit/analytics?period=all" ``` # Assets & Balances Source: https://docs.leokit.dev/api-reference/assets-balances Retrieve supported tokens and query wallet balances across multiple chains ## GET /leokit/assets Get list of supported tokens with current prices and metadata. ### Request **Authentication:** `Api-Key` header required ```http theme={null} GET /leokit/assets Api-Key: your_api_key_here ``` ### Response **Status Code:** `200 OK` **Cache:** 20 seconds ```json theme={null} { "tokens": [ { "identifier": "BTC.BTC", "blockchain": "BTC", "symbol": "BTC", "address": "", "decimals": 8, "price_usd": 98500.45, "icon": "https://static.leofinance.io/icons/btc.png", "chain_icon": "https://static.leofinance.io/chains/btc.png", "coingecko_id": "bitcoin" }, { "identifier": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "blockchain": "ETH", "symbol": "USDC", "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "decimals": 6, "price_usd": 1.0001, "icon": "https://static.leofinance.io/icons/usdc.png", "chain_icon": "https://static.leofinance.io/chains/eth.png", "coingecko_id": "usd-coin" }, { "identifier": "THOR.RUNE", "blockchain": "THOR", "symbol": "RUNE", "address": "", "decimals": 8, "price_usd": 4.25, "icon": "https://static.leofinance.io/icons/rune.png", "chain_icon": "https://static.leofinance.io/chains/thor.png", "coingecko_id": "thorchain" } ] } ``` ### Asset Format Each asset object contains the following fields: | Field | Type | Description | | -------------- | ------ | ----------------------------------------------------------- | | `identifier` | string | Unique asset identifier in format `CHAIN.SYMBOL[-ADDRESS]` | | `blockchain` | string | Blockchain short name (e.g., "BTC", "ETH", "THOR") | | `symbol` | string | Token symbol (e.g., "BTC", "USDC", "RUNE") | | `address` | string | Contract address for tokens, empty string for native assets | | `decimals` | number | Number of decimal places for the token | | `price_usd` | number | Current USD price | | `icon` | string | URL to token icon image | | `chain_icon` | string | URL to blockchain icon image | | `coingecko_id` | string | CoinGecko API identifier for price tracking | ### Asset Identifier Format The `identifier` field uses a standardized format: * **Native Assets:** `CHAIN.SYMBOL` (e.g., "BTC.BTC", "ETH.ETH", "THOR.RUNE") * **Token Assets:** `CHAIN.SYMBOL-ADDRESS` (e.g., "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") ### Filtering Rules The API automatically filters assets based on: 1. **Price Filter:** Tokens with `price_usd < $0.001` are excluded 2. **Banned Chains:** Chains in `BANNED_CHAINS` constant are filtered out 3. **Client Configuration:** Tokens on chains in client's `disabled_chains` are excluded 4. **Client-Specific Tokens:** Tokens from `custom_evm_tokens` table included if `client_api_key` matches ### Data Sources * **Primary:** Supabase `tokens` table (updated every 5 minutes) * **Secondary:** `custom_evm_tokens` table for client-specific additions ## GET /leokit/v2/assets Same data as `/leokit/assets` but in a **compact gzipped binary format** — \~170 KB on the wire vs \~3 MB JSON. Useful for mobile apps, embedded widgets, or any payload-sensitive client. The format is documented in detail at [Assets v2 (Compact Binary)](/api-reference/endpoint/v2-assets), including the wire layout and a TypeScript decoder. ```bash theme={null} curl -H "Api-Key: YOUR_API_KEY" \ -H "Accept-Encoding: gzip" \ https://api.leokit.dev/leokit/v2/assets \ --output assets.bin.gz ``` Both endpoints serve the same per-client filtered universe. Pick `/leokit/v2/assets` when payload size matters; pick `/leokit/assets` when you want full per-token JSON metadata (icons, coingecko\_id, etc.). ## POST /leokit/balances Get wallet balances across multiple chains in a single request. ### Request Body ```json theme={null} [ { "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "chain": "ETH" }, { "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "chain": "BTC" }, { "address": "thor1abc...", "chain": "THOR" } ] ``` **Request Format:** Array of wallet objects | Field | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------- | | `address` | string | Yes | Wallet address for the chain | | `chain` | string | Yes | Blockchain short name (e.g., "ETH") | ### Response **Status Code:** `200 OK` ```json theme={null} { "wallets": [ { "chain": "ETH", "balances": [ { "token_symbol": "ETH", "price": 3500.25, "balance": 2.5, "balance_usd": 8750.625, "icon": "https://static.leofinance.io/icons/eth.png" }, { "token_symbol": "USDC", "price": 1.0001, "balance": 5000, "balance_usd": 5000.5, "icon": "https://static.leofinance.io/icons/usdc.png" } ], "explorer": "https://etherscan.io/address/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "balance_usd": 13751.125 }, { "chain": "BTC", "balances": [ { "token_symbol": "BTC", "price": 98500.45, "balance": 1.25, "balance_usd": 123125.5625, "icon": "https://static.leofinance.io/icons/btc.png" } ], "explorer": "https://blockchain.com/btc/address/bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "balance_usd": 123125.5625 }, { "chain": "THOR", "balances": [ { "token_symbol": "RUNE", "price": 4.25, "balance": 1000, "balance_usd": 4250, "icon": "https://static.leofinance.io/icons/rune.png" }, { "token_symbol": "sTCY", "price": 0.15, "balance": 5000, "balance_usd": 750, "icon": "https://static.leofinance.io/icons/tcy.png" } ], "explorer": "https://runescan.io/address/thor1abc...", "balance_usd": 5000 } ] } ``` ### Response Format Each wallet object contains: | Field | Type | Description | | ------------- | ------ | ----------------------------------------- | | `chain` | string | Blockchain short name | | `balances` | array | Array of token balance objects | | `explorer` | string | Block explorer URL for the wallet address | | `balance_usd` | number | Total USD value of all tokens in wallet | ### Token Balance Object Each balance object contains: | Field | Type | Description | | -------------- | ------ | ---------------------------------- | | `token_symbol` | string | Token symbol (e.g., "ETH", "USDC") | | `price` | number | Current USD price per token | | `balance` | number | Token balance (in readable units) | | `balance_usd` | number | USD value of token balance | | `icon` | string | URL to token icon image | ## Supported Chains (30+) ### EVM Chains * **ETH** (Ethereum) * **ARB** (Arbitrum) * **AVAX** (Avalanche C-Chain) * **BASE** (Base) * **BSC** (Binance Smart Chain) * **POLYGON** (Polygon/Matic) * **OPTIMISM** (Optimism) * **FANTOM** (Fantom Opera) ### UTXO Chains * **BTC** (Bitcoin) * **LTC** (Litecoin) * **BCH** (Bitcoin Cash) * **DASH** (Dash) * **ZEC** (Zcash) * **DOGE** (Dogecoin) ### Cosmos Chains * **THOR** (THORChain) * **MAYA** (MAYAChain) * **GAIA** (Cosmos Hub) * **KUJI** (Kujira) ### Other Chains * **XRP** (Ripple) * **NEAR** (NEAR Protocol) * **SOL** (Solana) * **TRON** (Tron Network) ## Features ### Balance Fetching * **Parallel Fetching:** All chains queried simultaneously for optimal speed * **Scam Filter:** Tokens with `balance_usd < $0.10` are automatically excluded * **Staked Assets:** THORChain staked TCY (sTCY) automatically included for THOR wallets * **Multi-Chain Aggregation:** Uses Rango API for broad chain support * **Chain-Specific Fetchers:** Manual implementations for BTC, XRP, NEAR, and other specialized chains ### Data Sources * **Rango API:** Primary aggregator for most EVM and Cosmos chains * **Custom Fetchers:** Direct blockchain queries for BTC, XRP, NEAR * **THORChain API:** For RUNE and staked TCY balances * **Token Prices:** Real-time prices from CoinGecko via the `tokens` table ## Example Use Cases ### Building a Portfolio Dashboard ```javascript theme={null} // Request balances for multiple wallets const wallets = [ { address: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", chain: "ETH" }, { address: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", chain: "BTC" }, { address: "thor1abc...", chain: "THOR" } ]; const response = await fetch('/leokit/balances', { method: 'POST', headers: { 'Api-Key': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify(wallets) }); const data = await response.json(); // Calculate total portfolio value const totalValue = data.wallets.reduce((sum, wallet) => sum + wallet.balance_usd, 0); console.log(`Total Portfolio Value: $${totalValue.toFixed(2)}`); ``` ### Token Selector with Prices ```javascript theme={null} // Fetch all supported assets const response = await fetch('/leokit/assets', { headers: { 'Api-Key': 'your_api_key' } }); const { tokens } = await response.json(); // Filter tokens by chain const ethereumTokens = tokens.filter(token => token.blockchain === 'ETH'); // Sort by USD price const sortedTokens = tokens.sort((a, b) => b.price_usd - a.price_usd); ``` # Deposit Generation Source: https://docs.leokit.dev/api-reference/deposits Generate unsigned transactions for executing swaps across different blockchain ecosystems ## POST /leokit/deposit Generate unsigned transaction(s) for executing a swap based on a previously obtained quote. ### Request Body ```json theme={null} { "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "selected_protocol": "thorchain" } ``` | Field | Type | Required | Description | | ------------------- | ------ | -------- | ------------------------------------- | | `quote_id` | string | Yes | UUID from quote response | | `selected_protocol` | string | Yes | Protocol to use (must exist in quote) | ### Validation Rules 1. **Quote Existence** - Quote must exist in database 2. **Client Ownership** - Quote must belong to requesting client (API key match) 3. **Protocol Availability** - Selected protocol must be in quote's available protocols 4. **Quote Expiry** - Quote must not be expired (protocol-specific rules) 5. **Ecosystem Support** - Blockchain ecosystem must be supported ## Response Formats by Ecosystem The response format varies based on the source blockchain ecosystem. ## UTXO Chains (Bitcoin, Litecoin, Dogecoin, Dash) **Supported Chains:** BTC, LTC, DOGE, DASH ### PSBT Format (BTC, LTC, DOGE) ```json theme={null} { "type": "UTXO", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "from_asset": "BTC.BTC", "to_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "amount": "100000000", "decimals": 8, "memo": "=:ETH.USDC-0xA0b86991:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb:198500000" }, "unsigned_transaction": { "psbt_hex": "cHNidP8BAH0CAAAAAR...", "selected_utxos": [ { "txid": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890", "vout": 0, "value": 105000000, "script": "0014751e76e8199196d454941c45d1b3a323f1433bd6" } ], "chain": "BTC" } } ``` ### Raw Transaction Format (DASH) For DASH, `raw_unsigned_tx` is provided instead of `psbt_hex`: ```json theme={null} { "type": "UTXO", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "Xy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "from_asset": "DASH.DASH", "to_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "amount": "100000000", "decimals": 8, "memo": "=:ETH.USDC-0xA0b86991:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb:198500000" }, "unsigned_transaction": { "raw_unsigned_tx": "0200000001a1b2c3d4...", "selected_utxos": [ { "txid": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890", "vout": 0, "value": 105000000, "script": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac" } ], "chain": "DASH" } } ``` ### Key Features * **PSBT Format:** Bitcoin, Litecoin, Dogecoin use Partially Signed Bitcoin Transaction format * **OP\_RETURN Memo:** Embedded in transaction for THORChain/MAYA swaps * **UTXO Selection:** Automatic selection with fee estimation * **Change Outputs:** Only created if above dust threshold (546 sats for BTC/LTC, 1 DOGE for DOGE) ### Memo Length Limits * **BTC/LTC:** 150 bytes (OP\_RETURN limit: 80 bytes) * **DOGE:** 80 bytes ## EVM Chains (Ethereum, BSC, Arbitrum, etc.) **Supported Chains:** ETH, BSC, ARB, AVAX, BASE, POLYGON, OPTIMISM, FANTOM ### Native Token Transfer (ETH, BNB, etc.) ```json theme={null} { "type": "EVM", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "to_address": "0x1234567890AbcdEF1234567890aBcdef12345678", "from_asset": "ETH.ETH", "to_asset": "BTC.BTC", "amount": "1000000000000000000", "decimals": 18, "memo": "=:BTC.BTC:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh:9850000000" }, "unsigned_transactions": [ { "from": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "to": "0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146", "value": "0xde0b6b3a7640000", "data": "0x44bc937b0000000000000000000000001234567890abcdef1234567890abcdef12345678...", "nonce": 42, "chainId": 1, "gasLimit": "150000", "maxFeePerGas": "50000000000", "maxPriorityFeePerGas": "2000000000" } ] } ``` ### ERC20 Token Transfer with Approval ```json theme={null} { "type": "EVM", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "to_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "from_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "to_asset": "BTC.BTC", "amount": "2000000000", "decimals": 6, "memo": "=:BTC.BTC:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "unsigned_transactions": [ { "from": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "value": "0x0", "data": "0x095ea7b3000000000000000000000000d37bbe5744d730a1d98d8dc97c42f0ca46ad714600000000000000000000000000000000000000000000000000000000773594000", "nonce": 42, "chainId": 1, "gasLimit": "60000", "maxFeePerGas": "50000000000", "maxPriorityFeePerGas": "2000000000" }, { "from": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "to": "0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146", "value": "0x0", "data": "0x44bc937b...", "nonce": 43, "chainId": 1, "gasLimit": "180000", "maxFeePerGas": "50000000000", "maxPriorityFeePerGas": "2000000000" } ] } ``` ### Key Features * **Multiple Transactions:** ERC20 swaps require approval + deposit transactions * **USDT Special Case:** Allowance reset to 0 before setting new amount * **Nonce Sequencing:** Sequential nonce for each transaction in array * **EIP-1559:** Supports both legacy (gasPrice) and EIP-1559 (maxFeePerGas) formats * **Router Contracts:** THORChain/MAYA use depositWithExpiry function * **Direct Transfers:** Chainflip and NEAR use simple transfers to deposit addresses ### QR Code Enrichment (Chainflip & NEAR) When the selected protocol is `chainflip` or `near`, the deposit response is automatically enriched with a `deposit_address`, `payment_uri`, and `qr_url`. These allow users to complete swaps by scanning a QR code from any wallet app. ```json theme={null} { "type": "EVM", "protocol": "chainflip", "quote_id": "01936b4a-...", "details": { ... }, "unsigned_transactions": [ ... ], "deposit_address": "0x8a3c1f...", "payment_uri": "ethereum:0x8a3c1f...@1?value=500000000000000000", "qr_url": "https://cdn.leokit.dev/qr/01936b4a-....png" } ``` | Field | Type | Description | | ----------------- | ------ | -------------------------------------------------------------- | | `deposit_address` | string | The generated deposit address (Chainflip channel or NEAR) | | `payment_uri` | string | Chain-specific payment URI (BIP-21, EIP-681, Solana Pay, etc.) | | `qr_url` | string | CDN URL of a QR code PNG encoding the `payment_uri` | These fields are only present for `chainflip` and `near` protocols. Other protocols (thorchain, relay, etc.) do not include them. ## NEAR Protocol ### Native NEAR Transfer ```json theme={null} { "type": "NEAR", "protocol": "near", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "alice.near", "to_address": "bob.near", "from_asset": "NEAR.NEAR", "to_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "amount": "5000000000000000000000000", "decimals": 24 }, "unsigned_transaction": { "receiverId": "swap.1click.near", "actions": [ { "type": "Transfer", "params": { "deposit": "5000000000000000000000000" } } ] } } ``` ### NEP-141 Token Transfer ```json theme={null} { "type": "NEAR", "protocol": "near", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "alice.near", "to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "from_asset": "NEAR.USDC-a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near", "to_asset": "ETH.ETH", "amount": "2000000000", "decimals": 6 }, "unsigned_transaction": { "receiverId": "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near", "actions": [ { "type": "FunctionCall", "params": { "methodName": "ft_transfer", "args": { "receiver_id": "swap.1click.near", "amount": "2000000000", "memo": null }, "gas": "30000000000000", "deposit": "1" } } ] } } ``` ### Key Features * **YoctoNEAR:** All amounts in yoctoNEAR (10^24 for native NEAR) * **Fixed Gas:** 30 TGas for ft\_transfer calls * **Attached Deposit:** 1 yoctoNEAR required for token transfers * **Contract Parsing:** Extracts contract address from asset format ## Cosmos Chains **Supported Chains:** Cosmos Hub, Kujira, THORChain (native RUNE) ### MsgSend (Standard Cosmos Transfer) ```json theme={null} { "type": "COSMOS", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "cosmos1xyz...", "to_address": "thor1abc...", "from_asset": "GAIA.ATOM", "to_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "amount": "1000000", "decimals": 6, "memo": "=:ETH.USDC:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" }, "unsigned_transaction": { "msgs": [ { "typeUrl": "/cosmos.bank.v1beta1.MsgSend", "value": { "fromAddress": "cosmos1xyz...", "toAddress": "cosmos1depositaddress...", "amount": [ { "denom": "uatom", "amount": "1000000" } ] } } ], "fee": { "gas": "200000", "amount": [ { "denom": "uatom", "amount": "5000" } ] }, "memo": "=:ETH.USDC:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "chainId": "cosmoshub-4", "accountNumber": 12345, "sequence": 67 } } ``` ### MsgDeposit (THOR.RUNE Native Swaps) ```json theme={null} { "type": "COSMOS", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "thor1abc...", "to_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "from_asset": "THOR.RUNE", "to_asset": "BTC.BTC", "amount": "100000000", "decimals": 8, "memo": "=:BTC.BTC:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "unsigned_transaction": { "msgs": [ { "typeUrl": "/types.MsgDeposit", "value": { "coins": [ { "asset": "THOR.RUNE", "amount": "100000000" } ], "memo": "=:BTC.BTC:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "signer": "thor1abc..." } } ], "fee": { "gas": "250000", "amount": [ { "denom": "rune", "amount": "2000000" } ] }, "chainId": "thorchain-mainnet-v1", "accountNumber": 12345, "sequence": 67 } } ``` ### Key Features * **Gas Simulation:** Real-time estimation with 30% buffer * **Dynamic Fees:** Based on recommended\_gas\_rate from inbound\_addresses API * **Account Lookup:** Fetches sequence and account number automatically * **Fixed RUNE Fee:** 0.02 RUNE for THORChain transactions ## Other Ecosystems ### TRON ```json theme={null} { "type": "TRON", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "TXyz123...", "to_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "from_asset": "TRON.TRX", "to_asset": "BTC.BTC", "amount": "1000000", "decimals": 6, "memo": "=:BTC.BTC:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "unsigned_transaction": { "raw_data": {...}, "raw_data_hex": "0a02...", "txID": "abc123..." } } ``` ### Cardano ```json theme={null} { "type": "CARDANO", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "addr1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh...", "to_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "from_asset": "CARDANO.ADA", "to_asset": "BTC.BTC", "amount": "1000000", "decimals": 6, "memo": "=:BTC.BTC:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "unsigned_transaction": { "cbor_hex": "84a40081..." } } ``` ### XRP ```json theme={null} { "type": "XRP", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "rXyz123...", "to_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "from_asset": "XRP.XRP", "to_asset": "BTC.BTC", "amount": "1000000", "decimals": 6, "memo": "=:BTC.BTC:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "unsigned_transaction": { "tx_blob": "120000228000000024...", "hash": "ABC123..." } } ``` # Assets Source: https://docs.leokit.dev/api-reference/endpoint/assets /api-reference/openapi.json GET /assets Get supported tokens with prices # Balances Source: https://docs.leokit.dev/api-reference/endpoint/balances /api-reference/openapi.json POST /balances Get wallet balances across chains # Broadcast Transaction Source: https://docs.leokit.dev/api-reference/endpoint/broadcast /api-reference/openapi.json POST /broadcast Broadcast a signed UTXO transaction via LeoKit's NowNodes proxy (currently ZEC only) ## Overview `/leokit/broadcast` is a server-side proxy for broadcasting signed UTXO transactions on chains where public APIs don't allow CORS from a browser. Currently the only supported chain is **Zcash (ZEC)**, where the most reliable public node (NowNodes) requires a server-side API key. Use this endpoint when your client-side ZEC swap flow needs to push a signed transaction without exposing your NowNodes key. ## Endpoint ``` POST /leokit/broadcast ``` ## Request ### Headers | Header | Value | | -------------- | ------------------ | | `Content-Type` | `application/json` | | `Api-Key` | Your API key | ### Body | Field | Type | Required | Description | | -------- | ------ | -------- | ---------------------------------------- | | `chain` | string | Yes | Chain identifier. Currently only `zcash` | | `tx_hex` | string | Yes | Hex-encoded signed transaction | ## Response ### Success (200) ```json theme={null} { "data": { "txid": "f4d6c2..." }, "status": 200 } ``` | Field | Description | | ------ | ------------------------------------------------------ | | `txid` | Confirmed transaction ID returned by the upstream node | ## Errors | Status | Code | Description | | ------ | --------------------- | -------------------------------------------------------------- | | `400` | `BAD_REQUEST` | Missing/invalid `tx_hex`, or `chain` not in the supported list | | `405` | `METHOD_NOT_ALLOWED` | Non-POST request | | `502` | `BROADCAST_FAILED` | Upstream node rejected the broadcast (returned in error body) | | `503` | `SERVICE_UNAVAILABLE` | Server has no NowNodes API key configured | See the [error codes catalog](/reference/error-codes) for the full list. ## Example ```bash theme={null} curl -X POST https://api.leokit.dev/leokit/broadcast \ -H "Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "chain": "zcash", "tx_hex": "0500008085202f8901..." }' ``` ## Notes * Successful broadcasts return immediately once the upstream node accepts the transaction. Use [`/leokit/status`](/api-reference/transactions) to track confirmation. * ZEC transparent (`t-`) transactions are forwarded to NowNodes' BlockBook `sendtx` endpoint. Shielded (`z-`) transactions are not yet supported by this proxy. * This endpoint is a thin pass-through: the same `txid` you'd get by calling NowNodes directly. # CoinJoin Queues (Dash) Source: https://docs.leokit.dev/api-reference/endpoint/coinjoin-queues /api-reference/openapi.json GET /coinjoin/queues Real-time CoinJoin mixing queue activity on the Dash network ## Overview `/leokit/coinjoin/queues` exposes live CoinJoin mixing-queue activity on the Dash network. Data comes from a persistent background scanner that maintains TCP connections to Dash masternodes and collects `dsq` broadcasts as they happen. Use this to display real-time mixing-queue state in privacy wallets, or to audit CoinJoin liquidity across denominations. ## Endpoint ``` GET /leokit/coinjoin/queues ``` ## Request ### Authentication This endpoint is **public** — no `Api-Key` required. ### Query Parameters | Parameter | Type | Required | Description | | --------- | ------- | -------- | ------------------------------------------------------------------- | | `detail` | boolean | No | If `true`, include the full per-entry list. Default: summaries only | ## Response ### Success (200) — summary mode ```json theme={null} { "data": { "queues": [ { "denomination": "0.1 DASH", "ready": 3, "total_seen": 47 }, { "denomination": "0.01 DASH", "ready": 1, "total_seen": 22 }, { "denomination": "0.001 DASH", "ready": 0, "total_seen": 9 } ], "scanner": { "connected_masternodes": 12, "running_since": "2026-04-28T08:00:00.000Z", "last_dsq_at": "2026-04-28T12:01:14.000Z" } }, "status": 200 } ``` ### Success (200) — detail mode (`?detail=true`) ```json theme={null} { "data": { "queues": [ /* ... same as above ... */ ], "scanner": { /* ... */ }, "entries": [ { "denomination": "0.1 DASH", "masternode": "", "received_at": "2026-04-28T12:00:55.000Z", "ready": true } ] } } ``` ### Field Reference #### Queue Summary | Field | Description | | -------------- | ----------------------------------------------------- | | `denomination` | Mixing denomination label (e.g. `0.1 DASH`) | | `ready` | Count of ready (joinable) queues at this denomination | | `total_seen` | Lifetime count of `dsq` broadcasts seen since startup | #### Scanner Status | Field | Description | | ----------------------- | ----------------------------------------------------- | | `connected_masternodes` | Number of masternodes currently TCP-connected | | `running_since` | When the scanner started (ISO-8601) | | `last_dsq_at` | Timestamp of the most recent `dsq` broadcast received | ## Examples ### Summaries only ```bash theme={null} curl https://api.leokit.dev/leokit/coinjoin/queues ``` ### Full entry list ```bash theme={null} curl "https://api.leokit.dev/leokit/coinjoin/queues?detail=true" ``` ## Notes * Data resets when the scanner restarts. `total_seen` is a counter from process start, not a long-term cumulative. * The scanner only listens for `dsq` broadcasts; it does not participate in mixing itself. * For a deeper integration (push instead of poll), reach out via the [Dashboard](https://dash.leokit.dev) — there is no streaming variant of this endpoint yet. # Deposit Address Source: https://docs.leokit.dev/api-reference/endpoint/deposit-address /api-reference/openapi.json POST /deposit-address Get a deposit address with QR code for Chainflip or NEAR protocols (auto-selected if not specified) ## Overview `/leokit/deposit-address` returns a deposit address (plus payment URI and QR code) for protocols that don't require a signed transaction from the user — currently **Chainflip** and **NEAR**. The user simply sends funds to the address; the protocol handles the rest. This endpoint is similar to `/leokit/deposit` but specialized for address-based protocols. It also accepts `from_address` and `to_address` overrides for **wallet-less swap flows** (e.g. paid-on-receipt apps where the user has no connected wallet). If `protocol` is omitted, the best available chainflip/near quote is auto-selected by `expected_amount_out`. ## Endpoint ``` POST /leokit/deposit-address ``` ## Request ### Headers | Header | Value | | -------------- | ------------------ | | `Content-Type` | `application/json` | | `Api-Key` | Your API key | ### Body | Field | Type | Required | Description | | ------------------- | ------ | -------- | ---------------------------------------------------------------- | | `quote_id` | string | Yes | Quote ID from `/leokit/quote` or `/leokit/streaming-quotes` | | `protocol` | string | No | `chainflip` or `near`. If omitted, auto-selects the best offer | | `selected_protocol` | string | No | Alias for `protocol` | | `type` | string | No | Sub-protocol variant (e.g. Chainflip DCA). Default: regular swap | | `from_address` | string | No | Override refund/source address (for wallet-less swaps) | | `to_address` | string | No | Override destination/recipient address (for wallet-less swaps) | ## Response ### Success (200) ```json theme={null} { "data": { "deposit_address": "bc1q...", "amount": "0.01", "amount_raw": "1000000", "decimals": 8, "from_asset": "BTC.BTC", "to_asset": "ETH.ETH", "network": "BTC", "protocol": "chainflip", "chainflip_channel_id": 12345, "payment_uri": "bitcoin:bc1q...?amount=0.01", "qr_url": "https://cdn.leokit.dev/qr/01953c9a-....png", "expires_at": "2026-04-28T12:30:00.000Z" }, "status": 200 } ``` ### Field Reference | Field | Description | | ---------------------- | ------------------------------------------------------------ | | `deposit_address` | Address the user must send funds to | | `amount` | Decimal amount in source-asset units | | `amount_raw` | Same amount in base units (wei, sats, etc.) as a string | | `decimals` | Source-asset decimals | | `network` | Source chain code (`BTC`, `ETH`, `THOR`, etc.) | | `protocol` | Selected protocol (`chainflip` or `near`) | | `chainflip_channel_id` | Chainflip swap-channel ID (Chainflip only — `null` for NEAR) | | `payment_uri` | Wallet-scannable URI (BIP-21, EIP-681, Solana Pay, etc.) | | `qr_url` | CDN URL of a PNG QR code encoding the `payment_uri` | | `expires_at` | ISO-8601 timestamp after which the quote/channel expires | ## Wallet-less Swap Flow For applications without a connected wallet (e.g. embedded swap widgets), pass `from_address` and `to_address` directly: ```bash theme={null} curl -X POST https://api.leokit.dev/leokit/deposit-address \ -H "Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "quote_id": "01953c9a-...", "from_address": "bc1qsourceaddr...", "to_address": "0xRecipient..." }' ``` The user is then expected to send `amount` from the wallet of their choice to `deposit_address`. ## Errors | Status | Code | Description | | ------ | ----------------------------- | --------------------------------------------------------------- | | `400` | `SELECTED_QUOTE_IS_NOT_SET` | `quote_id` missing or empty | | `400` | `NO_DEPOSIT_ADDRESS_PROTOCOL` | Auto-selection failed: no chainflip/near quote in the offer set | | `400` | `UNSUPPORTED_PROTOCOL` | `protocol` is not `chainflip` or `near` | | `400` | Validation | Quote validation failed (expired, malformed, etc.) | | `401` | `INVALID_API_KEY` | Missing or invalid API key | | `502` | `MISSING_DEPOSIT_ADDRESS` | NEAR could not return a deposit address (live re-fetch failed) | See the [error codes catalog](/reference/error-codes) for the full list. ## See Also * [Generate Deposit](/api-reference/endpoint/generate-deposit) — for protocols that return unsigned transactions * [Simple Swap](/api-reference/endpoint/simple) — single-call quote + deposit-address for chainflip/near * [QR Code Enrichment](/api-reference/deposits#qr-code-enrichment-chainflip-near) # Gas Price Source: https://docs.leokit.dev/api-reference/endpoint/gas-price /api-reference/openapi.json GET /gas-price/{asset} Get current gas price of given network # Generate Deposit Source: https://docs.leokit.dev/api-reference/endpoint/generate-deposit /api-reference/openapi.json POST /deposit Prepare unsigned transactions for cross-chain swaps ## QR Code Enrichment When using **Chainflip** or **NEAR** as the selected protocol, the deposit response is automatically enriched with: | Field | Type | Description | | ----------------- | ------ | -------------------------------------------------------------- | | `deposit_address` | string | The generated deposit address (Chainflip channel or NEAR) | | `payment_uri` | string | Chain-specific payment URI (BIP-21, EIP-681, Solana Pay, etc.) | | `qr_url` | string | CDN URL of a QR code PNG encoding the `payment_uri` | These fields allow users to complete swaps by scanning a QR code from any wallet app, without needing to manually copy the deposit address. These fields are only present for `chainflip` and `near` protocols. Other protocols (thorchain, mayachain, relay, etc.) return only `unsigned_transactions`. ## Payment URI Standards The `payment_uri` follows the wallet standard for the source chain: | Source Chain | Standard | Example | | ---------------------------- | ---------- | ---------------------------------------------------- | | BTC, LTC, DOGE, DASH | BIP-21 | `bitcoin:bc1q...?amount=0.5` | | ETH, ARB, BASE (native) | EIP-681 | `ethereum:0x...@1?value=500000000000000000` | | ETH, ARB, BASE (ERC20) | EIP-681 | `ethereum:0xTOKEN@1/transfer?address=0x...&uint256=` | | SOL (native) | Solana Pay | `solana:So1...?amount=2` | | ZEC (transparent / shielded) | ZIP-321 | `zcash:t1...?amount=0.5` / `zcash:zs1...?amount=0.5` | | Other | Fallback | Plain deposit address | ## Chainflip Boost For Chainflip swaps, you can opt into the **Boost Pool** to get faster confirmation in exchange for a tip. | Field | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------------------------------------------------------ | | `boost_fee` | number | No | Maximum boost fee (basis points). Common values: `5`, `10`, `30`. `0` (default) = no boost | When `boost_fee > 0`, the deposit response includes a boost-channel ID. The user's deposit is fronted by liquidity providers as soon as it's seen in the mempool, instead of waiting for full chain confirmation. ```json theme={null} { "quote_id": "01953c9a-...", "selected_protocol": "chainflip", "boost_fee": 10 } ``` Refer to the Chainflip [Boost Pools documentation](https://docs.chainflip.io/) for the latest tier structure and capacity. The `boost_fee` parameter is silently ignored for non-Chainflip protocols. # Get Quote Source: https://docs.leokit.dev/api-reference/endpoint/get-quote /api-reference/openapi.json GET /quote Get quotes across 6 protocols # Notify Deposit Source: https://docs.leokit.dev/api-reference/endpoint/notify-deposit /api-reference/openapi.json POST /notify-deposit Notify NEAR 1Click API about completed deposits for faster swap processing ## Overview The notify-deposit endpoint notifies the NEAR 1Click API about a completed deposit transaction. This allows NEAR to preemptively verify the deposit, speeding up swap processing. This endpoint is specifically for NEAR protocol swaps. It's optional but recommended for faster swap execution. ## Endpoint ``` POST /leokit/notify-deposit ``` ## Request ### Headers | Header | Value | | -------------- | ------------------ | | `Content-Type` | `application/json` | | `Api-Key` | Your API key | ### Body ```json theme={null} { "txHash": "0x1234567890abcdef...", "depositAddress": "0xabcdef1234567890..." } ``` ### Parameters | Parameter | Type | Required | Description | | ---------------- | ------ | -------- | ------------------------------------------------------------ | | `txHash` | string | Yes | Transaction hash of the deposit (must start with `0x`) | | `depositAddress` | string | Yes | Deposit address provided in the quote (must start with `0x`) | ## Response ### Success (200) ```json theme={null} { "success": true, "status": "submitted", "correlationId": "abc123-def456", "message": "Deposit notification sent to NEAR" } ``` | Field | Description | | --------------- | ------------------------------------- | | `success` | Whether the notification was accepted | | `status` | Status from NEAR 1Click API | | `correlationId` | Tracking ID from NEAR | | `message` | Human-readable status message | ### Error Responses Missing or invalid parameters: ```json theme={null} { "error": "Missing required fields: txHash, depositAddress" } ``` Or invalid format: ```json theme={null} { "error": "Invalid format: txHash and depositAddress must be hex strings starting with 0x" } ``` NEAR API error forwarded: ```json theme={null} { "error": "NEAR 1Click API error", "details": { ... } } ``` Server error: ```json theme={null} { "error": "Failed to notify NEAR about deposit", "details": "Connection timeout" } ``` ## Example ```bash cURL theme={null} curl -X POST https://api.leokit.dev/leokit/notify-deposit \ -H "Content-Type: application/json" \ -H "Api-Key: your_api_key" \ -d '{ "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "depositAddress": "0xabcdef1234567890abcdef1234567890abcdef12" }' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.leokit.dev/leokit/notify-deposit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Api-Key': 'your_api_key' }, body: JSON.stringify({ txHash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', depositAddress: '0xabcdef1234567890abcdef1234567890abcdef12' }) }); const result = await response.json(); console.log(result); // { success: true, status: "submitted", correlationId: "...", message: "..." } ``` ```typescript TypeScript theme={null} interface NotifyDepositRequest { txHash: string; depositAddress: string; } interface NotifyDepositResponse { success: boolean; status: string; correlationId: string; message: string; } async function notifyDeposit( txHash: string, depositAddress: string ): Promise { const response = await fetch('https://api.leokit.dev/leokit/notify-deposit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Api-Key': process.env.LEOKIT_API_KEY! }, body: JSON.stringify({ txHash, depositAddress }) }); if (!response.ok) { throw new Error(`Notify failed: ${response.status}`); } return response.json(); } ``` ## When to Use Call this endpoint after broadcasting a deposit transaction for a NEAR-routed swap: Request a quote with NEAR as the destination or intermediary Call `/leokit/deposit` to get the deposit address and transaction Sign and broadcast the transaction to the source chain Call this endpoint with the transaction hash and deposit address Monitor the swap via `/leokit/status` ## Notes * This endpoint forwards requests to NEAR's 1Click API at `https://1click.chaindefuser.com/v0/deposit/submit` * The timeout for NEAR API calls is 10 seconds * Notification is optional but can significantly speed up swap processing * Only applicable for swaps routed through the NEAR protocol # List Routes Source: https://docs.leokit.dev/api-reference/endpoint/routes /api-reference/openapi.json GET /routes List protocols capable of swapping a given asset pair (respects client modularity settings) ## Overview `/leokit/routes` returns the protocols that can swap a given `from_asset` → `to_asset` pair, along with metadata for each (estimated time, intent-based or not, same-chain requirement). The response respects per-client `disabled_protocols` configured in the LeoKit Dashboard. Use this for UI like a "swap-via" picker, or to pre-filter protocols before requesting quotes. ## Endpoint ``` GET /leokit/routes ``` ## Request ### Authentication Authentication is **optional**. With an `Api-Key`, the response is filtered by your client's modularity settings (disabled protocols/chains). Without one, all globally-enabled protocols are returned. ### Query Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | -------------------------------------------------- | | `from_asset` | string | Yes | Source asset in `CHAIN.SYMBOL-ADDRESS` format | | `to_asset` | string | Yes | Destination asset in `CHAIN.SYMBOL-ADDRESS` format | ## Response ### Success (200) ```json theme={null} { "data": { "from_asset": "ETH.ETH", "to_asset": "BTC.BTC", "from_chain": "ETH", "to_chain": "BTC", "routes": [ { "protocol": "thorchain", "type": "cross-chain-dex", "estimated_seconds": 600, "is_intent_based": false, "requires_same_chain": false }, { "protocol": "chainflip", "type": "cross-chain-dex", "estimated_seconds": 120, "is_intent_based": false, "requires_same_chain": false } ], "unsupported": ["oneinch"] } } ``` ### Field Reference | Field | Description | | ------------- | ------------------------------------------------------- | | `from_chain` | Source chain code (e.g. `ETH`) | | `to_chain` | Destination chain code (e.g. `BTC`) | | `routes` | Array of usable protocols for this pair | | `unsupported` | Protocols that exist but can't route this specific pair | #### Route Object | Field | Type | Description | | --------------------- | ------- | -------------------------------------------------------------------------------------------- | | `protocol` | string | Protocol slug (e.g. `thorchain`, `chainflip`, `relay`, `near`, `harbor`) | | `type` | string | `cross-chain-dex`, `dex-aggregator`, `bridge`, `intent-bridge`, or `aggregator` | | `estimated_seconds` | number | Average time-to-settle for this protocol | | `is_intent_based` | boolean | True for solver-based intent protocols (Across, deBridge) | | `requires_same_chain` | boolean | True for protocols that only do same-chain swaps (e.g. 1inch). Cross-chain pairs unsupported | ### Caching Responses are cached for 60 seconds (`Cache-Control: public, max-age=60`). ## Errors | Status | Code | Description | | ------ | ------------- | ---------------------------------- | | `400` | `BAD_REQUEST` | Missing `from_asset` or `to_asset` | ## Examples ### Public request (all enabled protocols) ```bash theme={null} curl "https://api.leokit.dev/leokit/routes?from_asset=ETH.ETH&to_asset=BTC.BTC" ``` ### Authenticated request (filtered by client config) ```bash theme={null} curl -H "Api-Key: YOUR_API_KEY" \ "https://api.leokit.dev/leokit/routes?from_asset=ETH.ETH&to_asset=BTC.BTC" ``` ## Notes * Protocol metadata is static per protocol — `estimated_seconds` is an average, not a real-time measurement. * Internal protocol variants (e.g. `thorchain-aggregator-swapout`) are filtered out of the response. * For full per-protocol live quotes, follow up with [`/leokit/quote`](/api-reference/quotes) or [streaming quotes](/api-reference/endpoint/streaming-quotes). # Save Transaction Source: https://docs.leokit.dev/api-reference/endpoint/save-transaction /api-reference/openapi.json POST /save-transaction Save transaction hash for tracking # Simple Swap Source: https://docs.leokit.dev/api-reference/endpoint/simple /api-reference/openapi.json POST /simple One-call endpoint that quotes, generates deposit address, and returns a QR code ## How It Works ``` 1. POST /leokit/simple → Quotes Chainflip & NEAR, picks best, returns deposit + QR 2. User scans QR or sends → Sends funds to the deposit address 3. POST /leokit/status → Poll with quote_id for swap completion ``` Compared to the standard multi-step flow (`/quote` → `/deposit`), this reduces two API calls to one. Only works with deposit-address-based protocols (**Chainflip** and **NEAR**). ## Payment URI Standards The `payment_uri` follows the wallet standard for the source chain: | Source Chain | Standard | Example | | ----------------------- | ---------- | ---------------------------------------------------- | | BTC, LTC, DOGE, DASH | BIP-21 | `bitcoin:bc1q...?amount=0.5` | | ETH, ARB, BASE (native) | EIP-681 | `ethereum:0x...@1?value=500000000000000000` | | ETH, ARB, BASE (ERC20) | EIP-681 | `ethereum:0xTOKEN@1/transfer?address=0x...&uint256=` | | SOL (native) | Solana Pay | `solana:So1...?amount=2` | | Other | Fallback | Plain deposit address | ## Example: BTC to ETH ```bash theme={null} curl -X POST https://api.leokit.dev/leokit/simple \ -H "Content-Type: application/json" \ -H "Api-Key: YOUR_API_KEY" \ -d '{ "from_asset": "BTC.BTC", "to_asset": "ETH.ETH", "amount": "0.01", "from_address": "bc1q...", "to_address": "0x1234567890AbcdEF1234567890aBcdef12345678" }' ``` ## Errors | Status | Code | Description | | ------ | -------------------- | ------------------------------------------------------------------------- | | `400` | `INVALID_ASSET` | `from_asset` or `to_asset` is malformed or not in the supported list | | `400` | `INVALID_ADDRESS` | `from_address` or `to_address` not valid for the source/destination chain | | `400` | `UNSUPPORTED_PAIR` | No deposit-address-based route (Chainflip/NEAR) available for the pair | | `401` | `INVALID_API_KEY` | Missing or invalid `Api-Key` header | | `404` | `NO_QUOTE_AVAILABLE` | All deposit-address protocols rejected the pair | | `500` | `INTERNAL_ERROR` | Quote or deposit generation failed unexpectedly | See the [error codes catalog](/reference/error-codes) for the full list. # Streaming Quotes Source: https://docs.leokit.dev/api-reference/endpoint/streaming-quotes /api-reference/openapi.json GET /streaming-quotes Stream quote results in real-time as each protocol responds via Server-Sent Events ## Overview The streaming-quotes endpoint emits Server-Sent Events as each protocol returns a quote, instead of waiting for all protocols to complete. Use this for snappy UI — display the first quote in \~200ms instead of waiting 5+ seconds for the slowest aggregator. For a hands-on walkthrough see the [Streaming Quotes guide](/guides/streaming-quotes). ## Endpoint ``` GET /leokit/streaming-quotes ``` ## Request ### Authentication EventSource cannot set custom headers reliably, so authentication accepts both styles: | Method | Example | | ------------------------- | ------------------------------------------------- | | `Api-Key` header | `Api-Key: YOUR_API_KEY` (preferred when possible) | | `api_key` query parameter | `?api_key=YOUR_API_KEY` | ### Query Parameters | Parameter | Type | Required | Description | | -------------------- | ------ | -------- | ---------------------------------------------------- | | `from_asset` | string | Yes | Source asset in `CHAIN.SYMBOL-ADDRESS` format | | `to_asset` | string | Yes | Destination asset in `CHAIN.SYMBOL-ADDRESS` format | | `amount` | string | Yes | Amount in source-asset base units (wei, sats, etc.) | | `origin` | string | No | Source wallet address (improves quote accuracy) | | `destination` | string | No | Destination wallet address | | `streaming_interval` | number | No | THORChain/MAYAChain streaming interval (default `1`) | | `streaming_quantity` | number | No | THORChain/MAYAChain streaming sub-swap count | | `api_key` | string | No | Alternative to `Api-Key` header (see above) | ## Response ### Headers ``` Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive ``` ### Event Sequence Events are sent as `data: \n\n` chunks (legacy SSE format — no named `event:` fields). | Order | Event payload `type` | Description | | ----- | -------------------- | -------------------------------------------------------------------- | | 1 | `init` | Stream opened, includes `quote_id` and total protocols being queried | | 2..N | `quote` | One per protocol that returns a valid quote | | N+1 | `final` | Aggregated, sorted, deduplicated payload (after all settle) | | N+2 | `finished` | Stream closing | If no protocols return a usable quote, an `error` event is sent in place of `final`. ### Event Payloads ```json theme={null} { "type": "init", "quote_id": "01953c9a-...", "timestamp": "2026-04-28T12:00:00.000Z", "total": 7 } ``` ```json theme={null} { "type": "quote", "protocol": "chainflip", "data": { "expected_amount_out": "0.0498", "fees": [...], "out_asset_decimal": 18, "in_asset_decimal": 8 } } ``` ```json theme={null} { "type": "final", "quote_id": "01953c9a-...", "timestamp": "2026-04-28T12:00:00.000Z", "quotes": [ { "protocol": "chainflip", "data": { "...": "..." }, "expectedAmountOutNum": 0.0498, "totalFeesUsd": 1.23, "totalSwapSeconds": 120, "flags": ["FASTEST"] } ] } ``` ```json theme={null} { "type": "error", "code": "UNABLE_TO_RETRIEVE_QUOTES", "message": "...", "details": [ { "protocol": "thorchain", "message": "Pool unavailable" } ] } ``` ```json theme={null} { "type": "finished" } ``` ### Quote Object Fields (in `final` event) | Field | Description | | ---------------------- | ---------------------------------------------- | | `expectedAmountOutNum` | Numeric expected output (for sorting) | | `totalFeesUsd` | Total combined fees in USD | | `totalSwapSeconds` | Estimated time-to-settle | | `flags` | Tags like `FASTEST`, `CHEAPEST`, `BEST_OUTPUT` | ## Timeouts * **Total budget:** 8 seconds. Protocols that haven't responded by then are dropped. * **Per-protocol circuit breaker:** 5 seconds. Protocols that fail repeatedly are skipped. ## Examples ### Browser (EventSource) ```javascript theme={null} const url = new URL("https://api.leokit.dev/leokit/streaming-quotes"); url.searchParams.set("from_asset", "ETH.ETH"); url.searchParams.set("to_asset", "BTC.BTC"); url.searchParams.set("amount", "1000000000000000000"); url.searchParams.set("api_key", "YOUR_API_KEY"); const es = new EventSource(url.toString()); es.onmessage = (e) => { const event = JSON.parse(e.data); switch (event.type) { case "init": console.log("started", event.quote_id); break; case "quote": console.log("got", event.protocol); break; case "final": console.log("done", event.quotes.length); break; case "finished": es.close(); break; case "error": console.error(event); es.close(); break; } }; ``` ### Server (fetch) ```javascript theme={null} const res = await fetch( "https://api.leokit.dev/leokit/streaming-quotes?from_asset=ETH.ETH&to_asset=BTC.BTC&amount=1000000000000000000", { headers: { "Api-Key": "YOUR_API_KEY" } } ); const reader = res.body.getReader(); const decoder = new TextDecoder(); while (true) { const { value, done } = await reader.read(); if (done) break; const chunk = decoder.decode(value); for (const line of chunk.split("\n\n")) { if (!line.startsWith("data: ")) continue; const event = JSON.parse(line.slice(6)); // handle event } } ``` ## Errors | Status | Code | Description | | ------ | ---------------------- | ---------------------------------------------------------- | | `400` | `BAD_REQUEST` | Missing or malformed `from_asset`, `to_asset`, or `amount` | | `400` | `INVALID_ASSET_FORMAT` | Asset string not in `CHAIN.SYMBOL-ADDRESS` format | | `401` | `INVALID_API_KEY` | Missing or invalid API key | The `UNABLE_TO_RETRIEVE_QUOTES` error is delivered inside the SSE stream as a `type: "error"` event, not as an HTTP error. See the [error codes catalog](/reference/error-codes) for the full list. # Trace Source: https://docs.leokit.dev/api-reference/endpoint/trace /api-reference/openapi.json GET /trace/{traceId} Track your issue with Trace ID # Transaction Status Source: https://docs.leokit.dev/api-reference/endpoint/transaction-status /api-reference/openapi.json POST /status View status of your transaction # Assets v2 (Compact Binary) Source: https://docs.leokit.dev/api-reference/endpoint/v2-assets /api-reference/openapi.json GET /v2/assets Compact gzipped binary asset list — same data as /leokit/assets at ~170 KB instead of ~3 MB ## Overview `/leokit/v2/assets` returns the same supported-asset universe as [`/leokit/assets`](/api-reference/assets-balances), but in a **compact binary format**: * **\~170 KB gzipped** vs \~3 MB JSON * 5,000+ tokens with prices in a single response * Per-client cache, pre-built at asset-refresh time (no on-request serialization) * Strips icon URLs (clients derive them from the [CDN pattern](/sdk/utilities)) Use this when payload size matters — mobile apps, embedded widgets, anywhere you'd otherwise gzip the JSON yourself. ## Endpoint ``` GET /leokit/v2/assets ``` ## Request ### Authentication Authentication is **optional**. With an `Api-Key`, `disabled_chains` and client-restricted tokens are filtered out. Without one, the public asset universe is returned. ## Response ### Headers ``` Content-Type: application/octet-stream Content-Encoding: gzip Cache-Control: public, max-age=20 ``` ### Wire Format The body is a single gzipped blob. After decompression: ``` [ Header (9 bytes) ] magic : 4 bytes ASCII = "LK02" version : 1 byte = 2 timestamp : 4 bytes BE uint32 (unix seconds) [ Chain Dictionary ] chain_count : 2 bytes BE uint16 for each chain: name_len : 1 byte name : N bytes UTF-8 (e.g. "ETH", "BTC", "BSC") [ Address Dictionary ] address_count: 2 bytes BE uint16 for each address: addr_len : 1 byte address : N bytes UTF-8 [ Token Records ] token_count : 3 bytes BE uint24 for each token: chain_idx : 1 byte (index into Chain Dictionary) sym_len : 1 byte symbol : N bytes UTF-8 (e.g. "USDC", "ETH") addr_idx : 2 bytes BE uint16 (index into Address Dictionary, 0xFFFF = native) decimals : 1 byte price_usd : 4 bytes BE float32 flags : 1 byte (bit 0 = is_popular) ``` ### Reconstructing an Asset ID Asset IDs follow `CHAIN.SYMBOL-ADDRESS` format: ```typescript theme={null} const chain = chains[record.chain_idx]; const symbol = record.symbol; const address = record.addr_idx === 0xFFFF ? null : addresses[record.addr_idx]; const assetId = address ? `${chain}.${symbol}-${address}` : `${chain}.${symbol}`; ``` ### Caching The full binary is **per-client-pre-built** server-side and refreshed when the global asset list updates. Clients should cache locally for 20 seconds (the `max-age` value). ## Errors | Status | Description | | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `503` | Asset list not yet loaded (server just restarted). Body is plain text `"Assets not yet loaded"`. Retry after the `Retry-After` header (seconds). | ## Decoder Example (TypeScript) ```typescript theme={null} async function fetchV2Assets(apiKey?: string): Promise { const res = await fetch("https://api.leokit.dev/leokit/v2/assets", { headers: apiKey ? { "Api-Key": apiKey } : {}, }); // Browser/Bun: response is auto-decompressed const buf = new Uint8Array(await res.arrayBuffer()); const view = new DataView(buf.buffer); const decoder = new TextDecoder(); // Header const magic = decoder.decode(buf.subarray(0, 4)); // "LK02" if (magic !== "LK02") throw new Error("Bad magic"); const version = view.getUint8(4); const timestamp = view.getUint32(5, false); let off = 9; // Chain dictionary const chainCount = view.getUint16(off, false); off += 2; const chains: string[] = []; for (let i = 0; i < chainCount; i++) { const len = view.getUint8(off); off += 1; chains.push(decoder.decode(buf.subarray(off, off + len))); off += len; } // Address dictionary const addrCount = view.getUint16(off, false); off += 2; const addresses: string[] = []; for (let i = 0; i < addrCount; i++) { const len = view.getUint8(off); off += 1; addresses.push(decoder.decode(buf.subarray(off, off + len))); off += len; } // Tokens const tokenCount = (view.getUint8(off) << 16) | (view.getUint8(off + 1) << 8) | view.getUint8(off + 2); off += 3; const tokens: Asset[] = []; for (let i = 0; i < tokenCount; i++) { const chainIdx = view.getUint8(off); off += 1; const symLen = view.getUint8(off); off += 1; const symbol = decoder.decode(buf.subarray(off, off + symLen)); off += symLen; const addrIdx = view.getUint16(off, false); off += 2; const decimals = view.getUint8(off); off += 1; const priceUsd = view.getFloat32(off, false); off += 4; const flags = view.getUint8(off); off += 1; const chain = chains[chainIdx]; const address = addrIdx === 0xFFFF ? null : addresses[addrIdx]; tokens.push({ assetId: address ? `${chain}.${symbol}-${address}` : `${chain}.${symbol}`, chain, symbol, address, decimals, priceUsd, isPopular: (flags & 1) === 1, }); } return tokens; } ``` ## Notes * The binary format is versioned via the `LK02` magic + version byte. Future formats (`LK03`, etc.) will increment the magic. Always validate the magic before parsing. * Only **transparent** UTXO addresses are included. Shielded ZEC tokens are not part of the asset list. * For richer per-token metadata (icons, descriptions, etc.) use [`/leokit/assets`](/api-reference/assets-balances). # Register Webhook Source: https://docs.leokit.dev/api-reference/endpoint/webhook-create /api-reference/openapi.json POST /webhooks Register a new webhook endpoint to receive swap lifecycle events The full lifecycle, payload formats, signature verification, and Discord/Slack auto-detection are documented at [Webhooks](/api-reference/webhooks). The `secret` is only returned at creation time. Save it immediately — you'll need it to verify HMAC signatures on incoming webhook deliveries. # Delete Webhook Source: https://docs.leokit.dev/api-reference/endpoint/webhook-delete /api-reference/openapi.json DELETE /webhooks Delete a registered webhook by ID Pass the webhook `id` returned by [List Webhooks](/api-reference/endpoint/webhook-list) as the `id` query parameter. Once deleted, no further deliveries will be attempted. If a delivery is already in-flight (in the retry queue), it may still be attempted once before the worker observes the deletion. # List Webhooks Source: https://docs.leokit.dev/api-reference/endpoint/webhook-list /api-reference/openapi.json GET /webhooks List all registered webhooks for the authenticated client Returns every webhook currently registered under your API key. The `secret` is **not** included in this response — it was only available at creation time. See [Webhooks](/api-reference/webhooks) for the full event catalog and payload formats. # Analytics Source: https://docs.leokit.dev/api-reference/explorer/analytics /api-reference/openapi.json GET /explorer/analytics Period-bucketed swap analytics — timelines, top pairs, protocol/chain breakdown ## Endpoint ``` GET /leokit/explorer/analytics ``` No authentication required. Cached for 5 minutes per period. ## Request | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------ | | `period` | string | No | One of `7d`, `30d` (default), `90d`, `6m`, `1y`, `all` | The bucket size adapts to the period: | Period | Bucket size | | ---------------- | ----------------------- | | `7d`–`90d`, `6m` | 1 day | | `1y`, `all` | 1 week (Monday-aligned) | ## Response ```json theme={null} { "data": { "period": "30d", "generated_at": "2026-04-28T12:00:00.000Z", "summary": { "total_deposits": 51200, "total_quotes": 0, "total_volume_usd": 380120000.00, "total_fees_usd": 38012.45, "near_ecosystem_fees_usd": 18230.55, "conversion_rate": 0 }, "volume_timeline": [ { "date": "2026-04-01", "count": 1820, "volume_usd": 12480921.55, "fees_usd": 1248.09 } ], "protocols": [ { "name": "thorchain", "count": 18200, "volume_usd": 122480000.00, "success_rate": 1.0 } ], "top_pairs": [ { "from_asset": "ETH.USDC-0xA0b...", "to_asset": "BTC.BTC", "count": 482, "volume_usd": 12480.55 } ], "chains": [ { "chain": "ETH", "source_count": 18402, "dest_count": 4820 } ] } } ``` ## Field Reference #### Summary | Field | Description | | ------------------------- | ------------------------------------------------------------------------ | | `total_deposits` | Indexed swap count for the period | | `total_quotes` | Currently always `0` (quote-level data is not part of the explorer feed) | | `total_volume_usd` | USD volume across the period | | `total_fees_usd` | Sum of fees across the timeline (after merging NEAR ecosystem fees) | | `near_ecosystem_fees_usd` | NEAR-ecosystem fees received at `leodex.near` over the period | | `conversion_rate` | Currently always `0` (no quote data merged here) | #### `volume_timeline[]` | Field | Description | | ------------ | -------------------------------------------------------------------------------- | | `date` | Bucket start date (ISO `YYYY-MM-DD`). Weekly buckets are aligned to Monday | | `count` | Swap count in the bucket (includes synthetic NEAR-ecosystem entries when merged) | | `volume_usd` | USD volume in the bucket | | `fees_usd` | Affiliate fees in the bucket | NEAR-ecosystem fee transfers are merged into the timeline by date: each transfer adds its `fee_usd` and `implied_volume_usd` (computed as `fee / 10 bps`) to the matching bucket. #### `protocols[]` | Field | Description | | -------------- | ---------------------- | | `name` | Protocol slug | | `count` | Swap count | | `volume_usd` | USD volume | | `success_rate` | Currently always `1.0` | The `near` row reflects the merged NEAR-ecosystem traffic (implied volume + on-chain swap count). #### `top_pairs[]` Top 10 trading pairs in the period, sorted by `volume_usd`. #### `chains[]` Per-chain `source_count` (swaps originating on the chain) and `dest_count` (swaps landing on the chain), sorted by total. ## Errors | Status | Description | | ------ | -------------------------------------- | | `400` | `period` not one of the allowed values | # NEAR Earnings Source: https://docs.leokit.dev/api-reference/explorer/near-earnings /api-reference/openapi.json GET /explorer/near-earnings Affiliate fees collected by leodex.near from the NEAR Intents ecosystem ## Endpoint ``` GET /leokit/explorer/near-earnings ``` No authentication required. Cached for 10 minutes. ## Overview `leodex.near` is the LeoFinance fee-collection account for NEAR Intents. Incoming fungible-token (FT) transfers from `intents.near` to `leodex.near` represent affiliate fee payments. This endpoint aggregates all such transfers from NearBlocks (paginated until exhausted) and groups them by token contract. USD value is computed for stablecoin contracts (USDT, USDC). Other tokens have `usd_value: 0` since their on-chain price is not resolved here. ## Response ```json theme={null} { "data": { "generated_at": "2026-04-28T12:00:00.000Z", "total_usd": 18230.55, "tokens": [ { "token": "USDT", "contract": "usdt.tether-token.near", "amount": 12480.55, "decimals": 6, "usd_value": 12480.55, "tx_count": 482, "latest_tx": "abc123...", "latest_date": "2026-04-28T11:32:11.000Z" }, { "token": "USDC", "contract": "17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": 5750.00, "decimals": 6, "usd_value": 5750.00, "tx_count": 215, "latest_tx": "def456...", "latest_date": "2026-04-28T11:30:01.000Z" } ], "total_incoming_txs": 697 } } ``` ## Field Reference | Field | Description | | -------------------- | ------------------------------------------------------- | | `generated_at` | When this snapshot was built (ISO-8601) | | `total_usd` | Sum of `usd_value` across all token entries | | `total_incoming_txs` | Count of qualifying FT transfers (across all tokens) | | `tokens[]` | Per-token aggregation, sorted by `usd_value` descending | #### `tokens[i]` | Field | Description | | ------------- | -------------------------------------------------------- | | `token` | Token symbol from NearBlocks metadata | | `contract` | NEAR account ID of the token contract | | `amount` | Total received in human-readable units | | `decimals` | Token decimals | | `usd_value` | USD value (only computed for stablecoins; `0` otherwise) | | `tx_count` | Transfer count for this token | | `latest_tx` | Most recent transaction hash | | `latest_date` | Timestamp of `latest_tx` (ISO-8601) | ## Caching `X-Cache: HIT|MISS`. TTL 10 minutes. ## Errors | Status | Description | | ------ | ---------------------- | | `500` | NearBlocks API failure | ## Notes * The cache key is shared with `/leokit/explorer/stats` (`near_ecosystem_fees_usd`) and `/leokit/explorer/analytics` (`summary.near_ecosystem_fees_usd`). * Pagination uses NearBlocks' `page` parameter; the loop stops when a page returns fewer than 100 results. * Only `cause === "TRANSFER"` events from `involved_account_id === "intents.near"` are counted as fee transfers. # Onchain Lookup Source: https://docs.leokit.dev/api-reference/explorer/onchain /api-reference/openapi.json GET /explorer/onchain/{chain}/{hash} Raw chain-state lookup for a transaction hash on EVM or UTXO networks ## Endpoint ``` GET /leokit/explorer/onchain/{chain}/{hash} ``` No authentication required. ## Request | Path parameter | Type | Required | Description | | -------------- | ------ | -------- | -------------------------------------------------------- | | `chain` | string | Yes | Chain code (case-insensitive) — see supported list below | | `hash` | string | Yes | Transaction hash | ### Supported Chains #### EVM (via DRPC) `ETH`, `ARB` / `ARBITRUM`, `BASE`, `OP` / `OPTIMISM`, `BSC` / `BNB` / `BNBCHAIN`, `AVAX` / `AVAX_CCHAIN`, `POLYGON` / `MATIC`, `FTM`, `LINEA`, `SCROLL`, `BLAST`, `ZKSYNC`, `SONIC`, `HYPERLIQUID` #### UTXO (via mempool.space) `BTC`, `LTC`, `DOGE`, `BCH` ## Response ### EVM Chains ```json theme={null} { "data": { "type": "evm", "block_number": 19842018, "status": "success", "confirmations": 24, "gas_used": 165820, "effective_gas_price_gwei": "5.4321", "nonce": 142, "log_count": 4, "input_data": "0x..." } } ``` | Field | Description | | -------------------------- | ------------------------------------------------------------- | | `type` | `"evm"` | | `block_number` | Block height | | `status` | `"success"` (`0x1`), `"failed"` (`0x0`), or `null` if pending | | `confirmations` | `latestBlock - blockNumber` | | `gas_used` | Gas units consumed | | `effective_gas_price_gwei` | Effective gas price formatted as a 4-decimal string (gwei) | | `nonce` | Sender nonce | | `log_count` | Number of event logs | | `input_data` | Raw transaction calldata hex | ### UTXO Chains ```json theme={null} { "data": { "type": "utxo", "block_height": 842018, "confirmed_at": "2026-04-28T12:00:00.000Z", "fee_sat": 1820, "size": 412, "vsize": 240 } } ``` | Field | Description | | -------------- | ------------------------------------------ | | `type` | `"utxo"` | | `block_height` | Block height when confirmed | | `confirmed_at` | Block time as ISO-8601 | | `fee_sat` | Total fee in satoshis | | `size` | Transaction size in bytes | | `vsize` | Virtual size (witness-discounted) in bytes | ### Unknown Chain If the chain is not in either supported set: ```json theme={null} { "data": { "type": "unknown" } } ``` ## Errors | Status | Description | | ------ | --------------------------------- | | `400` | Missing `hash` path parameter | | `500` | Upstream RPC/explorer call failed | ## Examples ```bash theme={null} # Ethereum curl "https://api.leokit.dev/leokit/explorer/onchain/ETH/0xabc..." # Arbitrum (alias) curl "https://api.leokit.dev/leokit/explorer/onchain/ARBITRUM/0xdef..." # Bitcoin curl "https://api.leokit.dev/leokit/explorer/onchain/BTC/abc1234..." ``` ## Notes * EVM lookups return partial data if either `eth_getTransactionByHash` or `eth_getTransactionReceipt` fails — fields will be `null` rather than the request erroring out. * This endpoint is intentionally chain-agnostic at the response level (`type` discriminates EVM vs UTXO). Future chains will use new `type` values. * For multi-protocol swap status (the cross-chain settlement view), use [`/leokit/status`](/api-reference/transactions) instead. # Explorer API Overview Source: https://docs.leokit.dev/api-reference/explorer/overview Public, anonymized analytics over the global LeoKit swap dataset ## Overview The Explorer API exposes the public, anonymized swap dataset that powers [explorer.leokit.dev](https://explorer.leokit.dev). It provides aggregated stats, period analytics, transaction lookups, wallet activity, protocol pool TVL, NEAR-ecosystem fee tracking, and on-chain transaction lookups across EVM and UTXO networks. These endpoints are **public** — they do not require an `Api-Key`. They are not scoped to any individual client; the data covers all swaps brokered through LeoKit globally. ## Endpoints Find swaps by tx hash, wallet address, or partial match Top-level totals: 24h volume, total swaps, supported chains Period analytics: timelines, top pairs, protocol breakdown Paginated swap feed with chain/protocol filters Single-tx lookup with related-tx context Per-wallet summary, top pairs, activity heatmap Live TVL across MAYAChain, THORChain, Chainflip, LEO NEAR-ecosystem affiliate fees collected by leodex.near Raw chain-state lookup by chain + tx hash ## Common Conventions ### Authentication None. These endpoints are publicly accessible and intended to back analytics dashboards. ### Caching Most endpoints have server-side cache with `X-Cache: HIT|MISS` response headers. TTLs vary: | Endpoint | TTL | | ---------------------------------- | ------ | | `/explorer/stats` | 60 s | | `/explorer/analytics` | 5 min | | `/explorer/wallet/{addr}` | 60 s | | `/explorer/pools` | 5 min | | `/explorer/near-earnings` | 10 min | | `/explorer/transactions` | none | | `/explorer/search` | none | | `/explorer/tx/{id}` | none | | `/explorer/onchain/{chain}/{hash}` | none | ### Response Shape All responses follow: ```json theme={null} { "data": { /* ... */ } } ``` or for arrays: ```json theme={null} { "data": [ /* ... */ ], "count": 42 } ``` Errors use HTTP status + `{ "error": "...", "message": "..." }`. ### `ExplorerTransaction` Shape The shape used by `search`, `transactions`, `wallet.recent_txs`, and `tx`: ```json theme={null} { "id": "01953c9a-...", "tx_hash": "0xabc...", "from_asset": "ETH.USDC-0xA0b...", "to_asset": "BTC.BTC", "from_address": "0x1234...", "to_address": "bc1q...", "volume_usd": 1234.56, "protocol": "thorchain", "swap_date": "2026-04-28T12:00:00.000Z", "from_symbol": "USDC", "to_symbol": "BTC", "from_chain": "ETH", "to_chain": "BTC" } ``` # Pools Source: https://docs.leokit.dev/api-reference/explorer/pools /api-reference/openapi.json GET /explorer/pools Live TVL across MAYAChain, THORChain, Chainflip, and the LEO ecosystem ## Endpoint ``` GET /leokit/explorer/pools ``` No authentication required. Cached for 5 minutes. ## Response ```json theme={null} { "data": { "mayachain": { "pools": [ { "id": "BTC.BTC", "asset": "BTC.BTC", "token0": "BTC", "token1": "CACAO", "chain": "Bitcoin", "protocol": "mayachain", "tvl_usd": 12480000, "volume_24h_usd": 482015.55, "price_usd": 98500.45, "apy": 0.04, "lp_count": 42, "status": "available", "url": "https://www.mayascan.org/pool/BTC.BTC" } ], "total_tvl": 124800000, "cacao_price": 0.42 }, "thorchain": { "pools": [...], "total_tvl": 482000000, "rune_price": 4.25 }, "chainflip": { "pools": [...], "total_tvl": 122000000 }, "leo": { "pools": [...] } } } ``` ## Section Reference #### `mayachain` / `thorchain` | Field | Description | | ---------------------------- | ----------------------------------------------- | | `pools` | Array of `ProtocolPool` (see below) | | `total_tvl` | Sum of `tvl_usd` across `available` pools (USD) | | `cacao_price` / `rune_price` | Derived from highest-TVL pool (BTC depth ratio) | #### `chainflip` | Field | Description | | ----------- | ---------------------------------------------------------------- | | `pools` | Array of `ProtocolPool` from Chainflip RPC + DexScreener pricing | | `total_tvl` | Sum of `tvl_usd` across pools (USD) | #### `leo` | Field | Description | | ------- | ----------------------------------------------------------------------- | | `pools` | LEO/HBD/Hive-Engine pools from a different schema (see `LeoPool` below) | ### `ProtocolPool` Object | Field | Type | Description | | ---------------- | ------ | ------------------------------------------------------- | | `id` | string | Pool identifier (raw asset string) | | `asset` | string | Same as `id` | | `token0` | string | Asset symbol (e.g. `BTC`) | | `token1` | string | Quote symbol (`RUNE`, `CACAO`, or `USDC` for Chainflip) | | `chain` | string | Display chain name (e.g. `Bitcoin`, `BNB Chain`) | | `protocol` | string | `mayachain`, `thorchain`, or `chainflip` | | `tvl_usd` | number | Pool TVL in USD | | `volume_24h_usd` | number | 24-hour swap volume in USD | | `price_usd` | number | Asset price in USD | | `apy` | number | LP APY (decimal, e.g. `0.04` = 4%) | | `lp_count` | number | Number of liquidity providers | | `status` | string | `available`, `staged`, or `suspended` | | `url` | string | Native explorer link to the pool | ### `LeoPool` Object | Field | Type | Description | | ------------------- | ------ | ----------------------------------------------- | | `id` | string | LEO pool identifier | | `name` | string | Pool display name | | `token0` / `token1` | string | Asset symbols | | `chain` | string | Source chain (typically `Hive` for LEO pools) | | `dex` | string | DEX name | | `tvl_usd` | number | Pool TVL | | `volume_24h_usd` | number | 24-hour volume | | `price_usd` | number | Token price | | `price_change_24h` | number | 24-hour price change (decimal, can be negative) | | `url` | string | Pool URL | ## Caching `X-Cache: HIT|MISS`. TTL 5 minutes. Source endpoints (Midgard, Chainflip RPC, DexScreener, Hive Engine) all have an 8-second timeout — partial results are returned if any source times out. ## Errors The endpoint never errors as a unit; if all sources fail you get empty arrays, not a non-200 response. # Search Source: https://docs.leokit.dev/api-reference/explorer/search /api-reference/openapi.json GET /explorer/search Find swaps by transaction hash, wallet address, or partial match ## Endpoint ``` GET /leokit/explorer/search ``` No authentication required. ## Request | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------------------------------------------------- | | `q` | string | Yes | Search term (tx hash, wallet address, or partial). Max 200 chars | The endpoint auto-detects the input type: | Detected type | Match rule | | ---------------- | ------------------------------------------------------------------ | | `address_evm` | `0x[40 hex]` — exact match against `from_address` or `to_address` | | `address_thor` | `thor1[bech32]` — same | | `address_maya` | `maya1[bech32]` — same | | `address_btc` | `bc1...`, `1...`, `3...` — same | | `address_ltc` | `ltc1...`, `L...`, `M...` — same | | `address_doge` | Dogecoin format — same | | `address_near` | `*.near` or 64-hex implicit account — same | | `address_cosmos` | `kujira1...`, `osmo1...`, `cosmos1...` — same | | `tx_hash` | Falls through if not an address — exact then partial `ilike` match | | `text` | Last-resort partial match against either address column | ## Response ### Success (200) — wallet hit When the input is a recognized wallet address, the response includes a `wallet_summary`: ```json theme={null} { "data": [ /* up to 50 ExplorerTransaction objects */ ], "count": 50, "search_type": "address_evm", "wallet_summary": { "total_volume_usd": 482015.67, "transaction_count": 122, "first_seen": "2025-08-15T09:14:22.000Z" } } ``` ### Success (200) — tx-hash hit ```json theme={null} { "data": [ /* up to 20 ExplorerTransaction objects */ ], "count": 1, "search_type": "tx_hash" } ``` ### Success (200) — no results ```json theme={null} { "data": [], "count": 0, "search_type": "tx_hash" } ``` ## Errors | Status | Description | | ------ | -------------------------------------------------- | | `400` | Missing `q` parameter or `q` longer than 200 chars | ## Examples ```bash theme={null} # Wallet address curl "https://api.leokit.dev/leokit/explorer/search?q=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" # Transaction hash curl "https://api.leokit.dev/leokit/explorer/search?q=0xabc123..." # Partial address (text search) curl "https://api.leokit.dev/leokit/explorer/search?q=742d35" ``` # Stats Source: https://docs.leokit.dev/api-reference/explorer/stats /api-reference/openapi.json GET /explorer/stats Top-level totals over the entire LeoKit swap dataset (24h + lifetime) ## Endpoint ``` GET /leokit/explorer/stats ``` No authentication required. Cached for 60 seconds. ## Response ```json theme={null} { "data": { "total_swaps": 482015, "swaps_24h": 1842, "volume_24h_usd": 12480921.55, "total_volume_usd": 4720112350.20, "total_fees_usd": 481200.45, "near_ecosystem_fees_usd": 18230.55, "unique_chains": 28, "unique_wallets_24h": 982, "avg_swap_size_usd": 6770.40, "success_rate": 1.0, "protocols_active": 9, "supported_chains": 32, "supported_protocols": 11, "volume_by_protocol": { "thorchain": { "count": 192015, "volume_usd": 1820000000 }, "chainflip": { "count": 122104, "volume_usd": 945000000 }, "near": { "count": 51200, "volume_usd": 380000000 } } } } ``` ## Field Reference | Field | Description | | ------------------------- | ------------------------------------------------------------------------ | | `total_swaps` | Lifetime swap count | | `swaps_24h` | Swaps in the past 24 hours | | `volume_24h_usd` | USD volume in the past 24 hours | | `total_volume_usd` | USD volume across the most recent 10,000 indexed rows | | `total_fees_usd` | Sum of `(affiliate_fee_bps / 10000) * volume_usd` across the same window | | `near_ecosystem_fees_usd` | NEAR-ecosystem affiliate fees received at `leodex.near` | | `unique_chains` | Distinct source chains observed in the indexed window | | `unique_wallets_24h` | Distinct `from_address` values in the past 24 hours | | `avg_swap_size_usd` | `volume_24h_usd / swaps_24h` | | `success_rate` | Currently always `1.0` (failed swaps are not indexed) | | `protocols_active` | Distinct protocol names observed in the indexed window | | `supported_chains` | Total chains LeoKit can swap (configuration-derived, not data-derived) | | `supported_protocols` | Total protocols LeoKit can route to | | `volume_by_protocol` | Per-protocol `count` + `volume_usd` | ## Caching | Header | Value | | --------- | --------------- | | `X-Cache` | `HIT` or `MISS` | The cache TTL is 60 seconds. ## Errors | Status | Description | | ------ | --------------------------------------------- | | `500` | Failed to query Supabase or aggregate results | # Transactions Source: https://docs.leokit.dev/api-reference/explorer/transactions /api-reference/openapi.json GET /explorer/transactions Paginated swap feed with chain and protocol filters ## Endpoint ``` GET /leokit/explorer/transactions ``` No authentication required. ## Request | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ------------------------------------------------------------------------- | | `cursor` | string | No | ISO timestamp from a previous `next_cursor`. Returns rows older than this | | `limit` | number | No | Page size, 1–100. Default `50` | | `chain` | string | No | Filter by source or destination chain (e.g. `ETH`, `BTC`) | | `protocol` | string | No | Filter by protocol slug (e.g. `thorchain`) | ## Response ```json theme={null} { "data": [ /* up to `limit` ExplorerTransaction objects, newest first */ ], "next_cursor": "2026-04-28T11:42:18.000Z", "has_more": true } ``` | Field | Description | | ------------- | --------------------------------------------------------------------------------- | | `data` | Array of `ExplorerTransaction` (see [overview](/api-reference/explorer/overview)) | | `next_cursor` | Pass to next request as `cursor` to fetch the next page. `null` when exhausted | | `has_more` | `true` if more rows exist after this page | ## Errors | Status | Description | | ------ | ---------------------- | | `500` | Supabase query failure | ## Pagination Pattern ```javascript theme={null} let cursor = null; do { const url = new URL("https://api.leokit.dev/leokit/explorer/transactions"); url.searchParams.set("limit", "100"); if (cursor) url.searchParams.set("cursor", cursor); const { data, next_cursor, has_more } = await fetch(url).then(r => r.json()); for (const tx of data) handle(tx); cursor = next_cursor; } while (cursor && has_more); ``` ## Examples ```bash theme={null} # Latest 50 curl "https://api.leokit.dev/leokit/explorer/transactions" # Filter to BTC source/destination, 20 per page curl "https://api.leokit.dev/leokit/explorer/transactions?chain=BTC&limit=20" # Filter to Chainflip curl "https://api.leokit.dev/leokit/explorer/transactions?protocol=chainflip" # Next page curl "https://api.leokit.dev/leokit/explorer/transactions?cursor=2026-04-28T11:42:18.000Z" ``` # Transaction Detail Source: https://docs.leokit.dev/api-reference/explorer/tx /api-reference/openapi.json GET /explorer/tx/{id} Single-transaction lookup with native-scanner URL and related-tx context ## Endpoint ``` GET /leokit/explorer/tx/{id} ``` `{id}` is the swap row id or transaction hash. As a fallback, you can pass `?tx_hash=0x...` to look up by hash without using the path segment. No authentication required. ## Response ### Success (200) ```json theme={null} { "data": { "id": "01953c9a-...", "tx_hash": "0xabc...", "from_asset": "ETH.USDC-0xA0b...", "to_asset": "BTC.BTC", "from_address": "0x1234...", "to_address": "bc1q...", "volume_usd": 1234.56, "protocol": "thorchain", "swap_date": "2026-04-28T12:00:00.000Z", "from_symbol": "USDC", "to_symbol": "BTC", "from_chain": "ETH", "to_chain": "BTC", "scanner_url": "https://etherscan.io/tx/0xabc...", "related_txs": [ /* up to 5 ExplorerTransaction objects from the same `from_address` */ ] } } ``` | Field | Description | | ------------- | ----------------------------------------------------------------------- | | `scanner_url` | Native chain scanner URL for `tx_hash` (Etherscan, mempool.space, etc.) | | `related_txs` | Up to 5 most recent other swaps from the same `from_address` | The base fields match `ExplorerTransaction` (see [overview](/api-reference/explorer/overview)). ## Errors | Status | Description | | ------ | -------------------------------------------------- | | `400` | Missing `id` path parameter and no `tx_hash` query | | `404` | No row matched `id` or `tx_hash` | ## Examples ```bash theme={null} # By tx hash (path) curl "https://api.leokit.dev/leokit/explorer/tx/0xabc..." # By tx hash (query) curl "https://api.leokit.dev/leokit/explorer/tx/x?tx_hash=0xabc..." # By internal swap id curl "https://api.leokit.dev/leokit/explorer/tx/01953c9a-..." ``` # Wallet Source: https://docs.leokit.dev/api-reference/explorer/wallet /api-reference/openapi.json GET /explorer/wallet/{addr} Per-wallet activity summary, top pairs, frequent counterparts, and a 365-day heatmap ## Endpoint ``` GET /leokit/explorer/wallet/{addr} ``` No authentication required. Cached for 60 seconds. ## Request | Path parameter | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------ | | `addr` | string | Yes | Wallet address (max 200 chars) | The endpoint uses case-insensitive matching, so EVM addresses match in any case. ## Response ```json theme={null} { "data": { "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "summary": { "total_volume_usd": 482015.67, "transaction_count": 122, "received_count": 14, "first_seen": "2025-08-15T09:14:22.000Z", "last_seen": "2026-04-27T18:32:11.000Z", "avg_swap_usd": 3950.95 }, "relations": { "frequent_counterparts": [ { "address": "bc1q...", "count": 14, "volume_usd": 122480.55 } ], "received_from": [ { "address": "0x1234...", "count": 3, "volume_usd": 9420.10 } ] }, "top_pairs": [ { "from_symbol": "USDC", "to_symbol": "BTC", "from_asset": "ETH.USDC-0xA0b...", "to_asset": "BTC.BTC", "count": 24, "volume_usd": 122480.55 } ], "volume_timeline": [ { "date": "2026-04-01", "count": 4, "volume_usd": 12480.55 } ], "activity_heatmap": [ { "date": "2026-04-28", "count": 3 } ], "recent_txs": [ /* up to 10 ExplorerTransaction objects, newest first */ ] } } ``` ## Field Reference #### `summary` | Field | Description | | ------------------- | ---------------------------------------------------------------- | | `total_volume_usd` | USD volume sent (where address is `from_address`) | | `transaction_count` | Swaps initiated by this wallet | | `received_count` | Swaps where this wallet was the recipient (excluding self-sends) | | `first_seen` | Oldest swap timestamp (ISO-8601) | | `last_seen` | Most recent swap timestamp | | `avg_swap_usd` | `total_volume_usd / transaction_count` | #### `relations` | Field | Description | | ----------------------- | -------------------------------------------------------------- | | `frequent_counterparts` | Top 5 destination addresses this wallet sent to | | `received_from` | Top 5 source addresses that sent here (and aren't this wallet) | #### `top_pairs` Top 8 sent-from-this-wallet pairs by count. #### `volume_timeline` Last 30 days, daily buckets. Days with no activity are omitted. #### `activity_heatmap` Last 365 days, daily buckets, count only (no volume). For dashboard heatmap rendering. #### `recent_txs` Up to 10 most recent swaps initiated by this wallet (`ExplorerTransaction` shape — see [overview](/api-reference/explorer/overview)). ## Errors | Status | Description | | ------ | --------------------------------------- | | `400` | Missing `addr` or longer than 200 chars | | `404` | No swaps found from or to this address | ## Caching `Cache-Control: public, max-age=60`. The cache is per-address. ## Examples ```bash theme={null} curl "https://api.leokit.dev/leokit/explorer/wallet/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" curl "https://api.leokit.dev/leokit/explorer/wallet/bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" curl "https://api.leokit.dev/leokit/explorer/wallet/thor1abc..." ``` # Gas Prices Source: https://docs.leokit.dev/api-reference/gas-prices Get current and historical gas prices for supported blockchains ## GET /leokit/gas-price/\[asset] Get current or historical gas prices for a specific chain. ### Request ```http theme={null} GET /leokit/gas-price/ETH ``` OR ```http theme={null} GET /leokit/gas-price/BTC.BTC ``` **Path Parameter:** Chain name or asset identifier (e.g., "ETH", "BTC", "BTC.BTC") ## Response Formats The API returns different response formats based on the chain's pricing model. ## Fixed Price Chains (Cosmos) For Cosmos-based chains with fixed gas prices. ### Response **Status Code:** `200 OK` ```json theme={null} { "type": "fixed", "units": "rune", "gas_prices": 0.02 } ``` ### Fixed Price Chains | Chain | Token | Gas Price | | ----- | ----- | --------- | | THOR | RUNE | 0.02 | | MAYA | CACAO | 0.02 | | GAIA | ATOM | 0.025 | | KUJI | KUJI | 0.00119 | ### Response Fields | Field | Type | Description | | ------------ | ------ | ----------------------------------------- | | `type` | string | Always "fixed" for Cosmos chains | | `units` | string | Token denomination (e.g., "rune", "atom") | | `gas_prices` | number | Fixed gas price per transaction | ## Dynamic Price Chains (EVM, UTXO) For chains with variable gas prices. ### Response **Status Code:** `200 OK` ```json theme={null} { "type": "dynamic", "units": "gwei", "gas_prices": [ [50.5, 1705324800], [48.2, 1705324740], [52.1, 1705324680], [49.8, 1705324620] ] } ``` ### Response Fields | Field | Type | Description | | ------------ | ------ | --------------------------------------------------- | | `type` | string | Always "dynamic" for EVM and UTXO chains | | `units` | string | Unit of measurement (e.g., "gwei", "satoshi/vbyte") | | `gas_prices` | array | Array of \[price, timestamp] tuples | ### Gas Prices Array Format Each entry in the `gas_prices` array is a tuple: ```javascript theme={null} [price, timestamp] ``` * **price** (number): Gas price in specified units * **timestamp** (number): Unix timestamp * **ordering**: Descending by timestamp (most recent first) * **limit**: Last 200 entries ### Units by Chain Different chains use different units for gas pricing: **EVM Chains (gwei):** * ETH (Ethereum) * ARB (Arbitrum) * AVAX (Avalanche) * BSC (Binance Smart Chain) * BASE (Base) * POLYGON (Polygon) * OPTIMISM (Optimism) * FANTOM (Fantom) **UTXO Chains:** * **Bitcoin**: `satoshi/vbyte` * **Litecoin**: `satoshi/vbyte` * **Dogecoin**: `shibes/vbyte` ## Example Responses ### Ethereum (Dynamic) ```json theme={null} { "type": "dynamic", "units": "gwei", "gas_prices": [ [45.2, 1705324800], [47.8, 1705324740], [44.5, 1705324680], [46.1, 1705324620], [48.9, 1705324560] ] } ``` ### Bitcoin (Dynamic) ```json theme={null} { "type": "dynamic", "units": "satoshi/vbyte", "gas_prices": [ [25, 1705324800], [23, 1705324740], [26, 1705324680], [24, 1705324620], [22, 1705324560] ] } ``` ### THORChain (Fixed) ```json theme={null} { "type": "fixed", "units": "rune", "gas_prices": 0.02 } ``` ### Cosmos Hub (Fixed) ```json theme={null} { "type": "fixed", "units": "atom", "gas_prices": 0.025 } ``` ## Data Source * **Database:** Fetched from `gas_prices` table * **Updates:** Periodically updated by background workers * **Historical Data:** Available for trend analysis and prediction * **Retention:** Last 200 data points per chain ## Use Cases ### Estimating Transaction Costs Use gas prices to calculate estimated transaction fees before initiating a swap: ```javascript theme={null} // Fetch current gas price const response = await fetch('/leokit/gas-price/ETH', { headers: { 'Api-Key': 'your_api_key' } }); const data = await response.json(); if (data.type === 'dynamic') { // Get most recent price (first in array) const currentGasPrice = data.gas_prices[0][0]; console.log(`Current ETH gas price: ${currentGasPrice} ${data.units}`); // Calculate estimated fee (example: standard ETH transfer uses ~21000 gas) const gasLimit = 21000; const estimatedFeeGwei = currentGasPrice * gasLimit; const estimatedFeeETH = estimatedFeeGwei / 1e9; console.log(`Estimated transaction fee: ${estimatedFeeETH} ETH`); } else if (data.type === 'fixed') { console.log(`Fixed gas price: ${data.gas_prices} ${data.units}`); } ``` ### Gas Price Trends Analyze historical gas prices to recommend optimal transaction timing: ```javascript theme={null} const response = await fetch('/leokit/gas-price/ETH', { headers: { 'Api-Key': 'your_api_key' } }); const data = await response.json(); if (data.type === 'dynamic') { const prices = data.gas_prices.map(entry => entry[0]); // Calculate average const average = prices.reduce((a, b) => a + b, 0) / prices.length; // Get current price const current = prices[0]; // Determine if now is a good time to transact if (current < average * 0.9) { console.log('Gas prices are below average - good time to transact!'); } else if (current > average * 1.1) { console.log('Gas prices are above average - consider waiting'); } else { console.log('Gas prices are near average'); } } ``` ### Chart Visualization Display gas price trends over time: ```javascript theme={null} const response = await fetch('/leokit/gas-price/BTC', { headers: { 'Api-Key': 'your_api_key' } }); const data = await response.json(); if (data.type === 'dynamic') { // Prepare data for charting library const chartData = data.gas_prices.map(([price, timestamp]) => ({ x: new Date(timestamp * 1000), // Convert to milliseconds y: price })); // chartData is now ready for use with Chart.js, D3, etc. console.log('Chart data points:', chartData.length); } ``` ## Best Practices 1. **Cache Responses**: Gas prices don't change every second - cache for 10-30 seconds 2. **Handle Both Types**: Always check the `type` field and handle both "fixed" and "dynamic" responses 3. **Current Price**: For dynamic prices, the first array entry `gas_prices[0][0]` is the most recent 4. **Error Handling**: Implement fallback values if the endpoint is unavailable 5. **Display Units**: Always show the `units` field to users for clarity ## Error Responses **Unsupported Chain (404)** ```json theme={null} { "error": "Gas price data not available for this chain", "status": 404 } ``` **Invalid API Key (403)** ```json theme={null} { "error": "Unknown Api-Key", "status": 403, "code": "CLIENT_CONFIG_IS_NOT_SET" } ``` # API Introduction Source: https://docs.leokit.dev/api-reference/introduction Get started with the LeoKit API # API Introduction LeoKit provides a simple REST API for cross-chain swap quotes and execution. ### Base URL [https://api.leokit.dev](https://api.leokit.dev) ### Authentication All requests require an **Api-Key** header: ```bash theme={null} Api-Key: YOUR_API_KEY ``` Get your key from the LeoKit Dashboard → API Keys. ### Testing the API You can test endpoints directly in this documentation using the interactive "Try it" panels on each endpoint page. ### Endpoints Explore the full API: * [Get Quote](/api-reference/endpoint/get-quote) — Fetch best cross-chain routes * [Generate Deposit](/api-reference/endpoint/generate-deposit) — Prepare swap deposit * [Transaction Status](/api-reference/endpoint/transaction-status) — Track swap progress * [Gas Price](/api-reference/endpoint/gas-price) — Current network fees * [Trace](/api-reference/endpoint/trace) — Debug transactions Ready to integrate? [Start Integrating →](https://dash.leokit.dev/admin/login) # Quote Endpoints Source: https://docs.leokit.dev/api-reference/quotes Get swap quotes from multiple protocols and stream real-time updates ## GET /leokit/quote Get swap quotes from multiple protocols in a single request. ### Request Parameters | Parameter | Type | Required | Description | | -------------------- | ------ | -------- | ------------------------------------------------------------ | | `from_asset` | string | Yes | Source asset in format `CHAIN.SYMBOL-ADDRESS` | | `to_asset` | string | Yes | Destination asset in format `CHAIN.SYMBOL-ADDRESS` | | `amount` | string | Yes | Amount to swap in base units (e.g., "1000000" for 1 USDC) | | `destination` | string | Yes | Recipient address for the output asset | | `origin` | string | No | Source address (optional, some protocols use for validation) | | `affiliate` | string | No | Affiliate address for revenue sharing | | `slippage_bps` | number | No | Slippage tolerance in basis points (default: 150 = 1.5%) | | `streaming_interval` | number | No | Streaming swap interval for THORChain/MAYA (default: 3) | | `streaming_quantity` | number | No | Number of streaming swap chunks (default: 1) | ### Response Format **Status Code:** `200 OK` ```json theme={null} { "quotes": [ { "protocol": "thorchain", "data": { "expected_amount_out": "1985000000", "total_swap_seconds": 180, "fees": [ { "type": "Affiliate Fee", "asset": "BTC.BTC", "amount": 0.00003, "usd": 1.2 }, { "type": "Outbound Fee", "asset": "ETH.ETH", "amount": 0.001, "usd": 3.5 }, { "type": "Network Fee", "asset": "THOR.RUNE", "amount": 0.02, "usd": 0.08 } ], "route": [ "BTC.BTC", "THOR.RUNE", "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" ], "in_asset_decimal": "8", "out_asset_decimal": "6", "out_asset_price": "1.00000", "from_asset_price": "98500.00000", "recommended_slippage": 150 }, "expectedAmountOutNum": 1985.0, "totalFeesUsd": 4.78, "totalSwapSeconds": 180, "flags": ["OPTIMAL", "CHEAPEST"] }, { "protocol": "chainflip", "data": { "expected_amount_out": "1983500000", "total_swap_seconds": 60, "fees": [ { "type": "Deposit Fee", "asset": "BTC.BTC", "amount": 0.00001, "usd": 0.98 }, { "type": "Network Fee", "asset": "BTC.BTC", "amount": 0.000015, "usd": 1.47 }, { "type": "Broadcast Fee", "asset": "ETH.USDC", "amount": 2.5, "usd": 2.5 } ], "route": ["BTC", "USDC"], "in_asset_decimal": "8", "out_asset_decimal": "6", "out_asset_price": "1.00000", "from_asset_price": "98500.00000" }, "expectedAmountOutNum": 1983.5, "totalFeesUsd": 4.95, "totalSwapSeconds": 60, "flags": ["FASTEST"] } ], "timestamp": "2026-01-15T12:34:56.789Z", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890" } ``` ### Helper Fields (top-level on each quote) The server adds these fields to every quote to simplify client-side sorting and filtering. They are computed once per response by the optimal-route ranker. | Field | Type | Description | | ---------------------- | --------- | ------------------------------------------------------------------------ | | `expectedAmountOutNum` | number | `data.expected_amount_out` parsed and scaled by destination decimals | | `totalFeesUsd` | number | Sum of all `fees[].usd` for this quote | | `totalSwapSeconds` | number | `data.total_swap_seconds` (mirrored at top level for consistent sorting) | | `flags` | string\[] | Performance tags (see below). Empty array if no flag applies | ## Quote Flags Quotes are automatically tagged with performance indicators: * **FASTEST** - Lowest `totalSwapSeconds` * **CHEAPEST** - Lowest `totalFeesUsd` * **OPTIMAL** - Highest `expectedAmountOutNum` (tie-broken by fees, then speed) * **BEST\_OUTPUT** - Highest output across all quotes (alias of OPTIMAL on the winning quote) ### Fee Breakdown Examples Each quote includes a detailed fee breakdown with the following structure: ```json theme={null} { "type": "Fee Type", "asset": "CHAIN.SYMBOL", "amount": 0.001, "usd": 3.5 } ``` **Common Fee Types:** * **Affiliate Fee** - Revenue sharing with integrators * **Outbound Fee** - Blockchain network fee for sending output * **Network Fee** - Protocol or router fee * **Deposit Fee** - Fee for depositing into protocol * **Broadcast Fee** - Fee for broadcasting transaction to destination chain ## Error Responses **Missing Required Parameters (400)** ```json theme={null} { "error": "Missing required params: from_asset, to_asset, amount", "status": 400 } ``` **No Quotes Available (404)** ```json theme={null} { "error": "No quotes available from any protocol", "status": 404 } ``` **Internal Server Error (500)** ```json theme={null} { "error": "Internal server error", "details": "Error message", "status": 500 } ``` ## GET /leokit/streaming-quotes Stream quotes as Server-Sent Events (SSE) for real-time updates as protocols respond. ### Request Parameters Same as `/leokit/quote` endpoint. ### Response Format **Content-Type:** `text/event-stream` **Event Stream:** ``` data: {"type":"init","quote_id":"01936b4a-7c8e-7890-abcd-ef1234567890","timestamp":"2026-01-15T12:34:56.789Z","total":6} data: {"type":"quote","protocol":"chainflip","data":{"expected_amount_out":"1983500000",...}} data: {"type":"quote","protocol":"thorchain","data":{"expected_amount_out":"1985000000",...}} data: {"type":"quote","protocol":"relay","data":{"expected_amount_out":"1980000000",...}} data: {"type":"final","quotes":[...],"timestamp":"2026-01-15T12:34:58.123Z","quote_id":"01936b4a-7c8e-7890-abcd-ef1234567890"} data: {"type":"finished"} ``` ### Event Types | Type | Description | | ---------- | --------------------------------------------------------- | | `init` | Initial event with quote\_id and total expected protocols | | `quote` | Individual quote as it resolves from each protocol | | `final` | Aggregated final result with all quotes and flags | | `finished` | Stream completion signal | | `error` | Error occurred (e.g., no quotes available) | ### Stream Characteristics * **Safety Timeout:** 7 seconds maximum * **Connection:** Keep-alive with heartbeat * **Cache-Control:** `no-cache` * **Client Disconnect:** Gracefully handled, quote still logged to database ### Error Event ``` data: {"type":"error","data":"No quotes available from any protocol"} ``` # Transaction Management Source: https://docs.leokit.dev/api-reference/transactions Save and track the status of swap transactions ## POST /leokit/save-transaction Save the transaction hash after signing and broadcasting the unsigned transaction. ### Request Body ```json theme={null} { "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "tx_hash": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890" } ``` | Field | Type | Required | Description | | ---------- | ------ | -------- | -------------------------------- | | `quote_id` | string | Yes | UUID from quote response | | `tx_hash` | string | Yes | Transaction hash from blockchain | ### Success Response **Status Code:** `200 OK` ```json theme={null} { "message": "Transaction saved successfully" } ``` ### Error Response **Status Code:** `400 Bad Request` ```json theme={null} { "error": "Transaction not saved, please make sure you have entered the correct quote_id." } ``` ### Database Behavior * Upserts to `tx_status` table * Links transaction to deposit via quote\_id * Initial status: `"indexing"` * Used by status endpoint to track swap progress ## POST /leokit/status Get the current status of a swap transaction. ### Request Body ```json theme={null} { "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "tx_id": "0xa1b2c3d4..." } ``` | Field | Type | Required | Description | | ---------- | ------ | ----------- | ----------------------------------------------------- | | `quote_id` | string | Yes | UUID from quote response | | `tx_id` | string | Conditional | Required if not previously saved via save-transaction | ### Response **Status Code:** `200 OK` ```json theme={null} { "protocol": "thorchain", "network": "BTC", "req_id": "F8A2B1C3D4E5", "hash": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890", "status": "completed", "status_url": "https://track.ninerealms.com/tx/F8A2B1C3D4E5", "scanner": "https://etherscan.io/tx/0xa1b2c3...", "native_scanner": "https://blockchain.com/btc/tx/a1b2c3...", "type": "swap", "in_amount": 100000000, "from_token": "BTC.BTC", "to_token": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "from_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "date": 1705324800 } ``` ### Response Fields | Field | Type | Description | | ---------------- | ------ | ---------------------------------------------------- | | `protocol` | string | Protocol used for the swap | | `network` | string | Source blockchain network | | `req_id` | string | Protocol-specific request ID | | `hash` | string | Transaction hash on source chain | | `status` | string | Current transaction status (see Status Values below) | | `status_url` | string | Protocol-specific tracking URL | | `scanner` | string | Block explorer URL for destination chain | | `native_scanner` | string | Block explorer URL for source chain | | `type` | string | Transaction type (usually "swap") | | `in_amount` | number | Input amount in base units | | `from_token` | string | Source asset identifier | | `to_token` | string | Destination asset identifier | | `from_address` | string | Source wallet address | | `to_address` | string | Destination wallet address | | `date` | number | Unix timestamp of transaction creation | ## Status Values | Status | Description | | ----------- | ---------------------------------------------------------- | | `indexing` | Transaction submitted, waiting for blockchain confirmation | | `success` | Transaction confirmed on source chain | | `completed` | Swap fully executed, output received | | `failed` | Transaction failed or reverted | ## Status Flow The status endpoint follows this flow: 1. **Check Cache:** Queries `tx_status` table for cached status 2. **If Completed:** Returns cached data immediately 3. **If Pending:** Fetches from protocol-specific API 4. **Update Database:** Saves new status to cache This caching mechanism ensures fast response times for completed transactions while providing real-time updates for pending swaps. ## Protocol-Specific Status URLs Each protocol provides its own tracking interface: * **THORChain:** `https://track.ninerealms.com/tx/{req_id}` * **MAYAChain:** `https://track.mayachain.info/tx/{req_id}` * **Chainflip:** `https://scan.chainflip.io/tx/{tx_hash}` * **Relay:** `https://relay.link/status/{req_id}` * **NEAR:** `https://1click.chaindefuser.com/status/{req_id}` ## Example Usage ### Complete Transaction Flow 1. **Get Quote:** ```bash theme={null} GET /leokit/quote?from_asset=BTC.BTC&to_asset=ETH.USDC-0xA0b86991...&amount=100000000&destination=0x742d35... ``` 2. **Generate Deposit:** ```bash theme={null} POST /leokit/deposit { "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "selected_protocol": "thorchain" } ``` 3. **Sign & Broadcast Transaction** (client-side) 4. **Save Transaction Hash:** ```bash theme={null} POST /leokit/save-transaction { "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "tx_hash": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890" } ``` 5. **Check Status (polling):** ```bash theme={null} POST /leokit/status { "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890" } ``` ### Status Polling Best Practices * **Initial Poll:** Wait 10-15 seconds after broadcast before first status check * **Polling Interval:** Check every 15-30 seconds for pending transactions * **Stop Condition:** Stop polling when status is `completed` or `failed` * **Timeout:** Consider transaction failed if status remains `indexing` for > 30 minutes # Webhooks Source: https://docs.leokit.dev/api-reference/webhooks Register outbound webhooks to receive real-time swap lifecycle notifications ## Overview LeoKit webhooks push real-time event notifications to your server as swaps progress through their lifecycle. Discord and Slack webhook URLs are auto-detected and receive formatted embeds/blocks instead of raw JSON. ### Supported Events | Event | Description | When it fires | | ------------------- | --------------------- | ----------------------------------------------------- | | `quote.created` | A quote was requested | After `/leokit/quote` responds and the quote is saved | | `deposit.initiated` | A deposit was built | After `/leokit/deposit` returns transaction data | | `swap.pending` | Transaction submitted | After `/leokit/save-transaction` records the tx hash | | `swap.success` | Swap completed | Status polling detects completion | | `swap.failed` | Swap failed | Status polling detects failure | | `swap.refunded` | Swap refunded | Status polling detects refund | ### Webhook Types | Type | Auto-detected | Payload format | Signing | | ---------- | ------------------------------------- | --------------- | ----------- | | `standard` | Default | Raw JSON | HMAC-SHA256 | | `discord` | `discord.com` / `discordapp.com` URLs | Discord embed | None | | `slack` | `hooks.slack.com` URLs | Slack Block Kit | None | *** ## POST /leokit/webhooks Register a new webhook endpoint. ### Authentication Requires `Api-Key` header. ### Request Body ```json theme={null} { "url": "https://your-server.com/webhooks/leokit", "events": ["swap.success", "swap.failed", "swap.refunded"], "type": "standard" } ``` | Field | Type | Required | Description | | -------- | --------- | -------- | --------------------------------------------------------------------- | | `url` | string | Yes | HTTPS endpoint URL. Discord/Slack URLs are auto-detected. | | `events` | string\[] | No | Events to subscribe to. Defaults to all 6 events. | | `type` | string | No | `standard`, `discord`, or `slack`. Auto-detected from URL if omitted. | ### Response **Status Code:** `200 OK` ```json theme={null} { "data": { "id": "01953c9a-...", "url": "https://your-server.com/webhooks/leokit", "events": ["swap.success", "swap.failed", "swap.refunded"], "type": "standard", "is_active": true, "created_at": "2026-02-07T12:00:00.000Z", "secret": "a1b2c3d4e5f6..." } } ``` **Important:** The `secret` is only returned on creation. Save it immediately — you'll need it to verify webhook signatures. *** ## GET /leokit/webhooks List all webhooks for the authenticated client. ### Authentication Requires `Api-Key` header. ### Response ```json theme={null} { "data": { "webhooks": [ { "id": "01953c9a-...", "url": "https://your-server.com/webhooks/leokit", "events": ["swap.success", "swap.failed", "swap.refunded"], "type": "standard", "is_active": true, "created_at": "2026-02-07T12:00:00.000Z", "updated_at": "2026-02-07T12:00:00.000Z" } ] } } ``` *** ## DELETE /leokit/webhooks?id= Delete a webhook by ID. ### Authentication Requires `Api-Key` header. ### Query Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | -------------------- | | `id` | string | Yes | Webhook ID to delete | ### Response ```json theme={null} { "data": { "deleted": true } } ``` *** ## Webhook Payload (Standard) Standard webhooks receive a JSON payload with HMAC-SHA256 signature headers. ### Headers | Header | Description | | ---------------------- | ------------------------------------- | | `X-LeoKit-Event` | Event type (e.g., `swap.success`) | | `X-LeoKit-Signature` | HMAC-SHA256 signature: `sha256=` | | `X-LeoKit-Delivery-ID` | Unique delivery ID | | `X-LeoKit-Timestamp` | Unix timestamp (seconds) | ### Body ```json theme={null} { "event": "swap.success", "timestamp": "2026-02-07T12:30:00.000Z", "data": { "quote_id": "01953c9a-...", "protocol": "chainflip", "status": "completed", "from_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "to_asset": "BTC.BTC", "from_address": "0x1234...", "to_address": "bc1q...", "tx_hash": "0xabc...", "scanner_url": "https://etherscan.io/tx/0xabc..." } } ``` ### Verifying Signatures Compute HMAC-SHA256 of the raw request body using your webhook `secret`, then compare to the `X-LeoKit-Signature` header: ```javascript theme={null} import crypto from "crypto"; function verifyWebhook(body, secret, signature) { const expected = "sha256=" + crypto .createHmac("sha256", secret) .update(body) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature) ); } ``` *** ## Delivery & Retry * **Timeout:** 10 seconds per delivery attempt * **Max attempts:** 5 * **Backoff schedule:** 30s, 1m, 5m, 15m, 1h * **Final state:** Marked as `failed` after 5 failed attempts Deliveries are processed by a background worker on the leader instance. ## Rate Limits * **Webhooks per client:** 20 active webhooks max * **Events per webhook:** No per-event rate limit; high-volume clients may see deliveries batched within the same backoff window * **TLS required:** `url` must be `https://` (Discord/Slack URLs are also enforced as HTTPS) *** ## Discord Webhooks Discord webhook URLs (`discord.com/api/webhooks/...`) automatically receive formatted embed payloads with color-coded event types: * Green: `swap.success` * Red: `swap.failed` * Yellow: `swap.refunded` * Blue: `quote.created`, `deposit.initiated`, `swap.pending` No signature verification — Discord handles authentication via the webhook URL secret. ## Slack Webhooks Slack incoming webhook URLs (`hooks.slack.com/services/...`) automatically receive Block Kit formatted payloads with header blocks, mrkdwn fields, and contextual metadata. *** ## Examples ### Register a Discord webhook ```bash theme={null} curl -X POST https://api.leokit.dev/leokit/webhooks \ -H "Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://discord.com/api/webhooks/123/abc"}' ``` Response will show `"type": "discord"` (auto-detected). ### Register for specific events only ```bash theme={null} curl -X POST https://api.leokit.dev/leokit/webhooks \ -H "Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/hook", "events": ["swap.success", "swap.failed"] }' ``` ### List webhooks ```bash theme={null} curl https://api.leokit.dev/leokit/webhooks \ -H "Api-Key: YOUR_API_KEY" ``` ### Delete a webhook ```bash theme={null} curl -X DELETE "https://api.leokit.dev/leokit/webhooks?id=01953c9a-..." \ -H "Api-Key: YOUR_API_KEY" ``` # FAQ Source: https://docs.leokit.dev/faq Frequently asked questions about LeoKit **How do I earn revenue with LeoKit?**\ Set your affiliate fee in the dashboard (0–1000 BPS). It's added to every swap — you keep the difference. **What chains and tokens are supported?**\ 107+ DEXes, 20,000+ tokens across all major chains (BTC, ETH, SOL, AVAX, and more) via THORChain, Maya Protocol, Chainflip, Near Intents, and Relay. **Do I need an SDK?**\ No — LeoKit uses simple REST APIs. No complex SDKs required. **How do I toggle assets or protocols?**\ Use the dashboard — one-click toggles for full modularity. **How is support?**\ 24/7 dedicated help. Reach out via the [landing page form](https://leokit.dev). More questions? [Contact us](https://leokit.dev). # Custom Fees & Payouts Source: https://docs.leokit.dev/fees Monetize swaps with full control over your revenue Earn on every swap. ### Set Your Affiliate Fee * Go to **Fee Configuration** in the dashboard * Choose any rate from 0–1000 BPS (0.00%–10.00%) * 0 BPS = free swaps (ideal for testing or high-volume apps) Your fee is added on top of protocol costs — users see the total, you keep the difference. LeoKit charges 10 BPS. Whatever you set in your dashboard is kept by you (i.e. set 50 BPS and your user pays 60 BPS in total fees. 10 to LeoKit and 50 to you) ### Configure Payout Wallets * Set protocol-specific addresses for fee collection * Fees route directly to your wallets * No middleman — full control Revenue settles automatically based on your settings. [Open Dashboard →](https://dash.leokit.dev/admin/login) # Full Modularity Source: https://docs.leokit.dev/full-modularity Toggle chains, assets, and protocols with ease Enable only what your users need — reduce risk and simplify your integration. ### Toggle What You Support * Dashboard → **Asset Configuration** or **Protocol Configuration** * Turn individual chains, tokens, or protocols on/off with a single switch * Examples: * Disable high-risk assets * Limit to supported chains (e.g., only ETH and BTC) * Turn off specific protocols if preferred No code changes required after toggling — updates apply instantly. ### Why It Matters * Lower attack surface * Cleaner UI for your users * Full control over supported routes [Open Dashboard →](https://dash.leokit.dev/admin/login) # Authentication Source: https://docs.leokit.dev/getting-started/authentication Learn how to authenticate with the LeoKit API using API keys and configure your client settings ## API Key Authentication All LeoKit API endpoints require authentication via an API key. You can pass the API key using either the request header (recommended) or query parameter. ### Header Method (Recommended) Pass your API key in the `Api-Key` header: ```http theme={null} Api-Key: your_api_key_here ``` ### Query Parameter Method (Alternative) Alternatively, you can pass the API key as a query parameter: ```http theme={null} GET /leokit/quote?api_key=your_api_key_here&... ``` ## Client Configuration Each API key is associated with a client configuration that controls various aspects of the API behavior. These settings allow you to customize which chains and protocols are available to your application. ### Disabled Chains You can exclude specific blockchain networks from quotes and asset lists by providing an array of blockchain short names. **Example:** ```json theme={null} { "disabled_chains": ["SOL", "AVAX", "FANTOM"] } ``` This configuration would exclude Solana, Avalanche, and Fantom from all API responses. ### Disabled Protocols You can exclude specific DEX protocols from quote aggregation by providing an array of protocol UUIDs. **Example:** ```json theme={null} { "disabled_protocols": ["thorchain-uuid", "maya-uuid"] } ``` This configuration would prevent THORChain and MAYAChain quotes from being included in results. ### Client Fees Configure revenue sharing for each protocol using the client fees setting. This allows you to earn a percentage of each swap. **Example:** ```json theme={null} { "client_fees": { "fee_bps": 30, "thorchain_address": "thor1...", "mayachain_address": "maya1...", "chainflip_address": "0x...", "relay_address": "0x...", "near_address": "example.near" } } ``` **Parameters:** * `fee_bps`: Fee in basis points (30 = 0.30%) * Protocol-specific addresses: Your wallet addresses for receiving fees on each protocol ## Invalid API Key Response If you provide an invalid or missing API key, the API will return a 403 Forbidden error: **Status Code:** `403 Forbidden` ```json theme={null} { "error": "Unknown Api-Key", "status": 403, "code": "CLIENT_CONFIG_IS_NOT_SET" } ``` Make sure to include a valid API key with every request to avoid authentication errors. # Introduction Source: https://docs.leokit.dev/getting-started/introduction Learn about LeoKit API - a unified interface for cross-chain cryptocurrency swaps ## Overview The LeoKit API provides a unified interface for cross-chain cryptocurrency swaps, supporting multiple blockchain ecosystems and aggregating quotes from various decentralized exchange (DEX) protocols. The API handles the entire swap lifecycle from quote generation to transaction execution. ## Supported Protocols LeoKit aggregates quotes from 6 leading DEX protocols: * **THORChain** - Cosmos-based native cross-chain DEX * **MAYAChain** - THORChain fork with CACAO support * **Chainflip** - Substrate-based cross-chain protocol * **Relay** - EVM-focused aggregator and bridge * **NEAR** - NEAR Protocol native swaps via 1Click * **LeoBridge** - LeoFinance proprietary bridge ## Supported Blockchain Ecosystems LeoKit supports 8 major blockchain ecosystems with broad network coverage: * **EVM** - Ethereum, BSC, Arbitrum, Polygon, Avalanche, Base, Optimism, Fantom * **UTXO** - Bitcoin, Litecoin, Dogecoin, Dash, Bitcoin Cash, Zcash * **Cosmos** - Cosmos Hub, Kujira, THORChain, MAYAChain * **NEAR** - NEAR Protocol * **TRON** - Tron Network * **Cardano** - Cardano blockchain * **XRP** - Ripple/XRP Ledger * **Solana** - Solana blockchain (partial support) ## Base Information ### Content Type All API requests and responses use `application/json` format. ### CORS CORS is enabled for all origins, making it easy to integrate LeoKit into web applications. ### Rate Limiting Rate limiting is configured per client API key. Each API key has its own rate limit settings to ensure fair usage. ### Cache Strategy LeoKit implements strategic caching to optimize performance: * **Quote responses**: 5 seconds * **Assets list**: 20 seconds * **Completed transactions**: Long-term caching This caching strategy reduces latency while ensuring data freshness where it matters most. # Quick Start Source: https://docs.leokit.dev/getting-started/quick-start Get started with LeoKit API - make your first swap quote request in minutes ## Making Your First API Call Ready to get started? Here's a simple example of fetching swap quotes: ```bash theme={null} curl -X GET "https://api.leokit.dev/leokit/quote?from_asset=BTC.BTC&to_asset=ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&amount=100000000&destination=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \ -H "Api-Key: your_api_key_here" ``` This request will return quotes from all available protocols for swapping 1 BTC to USDC on Ethereum. ## Common API Endpoints Here's a quick reference of the most commonly used endpoints: | Endpoint | Method | Purpose | | -------------------------- | --------- | ---------------------------------- | | `/leokit/quote` | GET | Get swap quotes from all protocols | | `/leokit/streaming-quotes` | GET (SSE) | Stream quotes in real-time | | `/leokit/deposit` | POST | Generate unsigned transaction | | `/leokit/save-transaction` | POST | Save broadcasted transaction hash | | `/leokit/status` | POST | Get swap transaction status | | `/leokit/assets` | GET | List supported tokens with prices | | `/leokit/balances` | POST | Get multi-chain wallet balances | ## Common Parameters These parameters are used across multiple endpoints: | Parameter | Used In | Type | Description | | ------------------- | ------------------------ | ------ | ---------------------------- | | `from_asset` | Quote | string | Source asset identifier | | `to_asset` | Quote | string | Destination asset identifier | | `amount` | Quote | string | Amount in base units | | `destination` | Quote | string | Recipient address | | `origin` | Quote | string | Source address (optional) | | `quote_id` | Deposit, Status, Save TX | string | UUID from quote | | `selected_protocol` | Deposit | string | Protocol to use | | `tx_hash` | Save TX | string | Transaction hash | ## Response Time Expectations Understanding typical response times helps you build better user experiences: | Operation | Typical Time | Maximum Time | | --------------------- | ------------ | ------------------- | | Quote (HTTP) | 2-4 seconds | 10 seconds | | Quote (Streaming) | 3-6 seconds | 7 seconds (timeout) | | Deposit Generation | 1-3 seconds | 10 seconds | | Status Check (cached) | \<100ms | 1 second | | Status Check (API) | 1-2 seconds | 5 seconds | | Assets List | \<100ms | 1 second (cached) | | Balances | 2-5 seconds | 15 seconds | ## Best Practices Follow these best practices to build robust integrations: 1. **Always validate quote\_id** before requesting deposit generation 2. **Poll status endpoint** every 10-30 seconds, not more frequently 3. **Cache assets list** for at least 20 seconds to reduce API calls 4. **Use streaming quotes** for better UX with real-time updates 5. **Handle partial quote failures** gracefully - some protocols may fail while others succeed 6. **Implement exponential backoff** for failed requests 7. **Save transaction hashes** immediately after broadcast 8. **Monitor trace IDs** for debugging production issues 9. **Set reasonable slippage** (1.5-3% for most swaps) 10. **Validate addresses** before submitting to quote endpoint ## Protocol Support by Ecosystem Different protocols support different blockchain ecosystems: | Protocol | EVM | UTXO | Cosmos | NEAR | Other | | --------- | --- | ---- | ------ | ---- | ------------- | | THORChain | ✅ | ✅ | ✅ | ❌ | ✅ (XRP, TRON) | | MAYAChain | ✅ | ✅ | ✅ | ❌ | ❌ | | Chainflip | ✅ | ✅ | ❌ | ❌ | ❌ | | Relay | ✅ | ❌ | ❌ | ❌ | ❌ | | NEAR | ✅ | ❌ | ❌ | ✅ | ❌ | | Rango | ✅ | ✅ | ✅ | ✅ | ✅ (All) | ## Error Code Categories Understanding error codes helps with debugging: * **400-level**: Client errors (validation, parameters, quotes) * **403**: Authentication failures * **404**: Resources not found (quotes, traces) * **500-level**: Server errors (internal, external API failures) ## Next Steps Now that you understand the basics, explore the detailed endpoint documentation to learn about: * Fetching and comparing swap quotes * Generating unsigned transactions * Tracking swap status * Managing multi-chain assets and balances # Complete Swap Flow Source: https://docs.leokit.dev/guides/complete-swap-flow Step-by-step guide to executing a complete cross-chain swap from BTC to ETH USDC This guide walks you through a complete swap flow from Bitcoin (BTC) to Ethereum USDC using the LeoKit API. We'll cover all five steps: getting a quote, generating a deposit transaction, signing and broadcasting, saving the transaction hash, and tracking the swap status. ## Overview The complete swap flow consists of 5 steps: 1. **Get Quote** - Request swap rates from multiple protocols 2. **Generate Deposit** - Create an unsigned transaction 3. **Sign & Broadcast** - Sign with wallet and broadcast to blockchain 4. **Save Transaction** - Register the transaction hash with LeoKit 5. **Track Status** - Monitor swap progress until completion ## Example: BTC → ETH USDC Let's swap 1 BTC (100,000,000 satoshis) to USDC on Ethereum. ### Step 1: Get Quote Request quotes from all supported protocols to find the best rate. ```bash cURL theme={null} curl -X GET 'https://api.leodex.io/leokit/quote?from_asset=BTC.BTC&to_asset=ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&amount=100000000&destination=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb&origin=bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh&slippage_bps=150' \ -H 'Api-Key: your_api_key_here' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.leodex.io/leokit/quote?" + new URLSearchParams({ from_asset: "BTC.BTC", to_asset: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", amount: "100000000", // 1 BTC destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", origin: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", slippage_bps: "150", // 1.5% slippage tolerance }), { headers: { "Api-Key": "your_api_key_here", }, } ); const quote = await response.json(); console.log("Quote ID:", quote.quote_id); console.log( "Best protocol:", quote.quotes.find((q) => q.data.flags?.includes("OPTIMAL")).protocol ); ``` **Response:** ```json theme={null} { "quotes": [ { "protocol": "thorchain", "data": { "expected_amount_out": "9850000000", "total_swap_seconds": 180, "fees": [...], "route": ["BTC.BTC", "THOR.RUNE", "ETH.USDC-0xA0b86991..."], "flags": ["OPTIMAL", "CHEAPEST"] } } ], "timestamp": "2026-01-15T12:34:56.789Z", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890" } ``` Save the `quote_id` - you'll need it for all subsequent steps. Quote IDs expire after 15 minutes. ### Step 2: Generate Deposit Transaction Create an unsigned transaction using the quote ID and your selected protocol. ```bash cURL theme={null} curl -X POST 'https://api.leodex.io/leokit/deposit' \ -H 'Api-Key: your_api_key_here' \ -H 'Content-Type: application/json' \ -d '{ "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "selected_protocol": "thorchain" }' ``` ```javascript JavaScript theme={null} const deposit = await fetch("https://api.leodex.io/leokit/deposit", { method: "POST", headers: { "Api-Key": "your_api_key_here", "Content-Type": "application/json", }, body: JSON.stringify({ quote_id: quote.quote_id, selected_protocol: "thorchain", }), }); const depositData = await deposit.json(); const psbt = depositData.unsigned_transaction.psbt_hex; ``` **Response:** ```json theme={null} { "type": "UTXO", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "details": { "from_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "from_asset": "BTC.BTC", "to_asset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "amount": "100000000", "decimals": 8, "memo": "=:ETH.USDC-0xA0b86991:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb:985000000" }, "unsigned_transaction": { "psbt_hex": "cHNidP8BAH0CAAAAAR...", "selected_utxos": [...], "chain": "BTC" } } ``` For Bitcoin transactions, LeoKit returns a PSBT (Partially Signed Bitcoin Transaction) that you'll sign in the next step. ### Step 3: Sign & Broadcast (Client-Side) Sign the transaction with your wallet and broadcast it to the Bitcoin network. ```javascript theme={null} // Using bitcoinjs-lib import * as bitcoin from "bitcoinjs-lib"; const psbt = bitcoin.Psbt.fromHex(depositData.unsigned_transaction.psbt_hex); // Sign with wallet (example with private key) psbt.signAllInputs(keyPair); psbt.finalizeAllInputs(); const txHex = psbt.extractTransaction().toHex(); // Broadcast to Bitcoin network const txHash = await broadcastBitcoinTransaction(txHex); ``` Never expose your private keys. This example is for educational purposes. Use secure wallet libraries in production. ### Step 4: Save Transaction Hash Register the transaction hash with LeoKit to enable status tracking. ```bash cURL theme={null} curl -X POST 'https://api.leodex.io/leokit/save-transaction' \ -H 'Api-Key: your_api_key_here' \ -H 'Content-Type: application/json' \ -d '{ "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "tx_hash": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890" }' ``` ```javascript JavaScript theme={null} await fetch("https://api.leodex.io/leokit/save-transaction", { method: "POST", headers: { "Api-Key": "your_api_key_here", "Content-Type": "application/json", }, body: JSON.stringify({ quote_id: quote.quote_id, tx_hash: txHash, }), }); ``` ### Step 5: Track Status Poll the status endpoint to monitor your swap progress. ```bash cURL theme={null} curl -X POST 'https://api.leodex.io/leokit/status' \ -H 'Api-Key: your_api_key_here' \ -H 'Content-Type: application/json' \ -d '{ "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890" }' ``` ```javascript JavaScript theme={null} // Poll every 10 seconds const checkStatus = async () => { const status = await fetch("https://api.leodex.io/leokit/status", { method: "POST", headers: { "Api-Key": "your_api_key_here", "Content-Type": "application/json", }, body: JSON.stringify({ quote_id: quote.quote_id, }), }); const data = await status.json(); if (data.status === "completed") { console.log("Swap completed!"); console.log("Output transaction:", data.scanner); return data; } else if (data.status === "failed") { console.error("Swap failed"); return data; } else { console.log("Status:", data.status); setTimeout(checkStatus, 10000); // Check again in 10s } }; checkStatus(); ``` ## Status Values | Status | Description | | ------------------- | ------------------------------------------------- | | `pending` | Transaction broadcasted, waiting for confirmation | | `observed` | Transaction confirmed on source chain | | `inbound_observed` | Protocol detected the deposit | | `swap_in_progress` | Cross-chain swap executing | | `outbound_observed` | Tokens being sent to destination | | `completed` | Swap successful, tokens received | | `failed` | Swap failed (reverted or error) | ## Best Practices **Use appropriate polling intervals** - Poll every 10-15 seconds. Faster polling won't speed up the swap. **Save quote\_id persistently** - Store it in your database to track swaps even after page refreshes. **Handle timeouts gracefully** - Cross-chain swaps can take 5-10 minutes. Show progress indicators to users. **Test with small amounts first** - Always test new integrations with minimal amounts on testnet/mainnet. ## Next Steps Learn how to handle token approvals for ERC20 swaps Debug failed swaps using trace IDs # ERC20 Token Approvals Source: https://docs.leokit.dev/guides/erc20-approvals Handle ERC20 token approvals for cross-chain swaps When swapping ERC20 tokens (like USDC, USDT, DAI), you need to approve the protocol contract to spend your tokens before the actual swap. This guide explains the two-transaction approval flow and common edge cases. ## Why Approvals Are Needed ERC20 tokens require explicit approval before a smart contract can transfer them on your behalf. This is a security feature that prevents contracts from moving your tokens without permission. **Native tokens like ETH don't require approval** - Only ERC20 tokens need the approval step. ## Two-Transaction Flow When depositing ERC20 tokens, LeoKit returns **two unsigned transactions**: 1. **Approval Transaction** - Authorizes the protocol contract to spend your tokens 2. **Deposit Transaction** - Transfers tokens to initiate the swap Both must be sent sequentially, with the approval transaction confirmed before the deposit. ## Example: ETH USDC → BTC Let's swap 2000 USDC on Ethereum to BTC. ### Step 1: Get Quote ```javascript theme={null} const quote = await fetch( "https://api.leodex.io/leokit/quote?" + new URLSearchParams({ from_asset: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", to_asset: "BTC.BTC", amount: "2000000000", // 2000 USDC (6 decimals) destination: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", origin: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", }), { headers: { "Api-Key": "your_api_key_here" } } ).then((r) => r.json()); ``` ### Step 2: Generate Deposit (Returns 2 Transactions) ```javascript theme={null} const deposit = await fetch("https://api.leodex.io/leokit/deposit", { method: "POST", headers: { "Api-Key": "your_api_key_here", "Content-Type": "application/json", }, body: JSON.stringify({ quote_id: quote.quote_id, selected_protocol: "thorchain", }), }).then((r) => r.json()); console.log("Number of transactions:", deposit.unsigned_transactions.length); // Output: 2 ``` **Response Structure:** ```json theme={null} { "type": "EVM", "protocol": "thorchain", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "unsigned_transactions": [ { // Transaction #1: Approval "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "data": "0x095ea7b3...", "value": "0x0", "chainId": 1 }, { // Transaction #2: Deposit "to": "0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146", "data": "0x1fece7b4...", "value": "0x0", "chainId": 1 } ] } ``` ### Step 3: Sign & Send Both Transactions **Critical:** Wait for the approval transaction to be confirmed before sending the deposit transaction. Sending them simultaneously will cause the deposit to fail. ```javascript theme={null} import { ethers } from "ethers"; const provider = new ethers.providers.Web3Provider(window.ethereum); const signer = provider.getSigner(); // Send approval transaction first const approvalTx = await signer.sendTransaction( deposit.unsigned_transactions[0] ); console.log("Approval TX sent:", approvalTx.hash); // Wait for confirmation (1-2 blocks recommended) await approvalTx.wait(); console.log("Approval confirmed!"); // Then send deposit transaction const depositTx = await signer.sendTransaction( deposit.unsigned_transactions[1] ); console.log("Deposit TX sent:", depositTx.hash); const receipt = await depositTx.wait(); console.log("Deposit confirmed!"); // Save the deposit transaction hash await fetch("https://api.leodex.io/leokit/save-transaction", { method: "POST", headers: { "Api-Key": "your_api_key_here", "Content-Type": "application/json", }, body: JSON.stringify({ quote_id: quote.quote_id, tx_hash: depositTx.hash, }), }); ``` ## Understanding Approval Amounts LeoKit approves the **exact swap amount** by default. This is more secure than infinite approvals but requires a new approval for each swap. ### Exact Amount Approval (Default) ```solidity theme={null} // Approves exactly 2000 USDC approve(routerAddress, 2000000000) ``` **Pros:** * More secure (limits exposure) * User-friendly (clear intent) **Cons:** * Requires approval for every swap * Higher gas costs over time ### Infinite Approval (Not Recommended) Some protocols support infinite approvals, but LeoKit doesn't use them for security reasons: ```solidity theme={null} // NOT used by LeoKit approve(routerAddress, type(uint256).max) ``` ## USDT Edge Case: Approval Reset **USDT requires allowance reset to zero** before changing approval amounts. This is a quirk of the USDT token contract. If you're swapping USDT and have an existing approval, LeoKit will return **three transactions**: 1. **Reset Approval** - Set allowance to 0 2. **New Approval** - Set allowance to swap amount 3. **Deposit** - Execute the swap ```javascript theme={null} // USDT swap may return 3 transactions const deposit = await fetch("https://api.leodex.io/leokit/deposit", { method: "POST", headers: { "Api-Key": "your_api_key_here", "Content-Type": "application/json", }, body: JSON.stringify({ quote_id: quote.quote_id, selected_protocol: "thorchain", }), }).then((r) => r.json()); if (deposit.unsigned_transactions.length === 3) { console.log("USDT detected - requires approval reset"); // 1. Reset to zero const resetTx = await signer.sendTransaction( deposit.unsigned_transactions[0] ); await resetTx.wait(); // 2. Approve new amount const approveTx = await signer.sendTransaction( deposit.unsigned_transactions[1] ); await approveTx.wait(); // 3. Deposit const depositTx = await signer.sendTransaction( deposit.unsigned_transactions[2] ); await depositTx.wait(); } ``` ## Checking Current Allowance You can check if an approval already exists using ethers.js: ```javascript theme={null} const tokenContract = new ethers.Contract( "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC address ["function allowance(address owner, address spender) view returns (uint256)"], provider ); const currentAllowance = await tokenContract.allowance( "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", // Your address "0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146" // Router address ); console.log("Current allowance:", currentAllowance.toString()); ``` ## Gas Optimization Approvals cost gas. Here are strategies to minimize costs: If you're doing multiple swaps of the same token, consider approving a larger amount once instead of per-swap approvals. **Note:** LeoKit doesn't currently support this, but you can manually approve higher amounts. Some tokens support gasless approvals via EIP-2612 permits. This requires signature-based approvals instead of transactions. Not yet supported by LeoKit. Submit approval transactions during low gas periods (typically weekends) to save costs. ## Error Handling Common approval errors and solutions: | Error | Cause | Solution | | ------------------------- | ------------------------------ | ----------------------------------------- | | `Insufficient allowance` | Approval not confirmed yet | Wait for approval TX confirmation | | `Execution reverted` | Insufficient token balance | Check token balance includes amount + gas | | `Transaction underpriced` | Gas price too low | Increase gas price | | `Nonce too low` | Transactions sent out of order | Ensure sequential nonce management | ## Best Practices **Always wait for approval confirmation** - Don't send the deposit transaction until the approval is confirmed on-chain. **Handle USDT specially** - Check for 3-transaction responses when swapping USDT. **Show clear UI feedback** - Inform users they'll need to sign 2+ transactions before starting. **Estimate total gas costs** - Calculate approval + deposit gas costs and show users upfront. **Support transaction resumption** - If approval succeeds but deposit fails, allow users to retry just the deposit. ## Next Steps See the full swap workflow from quote to completion Debug failed approvals using trace IDs # Error Debugging Source: https://docs.leokit.dev/guides/error-debugging Debug failed transactions using trace IDs and error tracking LeoKit provides comprehensive error tracking through trace IDs. Every API error includes a `trace_id` that you can use to retrieve detailed debugging information, including the full request context, error details, and timestamps. ## How Trace IDs Work When an API request fails, the error response includes a `trace_id`: ```json theme={null} { "error": "Insufficient balance (including gas fees).", "status": 400, "code": "INSUFFICIENT_BALANCE", "trace_id": "tr_abc123xyz456", "context": { "required": "1.05 ETH", "available": "1.00 ETH" } } ``` You can use this `trace_id` to retrieve full error details from the tracking endpoint. ## Retrieving Error Details ### Endpoint ``` GET /leokit/trace/[traceId] ``` ### Example Request ```bash cURL theme={null} curl -X GET 'https://api.leodex.io/leokit/trace/tr_abc123xyz456' \ -H 'Api-Key: your_api_key_here' ``` ```javascript JavaScript theme={null} const trace = await fetch( "https://api.leodex.io/leokit/trace/tr_abc123xyz456", { headers: { "Api-Key": "your_api_key_here" } } ).then(r => r.json()); console.log("Error occurred at:", trace.timestamp); console.log("Endpoint:", trace.endpoint); console.log("Error details:", trace.error_details); ``` ### Response ```json theme={null} { "trace_id": "tr_abc123xyz456", "client_api": "client_key_here", "error_details": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient balance (including gas fees).", "status": 400, "context": { "required": "1.05 ETH", "available": "1.00 ETH", "gas_estimate": "0.05 ETH" } }, "timestamp": "2026-01-15T12:34:56.789Z", "endpoint": "/leokit/deposit", "request_params": { "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "selected_protocol": "thorchain" } } ``` ## Response Fields | Field | Description | | ----------------------- | ----------------------------------------- | | `trace_id` | Unique identifier for this error | | `client_api` | Your API key (for verification) | | `error_details.code` | Error code (e.g., `INSUFFICIENT_BALANCE`) | | `error_details.message` | Human-readable error message | | `error_details.status` | HTTP status code | | `error_details.context` | Additional context (varies by error) | | `timestamp` | When the error occurred (ISO 8601) | | `endpoint` | Which API endpoint failed | | `request_params` | Parameters that were sent | ## Common Use Cases ### 1. Debugging Failed Deposits ```javascript theme={null} async function debugFailedDeposit(traceId) { const trace = await fetch( `https://api.leodex.io/leokit/trace/${traceId}`, { headers: { "Api-Key": "your_api_key_here" } } ).then(r => r.json()); console.log("Failed endpoint:", trace.endpoint); console.log("Quote ID:", trace.request_params.quote_id); console.log("Protocol:", trace.request_params.selected_protocol); console.log("Error:", trace.error_details.message); // Check specific error types if (trace.error_details.code === "INSUFFICIENT_BALANCE") { console.log("User needs:", trace.error_details.context.required); console.log("User has:", trace.error_details.context.available); } } ``` ### 2. Error Pattern Analysis Track error frequencies to identify systemic issues: ```javascript theme={null} async function analyzeErrorPatterns(traceIds) { const traces = await Promise.all( traceIds.map(id => fetch(`https://api.leodex.io/leokit/trace/${id}`, { headers: { "Api-Key": "your_api_key_here" } }).then(r => r.json()) ) ); // Group by error code const errorCounts = traces.reduce((acc, trace) => { const code = trace.error_details.code; acc[code] = (acc[code] || 0) + 1; return acc; }, {}); console.log("Error distribution:", errorCounts); // Output: { INSUFFICIENT_BALANCE: 5, QUOTE_EXPIRED: 2, ... } } ``` ### 3. Customer Support Integration Display error details to support agents: ```javascript theme={null} function SupportTicket({ traceId }) { const [trace, setTrace] = useState(null); useEffect(() => { fetch(`https://api.leodex.io/leokit/trace/${traceId}`, { headers: { "Api-Key": "your_api_key_here" } }) .then(r => r.json()) .then(setTrace); }, [traceId]); if (!trace) return

Loading error details...

; return (

Error Report

Time:
{new Date(trace.timestamp).toLocaleString()}
Error Code:
{trace.error_details.code}
Message:
{trace.error_details.message}
Endpoint:
{trace.endpoint}
Request Parameters:
{JSON.stringify(trace.request_params, null, 2)}
); } ``` ### 4. Automated Monitoring Set up alerts for critical errors: ```javascript theme={null} async function monitorErrors(traceIds) { const criticalErrors = [ 'INTERNAL_SERVER_ERROR', 'PROTOCOL_TIMEOUT', 'BLOCKCHAIN_SYNC_ERROR' ]; for (const traceId of traceIds) { const trace = await fetch( `https://api.leodex.io/leokit/trace/${traceId}`, { headers: { "Api-Key": "your_api_key_here" } } ).then(r => r.json()); if (criticalErrors.includes(trace.error_details.code)) { // Send alert to monitoring system await sendAlert({ severity: 'critical', message: trace.error_details.message, traceId: trace.trace_id, timestamp: trace.timestamp }); } } } ``` ## Error Categories Errors are grouped into categories for easier debugging: ### Validation Errors (HTTP 400) Client-side issues like invalid parameters or malformed requests. **Common Codes:** * `BAD_REQUEST` - Malformed JSON or invalid types * `WRONG_PROTOCOL` - Unsupported protocol selected * `INVALID_TOLERANCE_BPS` - Slippage tolerance out of range **Debugging:** ```javascript theme={null} if (trace.error_details.status === 400) { console.log("Fix client-side validation:"); console.log("- Check parameter types"); console.log("- Verify required fields"); console.log("- Validate addresses"); } ``` ### Balance & Amount Errors (HTTP 400) Insufficient funds or amounts outside valid ranges. **Common Codes:** * `INSUFFICIENT_BALANCE` - Not enough tokens/gas * `NOT_ENOUGH_GAS` - Can't pay transaction fees * `UNDER_DUST_LIMIT` - Output amount too small **Debugging:** ```javascript theme={null} if (trace.error_details.code === 'INSUFFICIENT_BALANCE') { const { required, available } = trace.error_details.context; console.log(`User needs ${required} but has ${available}`); console.log("Suggest: Reduce swap amount or add funds"); } ``` ### Quote & Transaction Errors (HTTP 400/404) Issues with quotes or transaction generation. **Common Codes:** * `QUOTE_EXPIRED` - Quote older than 15 minutes * `QUOTE_NOT_FOUND` - Invalid quote\_id * `NO_ROUTES_AVAILABLE` - No protocols support this swap **Debugging:** ```javascript theme={null} if (trace.error_details.code === 'QUOTE_EXPIRED') { console.log("Quote expired at:", trace.timestamp); console.log("Suggest: Request a new quote"); } ``` ### Server & Protocol Errors (HTTP 500/503) Backend or protocol integration issues. **Common Codes:** * `INTERNAL_SERVER_ERROR` - Unexpected server error * `PROTOCOL_TIMEOUT` - Protocol didn't respond in time * `BLOCKCHAIN_SYNC_ERROR` - Node syncing issue **Debugging:** ```javascript theme={null} if (trace.error_details.status >= 500) { console.log("Server-side issue - retry may help"); console.log("If persists, contact support with trace_id:", trace.trace_id); } ``` ## Retention Policy Error logs are retained for **30 days**. Download critical traces within this window. After 30 days, the trace endpoint will return: ```json theme={null} { "error": "Trace not found or expired", "status": 404, "code": "TRACE_NOT_FOUND" } ``` ## Best Practices **Always save trace\_id** - Store it in your database alongside transaction records for later debugging. **Show trace\_id to users** - Include it in error messages so users can reference it in support tickets. **Implement retry logic** - For transient errors like `PROTOCOL_TIMEOUT`, automatically retry with exponential backoff. **Log error patterns** - Track which errors occur most frequently to improve your integration. **Use context fields** - The `context` object contains actionable debugging information specific to each error type. ## Error Response Handling Build a robust error handler that uses trace IDs: ```javascript theme={null} async function handleApiError(error) { if (!error.trace_id) { console.error("No trace_id available"); return; } // Fetch full error details const trace = await fetch( `https://api.leodex.io/leokit/trace/${error.trace_id}`, { headers: { "Api-Key": "your_api_key_here" } } ).then(r => r.json()); // Log to monitoring service await logToMonitoring({ traceId: trace.trace_id, errorCode: trace.error_details.code, endpoint: trace.endpoint, timestamp: trace.timestamp }); // Show user-friendly message const userMessage = getUserFriendlyMessage(trace.error_details.code); showNotification({ type: 'error', message: userMessage, traceId: trace.trace_id }); // Decide if we should retry if (isRetryableError(trace.error_details.code)) { await retryWithBackoff(trace.endpoint, trace.request_params); } } function isRetryableError(code) { return [ 'PROTOCOL_TIMEOUT', 'INTERNAL_SERVER_ERROR', 'SERVICE_UNAVAILABLE' ].includes(code); } ``` ## Next Steps Complete list of all 46 error codes Learn the complete swap workflow # Integrating LeoKit Source: https://docs.leokit.dev/guides/integrating Step-by-step guide to add cross-chain swaps to your app Add cross-chain swaps to your wallet or app in minutes. ### 1. Get Your API Key Log in to the [LeoKit Dashboard](https://dash.leokit.dev/admin/login) → **API Keys** → Create New Key. Copy the key and keep it secure — you'll use it in every request. ### 2. Fetch a Quote Call the `/quote` endpoint to get the best routes across all protocols. ```bash theme={null} curl -H "Api-Key: YOUR_API_KEY" \ "https://api.leokit.dev/quote?from_asset=ETH.ETH&to_asset=BTC.BTC&amount=1000000000000000000&origin=0x0000000000000000000000000000000000000000&destination=bc1q00000000000000000000000000000000000000" ``` See full details in [Get Quote](/api-reference/endpoint/get-quote). ### 3. Display the QuoteParse the response and show users: * Expected output amount * Route (e.g., "via THORChain") * Your fee (added on top) Use the quote with the highest expectedAmountOutNum or preferred flags (FASTEST, CHEAPEST). ### 4. Execute the Swap When ready, use /swap to perform the transaction. Full details in [Generate Deposit](/api-reference/endpoint/generate-deposit) and [Transaction Status](/api-reference/endpoint/transaction-status). ### Test It Out Try live swaps powered by LeoKit at LeoDex.io. Questions? Reach out via the [landing page](https://leokit.dev/). # MCP Integration Source: https://docs.leokit.dev/guides/mcp-integration Use LeoKit with Claude Desktop and AI assistants via Model Context Protocol The LeoKit MCP Server enables AI assistants like Claude to interact with DeFi protocols for quoting and executing cross-chain swaps across 30+ blockchains. ## What is MCP? [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is an open standard that allows AI assistants to securely connect to external tools and data sources. The LeoKit MCP server exposes swap functionality as tools that Claude can use directly. ## Features * **Multi-protocol quotes** - Get swap quotes from THORChain, MAYAChain, Chainflip, Relay, and NEAR * **Cross-chain swaps** - Generate unsigned transactions for 30+ blockchains * **Transaction tracking** - Monitor swap status across protocols * **Asset discovery** - List all supported tokens with current prices * **Balance queries** - Check wallet balances across multiple chains ## Installation ```bash theme={null} cd apps/leokit-mcp npm install npm run build ``` ## Configuration ### Environment Variables ```bash theme={null} # Required: LeoKit API key export LEOKIT_API_KEY="your-api-key" # Optional: Custom API URL (defaults to https://api.leokit.dev) export LEOKIT_API_URL="https://api.leokit.dev" ``` Get your API key from [LeoKit Dashboard](https://dash.leokit.dev). ### Claude Desktop Setup Add to your Claude Desktop configuration file: Edit `~/.config/claude/claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "leokit": { "command": "node", "args": ["/path/to/apps/leokit-mcp/dist/index.js"], "env": { "LEOKIT_API_KEY": "your-api-key" } } } } ``` Edit `%APPDATA%\Claude\claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "leokit": { "command": "node", "args": ["C:\\path\\to\\apps\\leokit-mcp\\dist\\index.js"], "env": { "LEOKIT_API_KEY": "your-api-key" } } } } ``` After saving the config, restart Claude Desktop for the changes to take effect. ## Available Tools ### `leokit_get_quote` Get swap quotes from multiple DEX protocols. **Parameters:** | Parameter | Required | Description | | ------------- | -------- | -------------------------------------------- | | `from_asset` | Yes | Source asset (e.g., `ETH.ETH`, `BTC.BTC`) | | `to_asset` | Yes | Destination asset | | `amount` | Yes | Amount to swap (human-readable, e.g., `1.5`) | | `origin` | Yes | Sender wallet address | | `destination` | Yes | Recipient wallet address | | `api_key` | No | Override default API key | **Example prompt:** ``` Get a quote for swapping 1 ETH to BTC - from_asset: ETH.ETH - to_asset: BTC.BTC - amount: 1 - origin: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb - destination: bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh ``` *** ### `leokit_create_deposit` Generate unsigned transactions for executing a swap. **Parameters:** | Parameter | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------ | | `quote_id` | Yes | Quote ID from `leokit_get_quote` | | `protocol` | Yes | Protocol to use (`thorchain`, `mayachain`, `chainflip`, `relay`, `near`) | | `api_key` | No | Override default API key | The returned transaction is unsigned and must be signed by the user's wallet before broadcasting. *** ### `leokit_check_status` Check the status of a swap transaction. **Parameters:** | Parameter | Required | Description | | ---------- | -------- | -------------------------------- | | `quote_id` | Yes | Original quote ID | | `tx_id` | Yes | Transaction hash on source chain | | `protocol` | No | Protocol hint for faster lookup | | `api_key` | No | Override default API key | *** ### `leokit_get_assets` List supported tokens across blockchains. **Parameters:** | Parameter | Required | Description | | --------- | -------- | ----------------------------------------------------------------------- | | `chain` | No | Filter by chain (`ETH`, `BTC`, `ARB`, `BSC`, `POLYGON`, `AVAX`, `NEAR`) | | `api_key` | No | Override default API key | *** ### `leokit_get_balances` Get wallet balances across multiple chains. **Parameters:** | Parameter | Required | Description | | --------- | -------- | ------------------------------------- | | `wallets` | Yes | Array of `{ address, chain }` objects | | `api_key` | No | Override default API key | **Example:** ```json theme={null} { "wallets": [ { "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "chain": "ETH" }, { "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "chain": "BTC" } ] } ``` ## Example Workflow Here's how Claude might help you execute a swap: Ask Claude: "I want to swap 0.5 ETH for USDC on Arbitrum" Claude uses `leokit_get_quote` to fetch quotes from multiple protocols and presents the best options. Claude shows you quotes from THORChain, Chainflip, and Relay with expected output amounts and fees. You select THORChain. Claude uses `leokit_create_deposit` to generate the unsigned transaction. You sign the transaction with your wallet and broadcast it to the network. Ask Claude: "What's the status of my swap?" Claude uses `leokit_check_status` to monitor progress until completion. ## Asset Format LeoKit uses a standardized asset format: `CHAIN.SYMBOL-ADDRESS` **Native assets:** * `BTC.BTC` - Bitcoin * `ETH.ETH` - Ethereum * `NEAR.NEAR` - NEAR Protocol **Tokens:** * `ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` - USDC on Ethereum * `ARB.USDC-0xaf88d065e77c8cC2239327C5EDb3A432268e5831` - USDC on Arbitrum See the [Asset Format Reference](/reference/asset-format) for full details. ## Development ```bash theme={null} # Watch mode npm run dev # Test with MCP inspector npm run inspect # Build for production npm run build ``` ## Troubleshooting **Cause:** Config file not loaded or path incorrect **Solution:** 1. Verify the path to `dist/index.js` is absolute and correct 2. Ensure you ran `npm run build` in `apps/leokit-mcp` 3. Restart Claude Desktop after editing config **Cause:** Missing or invalid API key **Solution:** Verify `LEOKIT_API_KEY` is set correctly in the config's `env` section **Cause:** Unsupported swap route or insufficient liquidity **Solution:** Check that both assets are supported using `leokit_get_assets` ## Next Steps Detailed guide on executing swaps end-to-end Full list of 30+ supported blockchains # MCP Setup for Claude Desktop Source: https://docs.leokit.dev/guides/mcp-setup Step-by-step guide to configure Claude Desktop with LeoKit's MCP server ## Quick Setup Get LeoKit's cross-chain swap tools integrated into Claude Desktop in under 2 minutes. **Prerequisites:** * [Claude Desktop](https://claude.ai/download) installed * A LeoKit API key from [dash.leokit.dev](https://dash.leokit.dev) ## Setup Options Use our hosted MCP server - no installation required Run the MCP server on your machine via npm ## Remote Server (Recommended) ### Step 1: Get Your API Key 1. Visit [dash.leokit.dev](https://dash.leokit.dev) 2. Sign up or log in 3. Navigate to API Keys section 4. Create a new API key ### Step 2: Configure Claude Desktop Add this configuration to your Claude Desktop config file: **Config File Location:** * **macOS/Linux:** `~/.config/claude/claude_desktop_config.json` * **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` **Configuration:** ```json theme={null} { "mcpServers": { "leokit": { "transport": { "type": "http", "url": "https://mcp.leokit.dev/mcp", "headers": { "Authorization": "Bearer YOUR-LEOKIT-API-KEY" } } } } } ``` Replace `YOUR-LEOKIT-API-KEY` with your actual API key from the dashboard ### Step 3: Restart Claude Desktop Close and reopen Claude Desktop to load the new configuration. ### Step 4: Verify Setup Test the integration by asking Claude: ``` "List available LeoKit tools" ``` You should see tools like `leokit_get_quote`, `leokit_create_deposit`, etc. ## Local Installation If you prefer to run the MCP server locally: ### Step 1: Install via NPM ```bash theme={null} npm install -g @inleo/leokit-mcp ``` ### Step 2: Configure Claude Desktop Add this configuration: ```json theme={null} { "mcpServers": { "leokit": { "command": "npx", "args": ["-y", "@inleo/leokit-mcp"], "env": { "LEOKIT_API_KEY": "YOUR-LEOKIT-API-KEY" } } } } ``` ### Step 3: Restart and Verify Restart Claude Desktop and verify as described above. ## One-Line Setup Scripts For automated setup, use our installation scripts: ```bash macOS/Linux theme={null} curl -fsSL https://raw.githubusercontent.com/LeoFinance/leodex-backend/main/apps/leokit-mcp/scripts/setup-claude.sh | bash ``` ```powershell Windows theme={null} irm https://raw.githubusercontent.com/LeoFinance/leodex-backend/main/apps/leokit-mcp/scripts/setup-claude.ps1 | iex ``` ## Available Tools Once configured, you can use these tools through Claude: Get swap quotes from multiple DEX protocols across 30+ blockchains. **Example:** *"Get me a quote for swapping 1 ETH to USDC"* Generate unsigned transactions for executing swaps. **Example:** *"Create a deposit transaction for quote ID abc123"* Check the status of a swap transaction. **Example:** *"Check the status of my swap with transaction hash 0x..."* List all supported tokens and chains. **Example:** *"Show me all available assets on Arbitrum"* Check wallet balances across multiple chains. **Example:** *"Check my ETH balance at 0x..."* ## Example Usage Here's how to use LeoKit tools through Claude: ``` "Get me a quote for swapping 1 BTC to ETH. My origin address is bc1q... and destination is 0x..." ``` Claude will fetch quotes from multiple protocols (THORChain, MAYAChain, Chainflip, etc.) and present the best options. ``` "Create a deposit transaction for the THORChain quote" ``` Claude will generate the unsigned transaction. You can then sign and broadcast it using your preferred wallet. ## Troubleshooting **Solutions:** * Verify your API key is correct * Check the config file path matches your OS * Ensure Claude Desktop was fully restarted * Check for JSON syntax errors in the config file **Solutions:** * Confirm your API key is active in the dashboard * Verify the API key is correctly placed in the config * Check that there are no extra spaces in the Bearer token **Solutions:** * For remote setup: Check internet connection * For local setup: Verify npm package is installed * Check firewall settings aren't blocking connections ## Advanced Configuration ### Custom API Endpoint To use a self-hosted LeoKit API: ```json theme={null} { "mcpServers": { "leokit": { "transport": { "type": "http", "url": "https://mcp.leokit.dev/mcp", "headers": { "Authorization": "Bearer YOUR-API-KEY" } } } } } ``` Then set the environment variable: ```bash theme={null} export LEOKIT_API_URL="https://your-api.example.com" ``` ### Multiple Configurations You can run both local and remote setups simultaneously with different names: ```json theme={null} { "mcpServers": { "leokit-remote": { "transport": { "type": "http", "url": "https://mcp.leokit.dev/mcp", "headers": { "Authorization": "Bearer YOUR-API-KEY" } } }, "leokit-local": { "command": "npx", "args": ["-y", "@inleo/leokit-mcp"], "env": { "LEOKIT_API_KEY": "YOUR-API-KEY" } } } } ``` ## Need Help? * **Dashboard:** [dash.leokit.dev](https://dash.leokit.dev) * **Documentation:** [docs.leokit.dev](https://docs.leokit.dev) * **Support:** Join our [Discord](https://discord.gg/leofinance) ## Next Steps Learn more about using MCP tools Understand the full swap process # Streaming Quotes Source: https://docs.leokit.dev/guides/streaming-quotes Real-time quote updates using Server-Sent Events (SSE) LeoKit's streaming quotes endpoint uses Server-Sent Events (SSE) to deliver real-time quote updates as they arrive from different protocols. This provides a better user experience than waiting for all quotes to resolve before displaying results. ## Why Use Streaming? Display the first quote in \~1-2 seconds instead of waiting 5-10 seconds for all protocols Update your UI as each quote arrives, showing users better rates in real-time Users see immediate feedback instead of loading spinners Reduce perceived wait time with incremental updates ## How It Works 1. Client opens SSE connection with quote parameters 2. Server immediately responds with `init` event containing quote\_id 3. Server sends `quote` events as each protocol responds 4. Server sends `final` event with all quotes aggregated 5. Server sends `finished` event and closes connection ## Implementation ### Basic Example ```javascript theme={null} const eventSource = new EventSource( "https://api.leodex.io/leokit/streaming-quotes?" + new URLSearchParams({ from_asset: "BTC.BTC", to_asset: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", amount: "100000000", destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", api_key: "your_api_key_here", }) ); let quoteId = null; eventSource.onmessage = (event) => { const data = JSON.parse(event.data); switch (data.type) { case "init": quoteId = data.quote_id; console.log("Waiting for", data.total, "quotes..."); break; case "quote": console.log("Received quote from", data.protocol); console.log("Expected output:", data.data.expected_amount_out); // Update UI with quote break; case "final": console.log("All quotes received:", data.quotes.length); console.log( "Best quote:", data.quotes.find((q) => q.data.flags?.includes("OPTIMAL")) ); break; case "finished": eventSource.close(); console.log("Stream completed"); break; case "error": console.error("Error:", data.data); eventSource.close(); break; } }; eventSource.onerror = (error) => { console.error("SSE error:", error); eventSource.close(); }; ``` ## Event Types ### 1. `init` Event Sent immediately when the connection opens. Contains the quote\_id you'll use for the swap. ```json theme={null} { "type": "init", "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "total": 3, "timestamp": "2026-01-15T12:34:56.789Z" } ``` **Fields:** * `quote_id` - UUID to use for deposit/status endpoints * `total` - Expected number of quotes (one per protocol) * `timestamp` - When the quote request was received ### 2. `quote` Event Sent each time a protocol returns a quote. May arrive in any order. ```json theme={null} { "type": "quote", "protocol": "thorchain", "data": { "expected_amount_out": "9850000000", "total_swap_seconds": 180, "fees": { "network": [ { "asset": "BTC.BTC", "amount": "10000", "type": "network" } ], "affiliate": [], "liquidity": "150000000" }, "route": ["BTC.BTC", "THOR.RUNE", "ETH.USDC-0xA0b86991..."], "flags": ["OPTIMAL", "CHEAPEST"] }, "timestamp": "2026-01-15T12:34:57.123Z" } ``` **Key Fields:** * `protocol` - Which protocol provided this quote * `data.expected_amount_out` - Output amount (in base units) * `data.flags` - Array of flags like `["OPTIMAL", "FASTEST"]` ### 3. `final` Event Sent after all protocols have responded. Contains complete array of quotes sorted by best rate. ```json theme={null} { "type": "final", "quotes": [ { "protocol": "thorchain", "data": { ... } }, { "protocol": "mayaprotocol", "data": { ... } } ], "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890", "timestamp": "2026-01-15T12:34:59.456Z" } ``` ### 4. `finished` Event Signals the stream is complete. Close the connection after receiving this. ```json theme={null} { "type": "finished" } ``` ### 5. `error` Event Sent if an error occurs during quote generation. ```json theme={null} { "type": "error", "data": { "code": "INSUFFICIENT_LIQUIDITY", "message": "No protocols can handle this swap amount", "status": 400 } } ``` ## React Integration Here's a React hook for streaming quotes: ```javascript theme={null} import { useState, useEffect } from 'react'; function useStreamingQuotes(params) { const [quotes, setQuotes] = useState([]); const [quoteId, setQuoteId] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const eventSource = new EventSource( `https://api.leodex.io/leokit/streaming-quotes?${new URLSearchParams({ ...params, api_key: 'your_api_key_here' })}` ); eventSource.onmessage = (event) => { const data = JSON.parse(event.data); switch (data.type) { case 'init': setQuoteId(data.quote_id); break; case 'quote': // Add quote to array incrementally setQuotes(prev => [...prev, data]); break; case 'final': // Replace with sorted final array setQuotes(data.quotes); setLoading(false); break; case 'finished': eventSource.close(); break; case 'error': setError(data.data); setLoading(false); eventSource.close(); break; } }; eventSource.onerror = () => { setError({ message: 'Connection failed' }); setLoading(false); eventSource.close(); }; return () => eventSource.close(); }, [params]); return { quotes, quoteId, loading, error }; } // Usage in component function SwapWidget() { const { quotes, quoteId, loading } = useStreamingQuotes({ from_asset: 'BTC.BTC', to_asset: 'ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', amount: '100000000', destination: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', }); return (
{loading &&

Loading quotes...

} {quotes.map(quote => ( ))}
); } ``` ## Handling Disconnects SSE connections can drop due to network issues. Implement automatic reconnection: ```javascript theme={null} function createReconnectingEventSource(url, maxRetries = 3) { let retries = 0; let eventSource = null; function connect() { eventSource = new EventSource(url); eventSource.onmessage = (event) => { retries = 0; // Reset on successful message handleMessage(event); }; eventSource.onerror = () => { eventSource.close(); if (retries < maxRetries) { retries++; console.log(`Reconnecting... (attempt ${retries})`); setTimeout(connect, 1000 * retries); // Exponential backoff } else { console.error('Max retries reached'); } }; } connect(); return { close: () => eventSource?.close() }; } ``` ## Best Practices **Always close connections** - Call `eventSource.close()` when done to prevent memory leaks. **Handle all event types** - Don't assume you'll only receive `quote` events. Handle `error` and `finished` too. **Save quote\_id from init** - You need this for the deposit endpoint. Don't wait for the `final` event. **Show incremental updates** - Display quotes as they arrive for better UX. Don't wait for `final`. **Implement timeouts** - Close the connection if no events arrive within 30 seconds. ## SSE vs Regular Quote Endpoint | Feature | Streaming (`/streaming-quotes`) | Regular (`/quote`) | | ------------- | ------------------------------- | -------------------- | | Response time | 1-2s for first quote | 5-10s for all quotes | | Updates | Incremental | Single response | | Connection | Persistent (SSE) | One-time HTTP | | Use case | Interactive UIs | Batch processing | ## Browser Compatibility EventSource is supported in all modern browsers: * ✅ Chrome 6+ * ✅ Firefox 6+ * ✅ Safari 5+ * ✅ Edge 79+ * ❌ IE 11 (use polyfill) For IE11 support, use the [EventSource polyfill](https://github.com/Yaffle/EventSource): ```javascript theme={null} import 'event-source-polyfill'; ``` ## Troubleshooting **Cause:** Invalid API key or malformed parameters **Solution:** Check the `error` event for details. Verify API key is correct. **Cause:** No protocols support the requested swap route **Solution:** Check `/leokit/assets` for supported assets. Verify asset identifiers. **Cause:** Network issue or server timeout **Solution:** Implement client-side timeout (30s recommended). Close and retry. **Cause:** Protocols respond at different speeds **Solution:** This is normal. Sort quotes by `expected_amount_out` in your UI. ## Next Steps Use the quote\_id from streaming to execute a swap Full API documentation for streaming quotes # LeoKit Documentation Source: https://docs.leokit.dev/index The ultimate cross-chain swap API for wallets and apps # Welcome to LeoKit LeoKit is the B2B API for adding seamless cross-chain swaps to your wallet, DeFi app, or platform — with built-in monetization. **Key Features** * **Earn Revenue**: Custom affiliate fees (0–1000 BPS) — you set the margin on every swap. * **Simple Integration**: REST APIs only — no bloated SDKs. * **Full Coverage**: 107+ DEXes, 20,000+ tokens across THORChain, Maya, Chainflip, Near Intents, Relay. * **Powerful Dashboard**: Toggle chains/assets/protocols, manage keys, monitor performance — included. * **Live Example**: See LeoKit powering swaps at [LeoDex.io](https://leodex.io) ### Get Started 1. [Quickstart →](./quickstart) — Get your API key and make your first quote in minutes. 2. [API Reference →](./api-reference) — Full endpoint details and code examples. Ready to integrate? [Start Integrating →](https://dash.leokit.dev/admin/login) If you need a LeoKit Dashboard Admin Account [Reach Out to Us ->](https://leokit.dev/#contact) # Quickstart Source: https://docs.leokit.dev/quickstart Integrate LeoKit in minutes Get up and running with cross-chain swaps fast. ### 1. Get Your API Key * Sign up or log in at the [LeoKit Dashboard](https://dash.leokit.dev/admin/login) * Navigate to **API Keys** → Create New Key * Copy your key (keep it secret!) ### 2. Make Your First Quote Use the `/quote` endpoint to fetch real-time routes: ```bash theme={null} curl -H "Api-Key: YOUR_API_KEY" \ "https://api.leokit.dev/quote?from_asset=ETH.ETH&to_asset=BTC.BTC&amount=1000000000000000000" ``` See full details in [Get Quote](./api-reference/endpoint/get-quote). ### 3. Display & Execute * Parse the response for best route (sort by expectedAmountOutNum or flags) * Show users the quote with your custom fee applied ### 4. Set Your Fees & Address to Receive Them [https://leokit.dev/assets/fees-screenshot\_1767462365419-Do79Bq68.png](https://leokit.dev/assets/fees-screenshot_1767462365419-Do79Bq68.png) To earn on your swaps, use LeoKit's dashboard to: 1. Set your fee rate (how much you'll charge your users) 2. Set the address(es) you'll earn fees into # Asset Format Source: https://docs.leokit.dev/reference/asset-format Asset identifier format specification with examples for all ecosystems ## Format Specification LeoKit uses a standardized asset identifier format across all supported blockchains: ``` CHAIN.SYMBOL-ADDRESS ``` ### Components | Component | Description | Required | Format | | ----------- | --------------------- | --------------- | ------------------------------- | | **CHAIN** | Blockchain short name | Yes | Uppercase (BTC, ETH, BSC, etc.) | | **SYMBOL** | Token symbol | Yes | Usually uppercase | | **ADDRESS** | Contract address | For tokens only | Chain-specific format | ### Separator Rules * **Period (.)** separates CHAIN from SYMBOL * **Hyphen (-)** separates SYMBOL from ADDRESS * **No hyphen** for native assets (CHAIN.SYMBOL only) ## Native Assets Native assets are the base currency of their blockchain and don't have a contract address. ### Bitcoin & UTXO Chains ``` BTC.BTC # Bitcoin LTC.LTC # Litecoin DOGE.DOGE # Dogecoin DASH.DASH # Dash BCH.BCH # Bitcoin Cash ZEC.ZEC # Zcash ``` ### Ethereum & EVM Chains ``` ETH.ETH # Ethereum ARB.ETH # Arbitrum (native ETH) AVAX.AVAX # Avalanche BASE.ETH # Base (native ETH) BSC.BNB # Binance Smart Chain POLYGON.MATIC # Polygon OPTIMISM.ETH # Optimism (native ETH) FANTOM.FTM # Fantom ``` ### Cosmos Chains ``` THOR.RUNE # THORChain MAYA.CACAO # MAYAChain (10 decimals!) GAIA.ATOM # Cosmos Hub KUJI.KUJI # Kujira ``` ### Other Chains ``` NEAR.NEAR # NEAR Protocol XRP.XRP # Ripple SOL.SOL # Solana TRON.TRX # Tron ADA.ADA # Cardano ``` ## Token Assets Token assets include a contract address after the hyphen separator. ### ERC-20 Tokens (Ethereum) ``` ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7 ETH.WBTC-0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 ETH.DAI-0x6B175474E89094C44Da98b954EedeAC495271d0F ETH.LINK-0x514910771AF9Ca656af840dff83E8264EcF986CA ``` **Format Notes:** * Address is 42 characters (0x + 40 hex digits) * Checksummed addresses preferred * Case-insensitive but checksum validation applied ### BEP-20 Tokens (BSC) ``` BSC.BUSD-0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56 BSC.CAKE-0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82 BSC.USDT-0x55d398326f99059fF775485246999027B3197955 ``` **Format Notes:** * Same address format as Ethereum (EVM compatible) * Different contract addresses than Ethereum versions ### Arbitrum Tokens ``` ARB.USDC-0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8 ARB.USDT-0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 ARB.ARB-0x912CE59144191C1204E64559FE8253a0e49E6548 ``` ### Polygon Tokens ``` POLYGON.USDC-0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 POLYGON.USDT-0xc2132D05D31c914a87C6611C10748AEb04B58e8F POLYGON.WETH-0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619 ``` ### Base Tokens ``` BASE.USDC-0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 BASE.DAI-0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb ``` ### Avalanche Tokens ``` AVAX.USDC-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E AVAX.USDT-0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7 AVAX.WETH-0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB ``` ### NEAR Tokens NEAR tokens use account names as addresses, which can include periods and hyphens. ``` NEAR.USDC-a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near NEAR.USDT-dac17f958d2ee523a2206206994597c13d831ec7.factory.bridge.near NEAR.WBTC-2260fac5e5542a773aa44fbcfedf7c193bc2c599.factory.bridge.near ``` **Format Notes:** * Contract addresses can contain periods and hyphens * Parsing requires finding the FIRST hyphen after the symbol * Example: `NEAR.USDC-a0b86991...bridge.near` * Chain: `NEAR` * Symbol: `USDC` * Address: `a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near` ## THORChain Synths THORChain synthetic assets represent assets backed by collateral in THORChain pools. ``` THOR.BTC # Synthetic Bitcoin THOR.ETH # Synthetic Ethereum THOR.USDC # Synthetic USDC ``` **Format Notes:** * No address component (similar to native assets) * Backed by THORChain liquidity pools * Can be swapped instantly on THORChain ## Parsing Logic ### Detecting Native vs Token Assets ```javascript theme={null} function isNativeAsset(asset) { // No hyphen means native asset if (!asset.includes('-')) return true; // Hyphen present but empty address also means native const parts = asset.split('-'); return parts[1] === ''; } // Examples isNativeAsset('BTC.BTC') // true isNativeAsset('ETH.ETH') // true isNativeAsset('ETH.USDC-0xA0b...') // false ``` ### Extracting Components ```javascript theme={null} function parseAsset(asset) { // Split by period to get chain and rest const [chain, rest] = asset.split('.'); // Split by first hyphen to get symbol and address const firstHyphenIndex = rest.indexOf('-'); if (firstHyphenIndex === -1) { // Native asset return { chain: chain, symbol: rest, address: null, isNative: true }; } // Token asset return { chain: chain, symbol: rest.substring(0, firstHyphenIndex), address: rest.substring(firstHyphenIndex + 1), isNative: false }; } // Examples parseAsset('BTC.BTC') // { chain: 'BTC', symbol: 'BTC', address: null, isNative: true } parseAsset('ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48') // { chain: 'ETH', symbol: 'USDC', address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', isNative: false } parseAsset('NEAR.USDC-a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near') // { chain: 'NEAR', symbol: 'USDC', address: 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near', isNative: false } ``` ### Handling Edge Cases ```javascript theme={null} function normalizeAsset(asset) { // Convert to uppercase for chain portion const [chainPart, ...rest] = asset.split('.'); const chain = chainPart.toUpperCase(); // Handle NEAR special case (can have hyphens in address) if (chain === 'NEAR') { const restStr = rest.join('.'); const firstHyphen = restStr.indexOf('-'); if (firstHyphen === -1) { return `${chain}.${restStr}`; } const symbol = restStr.substring(0, firstHyphen); const address = restStr.substring(firstHyphen + 1); return `${chain}.${symbol}-${address}`; } // Standard handling for other chains return `${chain}.${rest.join('.')}`; } ``` ## Validation Rules ### Chain Validation ```javascript theme={null} const SUPPORTED_CHAINS = [ 'BTC', 'ETH', 'BSC', 'POLYGON', 'ARB', 'AVAX', 'BASE', 'OPTIMISM', 'FANTOM', 'LTC', 'DOGE', 'DASH', 'BCH', 'ZEC', 'THOR', 'MAYA', 'GAIA', 'KUJI', 'NEAR', 'XRP', 'SOL', 'TRON', 'ADA' ]; function isValidChain(chain) { return SUPPORTED_CHAINS.includes(chain.toUpperCase()); } ``` ### Address Validation ```javascript theme={null} function validateAddress(chain, address) { if (!address) return true; // Native asset // EVM chains if (['ETH', 'BSC', 'POLYGON', 'ARB', 'AVAX', 'BASE', 'OPTIMISM', 'FANTOM'].includes(chain)) { return /^0x[a-fA-F0-9]{40}$/.test(address); } // NEAR if (chain === 'NEAR') { return /^[a-z0-9._-]+$/.test(address); } // Bitcoin-like (depends on library for full validation) if (['BTC', 'LTC', 'DOGE', 'DASH', 'BCH', 'ZEC'].includes(chain)) { // Use appropriate library for address validation return true; // Simplified } return true; } ``` ## Common Patterns ### Same Token, Different Chains USDC exists on multiple chains with different addresses: ``` ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 # Ethereum ARB.USDC-0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8 # Arbitrum POLYGON.USDC-0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 # Polygon BASE.USDC-0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 # Base BSC.USDC-0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d # BSC ``` ### Wrapped Native Assets Some chains use wrapped versions of native assets: ``` ETH.WETH-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 # Wrapped Ether BSC.WBNB-0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c # Wrapped BNB POLYGON.WMATIC-0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270 # Wrapped MATIC AVAX.WAVAX-0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7 # Wrapped AVAX ``` ### Bridged Assets Bridged assets maintain their symbol but use different addresses: ``` # USDC Native vs Bridged ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 # Native USDC on Ethereum ARB.USDC-0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8 # Bridged USDC.e on Arbitrum ARB.USDC-0xaf88d065e77c8cC2239327C5EDb3A432268e5831 # Native USDC on Arbitrum ``` ## Best Practices 1. **Always validate chain support** before accepting asset identifiers 2. **Use case-insensitive comparison** for chain names 3. **Preserve address checksums** when displaying to users 4. **Handle NEAR contract names** specially (they can contain hyphens) 5. **Cache asset metadata** (decimals, symbols) to avoid repeated lookups 6. **Normalize inputs** by trimming whitespace and converting chain to uppercase 7. **Validate address formats** per chain type before API calls 8. **Document token versions** (native vs bridged) in your UI ## Example Usage ### Quote Request ```javascript theme={null} const quoteParams = { from_asset: 'BTC.BTC', to_asset: 'ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', amount: '100000000', // 1 BTC in satoshis destination: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', origin: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh' }; ``` ### Deposit Request ```javascript theme={null} const depositParams = { quote_id: 'uuid-from-quote', selected_protocol: 'thorchain' }; ``` ### Asset Display ```javascript theme={null} function formatAssetDisplay(asset) { const { chain, symbol, address, isNative } = parseAsset(asset); if (isNative) { return `${symbol} (${chain})`; } return `${symbol} on ${chain}`; } formatAssetDisplay('BTC.BTC') // "BTC (BTC)" formatAssetDisplay('ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48') // "USDC on ETH" ``` # Unit Conversions Source: https://docs.leokit.dev/reference/conversions Unit conversion utilities and decimal handling for all supported blockchains ## Overview Different blockchains use different decimal places and units for their native assets and tokens. This reference provides conversion formulas and utilities for accurate amount handling across all supported chains. ## Common Conversion Formulas ### Base Units to Display Units ```javascript theme={null} function toDisplayUnits(amount, decimals) { return amount / Math.pow(10, decimals); } // Examples toDisplayUnits(100000000, 8); // 1.0 BTC toDisplayUnits(1000000, 6); // 1.0 USDC toDisplayUnits(1000000000000000000, 18); // 1.0 ETH ``` ### Display Units to Base Units ```javascript theme={null} function toBaseUnits(amount, decimals) { return Math.floor(amount * Math.pow(10, decimals)); } // Examples toBaseUnits(1.0, 8); // 100000000 satoshis toBaseUnits(1.0, 6); // 1000000 (USDC) toBaseUnits(1.0, 18); // 1000000000000000000 wei ``` ### Safe Conversion with BigInt For JavaScript environments supporting BigInt: ```javascript theme={null} function toBaseUnitsSafe(amount, decimals) { const [whole, fraction = ''] = amount.toString().split('.'); const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals); return BigInt(whole + paddedFraction); } // Examples toBaseUnitsSafe('1.0', 18); // 1000000000000000000n toBaseUnitsSafe('0.5', 18); // 500000000000000000n toBaseUnitsSafe('1.23456789', 8); // 123456789n ``` ## Bitcoin & UTXO Chains ### Bitcoin (BTC) **Decimals:** 8 **Base Unit:** Satoshi **Conversion:** 1 BTC = 100,000,000 satoshis ```javascript theme={null} // Satoshis to BTC const btc = satoshis / 100000000; // Or const btc = satoshis / 1e8; // BTC to Satoshis const satoshis = btc * 100000000; // Or const satoshis = Math.floor(btc * 1e8); // Examples 100000000 // satoshis → 1.0 BTC 50000000 // satoshis → 0.5 BTC 1234567 // satoshis → 0.01234567 BTC ``` ### Litecoin (LTC) **Decimals:** 8 **Base Unit:** Litoshi **Conversion:** 1 LTC = 100,000,000 litoshis ```javascript theme={null} // Same as Bitcoin const ltc = litoshis / 1e8; const litoshis = Math.floor(ltc * 1e8); ``` ### Dogecoin (DOGE) **Decimals:** 8 **Base Unit:** Shibe **Conversion:** 1 DOGE = 100,000,000 shibes ```javascript theme={null} const doge = shibes / 1e8; const shibes = Math.floor(doge * 1e8); // Important: DOGE dust limit const DOGE_DUST_LIMIT = 100000000; // 1 DOGE if (shibes < DOGE_DUST_LIMIT) { console.warn('Amount below dust limit'); } ``` ### Dash (DASH) **Decimals:** 8 **Base Unit:** Duff **Conversion:** 1 DASH = 100,000,000 duffs ```javascript theme={null} const dash = duffs / 1e8; const duffs = Math.floor(dash * 1e8); ``` ### Bitcoin Cash (BCH) **Decimals:** 8 **Base Unit:** Satoshi **Conversion:** 1 BCH = 100,000,000 satoshis ```javascript theme={null} const bch = satoshis / 1e8; const satoshis = Math.floor(bch * 1e8); ``` ### Zcash (ZEC) **Decimals:** 8 **Base Unit:** Zatoshi **Conversion:** 1 ZEC = 100,000,000 zatoshis ```javascript theme={null} const zec = zatoshis / 1e8; const zatoshis = Math.floor(zec * 1e8); ``` ## Ethereum & EVM Chains ### Ethereum (ETH) **Decimals:** 18 **Base Unit:** Wei **Conversion:** 1 ETH = 1,000,000,000,000,000,000 wei ```javascript theme={null} // Wei to ETH const eth = wei / 1000000000000000000; // Or const eth = wei / 1e18; // Using ethers.js library import { ethers } from 'ethers'; const eth = ethers.utils.formatEther(wei); const wei = ethers.utils.parseEther(eth); // Gwei to ETH (for gas prices) const ethFromGwei = gwei / 1000000000; // Or const ethFromGwei = gwei / 1e9; // Wei to Gwei const gwei = wei / 1e9; // Examples 1000000000000000000 // wei → 1.0 ETH 1000000000 // wei → 1.0 gwei 25500000000 // wei → 25.5 gwei (typical gas price) ``` ### Gas Price Conversions ```javascript theme={null} // Gwei to Wei function gweiToWei(gwei) { return Math.floor(gwei * 1e9); } // Wei to Gwei function weiToGwei(wei) { return wei / 1e9; } // Examples gweiToWei(25.5); // 25500000000 wei weiToGwei(25500000000); // 25.5 gwei ``` ### ERC-20 Tokens Different ERC-20 tokens use different decimal places: | Token | Decimals | Example Conversion | | ----- | -------- | ------------------------------ | | USDC | 6 | 1000000 → 1.0 USDC | | USDT | 6 | 1000000 → 1.0 USDT | | DAI | 18 | 1000000000000000000 → 1.0 DAI | | WBTC | 8 | 100000000 → 1.0 WBTC | | LINK | 18 | 1000000000000000000 → 1.0 LINK | ```javascript theme={null} // Generic ERC-20 conversion function convertToken(amount, decimals) { return amount / Math.pow(10, decimals); } // Examples convertToken(1000000, 6); // 1.0 USDC convertToken(1000000000000000000, 18); // 1.0 DAI convertToken(100000000, 8); // 1.0 WBTC ``` ## Cosmos Chains ### THORChain (THOR) **Decimals:** 8 **Base Unit:** Tor (1/100,000,000 RUNE) **Conversion:** 1 RUNE = 100,000,000 tor ```javascript theme={null} const rune = tor / 1e8; const tor = Math.floor(rune * 1e8); // Fixed fee const THOR_FIXED_FEE = 2000000; // 0.02 RUNE ``` ### MAYAChain (MAYA) **CRITICAL:** CACAO uses **10 decimals**, not 8! **Decimals:** 10 (for CACAO), 8 (for other assets) **Base Unit:** Smallest CACAO unit **Conversion:** 1 CACAO = 10,000,000,000 base units ```javascript theme={null} // CACAO conversion (10 decimals) const cacao = baseUnits / 1e10; const baseUnits = Math.floor(cacao * 1e10); // Normalize CACAO to standard 1e8 format function normalizeCacao(amount) { return (amount / 1e10) * 1e8; } // Examples 10000000000 // → 1.0 CACAO (10 decimals) 100000000000 // → 10.0 CACAO (10 decimals) // Normalization normalizeCacao(10000000000); // → 100000000 (standard 1e8) ``` **Important:** Always check if asset is MAYA.CACAO: ```javascript theme={null} function getDecimals(asset) { return asset === 'MAYA.CACAO' ? 10 : 8; } ``` ### Cosmos Hub (GAIA) **Decimals:** 6 **Base Unit:** uatom (micro-ATOM) **Conversion:** 1 ATOM = 1,000,000 uatom ```javascript theme={null} const atom = uatom / 1e6; const uatom = Math.floor(atom * 1e6); // Examples 1000000 // uatom → 1.0 ATOM 500000 // uatom → 0.5 ATOM ``` ### Kujira (KUJI) **Decimals:** 6 **Base Unit:** ukuji (micro-KUJI) **Conversion:** 1 KUJI = 1,000,000 ukuji ```javascript theme={null} const kuji = ukuji / 1e6; const ukuji = Math.floor(kuji * 1e6); ``` ## NEAR Protocol **Decimals:** 24 **Base Unit:** YoctoNEAR **Conversion:** 1 NEAR = 1,000,000,000,000,000,000,000,000 yoctoNEAR ```javascript theme={null} // YoctoNEAR to NEAR const near = yoctoNEAR / 1e24; // Or const near = yoctoNEAR / 1000000000000000000000000; // NEAR to YoctoNEAR const yoctoNEAR = Math.floor(near * 1e24); // Examples 1000000000000000000000000 // → 1.0 NEAR 5000000000000000000000000 // → 5.0 NEAR // Using NEAR API library import { utils } from 'near-api-js'; const near = utils.format.formatNearAmount(yoctoNEAR); const yoctoNEAR = utils.format.parseNearAmount(near); ``` ### NEAR Token Conversions NEP-141 tokens have variable decimals: ```javascript theme={null} // Must fetch decimals from token metadata async function getNEP141Decimals(contractId) { const metadata = await contract.ft_metadata(); return metadata.decimals; } // Example: USDC on NEAR const NEAR_USDC_DECIMALS = 6; const usdc = amount / 1e6; ``` ### NEAR Gas Units ```javascript theme={null} // TGas (TeraGas) conversions const TGAS = 1000000000000; // 1 TGas = 10^12 gas // Standard ft_transfer gas const FT_TRANSFER_GAS = 30 * TGAS; // 30 TGas // Attached deposit (always 1 yoctoNEAR for tokens) const ATTACHED_DEPOSIT = '1'; ``` ## Other Chains ### Ripple (XRP) **Decimals:** 6 **Base Unit:** Drop **Conversion:** 1 XRP = 1,000,000 drops ```javascript theme={null} const xrp = drops / 1e6; const drops = Math.floor(xrp * 1e6); // Reserve requirement const XRP_RESERVE = 10000000; // 10 XRP in drops ``` ### Solana (SOL) **Decimals:** 9 **Base Unit:** Lamport **Conversion:** 1 SOL = 1,000,000,000 lamports ```javascript theme={null} const sol = lamports / 1e9; const lamports = Math.floor(sol * 1e9); // Using Solana web3.js import { LAMPORTS_PER_SOL } from '@solana/web3.js'; const sol = lamports / LAMPORTS_PER_SOL; const lamports = sol * LAMPORTS_PER_SOL; ``` ### Tron (TRON) **Decimals:** 6 **Base Unit:** Sun **Conversion:** 1 TRX = 1,000,000 sun ```javascript theme={null} const trx = sun / 1e6; const sun = Math.floor(trx * 1e6); ``` ### Cardano (ADA) **Decimals:** 6 **Base Unit:** Lovelace **Conversion:** 1 ADA = 1,000,000 lovelace ```javascript theme={null} const ada = lovelace / 1e6; const lovelace = Math.floor(ada * 1e6); ``` ## Basis Points (BPS) Conversions Basis points are commonly used for fees and slippage. ```javascript theme={null} // BPS to Percentage function bpsToPercent(bps) { return bps / 100; } // BPS to Decimal function bpsToDecimal(bps) { return bps / 10000; } // Percentage to BPS function percentToBps(percent) { return percent * 100; } // Examples bpsToPercent(150); // 1.5% bpsToDecimal(150); // 0.015 percentToBps(1.5); // 150 BPS // Apply BPS fee to amount function applyBpsFee(amount, feeBps) { return Math.floor(amount * feeBps / 10000); } applyBpsFee(100000000, 30); // 300000 (0.3% of 1 BTC) ``` ## Decimal Handling Best Practices ### Avoiding Floating Point Errors ```javascript theme={null} // ❌ Bad: Floating point precision issues const result = 0.1 + 0.2; // 0.30000000000000004 // ✅ Good: Use integer arithmetic function add(a, b, decimals) { const aInt = Math.floor(a * Math.pow(10, decimals)); const bInt = Math.floor(b * Math.pow(10, decimals)); return (aInt + bInt) / Math.pow(10, decimals); } add(0.1, 0.2, 8); // 0.3 (exact) ``` ### Using Big Number Libraries ```javascript theme={null} // Using bignumber.js import BigNumber from 'bignumber.js'; const amount = new BigNumber('1.23456789'); const baseUnits = amount.times(1e8).integerValue(); // 123456789 // Using ethers.js for Ethereum import { ethers } from 'ethers'; const eth = '1.5'; const wei = ethers.utils.parseEther(eth); // BigNumber { _hex: '0x14d1120d7b160000' } const ethBack = ethers.utils.formatEther(wei); // '1.5' ``` ### Rounding Strategies ```javascript theme={null} // Floor (always round down) Math.floor(1.9999 * 1e8) / 1e8; // 1.99990000 // Ceil (always round up) Math.ceil(1.0001 * 1e8) / 1e8; // 1.00010000 // Round (banker's rounding) Math.round(1.5555 * 1e8) / 1e8; // 1.55550000 // Truncate decimals function truncate(amount, decimals) { const multiplier = Math.pow(10, decimals); return Math.floor(amount * multiplier) / multiplier; } truncate(1.23456789, 6); // 1.234567 ``` ## Display Formatting ### Human-Readable Amounts ```javascript theme={null} function formatAmount(amount, decimals, displayDecimals = 6) { const converted = amount / Math.pow(10, decimals); return converted.toFixed(displayDecimals); } formatAmount(100000000, 8, 2); // "1.00" formatAmount(123456789, 8, 4); // "1.2346" formatAmount(1000000, 6, 2); // "1.00" ``` ### Compact Notation ```javascript theme={null} function formatCompact(amount, decimals) { const value = amount / Math.pow(10, decimals); if (value >= 1e9) return (value / 1e9).toFixed(2) + 'B'; if (value >= 1e6) return (value / 1e6).toFixed(2) + 'M'; if (value >= 1e3) return (value / 1e3).toFixed(2) + 'K'; return value.toFixed(2); } formatCompact(100000000000000, 8); // "1000000.00" or "1.00M" formatCompact(1000000000, 6); // "1000.00" or "1.00K" ``` ### Currency Formatting ```javascript theme={null} function formatCurrency(amount, decimals, currency = 'USD') { const value = amount / Math.pow(10, decimals); return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency, minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value); } formatCurrency(9850000000, 6, 'USD'); // "$9,850.00" ``` ## Validation ### Amount Range Validation ```javascript theme={null} function validateAmount(amount, decimals, min = 0, max = Infinity) { const value = amount / Math.pow(10, decimals); return value >= min && value <= max; } // Validate minimum swap amount validateAmount(100000000, 8, 0.01, 100); // true (1 BTC) validateAmount(100000, 8, 0.01, 100); // false (0.001 BTC < min) ``` ### Dust Threshold Checks ```javascript theme={null} const DUST_LIMITS = { 'BTC': 546, // satoshis 'LTC': 546, // litoshis 'DOGE': 100000000, // shibes (1 DOGE) 'BCH': 546 // satoshis }; function isDust(amount, chain) { return amount < (DUST_LIMITS[chain] || 0); } isDust(500, 'BTC'); // true (below 546 sats) isDust(1000, 'BTC'); // false ``` ## Quick Reference Table | Chain | Decimals | Base Unit | 1 Unit in Base | Example | | ---------- | -------- | --------- | ------------------------- | ------------------- | | BTC | 8 | satoshi | 100,000,000 | 1 BTC = 100M sats | | ETH | 18 | wei | 1,000,000,000,000,000,000 | 1 ETH = 1e18 wei | | USDC | 6 | base | 1,000,000 | 1 USDC = 1M | | NEAR | 24 | yoctoNEAR | 1e24 | 1 NEAR = 1e24 yocto | | THOR.RUNE | 8 | tor | 100,000,000 | 1 RUNE = 100M tor | | MAYA.CACAO | **10** | base | **10,000,000,000** | 1 CACAO = 10B | | GAIA.ATOM | 6 | uatom | 1,000,000 | 1 ATOM = 1M uatom | | SOL | 9 | lamport | 1,000,000,000 | 1 SOL = 1B lamports | | XRP | 6 | drop | 1,000,000 | 1 XRP = 1M drops | ## Common Conversion Patterns ### Convert Quote Amounts ```javascript theme={null} function convertQuoteAmount(quote) { const fromDecimals = getAssetDecimals(quote.from_asset); const toDecimals = getAssetDecimals(quote.to_asset); return { input: quote.amount / Math.pow(10, fromDecimals), output: quote.expected_amount_out / Math.pow(10, toDecimals) }; } ``` ### Calculate Exchange Rate ```javascript theme={null} function calculateRate(inputAmount, outputAmount, inputDecimals, outputDecimals) { const input = inputAmount / Math.pow(10, inputDecimals); const output = outputAmount / Math.pow(10, outputDecimals); return output / input; } // Example: 1 BTC → 65,000 USDC calculateRate(100000000, 65000000000, 8, 6); // 65000 ``` # Error Codes Source: https://docs.leokit.dev/reference/error-codes Complete reference of all 46 error codes in the LeoKit API with scenarios and solutions ## Overview This reference provides a complete catalog of all error types returned by the LeoKit API, organized by category with HTTP status codes, scenarios, and solutions. ## Validation Errors (HTTP 400) ### BAD\_REQUEST **Message:** "Invalid request. Please check your inputs and try again." **Scenarios:** * Malformed JSON in request body * Invalid parameter types * Missing required fields **Example Response:** ```json theme={null} { "error": "Invalid request. Please check your inputs and try again.", "status": 400, "code": "BAD_REQUEST" } ``` ### WRONG\_PROTOCOL **Message:** "Unsupported protocol. Please switch to a supported chain or asset." **Scenarios:** * Protocol not in SUPPORTED\_PROTOCOLS list * Protocol disabled in client configuration ### SELECTED\_PROTOCOL\_IS\_NOT\_SET **Message:** "Selected protocol field is not set in deposit request." **Scenarios:** * `selected_protocol` missing from deposit request * Empty string provided ### SELECTED\_QUOTE\_IS\_NOT\_SET **Message:** "Selected quote field is not set in deposit request." **Scenarios:** * `quote_id` missing from deposit request * Empty or invalid UUID format ### WRONG\_QUOTE\_FORMAT **Message:** "Received malformed quote data. Please try again." **Scenarios:** * Quote missing required fields (from\_asset, to\_asset, amount) * Invalid quote structure from protocol * Corrupted database record ### IN\_ADDRESS\_IS\_WRONG\_FORMAT **Message:** "Invalid input token address." **Scenarios:** * Malformed blockchain address * Address checksum failure (EVM chains) * Wrong address format for chain type ### OUT\_ADDRESS\_IS\_WRONG\_FORMAT **Message:** "Invalid output token address." **Scenarios:** * Destination address invalid for target chain * Address checksum failure * Unsupported address format ### INVALID\_TOLERANCE\_BPS **Message:** "Liquidity tolerance basis points must be less than 10,000 (100%). Please adjust your tolerance." **Scenarios:** * slippage\_bps >= 10000 * Negative slippage value ### CONFLICTING\_TOLERANCE\_PARAMS **Message:** "Conflicting tolerance parameters. Use only one of tolerance\_bps or liquidity\_tolerance\_bps." **Scenarios:** * Both tolerance\_bps and liquidity\_tolerance\_bps provided (MAYA) * Mutually exclusive parameters set ## Balance & Amount Errors (HTTP 400) ### INSUFFICIENT\_BALANCE **Message:** "Insufficient balance (including gas fees)." **Scenarios:** * Wallet balance \< swap amount + gas fees * Token balance insufficient for approval + swap **Example Response:** ```json theme={null} { "error": "Insufficient balance (including gas fees).", "status": 400, "code": "INSUFFICIENT_BALANCE", "context": { "required": "1.05 ETH", "available": "1.00 ETH" } } ``` ### NOT\_ENOUGH\_GAS **Message:** "Account doesn't have sufficient balance to pay gas fees." **Scenarios:** * Native token balance insufficient for gas * Gas estimate exceeds available balance ### UNDER\_DUST\_LIMIT **Message:** "Output amount is too small (below dust limit) and would be lost. Increase input or choose another token." **Scenarios:** * Bitcoin output \< 546 satoshis * Dogecoin output \< 1 DOGE * Amount below minimum economically viable threshold ### BELOW\_MIN\_SWAP\_AMOUNT **Message:** "Input amount is less than the minimum required for this swap (to cover fees). Increase your amount." **Scenarios:** * THORChain/MAYA amount \< recommended\_min\_amount\_in * Swap amount insufficient to cover protocol fees ## Slippage & Price Errors (HTTP 400) ### OVER\_SLIPPAGE\_LIMIT **Message:** "Slippage tolerance exceeded. Price moved more than allowed. Try increasing slippage or wait for stable prices." **Scenarios:** * Actual slippage > specified tolerance * Price volatility during swap execution ### PRICE\_IMPACT\_TOO\_HIGH **Message:** "Price impact is too high. The trade would move the market significantly." **Scenarios:** * Large swap in low-liquidity pool * Price impact > 10% * Relay/Rango flagged high impact ### OUTPUT\_BELOW\_PRICE\_LIMIT **Message:** "Output amount is below your price limit due to slippage or fees. Adjust tolerance or try later." **Scenarios:** * THORChain emit\_asset \< price\_limit in memo * Expected output degraded below minimum ### USE\_LOSES\_DANGEROUS\_AMOUNT **Message:** "This trade is extremely unfavorable — you'd lose a large portion of your funds. Blocked for your protection." **Scenarios:** * Rango detected > 18% loss * Swap would result in significant value loss * Likely scam token or extreme price impact ## Quote Errors (HTTP 404) ### NO\_VALID\_QUOTE **Message:** "No route found for this token pair. Try again later or use different tokens." **Scenarios:** * No protocols returned quotes * All quotes failed validation * Unsupported token pair ### ROUTE\_EXPIRED **Message:** "This route has expired. Please request a new quote." **Scenarios:** * THORChain quote past expiry timestamp * NEAR quote past timeWhenInactive * Quote older than protocol timeout ### QUOTE\_ID\_DOESNT\_EXISTS **Message:** "Quote id doesn't exist, try quoting again." **Scenarios:** * Invalid or fabricated quote\_id * Quote not found in database * Quote belongs to different client ### PROTOCOL\_DOESNT\_EXIST\_IN\_QUOTE **Message:** "Given protocol doesn't exist for this quote. Use other protocols or request a new quote." **Scenarios:** * selected\_protocol not in quote's available protocols * Protocol failed during quote generation ## Protocol-Specific Errors (HTTP 400) ### MEMO\_TOO\_LONG **Message:** "Generated memo is too long for the source chain. Try using a shorter affiliate name or THORName." **Scenarios:** * Bitcoin memo > 150 bytes * Dogecoin memo > 80 bytes * OP\_RETURN data exceeds chain limit ### INVALID\_DEST\_ADDRESS **Message:** "Destination address does not match the chain of the target asset. Please check the address and chain." **Scenarios:** * ETH address for BTC.BTC output * Wrong address format for destination chain * THORChain validation failed ### TOKEN\_NOT\_FOUND **Message:** "Token not found or unsupported on this chain." **Scenarios:** * Asset not in tokens database * Invalid asset identifier format * Unsupported token contract address ### BLOCKCHAIN\_IS\_NOT\_SUPPORTED **Message:** "This blockchain is not supported yet." **Scenarios:** * Chain not in supported ecosystems * Ecosystem handler not implemented * Chain disabled globally ### ALLOWANCE\_CANNOT\_BE\_SET **Message:** "Failed to approve token spending. Check your wallet and try again." **Scenarios:** * ERC20 approval transaction construction failed * Non-standard token contract * Network congestion preventing gas estimation ## System Errors (HTTP 500) ### INTERNAL\_SERVER\_ERROR **Message:** "Something went wrong on our side. Please try again later." **Scenarios:** * Uncaught exceptions * Database connection failures * External API timeouts ### DEPOSIT\_FAILED **Message:** "Deposit transaction failed. Please try again." **Scenarios:** * Transaction construction error * UTXO selection failed * Gas estimation failed * Protocol API error ### UNABLE\_TO\_RETRIEVE\_QUOTES **Message:** "Can't retrieve quotes. Try again in a few seconds." **Scenarios:** * All protocol APIs down * Network connectivity issues * Rate limiting exceeded ### UNABLE\_TO\_RETRIEVE\_CHAINS **Message:** "Failed to fetch chains price. Try again in a few seconds." **Scenarios:** * Price oracle unavailable * Database read error * CoinGecko API timeout ### UNABLE\_TO\_RETRIEVE\_USER\_BALANCE **Message:** "Could not load wallet balance. Check connection and refresh." **Scenarios:** * Blockchain node unavailable * RPC endpoint timeout * Invalid address format ### UNABLE\_TO\_RETRIEVE\_TOKEN\_PRICE **Message:** "Failed to fetch token price. Try again in a few seconds." **Scenarios:** * Price feed down * Token not listed on price oracle * Stale price data ### UNABLE\_TO\_RETRIEVE\_TRANSACTION\_FEE **Message:** "Could not estimate gas fees — network may be congested." **Scenarios:** * Gas oracle unavailable * Network congestion * Transaction simulation failed ## Authentication Errors (HTTP 403) ### CLIENT\_CONFIG\_IS\_NOT\_SET **Message:** "Unknown Api-Key" **Scenarios:** * Invalid API key * API key not found in database * Expired or revoked key ## Transaction Errors (HTTP 400) ### INVALID\_TRANSACTION\_HASH **Message:** "Invalid transaction hash provided." **Scenarios:** * Malformed tx hash format * Wrong length for chain type * Non-hexadecimal characters ### TX\_ID\_IS\_NOT\_SET **Message:** "Transaction ID is not set." **Scenarios:** * Missing tx\_id in status request * tx\_id not saved and not provided ## Tracing Errors (HTTP 400/404) ### TRACE\_ID\_IS\_NOT\_SET **Message:** "Trace ID is missing, please add ID that you want to track to end of the URL." **Scenarios:** * Empty traceId parameter * Missing path parameter ### TRACE\_ID\_DOES\_NOT\_EXIST **Message:** "This Trace ID does not exist." **Scenarios:** * Invalid trace ID * Trace log expired (>30 days) * Non-existent error log ## Other Errors ### ACCOUNT\_NOT\_FOUND **Message:** "Account is not found. Please check your address." **Scenarios:** * Cosmos account not initialized * Address never received funds * Wrong chain for address format ### WRONG\_DEPOSIT\_FORMAT **Message:** "Invalid deposit data for the selected route." **Scenarios:** * Corrupted deposit transaction * Invalid PSBT format * Missing required transaction fields ### PROTOCOL\_IS\_NOT\_SUPPORTED **Message:** "Given protocol is not supported for this action." **Scenarios:** * Protocol doesn't support requested operation * Ecosystem incompatibility * Feature not implemented for protocol ## HTTP Status Code Summary | Status Code | Category | Description | | ----------- | ------------ | -------------------------------------- | | 200 | Success | Request completed successfully | | 400 | Client Error | Validation, parameter, or quote errors | | 403 | Forbidden | Authentication failures | | 404 | Not Found | Quote or resource not found | | 500 | Server Error | Internal errors, external API failures | ## Error Handling Best Practices 1. **Always check the `code` field** for programmatic error handling 2. **Display the `error` message** to users for user-friendly feedback 3. **Use `context` field** when available for additional error details 4. **Implement retry logic** for 500-level errors with exponential backoff 5. **Save trace\_id** from error responses for debugging support requests 6. **Validate inputs client-side** to reduce 400-level errors 7. **Handle partial failures** gracefully when fetching quotes from multiple protocols # Fee Calculation Formulas Source: https://docs.leokit.dev/reference/fee-formulas Protocol-specific fee calculation formulas and fee structure explanations ## Overview Different protocols use different fee models for cross-chain swaps. This reference provides the exact formulas and calculations used by each protocol. ## THORChain Fee Structure THORChain fees consist of three main components: affiliate fees, outbound fees, and liquidity fees (slippage). ### Fee Components | Fee Type | Description | Calculation | Variable | | ------------- | --------------------------- | -------------------------------------- | -------- | | Affiliate Fee | Revenue share to integrator | `(inputAmount × affiliateBps) / 10000` | Optional | | Outbound Fee | Network fee for destination | Fixed per chain from API | Required | | Liquidity Fee | Pool slippage cost | `(inputAmount × slippageBps) / 10000` | Required | ### Formula ```typescript theme={null} // Affiliate Fee affiliateFee = (inputAmount * affiliateBps) / 10000; // Outbound Fee outboundFee = fixedOutboundFee; // From inbound_addresses API // Liquidity Fee (Slippage) liquidityFee = (inputAmount * slippageBps) / 10000; // Total Fee totalFee = affiliateFee + outboundFee + liquidityFee; // Expected Output expectedOutput = theoreticalOutput - totalFee; ``` ### Example Calculation Swap 1 BTC → ETH.USDC with 30 BPS affiliate fee and 150 BPS slippage: ```javascript theme={null} const inputAmount = 100000000; // 1 BTC in satoshis const affiliateBps = 30; const slippageBps = 150; const outboundFee = 100000; // 0.001 BTC from API const theoreticalOutput = 65000000000; // 65,000 USDC // Calculate fees const affiliateFee = (100000000 * 30) / 10000; // = 300000 (0.003 BTC) const liquidityFee = (100000000 * 150) / 10000; // = 1500000 (0.015 BTC) const totalFee = 300000 + 100000 + 1500000; // = 1900000 (0.019 BTC) // Expected output const expectedOutput = 65000000000 - (1900000 * 65000); // Convert to USDC // ≈ 64876500000 (64,876.5 USDC) ``` ### Basis Points (BPS) Explained | BPS | Percentage | Decimal | | ----- | ---------- | ------- | | 1 | 0.01% | 0.0001 | | 10 | 0.1% | 0.001 | | 30 | 0.3% | 0.003 | | 100 | 1% | 0.01 | | 150 | 1.5% | 0.015 | | 300 | 3% | 0.03 | | 10000 | 100% | 1.0 | ### Slippage Calculation ```typescript theme={null} const slippageBps = params.slippage_bps || params.liquidity_tolerance_bps || 150; // Validate maximum if (slippageBps >= 10000) { throw new Error("INVALID_TOLERANCE_BPS"); } // Calculate slippage fee const slippageFee = (inputAmount * slippageBps) / 10000; ``` **Default Slippage:** 150 BPS (1.5%) **Maximum Slippage:** 9999 BPS (99.99%) ### Streaming Swap Fee Impact Streaming swaps reduce slippage by splitting large swaps into smaller chunks: ```javascript theme={null} // Single swap const singleSwapSlippage = 500; // BPS // Streaming swap (10 chunks) const streamingQuantity = 10; const avgSlippagePerChunk = 50; // BPS const totalStreamingSlippage = 50 * 10; // Still 500 BPS total, but better execution ``` **Benefits:** * Reduced price impact per sub-swap * Better average execution price * Lower total slippage for large amounts ## MAYAChain Fee Structure MAYAChain uses a similar fee model to THORChain with some differences. ### Fee Components Same as THORChain but with MAYAChain-specific parameters: ```typescript theme={null} // Affiliate Fee affiliateFee = (inputAmount * affiliateBps) / 10000; // Outbound Fee outboundFee = fixedOutboundFee; // From inbound_addresses API // Liquidity Fee (Slippage) liquidityFee = (inputAmount * toleranceBps) / 10000; // Total Fee totalFee = affiliateFee + outboundFee + liquidityFee; // Expected Output expectedOutput = theoreticalOutput - totalFee; ``` ### CACAO Decimal Handling **Critical:** MAYA.CACAO uses 10 decimals instead of 8! ```typescript theme={null} const decimals = asset === 'MAYA.CACAO' ? 10 : 8; const normalizedAmount = (amount / Math.pow(10, decimals)) * 1e8; // Example: 10 CACAO const amount = '100000000000'; // 10 CACAO (10 decimals) const normalized = (100000000000 / 1e10) * 1e8; // = 1000000000 (in standard 1e8 format) ``` ### Tolerance Parameter Validation MAYAChain requires **only one** tolerance parameter: ```typescript theme={null} if (params.tolerance_bps && params.liquidity_tolerance_bps) { throw new Error("CONFLICTING_TOLERANCE_PARAMS"); } const tolerance = params.tolerance_bps || params.liquidity_tolerance_bps || 150; ``` ## Chainflip Fee Structure Chainflip uses multiple fee types with different multipliers. ### Fee Types | Type | Display Name | Multiplier | Description | | ------- | ------------- | ---------- | -------------------------------------- | | INGRESS | Deposit Fee | 1x | Fee to deposit to Chainflip vault | | NETWORK | Network Fee | 1x | Blockchain network fees | | EGRESS | Broadcast Fee | 1x | Fee to send from vault to destination | | BROKER | Affiliate Fee | 1.5x | Partner commission (multiplied by 1.5) | ### Formula ```typescript theme={null} // Sum all fees with multipliers const totalFee = fees.reduce((sum, fee) => { const multiplier = fee.type === 'BROKER' ? 1.5 : 1; return sum + (fee.amount * multiplier); }, 0); // Expected Output expectedOutput = inputAmount * exchangeRate - totalFee; ``` ### Example Calculation ```javascript theme={null} const fees = [ { type: 'INGRESS', amount: 5000 }, // 0.00005 BTC { type: 'NETWORK', amount: 3000 }, // 0.00003 BTC { type: 'EGRESS', amount: 8000 }, // 0.00008 BTC { type: 'BROKER', amount: 10000 } // 0.0001 BTC ]; const totalFee = fees.reduce((sum, fee) => { const multiplier = fee.type === 'BROKER' ? 1.5 : 1; return sum + (fee.amount * multiplier); }, 0); // Calculation: // INGRESS: 5000 * 1 = 5000 // NETWORK: 3000 * 1 = 3000 // EGRESS: 8000 * 1 = 8000 // BROKER: 10000 * 1.5 = 15000 // Total: 31000 satoshis (0.00031 BTC) ``` ### DCA (Dollar Cost Averaging) Impact Chainflip supports DCA to reduce price impact: ```javascript theme={null} const dcaParams = { numberOfChunks: 5, chunkIntervalBlocks: 2 }; // Fees are applied per chunk, but overall impact is reduced const feePerChunk = totalFee / numberOfChunks; ``` ### Boost Fee Optional boost fee for faster execution: ```javascript theme={null} const boostFeeBps = 10; // 0.1% const boostFee = (inputAmount * boostFeeBps) / 10000; // Total with boost const totalWithBoost = totalFee + boostFee; ``` ### Low Liquidity Warning When `lowLiquidityWarning: true`: ```javascript theme={null} const recommendedSlippage = quote.recommendedSlippageTolerancePercent; const recommendedSlippageBps = recommendedSlippage * 100; // Use higher slippage for low liquidity pools const adjustedSlippage = Math.max(slippageBps, recommendedSlippageBps); ``` ## Relay Fee Structure Relay uses a comprehensive fee model with multiple components. ### Fee Components | Fee Type | Description | Source | | --------------- | -------------------------- | --------------- | | Gas | Blockchain gas fees | Per transaction | | Relayer | Bridge relayer fee | Per bridge | | Relayer Gas | Gas for relayer operations | Per bridge | | Relayer Service | Service fee for relaying | Per bridge | | App | Application/affiliate fee | Optional | ### Formula ```typescript theme={null} // Extract fees from response const { gas, relayer, relayerGas, relayerService, app } = quote.fees; // Total USD cost const totalFeeUSD = (gas?.usd || 0) + (relayer?.usd || 0) + (relayerGas?.usd || 0) + (relayerService?.usd || 0) + (app?.usd || 0); // Output calculation expectedOutput = quote.details.currencyOut.amount; ``` ### Example Calculation ```javascript theme={null} const fees = { gas: { usd: 5.50, amount: '2000000000000000' }, relayer: { usd: 2.00, amount: '800000000000000' }, relayerGas: { usd: 1.50, amount: '600000000000000' }, relayerService: { usd: 1.00, amount: '400000000000000' }, app: { usd: 0.50, amount: '200000000000000' } }; const totalFeeUSD = 5.50 + 2.00 + 1.50 + 1.00 + 0.50; // = 10.50 USD ``` ### App Fee Configuration Set custom affiliate fees: ```javascript theme={null} const appFees = { recipient: '0xClientFeeAddress...', fee: '30' // BPS (0.3%) }; // Applied to total transaction value const appFeeAmount = (inputAmount * 30) / 10000; ``` ### Price Impact Calculation Relay differentiates between total impact and swap impact: ```javascript theme={null} const totalImpact = { usd: 25.50, percent: 1.275 // (25.50 / 2000) * 100 }; const swapImpact = { usd: 15.30, percent: 0.765 // (15.30 / 2000) * 100 }; // totalImpact = fees + slippage // swapImpact = slippage only ``` ### Multi-Step Route Fees Each step in a multi-hop route has separate fees: ```javascript theme={null} const route = { steps: [ { action: 'approve', estimatedFees: { gas: { usd: 2.0 } } }, { action: 'bridge', estimatedFees: { gas: { usd: 5.0 }, relayer: { usd: 3.0 } } }, { action: 'swap', estimatedFees: { gas: { usd: 4.0 } } } ] }; // Total fees const totalFees = route.steps.reduce((sum, step) => { return sum + Object.values(step.estimatedFees).reduce((s, f) => s + f.usd, 0); }, 0); // = 14.0 USD ``` ## NEAR Fee Structure NEAR uses a simplified fee model based on USD value difference. ### Formula ```typescript theme={null} // Single network fee combining all costs networkFee = quote.amountInUsd - quote.amountOutUsd; // Expected output expectedOutput = quote.amountOut; ``` ### Example Calculation ```javascript theme={null} const quote = { amountIn: '1000000000', // 1000 USDC amountInUsd: 1000.00, amountOut: '20150000000000000', // 0.02015 ETH amountOutUsd: 985.50 }; // Network fee in USD const networkFee = 1000.00 - 985.50; // = 14.50 USD // Represents all protocol costs combined ``` ### Gas Fees NEAR uses fixed gas amounts: ```javascript theme={null} // ft_transfer (token transfers) const ftTransferGas = '30000000000000'; // 30 TGas // Attached deposit (required for token operations) const attachedDeposit = '1'; // 1 yoctoNEAR ``` ## Fee Comparison Example Same swap across different protocols: ### Scenario: 1 BTC → ETH.USDC | Protocol | Affiliate | Outbound | Liquidity | Other | Total USD | | --------- | ------------- | -------- | --------------- | ----------- | --------- | | THORChain | \$20 (30 BPS) | \$65 | \$100 (150 BPS) | - | \$185 | | MAYAChain | \$20 (30 BPS) | \$60 | \$95 (150 BPS) | - | \$175 | | Chainflip | \$30 (BROKER) | - | - | \$25 (fees) | \$55 | | Relay | - | - | - | \$45 (all) | \$45 | **Note:** Actual fees vary based on network conditions, liquidity, and current rates. ## Fee Optimization Strategies ### Minimize Slippage ```javascript theme={null} // For stable pairs, use lower slippage const stablePairSlippage = 50; // 0.5% // For volatile pairs, use higher slippage const volatilePairSlippage = 300; // 3% // For large amounts, use streaming swaps (THORChain/MAYA) const streamingParams = { streaming_quantity: 10, streaming_interval: 3 }; ``` ### Choose Optimal Protocol ```javascript theme={null} // Compare total fees across protocols function selectOptimalProtocol(quotes) { return quotes.reduce((best, quote) => { const currentFees = quote.fees.reduce((sum, f) => sum + f.usd, 0); const bestFees = best.fees.reduce((sum, f) => sum + f.usd, 0); return currentFees < bestFees ? quote : best; }); } ``` ### Balance Speed vs Cost ```javascript theme={null} // Fast but potentially higher fees const fastestQuote = quotes.reduce((fastest, q) => q.total_swap_seconds < fastest.total_swap_seconds ? q : fastest ); // Cheapest but potentially slower const cheapestQuote = quotes.reduce((cheapest, q) => { const qFees = q.fees.reduce((sum, f) => sum + f.usd, 0); const cheapestFees = cheapest.fees.reduce((sum, f) => sum + f.usd, 0); return qFees < cheapestFees ? q : cheapest; }); ``` ## Best Practices 1. **Always display total fees in USD** for user transparency 2. **Break down fee components** to help users understand costs 3. **Show price impact separately** from network fees 4. **Use protocol-recommended slippage** for low liquidity pools 5. **Cache outbound fees** from THORChain/MAYA APIs (update hourly) 6. **Account for decimal differences** (especially MAYA.CACAO's 10 decimals) 7. **Multiply BROKER fees by 1.5x** for Chainflip calculations 8. **Compare effective rates** (output/input) across protocols, not just fees 9. **Warn users about high price impact** (>5%) before swap execution 10. **Update gas prices frequently** (every 1-2 minutes) for accurate estimates # Supported Chains Source: https://docs.leokit.dev/reference/supported-chains Complete list of 30+ supported blockchains with metadata and features ## Overview LeoKit supports cross-chain swaps across 30+ blockchains spanning multiple ecosystems including EVM, UTXO, Cosmos, and other specialized chains. ## EVM Chains EVM (Ethereum Virtual Machine) compatible chains use the same address format and transaction structure. | Short Name | Full Name | Chain ID | Gas Units | Native Asset | | ---------- | ------------------- | -------- | --------- | ------------- | | ETH | Ethereum Mainnet | 1 | gwei | ETH.ETH | | ARB | Arbitrum One | 42161 | gwei | ARB.ETH | | AVAX | Avalanche C-Chain | 43114 | gwei | AVAX.AVAX | | BASE | Base | 8453 | gwei | BASE.ETH | | BSC | Binance Smart Chain | 56 | gwei | BSC.BNB | | POLYGON | Polygon PoS | 137 | gwei | POLYGON.MATIC | | OPTIMISM | Optimism | 10 | gwei | OPTIMISM.ETH | | FANTOM | Fantom Opera | 250 | gwei | FANTOM.FTM | ### EVM Chain Features * **Address Format:** 0x-prefixed hexadecimal (42 characters) * **Token Standard:** ERC-20, BEP-20 (same interface) * **Gas Model:** Base fee + priority fee (EIP-1559 on most chains) * **Transaction Type:** Supports both legacy and EIP-1559 transactions * **Approval Required:** Yes, for token swaps (except native asset swaps) ### EVM-Specific Notes * **Ethereum (ETH):** Highest security, highest fees, most liquidity * **Arbitrum (ARB):** Layer 2 rollup, low fees, fast finality * **Avalanche (AVAX):** High throughput, subsecond finality * **Base (BASE):** Coinbase Layer 2, growing ecosystem * **BSC:** High speed, low fees, Binance ecosystem * **Polygon:** Low fees, high speed, popular for DeFi * **Optimism:** Layer 2 rollup, optimistic rollup design * **Fantom:** High speed, DeFi focused ## UTXO Chains UTXO (Unspent Transaction Output) chains use Bitcoin-like transaction models. | Short Name | Full Name | Decimals | Gas Units | Native Asset | SegWit Support | | ---------- | ------------ | -------- | ------------- | ------------ | -------------- | | BTC | Bitcoin | 8 | satoshi/vbyte | BTC.BTC | Yes (native) | | LTC | Litecoin | 8 | satoshi/vbyte | LTC.LTC | Yes (native) | | DOGE | Dogecoin | 8 | shibes/vbyte | DOGE.DOGE | No | | DASH | Dash | 8 | satoshi/vbyte | DASH.DASH | No | | BCH | Bitcoin Cash | 8 | satoshi/vbyte | BCH.BCH | No | | ZEC | Zcash | 8 | satoshi/vbyte | ZEC.ZEC | No | ### UTXO Chain Features * **Transaction Format:** PSBT (Partially Signed Bitcoin Transaction) for most * **Dust Limit:** Minimum output thresholds to prevent spam * BTC/LTC: 546 satoshis * DOGE: 100,000,000 shibes (1 DOGE) * **Memo Support:** OP\_RETURN outputs for cross-chain swaps * **Change Outputs:** Automatic change calculation and dust handling ### UTXO-Specific Notes * **Bitcoin (BTC):** Most secure, highest liquidity, SegWit native * **Litecoin (LTC):** Faster blocks (2.5 min), SegWit support * **Dogecoin (DOGE):** High transaction volume, meme culture * **DASH:** InstantSend for fast confirmations, raw tx format (not PSBT) * **Bitcoin Cash (BCH):** Larger blocks, lower fees * **Zcash (ZEC):** Privacy features available ### Memo Length Limits | Chain | OP\_RETURN Limit | Validation Limit | Notes | | ------ | ---------------- | ---------------- | --------------------- | | BTC | 80 bytes | 150 bytes | Extended memo support | | LTC | 80 bytes | 150 bytes | Extended memo support | | DOGE | 80 bytes | 80 bytes | No extended support | | Others | 80 bytes | Variable | Chain-specific | ## Cosmos Chains Cosmos SDK-based chains use account-based models with IBC (Inter-Blockchain Communication). | Short Name | Full Name | Decimals | Gas Units | Native Asset | Special Notes | | ---------- | ---------- | ---------------------- | --------- | ------------ | --------------------- | | THOR | THORChain | 8 | rune | THOR.RUNE | Fixed 0.02 RUNE fee | | MAYA | MAYAChain | 10 (CACAO), 8 (others) | cacao | MAYA.CACAO | 10 decimals for CACAO | | GAIA | Cosmos Hub | 6 | atom | GAIA.ATOM | Standard Cosmos chain | | KUJI | Kujira | 6 | kuji | KUJI.KUJI | DeFi-focused chain | ### Cosmos Chain Features * **Address Format:** Bech32 encoding with chain-specific prefix * THOR: `thor1...` * MAYA: `maya1...` * GAIA: `cosmos1...` * KUJI: `kujira1...` * **Transaction Type:** Cosmos SDK messages (MsgSend, MsgDeposit, etc.) * **Gas Model:** Fixed or simulated gas with buffer (typically 1.3x) * **Memo Support:** Native memo field in transactions ### Cosmos-Specific Notes * **THORChain (THOR):** Cross-chain liquidity protocol, uses MsgDeposit for native swaps * **MAYAChain (MAYA):** THORChain fork, **10 decimals for CACAO** (not 8!) * **Cosmos Hub (GAIA):** Main Cosmos chain, IBC hub * **Kujira (KUJI):** DeFi platform with native DEX ## Other Chains Specialized chains with unique architectures. | Short Name | Full Name | Decimals | Type | Native Asset | Special Features | | ---------- | ------------- | -------- | ------------- | ------------ | ----------------------- | | NEAR | NEAR Protocol | 24 | Account-based | NEAR.NEAR | YoctoNEAR units (10^24) | | XRP | Ripple | 6 | UTXO-like | XRP.XRP | Drops (10^6) | | SOL | Solana | 9 | Account-based | SOL.SOL | Lamports (10^9) | | TRON | Tron Network | 6 | Account-based | TRON.TRX | Sun units (10^6) | | ADA | Cardano | 6 | eUTXO | ADA.ADA | Extended UTXO model | ### Chain-Specific Details #### NEAR Protocol * **Decimals:** 24 (yoctoNEAR) * **Contract Format:** name.factory.bridge.near (can contain hyphens) * **Token Standard:** NEP-141 * **Gas:** Fixed 30 TGas for ft\_transfer * **Deposit Requirement:** 1 yoctoNEAR for token transfers #### Ripple (XRP) * **Decimals:** 6 (drops) * **Reserve Requirement:** Minimum balance required * **Destination Tags:** Supported for exchange deposits #### Solana (SOL) * **Decimals:** 9 (lamports) * **Token Standard:** SPL tokens * **High Performance:** Sub-second finality #### Tron (TRON) * **Decimals:** 6 (sun) * **Token Standard:** TRC-20 * **Energy System:** Alternative to gas fees #### Cardano (ADA) * **Decimals:** 6 (lovelace) * **UTXO Model:** Extended UTXO with smart contracts * **Native Tokens:** Multi-asset ledger ## Protocol Support Matrix Different protocols support different blockchain ecosystems. | Protocol | EVM | UTXO | Cosmos | NEAR | Other Chains | | ------------- | ---------------- | --------------------- | ------------ | ------ | ------------ | | THORChain | ✅ ETH, AVAX, BSC | ✅ BTC, LTC, DOGE, BCH | ✅ THOR, GAIA | ❌ | ✅ XRP, TRON | | MAYAChain | ✅ ETH, ARB | ✅ BTC, DASH | ✅ MAYA, THOR | ❌ | ❌ | | Chainflip | ✅ ETH, ARB | ✅ BTC | ❌ | ❌ | ❌ | | Relay | ✅ All EVM chains | ❌ | ❌ | ❌ | ❌ | | NEAR Protocol | ✅ ETH (bridged) | ✅ BTC (bridged) | ❌ | ✅ NEAR | ❌ | | Rango | ✅ All | ✅ All | ✅ All | ✅ All | ✅ All | ## Chain Selection Guidelines ### For Speed 1. **Fastest Finality:** Solana, Avalanche, Fantom 2. **Fast Confirmation:** NEAR, BSC, Polygon 3. **Standard Speed:** Ethereum, Bitcoin ### For Low Fees 1. **Lowest Fees:** Polygon, BSC, Fantom 2. **Low Fees:** Arbitrum, Base, Optimism 3. **Medium Fees:** Avalanche, NEAR ### For Security 1. **Highest Security:** Bitcoin, Ethereum 2. **High Security:** Litecoin, Cosmos chains 3. **Growing Security:** Layer 2s (Arbitrum, Optimism) ### For Liquidity 1. **Highest Liquidity:** Ethereum, Bitcoin 2. **High Liquidity:** BSC, Polygon, Arbitrum 3. **Growing Liquidity:** Base, NEAR, Avalanche ## Chain-Specific Limitations ### Bitcoin (BTC) * Memo length limited to 150 bytes for THORChain swaps * SegWit required for optimal fee calculation ### Dogecoin (DOGE) * Shorter memo limit (80 bytes) may require THORNames * Higher dust threshold (1 DOGE) * Non-SegWit transaction format ### NEAR Protocol * Limited token support (requires asset equivalents mapping) * 90-minute quote expiration * 1 yoctoNEAR deposit required for token transfers ### MAYAChain (MAYA) * CACAO uses 10 decimals (not 8 like other Cosmos chains) * Conflicting tolerance parameters not allowed * Router field required in responses ### EVM Chains * USDT requires allowance reset to 0 before new approval * Some tokens may require multiple approval transactions * Gas estimation may fail, requiring fallback values ## Adding New Chains To request support for additional chains, please contact [support@leofinance.io](mailto:support@leofinance.io) with: * Chain name and ticker symbol * Blockchain type (EVM/UTXO/Cosmos/Other) * Use case and expected volume * Community size and ecosystem maturity # SDK Getting Started Source: https://docs.leokit.dev/sdk/getting-started Install leokit-sdk and execute your first cross-chain swap ## Install ```bash theme={null} npm install leokit-sdk ethers ``` `ethers@^6` is a peer dependency. Optional dependencies (per-chain xchain libraries, ZCash, Solana, etc.) are loaded lazily — only what you actually use is bundled. The package exposes five entry points: | Import path | Use | | ---------------------- | ------------------------------------------------------- | | `leokit-sdk` | `LeoKitClient`, factory, top-level types | | `leokit-sdk/wallets` | `WalletManager` and adapter classes (lazy-loaded) | | `leokit-sdk/protocols` | Protocol handlers (`EvmProtocol`, `NearProtocol`, etc.) | | `leokit-sdk/utils` | Amount/chain/asset/error helpers, `EventEmitter` | | `leokit-sdk/cdn` | Icon-URL builders | ## Create a Client ```typescript theme={null} import { createLeoKit } from "leokit-sdk"; const sdk = createLeoKit({ apiKey: "your-leokit-api-key", network: "mainnet", }); ``` ### `LeoKitConfig` | Field | Type | Required | Description | | --------- | -------------------------- | -------- | ------------------------------------------------------- | | `apiKey` | string | Yes | API key from [dash.leokit.dev](https://dash.leokit.dev) | | `network` | `"mainnet"` \| `"testnet"` | No | Default: `"mainnet"` | | `apiUrl` | string | No | Default: `https://api.leokit.dev` | | `cdnUrl` | string | No | CDN base for icons. Default: LeoKit CDN | `sdk.getConfig()` returns the resolved `ResolvedConfig` (defaults filled in). `sdk.getNetwork()` returns the active `Network`. ## End-to-End Swap ```typescript theme={null} import { createLeoKit } from "leokit-sdk"; const sdk = createLeoKit({ apiKey: process.env.LEOKIT_API_KEY!, network: "mainnet" }); // 1. Connect a wallet (e.g. MetaMask in a browser) await sdk.connectWallet("metamask"); // 2. Get a quote const quote = await sdk.getQuote({ fromAsset: "ETH.ETH", toAsset: "BTC.BTC", amount: "1000000000000000000", // 1 ETH in wei destination: "bc1q...", // recipient address }); // 3. Execute const result = await sdk.executeSwap(quote); console.log("Tx hash:", result.txHash); // 4. Track to completion const stop = sdk.watchTransaction(quote.quoteId, (status) => { console.log(status.state); // "pending" | "success" | "failed" | "refunded" if (status.state !== "pending") stop(); }); ``` ## Where to Go Next 19 adapters: MetaMask, Phantom, Ledger, Keplr, Trust, WalletConnect… `getQuote`, `streamQuote`, `executeSwap`, `transfer`, transaction tracking THORChain / MAYAChain native limit orders `WebZjs`-backed shielded balance scan and transfer Amount math, chain helpers, asset parsing, error handling, events, icons ## Source * npm: [`leokit-sdk`](https://www.npmjs.com/package/leokit-sdk) * GitHub: [`LeoFinance/leokit-sdk`](https://github.com/LeoFinance/leokit-sdk) The SDK ships its own `README.md` with a long-form integration walkthrough and a React example. # Limit Orders Source: https://docs.leokit.dev/sdk/limit-orders THORChain / MAYAChain native limit orders — set a target price, walk away ## Overview Limit orders are a native feature of THORChain and MAYAChain — they're stored as memo-encoded transactions on the source chain, and the protocol's outbound vault settles them when the target price is met. The SDK exposes both the high-level client methods (recommended) and the low-level building blocks (for custom flows). `LIMIT_ORDER_NATIVE_ASSETS` lists the assets eligible as the order's source/destination. Currently this includes `RUNE`, `BTC.BTC`, `ETH.ETH`, `ETH.USDC`, `BCH.BCH`, `LTC.LTC`, `DOGE.DOGE`, `AVAX.AVAX`, `BSC.BNB`, `MAYA.CACAO`, and others. ## High-Level API ### Submit ```typescript theme={null} import { LIMIT_ORDER_NATIVE_ASSETS } from "leokit-sdk"; const orderId = await sdk.submitLimitOrder({ fromAsset: "ETH.ETH", toAsset: "BTC.BTC", amount: "1000000000000000000", // 1 ETH in wei targetPrice: "0.05", // 0.05 BTC per ETH destination: "bc1q...", protocol: "thorchain", // or "mayachain" }); ``` Returns the protocol-assigned order ID. The SDK builds the memo, signs the deposit transaction with the active wallet, and broadcasts it. ### Cancel ```typescript theme={null} const cancelMemo = await sdk.cancelLimitOrder({ orderId, protocol: "thorchain", }); ``` The cancel transaction is signed and broadcast for you. Returns the memo string used (useful for receipts/logs). ## Low-Level Utilities For custom flows where you don't want the SDK to sign or broadcast: ```typescript theme={null} import { fetchLimitQuote, toBaseUnits, getLimitOrderStatus, fetchInboundAddress, buildCancelMemo, getCancelVaultAddress, } from "leokit-sdk"; // Quote first (no on-chain action) const quote = await fetchLimitQuote({ fromAsset: "ETH.ETH", toAsset: "BTC.BTC", amount: "1000000000000000000", targetPrice: "0.05", protocol: "thorchain", }); // Inbound vault (where the user must deposit) const vault = await fetchInboundAddress("thorchain", "ETH"); // After broadcasting, check status const status = await getLimitOrderStatus(quote.quoteId); // LimitOrderStatus { state: "open" | "filled" | "cancelled", ... } // To cancel manually const memo = buildCancelMemo({ orderId, protocol: "thorchain" }); const cancelVault = getCancelVaultAddress("thorchain"); // chain-specific // Sign + broadcast a transaction with `memo` to `cancelVault` ``` `toBaseUnits` (under the limit-orders namespace) is a thin wrapper that handles THORChain's 1e8 normalization correctly across decimals — use it instead of generic amount math when building memos by hand. ## Types | Type | Description | | ------------------------ | ----------------------------------------------- | | `LimitQuoteParams` | Input to `fetchLimitQuote` / `submitLimitOrder` | | `LimitQuoteResponse` | Quote details + recommended slippage | | `LimitOrderStatus` | Lifecycle state + fill details | | `CancelLimitOrderParams` | Input to `cancelLimitOrder` / `buildCancelMemo` | All types are exported from the main `leokit-sdk` entry point. ## Notes * Limit orders only work for protocols that support them (THORChain, MAYAChain). Other protocols throw on `submitLimitOrder`. * The SDK handles asset normalization (THORChain uses `ASSET~` suffix in some memos); you don't need to format the memo yourself when using `submitLimitOrder` / `cancelLimitOrder`. * Use `getLimitOrderStatus` (or `sdk.watchTransaction(orderId, ...)` if your status feed exposes the order id) for fill tracking. # Quotes & Swaps Source: https://docs.leokit.dev/sdk/quotes-and-swaps Fetch quotes, stream them in real time, execute swaps, and track transactions ## Fetch a Quote ```typescript theme={null} const quote = await sdk.getQuote({ fromAsset: "ETH.ETH", toAsset: "BTC.BTC", amount: "1000000000000000000", // 1 ETH in wei destination: "bc1q...", origin: "0x...", // optional, improves accuracy slippageBps: 150, // optional, 1.5% }); ``` The returned `SwapQuote` includes the cheapest/fastest route across all enabled protocols, plus `quoteId` (used for subsequent `executeSwap` and tracking calls). `AbortSignal` is supported for cancellation: ```typescript theme={null} const ctrl = new AbortController(); setTimeout(() => ctrl.abort(), 5000); const q = await sdk.getQuote(params, ctrl.signal); ``` ## Stream Quotes For UI use cases — display the first quote in \~200ms instead of waiting for every protocol: ```typescript theme={null} const stop = sdk.streamQuote( { fromAsset: "ETH.ETH", toAsset: "BTC.BTC", amount: "1000000000000000000" }, { onInit: (e) => console.log("quote_id:", e.quoteId), onQuote: (e) => console.log(e.protocol, e.data.expected_amount_out), onComplete: (e) => console.log("best:", e.quotes[0]), onError: (e) => console.error(e.code, e.message), } ); // Optional: cancel mid-stream stop(); ``` `StreamQuoteCallbacks` exposes typed events: `StreamingQuoteInitEvent`, `StreamingQuoteQuoteEvent`, `StreamingQuoteFinalEvent`, `StreamingQuoteErrorEvent`, `StreamingQuoteFinishedEvent`. The streaming endpoint is documented at [Streaming Quotes](/api-reference/endpoint/streaming-quotes). ## Execute a Swap ```typescript theme={null} const result = await sdk.executeSwap(quote, { // SwapOptions — all optional feeOption: "fast", // fee tier hint (chain-dependent) preferredProtocol: "thorchain", // pin a route from the quote bundle }); ``` `SwapResult` contains `txHash` (or `txHashes` for multi-tx flows like ERC20 approve + swap), the chosen `protocol`, and any `depositInstruction` data. ERC20 approvals are auto-handled when needed. The SDK inserts the approval transaction first, then the swap. See the [ERC20 approvals guide](/guides/erc20-approvals) for details. ## Plain Transfer (no swap) `sdk.transfer()` sends native or token assets directly without going through a protocol: ```typescript theme={null} const tx = await sdk.transfer({ asset: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", amount: "100", // 100 USDC to: "0xrecipient...", }); // TransferResult { txHash } ``` Useful when your app needs simple "move funds" capability that's not part of a swap. ## Transaction Lifecycle After broadcasting, save the hash so the LeoKit backend can track and emit webhooks: ```typescript theme={null} await sdk.saveTransaction(quote.quoteId, result.txHash); ``` Poll once: ```typescript theme={null} const status = await sdk.getTransactionStatus(quote.quoteId); // TransactionStatus { state: "pending" | "success" | "failed" | "refunded", ... } ``` Or watch with backoff: ```typescript theme={null} const stop = sdk.watchTransaction(quote.quoteId, (status) => { if (status.state !== "pending") stop(); }, { intervalMs: 5000, maxIntervalMs: 30000 }); ``` `watchTransaction` polls `/leokit/status` and exits cleanly when the swap settles or fails. ## Assets & Balances ```typescript theme={null} const all = await sdk.getAssets(); // Asset[] const usdc = await sdk.getAsset("ETH.USDC-0xA0b..."); await sdk.refreshAssets(); // forces re-fetch const bal = await sdk.getBalance("ETH.ETH"); // single const all = await sdk.getAllBalances(); // every connected wallet × every chain const slim = await sdk.getBalancesForAddresses([ { chain: "ETH", address: "0x..." }, { chain: "BTC", address: "bc1q..." }, ]); ``` The asset list is cached for 20 seconds (per-client); `refreshAssets()` bypasses the cache. ## Events ```typescript theme={null} sdk.on("transaction", (e) => console.log(e.quoteId, e.state)); sdk.on("walletChange", (e) => console.log(e.from, "→", e.to)); sdk.on("error", (err) => console.error(err)); ``` `LeoKitEvents` is fully typed. Listeners can be removed via the returned `Unsubscribe` function or `sdk.off("transaction", listener)`. # Utilities Source: https://docs.leokit.dev/sdk/utilities Amount math, chain helpers, asset parsing, error handling, events, and CDN icons Most utilities live under the `leokit-sdk/utils` and `leokit-sdk/cdn` subpath imports. They're tree-shakeable — only what you import gets bundled. ## Amount Math ```typescript theme={null} import { toBaseUnits, fromBaseUnits, formatAmount, formatWithSeparators, applySlippage, addAmounts, subtractAmounts, multiplyAmounts, divideAmounts, percentageOf, compareAmounts, isPositive, isZero, parseAmount, truncateDecimals, getMinimumAmount, } from "leokit-sdk/utils"; toBaseUnits("1.5", 18); // "1500000000000000000" fromBaseUnits("1500000000000000000", 18); // "1.5" formatAmount("1234.5678", 2); // "1234.57" formatWithSeparators("1234567.89"); // "1,234,567.89" applySlippage("1000", 150); // "985" (150 bps = 1.5%) ``` All amount helpers accept and return strings to avoid `number` precision loss. Internally they use `bignumber.js`. ## Chain Helpers ```typescript theme={null} import { getChainFromNetwork, getNetworkFromChain, getNativeAsset, isEvmChain, isUtxoChain, isCosmosChain, getEvmChainId, getChainFromEvmId, getProtocolType, EVM_CHAINS, UTXO_CHAINS, COSMOS_CHAINS, } from "leokit-sdk/utils"; getNativeAsset("ETH"); // "ETH.ETH" getNativeAsset("BTC"); // "BTC.BTC" isEvmChain("ARB"); // true getEvmChainId("ETH"); // 1 getChainFromEvmId(42161); // "ARB" getProtocolType("ETH"); // "evm" ``` `CHAIN_CONFIGS` (from the main entry point) holds the full per-chain config used by the SDK — RPC URLs, explorer URLs, native asset, decimals. ```typescript theme={null} import { CHAIN_CONFIGS, getChainConfig, getRpcUrl, getTxExplorerUrl } from "leokit-sdk"; const cfg = getChainConfig("ETH"); const rpc = getRpcUrl("ETH", "mainnet"); const url = getTxExplorerUrl("ETH", "0xabc..."); ``` ## Asset Parsing LeoKit asset IDs follow `CHAIN.SYMBOL[-ADDRESS]` (e.g. `ETH.USDC-0xA0b...`). Helpers: ```typescript theme={null} import { getParsedAsset, getAssetContractAddress, isNativeAsset, isHiveEngineAsset, constructAsset, normalizeAddress, NULL_ADDRESS, } from "leokit-sdk/utils"; getParsedAsset("ETH.USDC-0xA0b..."); // { network: "ETH", symbol: "USDC", address: "0xA0b..." } isNativeAsset("BTC.BTC"); // true isNativeAsset("ETH.USDC-0xA0b..."); // false getAssetContractAddress("ETH.USDC-0xA0b..."); // "0xA0b..." constructAsset("ETH", "USDC", "0xA0b..."); // "ETH.USDC-0xA0b..." normalizeAddress("ETH", "0xA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48"); // → checksummed lowercase form ``` The full asset-format spec is at [Asset Format](/reference/asset-format). ## Errors ```typescript theme={null} import { LeoKitError, isLeoKitError, type ErrorCode } from "leokit-sdk/utils"; try { await sdk.executeSwap(quote); } catch (err) { if (isLeoKitError(err)) { console.log(err.code, err.statusCode, err.message); } else { throw err; } } ``` The full `ErrorCode` catalog is at [Error Codes](/reference/error-codes). `LeoKitError` mirrors the backend's error class so you get matching codes on both sides. ## Events The `LeoKitClient` is itself an `EventEmitter`. For your own internal pub/sub, the SDK exposes the same primitive: ```typescript theme={null} import { EventEmitter, createEventEmitter, type LeoKitEvents } from "leokit-sdk/utils"; const bus = createEventEmitter<{ ready: void; error: Error }>(); const off = bus.on("ready", () => console.log("ready")); bus.emit("ready"); off(); // unsubscribe ``` Built-in `LeoKitEvents` shape: `error`, `transaction`, `walletChange`, `connectionChange`. Type-safe `on`/`off`/`emit`. ## CDN Icons ```typescript theme={null} import { getAssetIcon, getNetworkIcon, getWalletIcon, getProtocolIcon } from "leokit-sdk/cdn"; getAssetIcon("ETH.USDC-0xA0b..."); // "https://cdn.leokit.dev/assets/eth/usdc.png" getNetworkIcon("ETH"); // "https://cdn.leokit.dev/networks/eth.png" getWalletIcon("metamask"); // "https://cdn.leokit.dev/wallets/metamask.png" getProtocolIcon("thorchain"); // "https://cdn.leokit.dev/protocols/thorchain.png" ``` The CDN is also exposed as `LEOKIT_CDN_BASE_URL` if you want to compose URLs yourself. Custom CDN bases can be set via `cdnUrl` in `LeoKitConfig`. ## EVM-Specific ```typescript theme={null} import { normalizeHexValue } from "leokit-sdk/utils"; normalizeHexValue("0x1"); // "0x1" normalizeHexValue("0x01"); // "0x1" normalizeHexValue("0X0123"); // "0x123" ``` Useful when comparing `eth_chainId` values across providers that disagree on padding. # Wallets Source: https://docs.leokit.dev/sdk/wallets Connect, disconnect, and manage 19 wallet adapters across EVM, UTXO, Cosmos, Solana, NEAR, Tron, and more ## Connect & Disconnect ```typescript theme={null} const conn = await sdk.connectWallet("metamask"); // returns WalletConnection await sdk.disconnectWallet("metamask"); await sdk.disconnectAll(); ``` | Method | Returns | Description | | ------------------------------- | ---------------------------------- | --------------------------------------------- | | `connectWallet(type, options?)` | `Promise` | Connects an adapter. Adapters are lazy-loaded | | `disconnectWallet(type)` | `Promise` | Cleanly disconnects a single adapter | | `disconnectAll()` | `Promise` | Disconnects every connected adapter | | `getConnectedWallets()` | `WalletConnection[]` | Currently-connected sessions | | `isConnected()` | `boolean` | At least one wallet connected? | | `getAddress(chain)` | `string \| undefined` | Resolved address for a chain | | `getAddresses()` | `Partial>` | All resolved addresses | | `getAvailableWallets()` | `WalletType[]` | Adapters detected in the current environment | | `getSupportedWallets()` | `WalletType[]` | All adapters compiled into the SDK | `onProvidersChanged(callback)` returns an unsubscribe function. It fires when EIP-6963 providers come and go (browser only). ## Adapters | `WalletType` | Capabilities | Notes | | ------------------------- | ------------------------------- | ----------------------------------------- | | `metamask` | EVM | EIP-6963 with multi-account support | | `coinbase` | EVM | Coinbase Wallet | | `phantom` | EVM, UTXO, Solana | | | `ledger` | EVM, UTXO, Cosmos | Hardware (WebUSB) | | `keystore` | EVM, UTXO, Cosmos, Solana, Tron | Encrypted keystore file | | `ctrl` | EVM, UTXO, Cosmos, Solana | CTRL/XDEFI | | `keplr` | Cosmos | | | `trust` | EVM, UTXO, Cosmos, Solana | Trust Wallet | | `talisman` | Substrate / EVM | | | `tronlink` | Tron | | | `walletconnect` | EVM (and others via WC v2) | Requires `projectId` in `ConnectOptions` | | `vultisig` | Multi-chain | | | `solflare` | Solana, EVM | | | `shielded-zcash-keystore` | ZCash transparent + shielded | See [Shielded ZCash](/sdk/zcash-shielded) | | `hot-wallet` | Multi-chain | | | `meteor` | NEAR | | | `okx` | EVM, UTXO, Solana | | | `near` | NEAR | NEAR Wallet | | `rabby` | EVM | | For type-safe lazy imports, use `leokit-sdk/wallets`: ```typescript theme={null} import { MetaMaskAdapter, LedgerAdapter, KeplrAdapter } from "leokit-sdk/wallets"; ``` ## Ledger Deep-Dive ```typescript theme={null} // Subscribe to detection progress (USB devices arriving / leaving) const unsubscribe = await sdk.onLedgerProgress((event) => { console.log(event.stage, event.message); }); // Pick an account from the device const accounts = await sdk.getLedgerDerivedAccounts("ETH"); // [{ address, publicKey, derivationPath, index }, ...] await sdk.selectLedgerAccount("ETH", 1); // use the second derivation // Re-detect after physical reconnect await sdk.redetectLedger(); ``` `@ledgerhq/hw-app-btc@^10.18.0` is required and pinned via the SDK's optional dependencies. ## MetaMask Multi-Account MetaMask exposes multiple accounts as a single provider. The SDK adds first-class methods for switching and revoking permissions: ```typescript theme={null} // Prompt MetaMask to expose more accounts const newAccounts = await sdk.requestAdditionalMetaMaskAccounts(); // Find the currently-selected one const active = await sdk.getMetaMaskSelectedAddress(); // React to user-driven account switches const off = await sdk.onMetaMaskAccountsChanged((accounts) => { console.log("active changed to", accounts[0]); }); // Revoke wallet permissions entirely await sdk.revokeMetaMaskPermissions(); ``` These methods are MetaMask-specific — they no-op on other EVM adapters. ## EIP-6963 Discovery The SDK ships an `EIP6963Store` that auto-tracks provider announcements: ```typescript theme={null} import { getEIP6963Store } from "leokit-sdk/wallets"; const store = getEIP6963Store(); const providers = store.getProviders(); // [{ rdns, name, icon, provider }] ``` Use this to render a wallet picker that includes every browser-installed provider, not just the ones your app pre-defines. ## Connection Events `LeoKitClient` extends `EventEmitter`: ```typescript theme={null} sdk.on("walletChange", (e) => console.log(e.from, "→", e.to)); sdk.on("error", (err) => console.error(err)); ``` # Shielded ZCash Source: https://docs.leokit.dev/sdk/zcash-shielded Scan and transfer shielded ZEC (Sapling/Orchard) via the WebZjs-backed adapter ## Overview The `ShieldedZcashKeystoreAdapter` runs a [WebZjs](https://github.com/Electric-Coin-Company/webzjs) light-client in the browser to: * Derive shielded (`zs1...`) and unified (`u1...`) addresses from a mnemonic. * Scan the blockchain for shielded notes belonging to the wallet. * Build and sign shielded transfer transactions. Shielded ZEC is the only privacy-preserving asset LeoKit supports natively in-wallet. Transparent (`t1.../t3...`) ZEC is handled by the regular UTXO flow. ## Connect ```typescript theme={null} await sdk.connectWallet("shielded-zcash-keystore", { mnemonic: "abandon abandon abandon ...", // 24 words // Optional: override WebZjs config webZjsConfig: { /* WebZjsConfig */ }, }); ``` The adapter is lazy-loaded — pulling in the WebZjs WASM only when the user actually opts in. ## Read Balance ```typescript theme={null} const balance = await sdk.getShieldedZcashBalance(); // ShieldedZcashBalance { // transparent: { confirmed: "0.0", unconfirmed: "0.0" }, // sapling: { confirmed: "0.5", unconfirmed: "0.0", spendable: "0.5" }, // orchard: { confirmed: "1.2", unconfirmed: "0.0", spendable: "1.2" }, // total: "1.7", // total_usd: 42.50 // } ``` ## Sync the Light Client Shielded ZEC requires scanning the chain for notes belonging to the wallet. The first sync can take minutes; subsequent syncs are incremental. ```typescript theme={null} await sdk.startShieldedZcashScan({ // ZcashScanProgress callbacks (optional) onProgress: (p) => console.log(p.scannedBlocks, "/", p.targetBlock), onComplete: () => console.log("synced"), onError: (err) => console.error(err), }); ``` The scan runs in the background; `getShieldedZcashBalance` reads from the in-memory note set, so refresh whenever a `onProgress` event reports new notes. ## Address Helpers ```typescript theme={null} import { getZcashAddressType, isZcashAddress, isShieldedZcashAddress, parseUnifiedAddress, extractTransparentFromUnified, resolveSwapDestinationAddress, } from "leokit-sdk"; getZcashAddressType("zs1..."); // "z" getZcashAddressType("u1..."); // "u" getZcashAddressType("t1..."); // "t" isShieldedZcashAddress("zs1..."); // true isShieldedZcashAddress("t1..."); // false // Unified address has both transparent and shielded receivers const { transparent, shielded } = parseUnifiedAddress("u1..."); const t = extractTransparentFromUnified("u1..."); // When swapping TO a unified address, pick the correct sub-receiver // based on the protocol's native address type: const dest = resolveSwapDestinationAddress({ unifiedAddress: "u1...", preferredType: "transparent", // many cross-chain protocols only support t-addresses }); ``` ## Shielded Transfers Shielded transfers are built via the protocol layer rather than `sdk.transfer()`. The shielded protocol handler exposes: ```typescript theme={null} import type { ShieldedTransferType, ShieldedTransferParams, ShieldedZcashUnsignedTx, ShieldedZcashProvider, } from "leokit-sdk"; ``` Through `sdk.executeSwap`, the SDK automatically selects the shielded protocol when the source asset is shielded ZEC. For non-swap shielded sends, see the SDK README's "Shielded Transfers" section. ## Notes * The first chain scan downloads \~1 GB of compact-block data. Run it on a fast connection and persist the WebZjs state to IndexedDB across sessions. * Shielded ZEC swaps are routed through `mayachain` (and increasingly `near` via shielded-pool support); the `ShieldedZcashProtocol` handler abstracts this. * A user-friendly setup wizard is built into LeoDex; reuse the same `ShieldedZcashKeystoreAdapter` API to clone that flow. # Support & Contact Source: https://docs.leokit.dev/support Get help integrating LeoKit Your success is our success. LeoKit's main feature is our 24/7 support. We're here to help you in any way we can. ### 24/7 Dedicated Support * Real human support, 365 days a year * Fast response times ### How to Reach Us * **Preferred**: Fill the inquiry form on the [landing page](https://leokit.dev) * Tell us about your project and needs * We'll reach out via your preferred method (Telegram, Discord, Email) * Dashboard issues: Log in and check Error Logs first * Open "Tech-Support" Channel to Create a Support Ticket in Our Discord - [https://discord.gg/kg9QrKHkpr](https://discord.gg/kg9QrKHkpr) ### Common Resources * [Quickstart](./quickstart) — Get up and running * [API Reference](./api-reference) — Full endpoint details * [Guides](./guides) — Integration tutorials Building something cool? We're excited to help. [Inquire Now →](https://leokit.dev)