# INTEGRATION\_GUIDE\_EN

> **Base URL**: `https://api.crosscurve.fi`\
> **Swagger UI**: <https://api.crosscurve.fi/api-docs/\\>
> **General Documentation**: <https://docs.crosscurve.fi>

***

## Quick Start

### Minimum Integration Flow

{% stepper %}
{% step %}

### Discovery

GET /networks — Get list of networks and contract addresses
{% endstep %}

{% step %}

### Token list

GET /tokenlist — Get list of tokens (filter: can\_swap)
{% endstep %}

{% step %}

### Routing

POST /routing/scan — Find swap route
{% endstep %}

{% step %}

### Estimate

POST /estimate — Get estimate and signature
{% endstep %}

{% step %}

### Build transaction

POST /tx/create — Build transaction
{% endstep %}

{% step %}

### Approve token (if needed)

Allow contract to use tokens (if not already done)
{% endstep %}

{% step %}

### Send transaction

Sign and send via ethers/web3
{% endstep %}

{% step %}

### Get requestId

GET /search?search= — Get requestId by hash (wait \~3 sec)
{% endstep %}

{% step %}

### Track status

GET /transaction/{id} — Track status until completed
{% endstep %}
{% endstepper %}

### Example: Swap CRV (Ethereum) → CRV (Arbitrum)

> Example for understanding the flow. Verify current addresses via `/tokenlist`.

```javascript
import { ethers } from 'ethers';

const API_BASE = 'https://api.crosscurve.fi';

// Initialize provider and signer (example for browser with MetaMask)
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

// 1. Find route
const routeResponse = await fetch(`${API_BASE}/routing/scan`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    params: {
      chainIdIn: 1,           // Ethereum
      chainIdOut: 42161,      // Arbitrum
      tokenIn: '0xD533a949740bb3306d119CC777fa900bA034cd52',   // CRV on Ethereum
      tokenOut: '0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978',  // CRV on Arbitrum
      amountIn: '1000000000000000000000'  // 1000 CRV (18 decimals)
    },
    slippage: 1  // 1%
  })
});

const routes = await routeResponse.json();
if (!routes.length) {
  throw new Error('No routes available for this pair');
}

const selectedRoute = routes[0]; // first route from list
console.log(`Expected output: ${selectedRoute.amountOut}`);
console.log(`Price impact: ${selectedRoute.priceImpact}%`);
console.log(`Fee: ${selectedRoute.totalFee.percent}%`);

// 2. Get estimate
const estimateResponse = await fetch(`${API_BASE}/estimate`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(selectedRoute)
});

const estimate = await estimateResponse.json();

// 3. Build transaction
// buildCalldata: true — returns ready calldata for sendTransaction
const txResponse = await fetch(`${API_BASE}/tx/create`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    from: '0xYourWalletAddress',      // With correct checksum!
    recipient: '0xRecipientAddress',  // Can match from
    routing: selectedRoute,
    estimate: estimate,
    buildCalldata: true               // Get ready calldata
  })
});

const txData = await txResponse.json();
// txData: { to, value, data } — ready for sendTransaction

// 4. Approve tokens (check allowance to avoid unnecessary TX)
const tokenContract = new ethers.Contract(
  '0xD533a949740bb3306d119CC777fa900bA034cd52', // CRV token
  ['function approve(address spender, uint256 amount) returns (bool)',
   'function allowance(address owner, address spender) view returns (uint256)'],
  signer
);
const ownerAddress = await signer.getAddress();
const allowance = await tokenContract.allowance(ownerAddress, txData.to);
if (allowance.lt('1000000000000000000000')) { // if allowance < amountIn
  await tokenContract.approve(txData.to, ethers.constants.MaxUint256);
  console.log('Token approved');
}

// 5. Send transaction to blockchain
const tx = await signer.sendTransaction({
  to: txData.to,
  data: txData.data,
  value: txData.value || '0'
});
console.log(`TX sent: ${tx.hash}`);

const receipt = await tx.wait();
console.log(`TX confirmed in block: ${receipt.blockNumber}`);

// 6. Get requestId
// Option A: via API (wait for indexing ~3 sec)
await new Promise(r => setTimeout(r, 3000));
const searchResponse = await fetch(`${API_BASE}/search?search=${tx.hash}`);
const searchResult = await searchResponse.json();
let requestId = searchResult.result[0]?.requestId;

// Option B: from transaction events (faster, no waiting)
if (!requestId) {
  const COMPLEX_OP_TOPIC = '0x830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c';
  const log = receipt.logs.find(l => l.topics[0] === COMPLEX_OP_TOPIC);
  if (log) {
    // Decode event to get nextRequestId
    const iface = new ethers.utils.Interface([
      'event ComplexOpProcessed(uint64 indexed currentChainId, bytes32 indexed currentRequestId, uint64 nextChainId, bytes32 nextRequestId, uint8 result, uint8 lastOp)'
    ]);
    const decoded = iface.parseLog(log);
    requestId = decoded.args.nextRequestId;
  }
}

console.log(`RequestId: ${requestId}`);

// 7. Track status (polling every 5 sec)
let status;
for (let i = 0; i < 60; i++) { // max 5 minutes
  const statusResponse = await fetch(`${API_BASE}/transaction/${requestId}`);
  status = await statusResponse.json();
  console.log(`Status: ${status.status}`);

  if (status.status === 'completed') {
    console.log('Swap completed!');
    break;
  }
  if (status.inconsistency) {
    console.log('Refund required, see /inconsistency');
    break;
  }
  if (status.destination?.emergency) {
    console.log('Recovery required, see /tx/create/emergency');
    break;
  }

  await new Promise(r => setTimeout(r, 5000)); // wait 5 sec
}
```

