> For the complete documentation index, see [llms.txt](https://docs.crosscurve.fi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.crosscurve.fi/rukovodstvo-po-integracii.md).

# Руководство по интеграции

> **Base URL**: `https://api.crosscurve.fi` \
> **Alternative Base URL**: `https://pusher-cdp.x.ubtk.dev` (CDP API) \
> **Swagger UI**: <https://api.crosscurve.fi/api-docs/> | <https://pusher-cdp.x.ubtk.dev/api-docs> \
> **TypeScript SDK**: [@crosscurve/sdk](https://www.npmjs.com/package/@crosscurve/sdk) \
> **Общая документация**: <https://docs.crosscurve.fi>
>
> 🚀 [**Быстрый старт**](broken://pages/ec086a21ff30331084446951ce6d4e78381de3c6) — запустите первый своп за 15 минут 🤝 [**Партнёрская программа**](/partnyoram.md) — fee sharing для интеграторов

***

## Содержание

1. [Обзор интеграции](#обзор-интеграции)
2. [TypeScript SDK](#typescript-sdk)
3. [Архитектура интеграции](#архитектура-интеграции)
4. [Поддерживаемые сети](#поддерживаемые-сети)
5. [Справочные данные](#справочные-данные)
6. [Выполнение кроссчейн-свопа](#выполнение-кроссчейн-свопа)
7. [Token Approval](#token-approval)
8. [Отслеживание транзакций](#отслеживание-транзакций)
9. [Обработка ошибок](#обработка-ошибок)
10. [API Reference](#api-reference)
11. [Примеры кода](#примеры-кода)
12. [Rate Limits и Best Practices](#rate-limits-и-best-practices)
13. [Ограничения](#ограничения)
14. [Адреса контрактов](#адреса-контрактов)
15. [Структура комиссий](#структура-комиссий)
16. [FAQ](#faq)
17. [Глоссарий](#глоссарий)
18. [Changelog](#changelog)

***

## Обзор интеграции

### Минимальный флоу

```
1. GET /networks          → Получить список сетей и адреса контрактов
2. GET /tokenlist         → Получить список токенов (фильтр: can_swap)
3. POST /routing/scan     → Найти маршрут обмена (включает signature)
4. POST /tx/create        → Сформировать транзакцию
5. [Approve токена]       → Разрешить контракту использовать токены (если ещё нет)
6. [Отправить TX]         → Подписать и отправить через ethers/web3
7. GET /search?search=    → Получить requestId по хешу (подождать ~3 сек)
8. GET /transaction/{id}  → Отслеживать статус до completed
```

### Пример: Своп CRV (Arbitrum) → CRV (Ethereum)

> Пример для ознакомления с флоу. Проверяйте актуальность адресов через `/tokenlist`.

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

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

// Инициализация провайдера и signer (пример для браузера с MetaMask)
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

// 1. Найти маршрут
const routeResponse = await fetch(`${API_BASE}/routing/scan`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    params: {
      chainIdIn: 42161,       // Arbitrum
      chainIdOut: 1,          // Ethereum
      tokenIn: '0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978',   // CRV on Arbitrum
      tokenOut: '0xD533a949740bb3306d119CC777fa900bA034cd52',  // CRV on Ethereum
      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]; // первый маршрут из списка
console.log(`Expected output: ${selectedRoute.amountOut}`);
console.log(`Price impact: ${selectedRoute.priceImpact}%`);
console.log(`Fee: ${selectedRoute.totalFee.percent}%`);
console.log(`Signature included: ${!!selectedRoute.signature}`);

// 2. Сформировать транзакцию
// buildCalldata: true — вернёт готовый calldata для sendTransaction
// signature уже включена в selectedRoute из /routing/scan
const txResponse = await fetch(`${API_BASE}/tx/create`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    from: '0xYourWalletAddress',      // С правильным checksum!
    recipient: '0xRecipientAddress',  // Может совпадать с from
    routing: selectedRoute,           // Включает signature
    buildCalldata: true               // Получить готовый calldata
  })
});

const txData = await txResponse.json();
// txData: { to, value, data } — готово для sendTransaction

// 3. Approve токенов (проверить allowance, чтобы не делать лишнюю TX)
const tokenContract = new ethers.Contract(
  '0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978', // CRV token on Arbitrum
  ['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')) { // если allowance < amountIn
  await tokenContract.approve(txData.to, ethers.constants.MaxUint256);
  console.log('Token approved');
}

// 4. Отправить транзакцию в блокчейн
const tx = await signer.sendTransaction({
  to: txData.to,
  data: txData.data,
  value: txData.value || '0'
});
console.log(`TX отправлена: ${tx.hash}`);

const receipt = await tx.wait();
console.log(`TX подтверждена в блоке: ${receipt.blockNumber}`);

// 5. Получить requestId
// Вариант A: через API (подождать индексацию ~3 сек)
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;

// Вариант B: из событий транзакции (быстрее, без ожидания)
if (!requestId) {
  const COMPLEX_OP_TOPIC = '0x830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c';
  const log = receipt.logs.find(l => l.topics[0] === COMPLEX_OP_TOPIC);
  if (log) {
    // Декодируем событие для получения 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}`);

// 6. Отслеживать статус (polling каждые 5 сек)
let status;
for (let i = 0; i < 60; i++) { // макс 5 минут
  const statusResponse = await fetch(`${API_BASE}/transaction/${requestId}`);
  status = await statusResponse.json();
  console.log(`Статус: ${status.status}`);

  if (status.status === 'completed') {
    console.log('Своп завершён!');
    break;
  }
  if (status.destination?.status === 'retry') {
    console.log('Требуется повтор через другой бридж, см. /tx/create/retry');
    break;
  }
  if (status.inconsistency) {
    console.log('Требуется возврат средств, см. /inconsistency');
    break;
  }
  if (status.destination?.emergency) {
    console.log('Требуется восстановление, см. /tx/create/emergency');
    break;
  }

  await new Promise(r => setTimeout(r, 5000)); // ждать 5 сек
}
```

> **Важно:** Адреса должны быть в формате checksum (EIP-55). Используйте `ethers.utils.getAddress(address)` для преобразования.

### Примеры для тестирования

| Исходная сеть    | Целевая сеть     | Токен In | Токен Out |
| ---------------- | ---------------- | -------- | --------- |
| Arbitrum (42161) | Ethereum (1)     | CRV      | CRV       |
| Optimism (10)    | Arbitrum (42161) | USDC     | USDC      |
| Arbitrum (42161) | Polygon (137)    | USDT     | USDT      |

> Адреса токенов берите из `/tokenlist`.

### Если маршрут не найден

Если `/routing/scan` возвращает пустой массив `[]`:

* Проверьте, что токены имеют тег `can_swap`
* Попробуйте изменить сумму (слишком маленькая/большая)
* Попробуйте другую пару токенов
* Для маршрута нужна ликвидность в поддерживаемых пулах

### cURL примеры для быстрого тестирования

Тестирование API без написания кода:

```bash
# 1. Получить список сетей
curl -s "https://api.crosscurve.fi/networks?type=0" | jq 'keys'

# 2. Получить токены
curl -s "https://api.crosscurve.fi/tokenlist" | jq '.ethereum.tokens[:3]'

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

# 4. Проверить статус транзакции
curl -s "https://api.crosscurve.fi/transaction/<REQUEST_ID>" | jq '{status, inconsistency}'

# 5. Получить цену токена
curl -s "https://api.crosscurve.fi/prices/0xD533a949740bb3306d119CC777fa900bA034cd52/1"
```

***

## TypeScript SDK

Альтернатива прямым API-вызовам — TypeScript SDK с типизацией и встроенным auto-recovery.

### Установка

```bash
npm install @crosscurve/sdk
# + один из провайдеров
npm install viem    # или ethers / web3
```

### Быстрый пример

```typescript
import { CrossCurveSDK, ViemAdapter } from '@crosscurve/sdk'
import { createWalletClient, createPublicClient, http } from 'viem'
import { arbitrum } from 'viem/chains'

// Инициализация
const sdk = new CrossCurveSDK()
await sdk.init()

// Настройка signer (Viem)
const walletClient = createWalletClient({ account, chain: arbitrum, transport: http() })
const publicClient = createPublicClient({ chain: arbitrum, transport: http() })
const signer = new ViemAdapter(walletClient, publicClient, account)

// Получение котировки
const quote = await sdk.getQuote({
  fromChain: 42161,       // Arbitrum
  toChain: 1,             // Ethereum
  fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',  // USDC on Arbitrum
  toToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',    // USDC on Ethereum
  amount: '1000000000',   // 1000 USDC (6 decimals)
  slippage: 0.5,          // 0.5%
  sender: account.address
})

// Выполнение свопа
const result = await sdk.executeQuote(quote, {
  signer,
  autoRecover: true  // автоматическое восстановление при сбоях
})

// Отслеживание
await sdk.trackTransaction(result.requestId)
```

### Ключевые методы

| Метод                | Описание                 |
| -------------------- | ------------------------ |
| `init()`             | Загрузка сетей и токенов |
| `getQuote()`         | Получение котировки      |
| `executeQuote()`     | Выполнение свопа         |
| `trackTransaction()` | Отслеживание статуса     |
| `recover()`          | Ручное восстановление    |

### Поддерживаемые провайдеры

* **ViemAdapter** (рекомендуется)
* **EthersV6Adapter**
* **EthersV5Adapter**
* **Web3Adapter**

> Полная документация: [GitHub](https://github.com/eywa-protocol/crosscurve-sdk) | [npm](https://www.npmjs.com/package/@crosscurve/sdk)

***

## Архитектура интеграции

### Диаграмма процесса

```
┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│     Ваше     │────▶│  CrossCurve  │────▶│   Блокчейн   │
│  приложение  │◀────│     API      │◀────│   (Source)   │
└──────────────┘     └──────────────┘     └──────────────┘
                            │
                            ▼
                     ┌──────────────┐
                     │    Oracle    │
                     │   Network    │
                     └──────────────┘
                            │
                            ▼
                     ┌──────────────┐
                     │   Блокчейн   │
                     │ (Destination)│
                     └──────────────┘
```

### Этапы кроссчейн-свопа

| Этап           | Описание                                       | API / Действие                    |
| -------------- | ---------------------------------------------- | --------------------------------- |
| 1. Discovery   | Получение справочных данных                    | `GET /networks`, `GET /tokenlist` |
| 2. Routing     | Поиск доступных маршрутов (включает signature) | `POST /routing/scan`              |
| 3. Transaction | Формирование calldata                          | `POST /tx/create`                 |
| 4. Approval    | Разрешение на использование токенов            | `token.approve()` (ERC-20)        |
| 5. Execution   | Отправка транзакции                            | `signer.sendTransaction()`        |
| 6. Lookup      | Получение requestId                            | `GET /search?search={txHash}`     |
| 7. Tracking    | Отслеживание статуса                           | `GET /transaction/{requestId}`    |

***

## Поддерживаемые сети

Актуальный список через `GET /networks`. Примеры:

| Сеть     | Chain ID | Имя в API  |
| -------- | -------- | ---------- |
| Ethereum | 1        | `ethereum` |
| Arbitrum | 42161    | `arbitrum` |
| Optimism | 10       | `optimism` |
| Polygon  | 137      | `polygon`  |
| BSC      | 56       | `bsc`      |

> Поддерживается 20+ EVM-сетей. См. `/networks` для актуального списка.

***

## Справочные данные

### GET /networks

Получение списка поддерживаемых блокчейн-сетей.

**Параметры запроса:**

| Параметр | Тип    | Описание                             |
| -------- | ------ | ------------------------------------ |
| `type`   | number | Фильтр: `0` - mainnet, `1` - testnet |

**Пример запроса:**

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

**Пример ответа:**

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

> **Важно:** Ключи — имена сетей (`ethereum`), не chainId.

***

### GET /tokenlist

Получение списка токенов.

**Пример запроса:**

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

**Пример ответа:**

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

> **Важно:** Ключи - имена сетей. Для обмена проверяйте наличие тега `can_swap` у токена.

**Основные теги токенов:**

| Тег              | Описание                          |
| ---------------- | --------------------------------- |
| `can_swap`       | Доступен для кроссчейн-обмена     |
| `stable`         | Стейблкоин                        |
| `native`         | Нативный токен сети (ETH, BNB...) |
| `wrapped_native` | Wrapped версия (WETH, WBNB...)    |
| `curve_lp`       | LP токен Curve                    |

***

### GET /prices/{token}

Получение цены токена в USD.

**Параметры пути:**

| Параметр | Тип    | Описание                |
| -------- | ------ | ----------------------- |
| `token`  | string | Адрес токена (checksum) |

> ⚠️ **Важно:** Используйте адрес токена, не символ. Символы (USDC, CRV) не поддерживаются. Рекомендуется использовать `/prices/{token}/{chainId}` для однозначности.

**Пример:**

```bash
curl "https://api.crosscurve.fi/prices/0xD533a949740bb3306d119CC777fa900bA034cd52"
# Ответ: "0.360043"
```

***

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

Получение цены токена в конкретной сети.

**Пример:**

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

**Пример ответа:** `0.999827` (число, цена в USD)

***

### POST /prices

Пакетное получение цен нескольких токенов (batch запрос).

**Request Body:**

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

**Response:**

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

***

## Выполнение кроссчейн-свопа

### POST /routing/scan

Поиск доступных маршрутов для обмена.

**Headers:**

| Header         | Обязательно | Описание                                                                                                                                                                       |
| -------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Content-Type` | Да          | `application/json`                                                                                                                                                             |
| `api-key`      | Нет         | API ключ партнёра (Free или Standard). При наличии ответ включает поля `feeShare`, `feeShareRecipient`, `feeShareToken`. Standard ключи также позволяют задавать `feeShareBps` |

**Request Body:**

```json
{
  "params": {
    "chainIdIn": 1,
    "chainIdOut": 42161,
    "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "tokenOut": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
    "amountIn": "1000000000"
  },
  "slippage": 0.5,
  "feeShareBps": 50,
  "feeFromAmount": false,
  "feeToken": "0x0000000000000000000000000000000000000000",
  "from": "0xYourWalletAddress",
  "providers": ["AXELAR", "LAYER_ZERO"]
}
```

**Параметры:**

| Поле                | Тип       | Обязательно | Описание                                                                                        |
| ------------------- | --------- | ----------- | ----------------------------------------------------------------------------------------------- |
| `params.chainIdIn`  | number    | Да          | ID исходной сети                                                                                |
| `params.chainIdOut` | number    | Да          | ID целевой сети                                                                                 |
| `params.tokenIn`    | string    | Да          | Адрес исходного токена                                                                          |
| `params.tokenOut`   | string    | Да          | Адрес целевого токена                                                                           |
| `params.amountIn`   | string    | Да          | Сумма в минимальных единицах                                                                    |
| `slippage`          | number    | Да          | Допустимое проскальзывание (%)                                                                  |
| `feeFromAmount`     | boolean   | Нет         | Вычитать комиссию из входной суммы                                                              |
| `feeToken`          | string    | Нет         | Адрес токена для оплаты комиссий                                                                |
| `from`              | string    | Нет         | Адрес кошелька пользователя (для оценки газа)                                                   |
| `feeShareBps`       | number    | Нет         | Комиссия интегратора в базисных пунктах (например, `50` = 0.5%). Только для Standard API ключей |
| `providers`         | string\[] | Нет         | Фильтр провайдеров роутинга (например: `["AXELAR", "LAYER_ZERO"]`)                              |

**Response:**

```json
[
  {
    "query": {
      "params": { ... },
      "slippage": 0.5
    },
    "route": [...],
    "amountIn": "1000000000",
    "amountOut": "998500000",              // Минимум с учётом slippage (гарантированный)
    "amountOutWithoutSlippage": "1003500000", // Идеальный вариант без slippage
    "tokenInPrice": 1.0,
    "tokenOutPrice": 0.9998,
    "priceImpact": 0.15,
    "totalFee": {
      "type": "aggregation",
      "amount": "1500000",
      "percent": "0.15"
    },
    "sourceFee": {
      "token": "0x0000000000000000000000000000000000000000",
      "amount": "500000000000000",
      "usd": 1.2
    },
    "deliveryFee": {
      "token": "0x0000000000000000000000000000000000000000",
      "amount": "1500000000000000",
      "usd": 3.5
    },
    "feeShare": "50000000000000",
    "feeShareRecipient": "0x1234567890abcdef1234567890abcdef12345678",
    "feeShareToken": "0x0000000000000000000000000000000000000000",
    "txs": [
      {
        "chainId": 1,
        "gasFeeNative": "0.005",
        "gasFeeUsd": 12.5,
        "gasConsumption": 250000,
        "consumptions": [
          { "gasConsumption": 250000, "bridge": null, "type": "start" },
          { "gasConsumption": 150000, "bridge": "AXELAR", "type": "data" },
          { "gasConsumption": 80000, "bridge": "LAYER_ZERO", "type": "hash" }
        ]
      }
    ],
    "expectedFinalitySeconds": "180",
    "signature": "0x..."
  }
]
```

**Дополнительные поля ответа:**

| Поле                | Описание                                        |
| ------------------- | ----------------------------------------------- |
| `sourceFee`         | Комиссия на исходной сети (token, amount, usd)  |
| `deliveryFee`       | Комиссия за доставку (token, amount, usd)       |
| `feeShare`          | Доля комиссии для партнёра (только с `api-key`) |
| `feeShareRecipient` | Адрес получателя партнёрской комиссии           |
| `feeShareToken`     | Токен партнёрской комиссии                      |
| `consumptions`      | Детализация потребления газа по бриджам         |
| `signature`         | Подпись маршрута (для CDP API)                  |

> **Структура ответа:** Массив объектов, где каждый объект — это отдельный маршрут. Выбираете один маршрут и передаёте его целиком в `/tx/create`. Поле `route` внутри объекта — это массив шагов данного маршрута.

> Пустой массив `[]` — маршрут не найден. См. [Если маршрут не найден](#если-маршрут-не-найден).

***

### POST /estimate

> **DEPRECATED:** Этот эндпоинт больше не поддерживается. Подпись (`signature`) теперь включена непосредственно в ответ `/routing/scan`. Используйте маршрут из `/routing/scan` напрямую в `/tx/create`.

Получение оценки операции с подписью для выполнения.

**Request Body:** **Весь объект** маршрута из `/routing/scan` (передаётся целиком, не отдельные поля)

**Response:**

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

***

### POST /tx/create

Формирование данных транзакции для отправки в блокчейн.

**Request Body:**

```json
{
  "from": "0xYourAddress",
  "recipient": "0xRecipientAddress",
  "routing": { /* объект из /routing/scan, включает signature */ },
  "permit": {
    "v": 28,
    "r": "0x...",
    "s": "0x..."
  },
  "buildCalldata": true
}
```

**Параметры:**

| Поле            | Тип     | Обязательно | Описание                                                         |
| --------------- | ------- | ----------- | ---------------------------------------------------------------- |
| `from`          | string  | Да          | Адрес отправителя                                                |
| `recipient`     | string  | Да          | Адрес получателя                                                 |
| `routing`       | object  | Да          | **Весь объект** маршрута из `/routing/scan` (включает signature) |
| `estimate`      | object  | Нет         | **DEPRECATED** — не требуется, signature уже включена в routing  |
| `permit`        | object  | Нет         | EIP-2612 permit подпись                                          |
| `buildCalldata` | boolean | Нет         | Собрать calldata                                                 |

> **Примечание:** Поле `estimate` больше не требуется — подпись (`signature`) уже включена в объект `routing` из `/routing/scan`.

**Response (с `buildCalldata: true`):** — рекомендуется

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

Готово для `sendTransaction({ to, data, value })`.

**Response (без `buildCalldata`):**

```json
{
  "to": "0xContractAddress",
  "value": "1500000000000000",
  "args": [
    ["LM", "SW"],
    ["0x...", "0x..."],
    {
      "invoice": {
        "executionPrice": "1500000000000000",
        "deadline": "1699999999",
        "v": 27,
        "r": "0x...",
        "s": "0x..."
      },
      "feeShare": "0",
      "feeShareRecipient": "0x0000000000000000000000000000000000000000",
      "feeToken": "0x0000000000000000000000000000000000000000"
    },
    "0x..."
  ],
  "abi": "function start(string[] calldata operations, bytes[] calldata params, tuple(...) receipt, bytes bridgeOptions)"
}
```

Требует вызова через `ethers.Contract`.

***

## Token Approval

Перед выполнением свопа необходимо разрешить контракту CrossCurve использовать ваши токены.

> **Откуда брать spenderAddress?** Используйте `txData.to` из ответа `/tx/create` — это адрес контракта, который будет тратить ваши токены.

### Стандартный 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 из /tx/create
  amount: string,
  signer: ethers.Signer
) {
  const token = new ethers.Contract(tokenAddress, ERC20_ABI, signer);
  const ownerAddress = await signer.getAddress();

  // Проверить текущий allowance
  const currentAllowance = await token.allowance(ownerAddress, spenderAddress);

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

  // Approve максимальную сумму (или конкретную)
  const tx = await token.approve(spenderAddress, ethers.constants.MaxUint256);
  await tx.wait();

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

### Permit (EIP-2612) - Approve через подпись

Если токен поддерживает EIP-2612 (поле `permit: true` в tokenlist), можно использовать подпись вместо транзакции approve.

```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 };
}

// Использование permit в /tx/create
// deadline можно установить вручную (например, 30 минут от текущего времени)
const deadline = Math.floor(Date.now() / 1000) + 1800;
const permit = await signPermit(tokenIn, txData.to, amountIn, 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,  // routing уже включает signature
    permit: permit   // <-- Передаём подпись permit
  })
});
```

***

## Отслеживание транзакций

### GET /transaction/{requestId}

Получение статуса кроссчейн-транзакции.

**Параметры:**

| Параметр    | Тип    | Описание               |
| ----------- | ------ | ---------------------- |
| `requestId` | string | Уникальный 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 }
}
```

**Возможные статусы:**

| Статус        | Описание                                        |
| ------------- | ----------------------------------------------- |
| `in progress` | Операция выполняется                            |
| `completed`   | Успешно завершена                               |
| `failed`      | Ошибка выполнения                               |
| `reverted`    | Откат (требуется inconsistency)                 |
| `retry`       | Готов для повторной отправки через другой бридж |
| `canceled`    | Отменена                                        |

**Алгоритм обработки статуса:**

```
Проверить status:
├── "completed" → Готово ✅
├── "in progress" → Продолжить polling
├── "retry" → POST /tx/create/retry (повтор через другой бридж)
└── "failed" / "reverted" → Проверить флаги:
    ├── destination.emergency === true → POST /tx/create/emergency
    └── inconsistency === true → POST /inconsistency
```

**Расширенный ответ с bridgeState:**

```json
{
  "status": "in progress",
  "inconsistency": false,
  "source": {
    "chainId": 1,
    "transactionHash": "0x...",
    "from": "0x...",
    "events": [...],
    "status": "completed"
  },
  "oracle": {
    "relayChainId": 1,
    "requestId": "0x...",
    "status": "in progress",
    "height": null,
    "epoch": null,
    "time": null
  },
  "destination": {
    "chainId": 42161,
    "transactionHash": null,
    "events": [],
    "emergency": false,
    "status": "in progress",
    "bridgeState": {
      "AXELAR": { "txHash": null },
      "LAYER_ZERO": { "txHash": null }
    }
  }
}
```

***

### GET /search

Поиск транзакций по хешу или requestId.

**Параметры:**

| Параметр | Тип    | Обязательно | Описание                     |
| -------- | ------ | ----------- | ---------------------------- |
| `search` | string | Да          | Хеш транзакции или requestId |
| `limit`  | number | Нет         | Лимит результатов            |
| `offset` | number | Нет         | Смещение                     |

**Пример:**

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

***

### GET /history

История транзакций пользователя.

**Параметры:**

| Параметр  | Тип    | Обязательно | Описание       |
| --------- | ------ | ----------- | -------------- |
| `address` | string | Да          | Адрес кошелька |

**Пример:**

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

***

## Обработка ошибок

### Сценарии

| Флаг / Статус                 | Действие                                                  |
| ----------------------------- | --------------------------------------------------------- |
| `status: "completed"`         | Операция завершена ✅                                      |
| `destination.status: "retry"` | Вызвать `/tx/create/retry` для повтора через другой бридж |
| `destination.emergency: true` | Вызвать `/tx/create/emergency` для восстановления         |
| `inconsistency: true`         | Вызвать `/inconsistency` для возврата                     |

### GET /inconsistency/{requestId}

Получение параметров для возврата средств при inconsistency.

**Пример:**

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

***

### POST /inconsistency

Создание транзакции возврата.

> **Флоу:**
>
> 1. Вызовите `GET /inconsistency/{requestId}` — получите параметры (tokenIn, tokenOut, chainIdIn, chainIdOut, amountIn)
> 2. Подпишите данные о возврате кошельком пользователя (EIP-712 или personal\_sign)
> 3. Передайте подпись и оригинальный маршрут в этот эндпоинт

**Request Body:**

```json
{
  "requestId": "0x...",
  "signature": "0x...",
  "routing": { /* оригинальный маршрут из /routing/scan */ },
  "permit": {
    "v": 28,
    "r": "0x...",
    "s": "0x...",
    "deadline": 1703001600
  }
}
```

**Параметры:**

| Поле        | Тип    | Обязательно | Описание                                  |
| ----------- | ------ | ----------- | ----------------------------------------- |
| `requestId` | string | Да          | ID операции с inconsistency               |
| `signature` | string | Да          | Подпись пользователя (65 байт hex)        |
| `routing`   | object | Да          | Оригинальный маршрут из `/routing/scan`   |
| `permit`    | object | Нет         | EIP-2612 permit (если токен поддерживает) |

***

### POST /tx/create/emergency

Аварийное восстановление заблокированных средств.

> **Когда использовать:** При `destination.emergency: true` в статусе транзакции — средства заблокированы на destination chain и требуют ручного восстановления.

**Request Body:**

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

**Параметры:**

| Поле        | Тип    | Обязательно | Описание                           |
| ----------- | ------ | ----------- | ---------------------------------- |
| `requestId` | string | Да          | ID заблокированной операции        |
| `signature` | string | Да          | Подпись пользователя (65 байт hex) |

**Response:** Возвращает данные транзакции для восстановления средств.

***

### POST /tx/create/retry

Повторная отправка через альтернативный бридж.

> **Когда использовать:** При `destination.status: "retry"` — доставка через один бридж не удалась, но есть альтернативные бриджи.

**Request Body:**

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

**Параметры:**

| Поле        | Тип    | Обязательно | Описание                           |
| ----------- | ------ | ----------- | ---------------------------------- |
| `requestId` | string | Да          | ID операции для повтора            |
| `signature` | string | Да          | Подпись пользователя (65 байт hex) |

**Response:**

```json
{
  "to": "0x...",
  "abi": "function retry(tuple(...) params, uint256 nonce, address protocol, address bridge, bytes currentOptions, bool isHash)",
  "args": [
    {
      "requestId": "0x...",
      "data": "0x...",
      "chainIdTo": 42161,
      "to": "0x..."
    },
    "12345",
    "0x...",
    "0x...",
    "0x...",
    false
  ],
  "value": "1500000000000000"
}
```

**Пример использования:**

```javascript
async function executeRetry(requestId, signer) {
  // Создание подписи (такая же как для emergency)
  const messageHash = ethers.utils.solidityKeccak256(['string'], [requestId]);
  const messageHashBinary = ethers.utils.arrayify(messageHash);
  const signature = await signer.signMessage(messageHashBinary);

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

  const tx = await response.json();

  // Отправка транзакции
  const iface = new ethers.utils.Interface([tx.abi]);
  const data = iface.encodeFunctionData('retry', tx.args);

  const txResponse = await signer.sendTransaction({
    to: tx.to,
    data,
    value: tx.value
  });

  console.log('Retry transaction sent:', txResponse.hash);
  return txResponse.wait();
}
```

***

### Создание подписи для Emergency/Inconsistency/Retry

Для эндпоинтов `/tx/create/emergency` и `/inconsistency` требуется подпись, подтверждающая владение кошельком.

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

async function createRecoverySignature(requestId, signer) {
  // 1. Хешируем requestId как строку
  const messageHash = ethers.utils.solidityKeccak256(['string'], [requestId]);

  // 2. Преобразуем в байты
  const messageHashBinary = ethers.utils.arrayify(messageHash);

  // 3. Подписываем (добавляет Ethereum prefix)
  const signature = await signer.signMessage(messageHashBinary);

  return signature; // 132 символа (0x + 65 байт)
}

// Использование
const signature = await createRecoverySignature(requestId, signer);

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

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

***

### Типичные ошибки API

| Ошибка                                     | Причина                        | Решение                             |
| ------------------------------------------ | ------------------------------ | ----------------------------------- |
| `Can't find receiveRequest`                | requestId не найден            | Проверить requestId                 |
| `Can't find paymentTx`                     | Транзакция не найдена          | Проверить requestId                 |
| `Routing signature not valid`              | Истёк или изменён маршрут      | Повторить `/routing/scan`           |
| `Already completed`                        | Транзакция завершена           | Действие не требуется               |
| `Not an emergency`                         | Условия emergency не выполнены | Подождать или проверить статус      |
| `Uncorrect user address`                   | Подпись не от владельца        | Подписать кошельком из `from`       |
| `invalid signature string`                 | Неверный формат подписи        | Проверить 65-байтную hex подпись    |
| `Retry is not supported for single bridge` | Только один бридж доступен     | Использовать emergency вместо retry |
| `No remaining bridges available`           | Все бриджи уже использованы    | Использовать emergency              |

***

## API Reference

### Список эндпоинтов

#### Справочные данные

| Метод | Эндпоинт                    | Описание           |
| ----- | --------------------------- | ------------------ |
| GET   | `/networks`                 | Список сетей       |
| GET   | `/tokenlist`                | Список токенов     |
| GET   | `/validators`               | Статус валидаторов |
| GET   | `/ready`                    | Готовность сервиса |
| GET   | `/prices/{token}`           | Цена токена        |
| GET   | `/prices/{token}/{chainId}` | Цена в сети        |
| POST  | `/prices`                   | Пакетные цены      |

#### Маршрутизация и обмен

| Метод | Эндпоинт        | Описание                             |
| ----- | --------------- | ------------------------------------ |
| POST  | `/routing/scan` | Поиск маршрутов (включает signature) |
| POST  | `/estimate`     | **DEPRECATED** — Оценка операции     |
| POST  | `/tx/create`    | Создание транзакции                  |

#### Мониторинг

| Метод | Эндпоинт                   | Описание             |
| ----- | -------------------------- | -------------------- |
| GET   | `/transaction/{requestId}` | Статус транзакции    |
| GET   | `/search`                  | Поиск транзакций     |
| GET   | `/history`                 | История пользователя |
| GET   | `/transactions`            | Список транзакций    |

#### Обработка ошибок

| Метод | Эндпоинт                     | Описание                              |
| ----- | ---------------------------- | ------------------------------------- |
| GET   | `/inconsistency/{requestId}` | Параметры возврата                    |
| POST  | `/inconsistency`             | Транзакция возврата                   |
| POST  | `/tx/create/emergency`       | Аварийное восстановление              |
| POST  | `/tx/create/retry`           | Повторная отправка через другой бридж |

#### Supply и статистика

> Значения динамические, примеры актуальны на момент тестирования.

| Метод | Эндпоинт              | Описание                 | Формат ответа      |
| ----- | --------------------- | ------------------------ | ------------------ |
| GET   | `/supply/eywa/ts`     | Total Supply EYWA        | число              |
| GET   | `/supply/eywa/ms`     | Max Supply EYWA          | число              |
| GET   | `/supply/eywa/cmc`    | Данные для CoinMarketCap | число              |
| GET   | `/supply/eywa/cg`     | Данные для CoinGecko     | `{"result":"..."}` |
| GET   | `/points/multipliers` | Множители поинтов        | объект             |

#### NFT операции

| Метод | Эндпоинт         | Описание                | Обязательные параметры                  |
| ----- | ---------------- | ----------------------- | --------------------------------------- |
| GET   | `/nft/rarity`    | Редкость NFT            | `tokens` (query, string) ⚠️             |
| POST  | `/nft/estimate`  | Оценка NFT операции     | `wallet`, `tokenIds` (array of numbers) |
| POST  | `/nft/tx`        | Создание NFT транзакции | `wallet`, `tokenIds`, `estimate`        |
| POST  | `/nft/emergency` | Аварийная операция NFT  | `requestId`, `signature`                |

> ⚠️ **Известный баг:** `GET /nft/rarity` не парсит query параметр `tokens`. Эндпоинт временно недоступен.

**Флоу NFT операций:**

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

***

## Примеры кода

> Основной флоу см. в разделе [Обзор интеграции](#обзор-интеграции).

### TypeScript: Типы и 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' | 'reverted' | 'retry' | 'canceled';
  inconsistency: boolean;
  source: { status: string; chainId: number; transactionHash: string };
  destination?: {
    status: string;
    emergency: boolean;
    bridgeState?: Record<string, { txHash: string | null }>;
  };
}

// Polling с таймаутом
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; // требуется /inconsistency
    if (data.destination?.emergency) return data; // требуется /tx/create/emergency

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

### JavaScript: Получение сетей и токенов

```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: Поиск маршрута

```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]  # первый маршрут из списка
    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

# Пример использования
route = find_route(
    chain_in=42161,      # Arbitrum
    chain_out=1,         # Ethereum
    token_in='0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978',   # CRV on Arbitrum
    token_out='0xD533a949740bb3306d119CC777fa900bA034cd52',  # CRV on Ethereum
    amount='1000000000000000000000'  # 1000 CRV (18 decimals)
)
```

***

## Коды операций маршрута

| Код  | Описание                                |
| ---- | --------------------------------------- |
| `A`  | Добавление ликвидности                  |
| `S`  | Своп                                    |
| `R`  | Удаление ликвидности                    |
| `LM` | Lock/Mint (блокировка и минтинг)        |
| `BU` | Burn/Unlock (сжигание и разблокировка)  |
| `BM` | Burn/Mint                               |
| `Uw` | Unwrap (развёртывание нативного токена) |
| `W`  | Wrap (обёртывание нативного токена)     |
| `M`  | Emergency Mint                          |
| `U`  | Emergency Unlock                        |

***

## Rate Limits и Best Practices

### API ключи

Доступны два типа API ключей:

| Тип ключа | Запросов/мин | Комиссия                   | `feeShareBps` |
| --------- | ------------ | -------------------------- | ------------- |
| Free      | 20           | 0%                         | Недоступен    |
| Standard  | 60           | Из параметра `feeShareBps` | 1–10000 bps   |

Ключ передаётся в заголовке `api-key`:

```javascript
headers: {
  'Content-Type': 'application/json',
  'api-key': 'your-api-key-here'
}
```

> API доступен без ключа, но ключ необходим для повышенных лимитов и заработка комиссий.

**Тестовые ключи для разработки:**

* Free: `test-sdk-test-sdk-test-sdk-free`
* Standard: `test-sdk-test-sdk-test-sdk-standard`

Для получения production API ключа обращайтесь к BD: [@Eywa\_BDLead](https://t.me/Eywa_BDLead) / <a.gluhovskij@eywa.fi>

### Rate Limits

| Эндпоинт                  | Рекомендация                 |
| ------------------------- | ---------------------------- |
| `/networks`, `/tokenlist` | Кешировать локально          |
| `/routing/scan`           | По запросу пользователя      |
| `/tx/create`              | После выбора маршрута        |
| `/transaction/{id}`       | Polling с интервалом         |
| `/prices`                 | Кешировать при необходимости |

### Best Practices

1. **Кешируйте `/networks` и `/tokenlist`** — данные меняются редко

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

   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 адресов** — рекомендуется EIP-55 формат для совместимости

   ```javascript
   const address = ethers.utils.getAddress(userInput); // преобразует в checksum
   ```
3. **Проверяйте `can_swap`** перед вызовом `/routing/scan`

   ```javascript
   if (!token.tags.includes('can_swap')) {
     throw new Error('Token not available for cross-chain swap');
   }
   ```
4. **Проверяйте актуальность маршрута** — маршруты имеют ограниченный срок действия

   ```javascript
   // Если прошло много времени с момента получения маршрута,
   // рекомендуется запросить новый через /routing/scan
   ```
5. **Polling** — интервал 5-10 сек, обрабатывайте `inconsistency` и `emergency`
6. **Сохраняйте requestId** — храните после отправки транзакции для отслеживания и recovery
7. **Slippage** — используйте адекватные значения (0.5-1% для стейблов, 1-3% для волатильных)
8. **Используйте правильный кошелёк** для подписей recovery — должен совпадать с `from` оригинальной транзакции
9. **Учитывайте decimals токенов** — не все токены имеют 18 decimals

   ```javascript
   // ❌ Неправильно: hardcoded 18 decimals
   const amount = parseFloat(value) / 1e18;

   // ✅ Правильно: используйте decimals из tokenlist
   function formatAmount(rawAmount, decimals) {
     return parseFloat(rawAmount) / Math.pow(10, decimals);
   }

   // Примеры decimals:
   // USDC, USDT: 6
   // WBTC: 8
   // CRV, ETH, большинство токенов: 18
   ```

***

## Ограничения

| Параметр           | Описание                                           |
| ------------------ | -------------------------------------------------- |
| Минимальная сумма  | Зависит от пары токенов и ликвидности              |
| Максимальная сумма | Ограничена ликвидностью пулов                      |
| Slippage           | Указывается в процентах при вызове `/routing/scan` |
| Время выполнения   | Зависит от сетей и загрузки oracle network         |

***

## Адреса контрактов

Адреса контрактов доступны через `/networks`:

| Контракт  | Описание                           | Поле в /networks |
| --------- | ---------------------------------- | ---------------- |
| Portal    | Точка входа для кроссчейн операций | `portal`         |
| Synthesis | Контракт синтетических токенов     | `synthesis`      |
| Router    | Маршрутизатор свопов               | `router`         |

```javascript
// Получение адресов контрактов
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;
```

***

## Структура комиссий

Информация о комиссиях возвращается в ответе `/routing/scan`:

| Тип комиссии  | Описание           | Поле в ответе              |
| ------------- | ------------------ | -------------------------- |
| `dex`         | Комиссия DEX/пула  | В каждом шаге route        |
| `bridge`      | Комиссия моста     | В шагах bridgeIn/bridgeOut |
| `aggregation` | Комиссия агрегации | Общая комиссия CrossCurve  |
| `total`       | Итоговая комиссия  | `totalFee` в маршруте      |

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

// Детализация по шагам
route.route.forEach(step => {
  step.fees?.forEach(fee => {
    console.log(`${fee.type}: ${fee.percent}%`);
  });
});
```

***

## FAQ

### Общие вопросы

**Q: Нужен ли API ключ?** A: API доступен без ключа, но с ограниченным лимитом (20 запросов/мин). Доступны два типа ключей: Free (20 запросов/мин, без комиссии) и Standard (60 запросов/мин, комиссия через `feeShareBps`). См. [API ключи](#api-ключи).

**Q: Есть ли rate limits?** A: Free ключи — 20 запросов/мин, Standard ключи — 60 запросов/мин. Кешируйте справочные данные и избегайте частых запросов.

**Q: Какие сети поддерживаются?** A: Актуальный список доступен через `GET /networks`. Поддерживаются EVM-совместимые сети.

**Q: Почему `/routing/scan` возвращает пустой массив?** A: Маршрут не найден. См. [Если маршрут не найден](#если-маршрут-не-найден).

### Технические вопросы

**Q: Как получить requestId после отправки транзакции?** A: RequestId эмитируется в событиях контракта Portal. Используйте `GET /search?search={txHash}` для поиска.

**Q: Что делать при `inconsistency: true`?** A: Требуется возврат средств:

1. Вызовите `GET /inconsistency/{requestId}` — получите параметры для подписи
2. Подпишите данные кошельком пользователя
3. Вызовите `POST /inconsistency` с `requestId`, `signature` и оригинальным `routing`

**Q: Что делать при `emergency: true`?** A: Требуется восстановление средств. Вызовите `POST /tx/create/emergency` с `requestId` и `signature` (подпись пользователя).

**Q: Как работает permit (EIP-2612)?** A: Если токен поддерживает permit (`permit: true` в tokenlist), можно подписать разрешение оффчейн вместо отдельной транзакции approve. Передайте подпись `{v, r, s}` в `/tx/create`.

***

## Глоссарий

| Термин            | Описание                                                   |
| ----------------- | ---------------------------------------------------------- |
| **Portal**        | Контракт-точка входа для кроссчейн операций                |
| **Synthesis**     | Контракт для минтинга/сжигания синтетических токенов       |
| **Router**        | Контракт маршрутизации свопов                              |
| **RequestId**     | Уникальный идентификатор кроссчейн операции                |
| **Inconsistency** | Состояние, требующее возврата средств                      |
| **Emergency**     | Состояние, требующее восстановления средств                |
| **Retry**         | Повторная отправка через альтернативный бридж              |
| **can\_swap**     | Тег токена, указывающий на возможность кроссчейн обмена    |
| **Synth**         | Синтетический токен, представляющий актив с другой сети    |
| **bridgeIn**      | Операция блокировки токена на source chain                 |
| **bridgeOut**     | Операция разблокировки токена на destination chain         |
| **Oracle**        | Сеть валидаторов CrossCurve                                |
| **Slippage**      | Допустимое отклонение цены от ожидаемой                    |
| **sourceFee**     | Комиссия на исходной сети                                  |
| **deliveryFee**   | Комиссия за доставку на целевую сеть                       |
| **feeShare**      | Доля комиссии для партнёра (при использовании api-key)     |
| **bridgeState**   | Состояние доставки по каждому бриджу (AXELAR, LAYER\_ZERO) |

***

## Changelog

> История изменений API. Следите за обновлениями в Swagger UI.

| Дата    | Изменение                                                                                                                                                                                                  |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 2025-01 | **BREAKING:** /estimate endpoint deprecated — signature теперь включена в /routing/scan. Обновлён флоу: routing/scan → tx/create → send TX                                                                 |
| 2025-01 | Добавлено: CDP API URL, api-key для партнёров, новые параметры routing (feeFromAmount, feeToken, providers), sourceFee/deliveryFee/feeShare в ответе, endpoint /tx/create/retry, статус retry, bridgeState |
| 2025-12 | Документация создана: Quick Start, API Reference, примеры кода                                                                                                                                             |
| —       | Актуальные изменения API см. в Swagger UI                                                                                                                                                                  |

***

## Поддержка

* **Swagger UI**: <https://api.crosscurve.fi/api-docs/>
* **Документация**: <https://docs.crosscurve.fi>
* **Twitter/X**: [@crosscurvefi](https://x.com/crosscurvefi)

### Партнёрство и API-ключи

Для получения партнёрского API-ключа (fee sharing) обращайтесь к BD:

* **Email**: <a.gluhovskij@eywa.fi>
* **Telegram**: [@Eywa\_BDLead](https://t.me/Eywa_BDLead)
