# 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)