> **Important:** Addresses must be in checksum format (EIP-55). Use `ethers.utils.getAddress(address)` for conversion.

### Test Examples

| Source Network   | Target Network   | Token In | Token Out |
| ---------------- | ---------------- | -------- | --------- |
| Ethereum (1)     | Arbitrum (42161) | CRV      | CRV       |
| Optimism (10)    | Arbitrum (42161) | USDC     | USDC      |
| Arbitrum (42161) | Polygon (137)    | USDT     | USDT      |

> Get token addresses from `/tokenlist`.

### If No Route Found

If `/routing/scan` returns an empty array `[]`:

* Check that tokens have the `can_swap` tag
* Try changing the amount (too small/large)
* Try a different token pair
* Route requires liquidity in supported pools

### cURL Examples for Quick Testing

Test the API without writing code:

```bash
# 1. Get list of networks
curl -s "https://api.crosscurve.fi/networks?type=0" | jq 'keys'

# 2. Get tokens
curl -s "https://api.crosscurve.fi/tokenlist" | jq '.ethereum.tokens[:3]'

# 3. Find route (Ethereum CRV → Arbitrum CRV)
curl -X POST "https://api.crosscurve.fi/routing/scan" \
  -H "Content-Type: application/json" \
  -d '{
    "params": {
      "chainIdIn": 1,
      "chainIdOut": 42161,
      "tokenIn": "0xD533a949740bb3306d119CC777fa900bA034cd52",
      "tokenOut": "0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978",
      "amountIn": "1000000000000000000000"
    },
    "slippage": 1
  }' | jq '.[0] | {amountIn, amountOut, priceImpact}'

# 4. Get estimate (substitute result from routing/scan)
curl -X POST "https://api.crosscurve.fi/estimate" \
  -H "Content-Type: application/json" \
  -d '<ROUTE_FROM_STEP_3>' | jq '{deadline, fee, slippage}'

# 5. Check transaction status
curl -s "https://api.crosscurve.fi/transaction/<REQUEST_ID>" | jq '{status, inconsistency}'

# 6. Get token price
curl -s "https://api.crosscurve.fi/prices/0xD533a949740bb3306d119CC777fa900bA034cd52/1"
```

***

## Integration Architecture

### Process Diagram

```
┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│     Your     │────▶│  CrossCurve  │────▶│  Blockchain  │
│ Application  │◀────│     API      │◀────│   (Source)   │
└──────────────┘     └──────────────┘     └──────────────┘
                            │
                            ▼
                     ┌──────────────┐
                     │    Oracle    │
                     │   Network    │
                     └──────────────┘
                            │
                            ▼
                     ┌──────────────┐
                     │  Blockchain  │
                     │ (Destination)│
                     └──────────────┘
```

### Cross-Chain Swap Stages

| Stage       | Description                  | API / Action                      |
| ----------- | ---------------------------- | --------------------------------- |
| Discovery   | Get reference data           | `GET /networks`, `GET /tokenlist` |
| Routing     | Find available routes        | `POST /routing/scan`              |
| Estimation  | Calculate fees and signature | `POST /estimate`                  |
| Transaction | Build calldata               | `POST /tx/create`                 |
| Approval    | Allow token spending         | `token.approve()` (ERC-20)        |
| Execution   | Send transaction             | `signer.sendTransaction()`        |
| Lookup      | Get requestId                | `GET /search?search={txHash}`     |
| Tracking    | Track status                 | `GET /transaction/{requestId}`    |

***

## Supported Networks

Get current list via `GET /networks`. Examples:

| Network  | Chain ID | API Name   |
| -------- | -------- | ---------- |
| Ethereum | 1        | `ethereum` |
| Arbitrum | 42161    | `arbitrum` |
| Optimism | 10       | `optimism` |
| Polygon  | 137      | `polygon`  |
| BSC      | 56       | `bsc`      |

