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

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


---

# 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/rukovodstvo-po-integracii.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.