> 20+ EVM networks supported. See `/networks` for current list.

***

## Reference Data

### GET /networks

Get list of supported blockchain networks.

**Query Parameters:**

| Parameter | Type   | Description                          |
| --------- | ------ | ------------------------------------ |
| `type`    | number | Filter: `0` - mainnet, `1` - testnet |

**Example Request:**

```bash
curl "https://api.crosscurve.fi/networks?type=0"
```

**Example Response:**

```json
{
  "ethereum": {
    "name": "ethereum",
    "chainId": 1,
    "symbol": "ETH",
    "portal": "0x...",
    "synthesis": "0x...",
    "router": "0x...",
    "tokens": [...]
  }
}
```

> **Important:** Keys are network names (`ethereum`), not chainId.

***

### GET /tokenlist

Get list of tokens.

**Example Request:**

```bash
curl "https://api.crosscurve.fi/tokenlist"
```

**Example Response:**

```json
{
  "ethereum": {
    "tags": ["erc20", "native", "stable", "can_swap", ...],
    "tokens": [
      {
        "chainId": 1,
        "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "symbol": "USDC",
        "decimals": 6,
        "tags": ["erc20", "stable", "can_swap"],
        "permit": false
      }
    ]
  }
}
```

> **Important:** Keys are network names. For swaps, check that token has `can_swap` tag.

**Main Token Tags:**

| Tag              | Description                        |
| ---------------- | ---------------------------------- |
| `can_swap`       | Available for cross-chain swap     |
| `stable`         | Stablecoin                         |
| `native`         | Native network token (ETH, BNB...) |
| `wrapped_native` | Wrapped version (WETH, WBNB...)    |
| `curve_lp`       | Curve LP token                     |

***

### GET /prices/{token}

Get token price in USD.

**Path Parameters:**

| Parameter | Type   | Description              |
| --------- | ------ | ------------------------ |
| `token`   | string | Token address (checksum) |

> **Important:** Use token address, not symbol. Symbols (USDC, CRV) are not supported.\
> Recommended to use `/prices/{token}/{chainId}` for unambiguity.

**Example:**

```bash
curl "https://api.crosscurve.fi/prices/0xD533a949740bb3306d119CC777fa900bA034cd52"

# Response: "0.360043"
```

***

### GET /prices/{token}/{chainId}

Get token price in specific network.

**Example:**

```bash
curl "https://api.crosscurve.fi/prices/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/1"
```

**Example Response:** `0.999827` (number, price in USD)

***

### POST /prices

Batch get prices for multiple tokens.

**Request Body:**

```json
{
  "tokens": [
    {"address": "0xD533a949740bb3306d119CC777fa900bA034cd52", "chainId": 1},
    {"address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "chainId": 42161}
  ]
}
```

**Response:**

```json
[
  {"address": "0xD533a949740bb3306d119CC777fa900bA034cd52", "price": 0.35226},
  {"address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "price": 0.999835}
]
```

***

## Executing Cross-Chain Swaps

### POST /routing/scan

Find available routes for swap.

**Request Body:**

```json
{
  "params": {
    "chainIdIn": 1,
    "chainIdOut": 42161,
    "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "tokenOut": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
    "amountIn": "1000000000"
  },
  "slippage": 0.5
}
```

**Parameters:**

| Field               | Type   | Required | Description              |
| ------------------- | ------ | -------- | ------------------------ |
| `params.chainIdIn`  | number | Yes      | Source chain ID          |
| `params.chainIdOut` | number | Yes      | Target chain ID          |
| `params.tokenIn`    | string | Yes      | Source token address     |
| `params.tokenOut`   | string | Yes      | Target token address     |
| `params.amountIn`   | string | Yes      | Amount in smallest units |
| `slippage`          | number | Yes      | Allowed slippage (%)     |

**Response:**

```json
[
  {
    "query": {
      "params": { ... },
      "slippage": 0.5
    },
    "route": [...],
    "amountIn": "1000000000",
    "amountOut": "998500000",
    "amountOutWithoutSlippage": "1003500000",
    "tokenInPrice": 1.0,
    "tokenOutPrice": 0.9998,
    "priceImpact": 0.15,
    "totalFee": {
      "type": "aggregation",
      "amount": "1500000",
      "percent": "0.15"
    },
    "txs": [
      {
        "chainId": 1,
        "gasFeeNative": "0.005",
        "gasFeeUsd": 12.5,
        "gasConsumption": 250000
      }
    ],
    "expectedFinalitySeconds": "180"
  }
]
```

> Empty array `[]` — no route found. See "If No Route Found" in Quick Start.

***

### POST /estimate

Get operation estimate with signature for execution.

**Request Body:** Route object from `/routing/scan`

**Response:**

```json
{
  "priceInDollars": "1000.00",
  "stablePrice": "1000000000",
  "executionPrice": "998500000",
  "workerFee": "1500000",
  "deadline": "1703001600",
  "signature": "0x..."
}
```

***

### POST /tx/create

Build transaction data for blockchain submission.

**Request Body:**

```json
{
  "from": "0xYourAddress",
  "recipient": "0xRecipientAddress",
  "routing": { /* object from /routing/scan */ },
  "estimate": { /* object from /estimate */ },
  "permit": {
    "v": 28,
    "r": "0x...",
    "s": "0x..."
  },
  "buildCalldata": true
}
```

**Parameters:**

| Field           | Type    | Required | Description                |
| --------------- | ------- | -------- | -------------------------- |
| `from`          | string  | Yes      | Sender address             |
| `recipient`     | string  | Yes      | Recipient address          |
| `routing`       | object  | Yes      | Route from `/routing/scan` |
| `estimate`      | object  | Yes      | Estimate from `/estimate`  |
| `permit`        | object  | No       | EIP-2612 permit signature  |
| `buildCalldata` | boolean | No       | Build calldata             |

**Response (with `buildCalldata: true`):** — recommended

```json
{
  "to": "0xContractAddress",
  "value": "0",
  "data": "0x..."
}
```

Ready for `sendTransaction({ to, data, value })`.

**Response (without `buildCalldata`):**

```json
{
  "to": "0xContractAddress",
  "value": "0",
  "args": [["LM", "S", "BU"], ["0x...", ...], { executionPrice, deadline, v, r, s }],
  "abi": "function start(string[],bytes[],tuple(...)) payable"
}
```

Requires call via `ethers.Contract`.

***

## Token Approval

Before executing a swap, you must allow the CrossCurve contract to use your tokens.

> **Where to get spenderAddress?** Use `txData.to` from `/tx/create` response — this is the contract address that will spend your tokens.

### Standard Approve (ERC-20)

```javascript
import { ethers } from 'ethers';

const ERC20_ABI = [
  'function approve(address spender, uint256 amount) returns (bool)',
  'function allowance(address owner, address spender) view returns (uint256)'
];

async function approveToken(
  tokenAddress: string,
  spenderAddress: string,  // txData.to from /tx/create
  amount: string,
  signer: ethers.Signer
) {
  const token = new ethers.Contract(tokenAddress, ERC20_ABI, signer);
  const ownerAddress = await signer.getAddress();

  // Check current allowance
  const currentAllowance = await token.allowance(ownerAddress, spenderAddress);

  if (currentAllowance.gte(amount)) {
    console.log('Allowance already sufficient');
    return;
  }

  // Approve maximum amount (or specific)
  const tx = await token.approve(spenderAddress, ethers.constants.MaxUint256);
  await tx.wait();

  console.log('Token approved');
}
```

### Permit (EIP-2612) - Approve via Signature

If token supports EIP-2612 (`permit: true` field in tokenlist), you can use a signature instead of approve transaction.

```javascript
async function signPermit(
  tokenAddress: string,
  spenderAddress: string,
  amount: string,
  deadline: number,
  signer: ethers.Signer
) {
  const token = new ethers.Contract(tokenAddress, [
    'function name() view returns (string)',
    'function nonces(address owner) view returns (uint256)',
    'function DOMAIN_SEPARATOR() view returns (bytes32)'
  ], signer);

  const owner = await signer.getAddress();
  const nonce = await token.nonces(owner);
  const name = await token.name();
  const chainId = await signer.getChainId();

  const domain = {
    name: name,
    version: '1',
    chainId: chainId,
    verifyingContract: tokenAddress
  };

  const types = {
    Permit: [
      { name: 'owner', type: 'address' },
      { name: 'spender', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'nonce', type: 'uint256' },
      { name: 'deadline', type: 'uint256' }
    ]
  };

  const value = {
    owner: owner,
    spender: spenderAddress,
    value: amount,
    nonce: nonce.toNumber(),
    deadline: deadline
  };

  const signature = await signer._signTypedData(domain, types, value);
  const { v, r, s } = ethers.utils.splitSignature(signature);

  return { v, r, s };
}

// Using permit in /tx/create
// deadline taken from /estimate response
const permit = await signPermit(tokenIn, txData.to, amountIn, Number(estimate.deadline), signer);

const txResponse = await fetch(`${API_BASE}/tx/create`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    from: walletAddress,
    recipient: walletAddress,
    routing: route,
    estimate: estimate,
    permit: permit  // <-- Pass permit signature
  })
});
```

***

## Transaction Tracking

### GET /transaction/{requestId}

Get cross-chain transaction status.

**Parameters:**

| Parameter   | Type   | Description         |
| ----------- | ------ | ------------------- |
| `requestId` | string | Unique operation ID |

**Response:**

```json
{
  "status": "completed",
  "inconsistency": false,
  "source": { "status": "completed", "chainId": 1, "transactionHash": "0x..." },
  "oracle": { "status": "completed", "requestId": "0x..." },
  "destination": { "status": "completed", "chainId": 42161, "emergency": false }
}
```

**Possible Statuses:**

| Status        | Description                       |
| ------------- | --------------------------------- |
| `in progress` | Operation in progress             |
| `completed`   | Successfully completed            |
| `failed`      | Execution error                   |
| `reverted`    | Reverted (requires inconsistency) |
| `canceled`    | Canceled                          |

**Status Handling Algorithm:**

```
Check status:
├── "completed" → Done ✅
├── "in progress" → Continue polling
└── "failed" / "reverted" → Check flags:
    ├── destination.emergency === true → POST /tx/create/emergency
    └── inconsistency === true → POST /inconsistency
```

***

### GET /search

Search transactions by hash or requestId.

**Parameters:**

| Parameter | Type   | Required | Description                   |
| --------- | ------ | -------- | ----------------------------- |
| `search`  | string | Yes      | Transaction hash or requestId |
| `limit`   | number | No       | Results limit                 |
| `offset`  | number | No       | Offset                        |

**Example:**

```bash
curl "https://api.crosscurve.fi/search?search=0xTransactionHash"
```

***

### GET /history

User transaction history.

**Parameters:**

| Parameter | Type   | Required | Description    |
| --------- | ------ | -------- | -------------- |
| `address` | string | Yes      | Wallet address |

**Example:**

```bash
curl "https://api.crosscurve.fi/history?address=0xYourAddress"
```

***

## Error Handling

### Scenarios

| Flag                                       | Action                                   |
| ------------------------------------------ | ---------------------------------------- |
| `inconsistency: false`, `emergency: false` | Operation completed                      |
| `inconsistency: true`                      | Call `/inconsistency` for refund         |
| `emergency: true`                          | Call `/tx/create/emergency` for recovery |

### GET /inconsistency/{requestId}

Get parameters for refund on inconsistency.

**Example:**

```bash
curl "https://api.crosscurve.fi/inconsistency/0xRequestId"
```

***

### POST /inconsistency

Create refund transaction.

> **Flow:**
>
> 1. Call `GET /inconsistency/{requestId}` — get parameters (tokenIn, tokenOut, chainIdIn, chainIdOut, amountIn)
> 2. Sign refund data with user wallet (EIP-712 or personal\_sign)
> 3. Pass signature and original route to this endpoint

**Request Body:**

```json
{
  "requestId": "0x...",
  "signature": "0x...",
  "routing": { /* original route from /routing/scan */ },
  "permit": {
    "v": 28,
    "r": "0x...",
    "s": "0x...",
    "deadline": 1703001600
  }
}
```

**Parameters:**

| Field       | Type   | Required | Description                         |
| ----------- | ------ | -------- | ----------------------------------- |
| `requestId` | string | Yes      | Operation ID with inconsistency     |
| `signature` | string | Yes      | User signature (65 bytes hex)       |
| `routing`   | object | Yes      | Original route from `/routing/scan` |
| `permit`    | object | No       | EIP-2612 permit (if token supports) |

***

### POST /tx/create/emergency

Emergency recovery of locked funds.

> **When to use:** When `destination.emergency: true` in transaction status — funds are locked on destination chain and require manual recovery.

**Request Body:**

```json
{
  "requestId": "0x...",
  "signature": "0x..."
}
```

**Parameters:**

| Field       | Type   | Required | Description                   |
| ----------- | ------ | -------- | ----------------------------- |
| `requestId` | string | Yes      | Locked operation ID           |
| `signature` | string | Yes      | User signature (65 bytes hex) |

**Response:** Returns transaction data for fund recovery.

***

### Creating Signature for Emergency/Inconsistency

For `/tx/create/emergency` and `/inconsistency` endpoints, a signature confirming wallet ownership is required.

```javascript
import { ethers } from 'ethers';

async function createRecoverySignature(requestId, signer) {
  // 1. Hash requestId as string
  const messageHash = ethers.utils.solidityKeccak256(['string'], [requestId]);

  // 2. Convert to bytes
  const messageHashBinary = ethers.utils.arrayify(messageHash);

  // 3. Sign (adds Ethereum prefix)
  const signature = await signer.signMessage(messageHashBinary);

  return signature; // 132 characters (0x + 65 bytes)
}

// Usage
const signature = await createRecoverySignature(requestId, signer);

// For emergency:
await fetch(`${API_BASE}/tx/create/emergency`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ requestId, signature })
});

// For inconsistency:
await fetch(`${API_BASE}/inconsistency`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ requestId, signature, routing: selectedRoute })
});
```

***

### Common API Errors

| Error                         | Cause                        | Solution                             |
| ----------------------------- | ---------------------------- | ------------------------------------ |
| `Can't find receiveRequest`   | requestId not found          | Verify requestId                     |
| `Can't find paymentTx`        | Transaction not found        | Verify requestId                     |
| `Routing signature not valid` | Route expired or changed     | Repeat `/routing/scan` + `/estimate` |
| `Already completed`           | Transaction completed        | No action required                   |
| `Not an emergency`            | Emergency conditions not met | Wait or check status                 |
| `Uncorrect user address`      | Signature not from owner     | Sign with wallet from `from`         |
| `invalid signature string`    | Invalid signature format     | Verify 65-byte hex signature         |

***

## API Reference

### Endpoint List

#### Reference Data

| Method | Endpoint                    | Description       |
| ------ | --------------------------- | ----------------- |
| GET    | `/networks`                 | List of networks  |
| GET    | `/tokenlist`                | List of tokens    |
| GET    | `/validators`               | Validator status  |
| GET    | `/ready`                    | Service readiness |
| GET    | `/prices/{token}`           | Token price       |
| GET    | `/prices/{token}/{chainId}` | Price in network  |
| POST   | `/prices`                   | Batch prices      |

#### Routing and Swap

| Method | Endpoint        | Description        |
| ------ | --------------- | ------------------ |
| POST   | `/routing/scan` | Find routes        |
| POST   | `/estimate`     | Operation estimate |
| POST   | `/tx/create`    | Create transaction |

#### Monitoring

| Method | Endpoint                   | Description         |
| ------ | -------------------------- | ------------------- |
| GET    | `/transaction/{requestId}` | Transaction status  |
| GET    | `/search`                  | Search transactions |
| GET    | `/history`                 | User history        |
| GET    | `/transactions`            | Transaction list    |

#### Error Handling

| Method | Endpoint                     | Description        |
| ------ | ---------------------------- | ------------------ |
| GET    | `/inconsistency/{requestId}` | Refund parameters  |
| POST   | `/inconsistency`             | Refund transaction |
| POST   | `/tx/create/emergency`       | Emergency recovery |

#### Supply and Statistics

> Values are dynamic, examples current at time of testing.

| Method | Endpoint              | Description            | Response Format    |
| ------ | --------------------- | ---------------------- | ------------------ |
| GET    | `/supply/eywa/ts`     | Total Supply EYWA      | number             |
| GET    | `/supply/eywa/ms`     | Max Supply EYWA        | number             |
| GET    | `/supply/eywa/cmc`    | Data for CoinMarketCap | number             |
| GET    | `/supply/eywa/cg`     | Data for CoinGecko     | `{"result":"..."}` |
| GET    | `/points/multipliers` | Points multipliers     | object             |

#### NFT Operations

| Method | Endpoint         | Description             | Required Parameters                     |
| ------ | ---------------- | ----------------------- | --------------------------------------- |
| GET    | `/nft/rarity`    | NFT rarity              | `tokens` (query, string) ⚠️             |
| POST   | `/nft/estimate`  | NFT operation estimate  | `wallet`, `tokenIds` (array of numbers) |
| POST   | `/nft/tx`        | Create NFT transaction  | `wallet`, `tokenIds`, `estimate`        |
| POST   | `/nft/emergency` | NFT emergency operation | `requestId`, `signature`                |

> ⚠️ **Known bug:** `GET /nft/rarity` does not parse `tokens` query parameter. Endpoint temporarily unavailable.

**NFT Operations Flow:**

```
1. POST /nft/estimate { wallet, tokenIds: [1,2,3] } → estimate
2. POST /nft/tx { wallet, tokenIds, estimate } → tx data
```

***

## Code Examples

> Main flow in Quick Start section.

### TypeScript: Types and Polling

```typescript
interface SwapParams {
  chainIdIn: number;
  chainIdOut: number;
  tokenIn: string;
  tokenOut: string;
  amountIn: string;
  slippage: number;
  from: string;
  recipient: string;
}

interface TransactionStatus {
  status: 'in progress' | 'completed' | 'failed';
  inconsistency: boolean;
  source: { status: string; chainId: number; transactionHash: string };
  destination?: { status: string; emergency: boolean };
}

// Polling with timeout
async function trackTransaction(requestId: string, maxAttempts = 60): Promise<TransactionStatus> {
  const API_BASE = 'https://api.crosscurve.fi';

  for (let i = 0; i < maxAttempts; i++) {
    const res = await fetch(`${API_BASE}/transaction/${requestId}`);
    const data: TransactionStatus = await res.json();

    if (data.status === 'completed') return data;
    if (data.inconsistency) return data; // requires /inconsistency
    if (data.destination?.emergency) return data; // requires /tx/create/emergency

    await new Promise(r => setTimeout(r, 5000));
  }
  throw new Error('Timeout: transaction not completed');
}
```

### JavaScript: Getting Networks and Tokens

```javascript
async function getNetworksAndTokens() {
  const networks = await fetch('https://api.crosscurve.fi/networks?type=0').then(r => r.json());
  const tokensByNetwork = await fetch('https://api.crosscurve.fi/tokenlist').then(r => r.json());
  return { networks, tokensByNetwork };
}

function getSwappableTokens(tokensByNetwork, networkName) {
  const networkData = tokensByNetwork[networkName];
  if (!networkData) return [];

  return networkData.tokens.filter(token =>
    token.tags.includes('can_swap')
  );
}
```

### Python: Finding Route

```python
import requests

API_BASE = 'https://api.crosscurve.fi'

def find_route(chain_in: int, chain_out: int,
               token_in: str, token_out: str,
               amount: str, slippage: float = 0.5):

    response = requests.post(f'{API_BASE}/routing/scan', json={
        'params': {
            'chainIdIn': chain_in,
            'chainIdOut': chain_out,
            'tokenIn': token_in,
            'tokenOut': token_out,
            'amountIn': amount
        },
        'slippage': slippage
    })

    routes = response.json()

    if not routes:
        raise Exception('No routes available')

    selected = routes[0]  # first route from list
    print(f"Amount In: {selected['amountIn']}")
    print(f"Amount Out: {selected['amountOut']}")
    print(f"Price Impact: {selected['priceImpact']}%")
    print(f"Total Fee: {selected['totalFee']['percent']}%")

    return selected

# Example usage
route = find_route(
    chain_in=1,          # Ethereum
    chain_out=42161,     # Arbitrum
    token_in='0xD533a949740bb3306d119CC777fa900bA034cd52',   # CRV on Ethereum
    token_out='0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978',  # CRV on Arbitrum
    amount='1000000000000000000000'  # 1000 CRV (18 decimals)
)
```

***

## Route Operation Codes

| Code | Description                  |
| ---- | ---------------------------- |
| `A`  | Add liquidity                |
| `S`  | Swap                         |
| `R`  | Remove liquidity             |
| `LM` | Lock/Mint                    |
| `BU` | Burn/Unlock                  |
| `BM` | Burn/Mint                    |
| `Uw` | Unwrap (unwrap native token) |
| `W`  | Wrap (wrap native token)     |
| `M`  | Emergency Mint               |
| `U`  | Emergency Unlock             |

***

## Rate Limits and Best Practices

### Rate Limits

Public rate limits are not documented. For stable operation:

| Endpoint                  | Usage Example         |
| ------------------------- | --------------------- |
| `/networks`, `/tokenlist` | Cache locally         |
| `/routing/scan`           | On user request       |
| `/estimate`               | After route selection |
| `/transaction/{id}`       | Polling with interval |
| `/prices`                 | Cache when needed     |

### Best Practices

1. **Cache `/networks` and `/tokenlist`** — data changes rarely

   ```javascript
   let cache = { networks: null, tokens: null, time: 0 };
   const TTL = 3600000; // 1 hour

   async function getNetworks() {
     if (cache.networks && Date.now() - cache.time < TTL) return cache.networks;
     cache.networks = await fetch(`${API_BASE}/networks?type=0`).then(r => r.json());
     cache.time = Date.now();
     return cache.networks;
   }
   ```
2. **Checksum addresses** — EIP-55 format recommended for compatibility

   ```javascript
   const address = ethers.utils.getAddress(userInput); // converts to checksum
   ```
3. **Check `can_swap`** before calling `/routing/scan`

   ```javascript
   if (!token.tags.includes('can_swap')) {
     throw new Error('Token not available for cross-chain swap');
   }
   ```
4. **Check `deadline`** from `/estimate` before sending

   ```javascript
   const now = Math.floor(Date.now() / 1000);
   if (Number(estimate.deadline) < now + 60) {
     // deadline expires in <1 min — get new estimate
   }
   ```
5. **Polling** — 5-10 sec interval, handle `inconsistency` and `emergency`
6. **Save requestId** — store after sending transaction for tracking and recovery
7. **Slippage** — use appropriate values (0.5-1% for stables, 1-3% for volatile)
8. **Use correct wallet** for recovery signatures — must match `from` of original transaction
9. **Account for token decimals** — not all tokens have 18 decimals

   ```javascript
   // ❌ Wrong: hardcoded 18 decimals
   const amount = parseFloat(value) / 1e18;

   // ✅ Correct: use decimals from tokenlist
   function formatAmount(rawAmount, decimals) {
     return parseFloat(rawAmount) / Math.pow(10, decimals);
   }

   // Decimals examples:
   // USDC, USDT: 6
   // WBTC: 8
   // CRV, ETH, most tokens: 18
   ```

***

## Limitations

| Parameter      | Description                                       |
| -------------- | ------------------------------------------------- |
| Minimum amount | Depends on token pair and liquidity               |
| Maximum amount | Limited by pool liquidity                         |
| Slippage       | Specified in percent when calling `/routing/scan` |
| Execution time | Depends on networks and oracle network load       |

***

## Contract Addresses

Contract addresses available via `/networks`:

| Contract  | Description                            | Field in /networks |
| --------- | -------------------------------------- | ------------------ |
| Portal    | Entry point for cross-chain operations | `portal`           |
| Synthesis | Synthetic token contract               | `synthesis`        |
| Router    | Swap router                            | `router`           |

```javascript
// Getting contract addresses
const networks = await fetch('https://api.crosscurve.fi/networks?type=0').then(r => r.json());
const ethPortal = networks.ethereum.portal;
const ethRouter = networks.ethereum.router;
const ethSynthesis = networks.ethereum.synthesis;
```

***

## Fee Structure

Fee information is returned in `/routing/scan` response:

| Fee Type      | Description     | Response Field              |
| ------------- | --------------- | --------------------------- |
| `dex`         | DEX/pool fee    | In each route step          |
| `bridge`      | Bridge fee      | In bridgeIn/bridgeOut steps |
| `aggregation` | Aggregation fee | Total CrossCurve fee        |
| `total`       | Total fee       | `totalFee` in route         |

```javascript
const route = routes[0];
console.log(`Total fee: ${route.totalFee.percent}%`);
console.log(`Fee in USD: $${route.totalFee.amount}`);

// Details by step
route.route.forEach(step => {
  step.fees?.forEach(fee => {
    console.log(`${fee.type}: ${fee.percent}%`);
  });
});
```

***

## FAQ

<details>

<summary>General Questions</summary>

**Q: Is an API key required?**\
A: At time of writing, the API does not require authentication.

**Q: Are there rate limits?**\
A: Public limits are not documented. Cache reference data and avoid frequent requests.

**Q: Which networks are supported?**\
A: Current list available via `GET /networks`. EVM-compatible networks are supported.

**Q: Why does `/routing/scan` return an empty array?**\
A: No route found. See "If No Route Found" in Quick Start.

</details>

<details>

<summary>Technical Questions</summary>

**Q: How to get requestId after sending transaction?**\
A: RequestId is emitted in Portal contract events. Use `GET /search?search={txHash}` to find it.

**Q: What to do with `inconsistency: true`?**\
A: Refund required:

* Call `GET /inconsistency/{requestId}` — get parameters for signature
* Sign data with user wallet
* Call `POST /inconsistency` with `requestId`, `signature` and original `routing`

**Q: What to do with `emergency: true`?**\
A: Fund recovery required. Call `POST /tx/create/emergency` with `requestId` and `signature` (user signature).

**Q: How does permit (EIP-2612) work?**\
A: If token supports permit (`permit: true` in tokenlist), you can sign permission offchain instead of separate approve transaction. Pass signature `{v, r, s}` to `/tx/create`.

</details>

***

## Glossary

| Term              | Description                                             |
| ----------------- | ------------------------------------------------------- |
| **Portal**        | Entry point contract for cross-chain operations         |
| **Synthesis**     | Contract for minting/burning synthetic tokens           |
| **Router**        | Swap routing contract                                   |
| **RequestId**     | Unique identifier for cross-chain operation             |
| **Inconsistency** | State requiring fund refund                             |
| **Emergency**     | State requiring fund recovery                           |
| **can\_swap**     | Token tag indicating cross-chain swap capability        |
| **Synth**         | Synthetic token representing asset from another network |
| **bridgeIn**      | Token lock operation on source chain                    |
| **bridgeOut**     | Token unlock operation on destination chain             |
| **Oracle**        | CrossCurve validator network                            |
| **Slippage**      | Allowed deviation from expected price                   |

***

## Changelog

> API change history. Follow updates in Swagger UI.

| Date    | Change                                                           |
| ------- | ---------------------------------------------------------------- |
| 2025-12 | Documentation created: Quick Start, API Reference, code examples |
| —       | For current API changes, see Swagger UI                          |

***

## Support

* **Swagger UI**: <https://api.crosscurve.fi/api-docs/>
* **Documentation**: <https://docs.crosscurve.fi>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.crosscurve.fi/integration_guide_en.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
