> ## Documentation Index
> Fetch the complete documentation index at: https://wallet-docs.iexexchanger.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Конвертации — обмен активов в стейбл

> Своп накопленных активов (ETH, LINK…) в стейбл через те же API-ключи: котировка, создание, статус

Конвертации позволяют вашей CMS обменять накопленные на кошельках обменника активы (например, ETH,
LINK, TRX) в стейбл (USDT) **через те же ключи API, что и кошелёк** — отдельная интеграция с
DEX или биржей не нужна. Платформа сама выбирает подключённого провайдера и скрывает его детали за
единым контрактом из трёх эндпоинтов.

<Note>
  Какой именно движок исполнит своп (0x, 1inch, Uniswap, SunSwap, Jupiter, ChangeNOW, SimpleSwap…) и
  его ключи настраивает оператор в Админке. Для вашей интеграции это прозрачно: вы всегда вызываете
  один и тот же набор эндпоинтов. Если провайдер не выбран явно, платформа сама подбирает лучший по
  факту котировки среди исполнимых.
</Note>

Аутентификация — та же HMAC-подпись, что и у депозитов и выплат (см.
[Аутентификация](/authentication)). Ответы приходят в конверте `{ "ok": true, "data": … }` или
`{ "ok": false, "error": {…} }`. Применяется тот же [per-site rate-limit](/rate-limits). Все суммы и
курсы — строки.

## Same-chain и cross-chain

В зависимости от выбранного провайдера своп исполняется по одной из двух моделей:

* **Same-chain (DEX).** Источник и цель в одной сети, обмен идёт on-chain через роутер
  (`providerKind: "dex"`). Результат возвращается на тот же кошелёк сети. Курс рыночный.
* **Cross-chain (instant-swap).** Источник и цель могут быть в разных сетях; платформа отправляет
  актив на адрес провайдера, а результат принимает на свой кошелёк целевой сети
  (`providerKind: "instant_swap"`). Курс — плавающий или фиксированный, в зависимости от провайдера.

Для вашей интеграции запрос идентичен в обоих случаях — вы указываете `sourceAssetCode`,
`targetAssetCode`, `network` и `amountIn`. Поле `providerKind` в ответе показывает, по какой модели
прошёл обмен.

## Эндпоинты

| Метод  | Путь                            | Назначение                                     |
| ------ | ------------------------------- | ---------------------------------------------- |
| `POST` | `/v1/public/conversions/quote`  | живая котировка (preview), без создания заявки |
| `POST` | `/v1/public/conversions`        | создать конвертацию                            |
| `GET`  | `/v1/public/conversions/{uuid}` | статус конвертации                             |

Рекомендуемый поток — **quote → create → poll**: сначала получить живую котировку, показать её
оператору или принять решение в коде, затем создать заявку и опрашивать её статус до терминального.

Исполнение **асинхронное**: `POST /v1/public/conversions` создаёт заявку в статусе `queued` (или
`pending_approval`), а сам своп выполняется в фоне. Опрашивайте `GET /v1/public/conversions/{uuid}`,
пока статус не станет терминальным (`settled`, `failed`, `refunded`, `expired`, `cancelled`).

## 1. Котировка (сколько отдам / получу)

`POST /v1/public/conversions/quote` — узнать актуальный курс, ожидаемый выход и минимальную сумму к
получению без создания заявки. Заявка при этом не создаётся, балансы не блокируются.

### Запрос

<CodeGroup>
  ```bash curl theme={null}
  TS=$(date +%s)
  BODY='{"sourceAssetCode":"ETH","targetAssetCode":"USDT_ERC20","network":"ETHEREUM","amountIn":"1.5","maxSlippageBps":100}'
  SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed 's/^.* //')

  curl -X POST https://wallet.your-exchange.com/v1/public/conversions/quote \
    -H "X-Api-Id: pk_live_a1b2c3d4" \
    -H "X-Timestamp: $TS" \
    -H "X-Signature: $SIG" \
    -H "Content-Type: application/json" \
    -d "$BODY"
  ```

  ```javascript Node.js theme={null}
  import crypto from 'node:crypto';

  const BASE = 'https://wallet.your-exchange.com';
  const API_ID = 'pk_live_a1b2c3d4';
  const API_SECRET = process.env.API_SECRET;

  const body = JSON.stringify({
    sourceAssetCode: 'ETH',
    targetAssetCode: 'USDT_ERC20',
    network: 'ETHEREUM',
    amountIn: '1.5',
    maxSlippageBps: 100,
  });

  const ts = Math.floor(Date.now() / 1000).toString();
  const sig = crypto
    .createHmac('sha256', API_SECRET)
    .update(`${ts}.${body}`)
    .digest('hex');

  const res = await fetch(`${BASE}/v1/public/conversions/quote`, {
    method: 'POST',
    headers: {
      'X-Api-Id': API_ID,
      'X-Timestamp': ts,
      'X-Signature': sig,
      'Content-Type': 'application/json',
    },
    body,
  });

  const { ok, data, error } = await res.json();
  ```
</CodeGroup>

### Ответ

```json theme={null}
{
  "ok": true,
  "data": {
    "sourceAssetCode": "ETH",
    "targetAssetCode": "USDT_ERC20",
    "network": "ETHEREUM",
    "amountIn": "1.5",
    "amountOut": "4820.13",
    "minAmountOut": "4771.93",
    "rate": "3213.42",
    "slippageBps": 100,
    "ttlSeconds": 30,
    "executable": true
  }
}
```

### Параметры запроса (тело)

<ParamField body="sourceAssetCode" type="string" required>
  Код актива-источника (например, `ETH`, `LINK`, `TRX`). До 64 символов.
</ParamField>

<ParamField body="targetAssetCode" type="string" required>
  Код целевого актива — стейбл (например, `USDT_ERC20`, `USDT_TRC20`). До 64 символов.
</ParamField>

<ParamField body="network" type="string" required>
  Сеть исполнения (например, `ETHEREUM`, `TRON`). До 32 символов.
</ParamField>

<ParamField body="amountIn" type="string" required>
  Сумма к конвертации — строка-число, формат `^\d+(\.\d+)?$` (например, `"1.5"`). Без знака минус и
  без экспоненты.
</ParamField>

<ParamField body="maxSlippageBps" type="integer">
  Допустимое проскальзывание в bps (1% = 100 bps). Диапазон `1…5000`. Если не передан — берётся
  значение из настроек оператора.
</ParamField>

### Поля ответа котировки

<ResponseField name="sourceAssetCode" type="string">Код актива-источника (эхо запроса).</ResponseField>
<ResponseField name="targetAssetCode" type="string">Код целевого актива (эхо запроса).</ResponseField>
<ResponseField name="network" type="string">Сеть исполнения (эхо запроса).</ResponseField>
<ResponseField name="amountIn" type="string">Сумма к конвертации (эхо запроса).</ResponseField>
<ResponseField name="amountOut" type="string">Ожидаемый выход в целевом активе.</ResponseField>
<ResponseField name="minAmountOut" type="string">Минимум к получению с учётом slippage — ниже него своп не исполнится.</ResponseField>
<ResponseField name="rate" type="string">Курс = `amountOut / amountIn`.</ResponseField>
<ResponseField name="slippageBps" type="integer">Применённое проскальзывание в bps.</ResponseField>
<ResponseField name="ttlSeconds" type="integer">Сколько секунд котировка считается актуальной.</ResponseField>
<ResponseField name="executable" type="boolean">`true` — котировку можно исполнить; `false` — это только оценка (см. предупреждение ниже).</ResponseField>

<Warning>
  `executable: false` означает, что провайдер вернул только **оценочный** курс — нет рабочего
  драйвера или ключа на стороне оператора, либо нет ликвидности, либо сумма вне диапазона провайдера.
  Создать конвертацию по неисполнимой котировке нельзя: `POST /v1/public/conversions` сразу вернёт
  ошибку `VALIDATION_FAILED`. Сначала дождитесь, пока оператор настроит провайдера.
</Warning>

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

`POST /v1/public/conversions` — тело **идентично** запросу котировки. Платформа заново берёт живую
котировку, проверяет исполнимость, баланс hot-кошелька, минимальную сумму и гейты безопасности,
затем ставит заявку в очередь.

### Идемпотентность

Создание идемпотентно по заголовку `X-Idempotency-Key` (UUID): повторный запрос с тем же ключом
вернёт ту же заявку, а не создаст вторую. Ключ привязан к вашему сайту, поэтому одинаковый UUID у
разных обменников не пересекается. Подробнее — [Идемпотентность](/idempotency).

### Запрос

<CodeGroup>
  ```bash curl theme={null}
  TS=$(date +%s)
  BODY='{"sourceAssetCode":"ETH","targetAssetCode":"USDT_ERC20","network":"ETHEREUM","amountIn":"1.5","maxSlippageBps":100}'
  SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed 's/^.* //')

  curl -X POST https://wallet.your-exchange.com/v1/public/conversions \
    -H "X-Api-Id: pk_live_a1b2c3d4" \
    -H "X-Timestamp: $TS" \
    -H "X-Signature: $SIG" \
    -H "X-Idempotency-Key: 7c2f9e1a-4d6b-4a2e-9f10-3b8c1d2e5a40" \
    -H "Content-Type: application/json" \
    -d "$BODY"
  ```

  ```javascript Node.js theme={null}
  import crypto from 'node:crypto';
  import { randomUUID } from 'node:crypto';

  const BASE = 'https://wallet.your-exchange.com';
  const API_ID = 'pk_live_a1b2c3d4';
  const API_SECRET = process.env.API_SECRET;

  const body = JSON.stringify({
    sourceAssetCode: 'ETH',
    targetAssetCode: 'USDT_ERC20',
    network: 'ETHEREUM',
    amountIn: '1.5',
    maxSlippageBps: 100,
  });

  const ts = Math.floor(Date.now() / 1000).toString();
  const sig = crypto
    .createHmac('sha256', API_SECRET)
    .update(`${ts}.${body}`)
    .digest('hex');

  const res = await fetch(`${BASE}/v1/public/conversions`, {
    method: 'POST',
    headers: {
      'X-Api-Id': API_ID,
      'X-Timestamp': ts,
      'X-Signature': sig,
      'X-Idempotency-Key': randomUUID(),
      'Content-Type': 'application/json',
    },
    body,
  });

  const { ok, data, error } = await res.json();
  ```
</CodeGroup>

### Ответ

Возвращается объект конвертации в статусе `queued` (или `pending_approval`, если у оператора включён
порог второго подтверждения для крупных сумм). Поля `executed*` и `settledAt` пока `null`.

```json theme={null}
{
  "ok": true,
  "data": {
    "uuid": "8e0a1b2c-3d4e-4f5a-9b6c-7d8e9f0a1b2c",
    "status": "queued",
    "sourceAssetCode": "ETH",
    "targetAssetCode": "USDT_ERC20",
    "network": "ETHEREUM",
    "sourceAmount": "1.5",
    "quotedAmountOut": "4820.13",
    "executedAmountOut": null,
    "quotedRate": "3213.42",
    "executedRate": null,
    "minAmountOut": "4771.93",
    "providerKind": "dex",
    "failReason": null,
    "createdAt": "2026-06-25T20:00:00.000Z",
    "settledAt": null
  }
}
```

## 3. Статус конвертации

`GET /v1/public/conversions/{uuid}` — текущее состояние заявки. Видны только конвертации вашего
сайта; чужой `uuid` возвращает `NOT_FOUND` (404). Тело запроса пустое, поэтому в подписи
`message = "<timestamp>."` (таймстамп, затем точка).

### Запрос

<CodeGroup>
  ```bash curl theme={null}
  TS=$(date +%s)
  UUID="8e0a1b2c-3d4e-4f5a-9b6c-7d8e9f0a1b2c"
  SIG=$(printf '%s.' "$TS" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed 's/^.* //')

  curl https://wallet.your-exchange.com/v1/public/conversions/$UUID \
    -H "X-Api-Id: pk_live_a1b2c3d4" \
    -H "X-Timestamp: $TS" \
    -H "X-Signature: $SIG"
  ```

  ```javascript Node.js theme={null}
  import crypto from 'node:crypto';

  const BASE = 'https://wallet.your-exchange.com';
  const API_ID = 'pk_live_a1b2c3d4';
  const API_SECRET = process.env.API_SECRET;
  const uuid = '8e0a1b2c-3d4e-4f5a-9b6c-7d8e9f0a1b2c';

  const ts = Math.floor(Date.now() / 1000).toString();
  const sig = crypto
    .createHmac('sha256', API_SECRET)
    .update(`${ts}.`) // пустое тело: таймстамп + точка
    .digest('hex');

  const res = await fetch(`${BASE}/v1/public/conversions/${uuid}`, {
    headers: {
      'X-Api-Id': API_ID,
      'X-Timestamp': ts,
      'X-Signature': sig,
    },
  });

  const { ok, data, error } = await res.json();
  ```
</CodeGroup>

### Ответ (исполнено)

```json theme={null}
{
  "ok": true,
  "data": {
    "uuid": "8e0a1b2c-3d4e-4f5a-9b6c-7d8e9f0a1b2c",
    "status": "settled",
    "sourceAssetCode": "ETH",
    "targetAssetCode": "USDT_ERC20",
    "network": "ETHEREUM",
    "sourceAmount": "1.5",
    "quotedAmountOut": "4820.13",
    "executedAmountOut": "4818.40",
    "quotedRate": "3213.42",
    "executedRate": "3212.27",
    "minAmountOut": "4771.93",
    "providerKind": "dex",
    "failReason": null,
    "createdAt": "2026-06-25T20:00:00.000Z",
    "settledAt": "2026-06-25T20:00:45.000Z"
  }
}
```

### Поля объекта конвертации

<ResponseField name="uuid" type="string">Публичный идентификатор конвертации.</ResponseField>
<ResponseField name="status" type="string">Текущий статус. Полный список — [Статусы конвертаций](/conversions/statuses).</ResponseField>
<ResponseField name="sourceAssetCode" type="string">Код актива-источника.</ResponseField>
<ResponseField name="targetAssetCode" type="string">Код целевого актива (стейбл).</ResponseField>
<ResponseField name="network" type="string">Сеть исходного актива.</ResponseField>
<ResponseField name="sourceAmount" type="string">Сумма, отправленная на конвертацию.</ResponseField>
<ResponseField name="quotedAmountOut" type="string">Ожидаемый выход на момент создания (что вы видели в котировке).</ResponseField>
<ResponseField name="executedAmountOut" type="string | null">Фактически полученный выход. `null` до исполнения.</ResponseField>
<ResponseField name="quotedRate" type="string">Курс на момент создания.</ResponseField>
<ResponseField name="executedRate" type="string | null">Фактический курс после исполнения. `null` до исполнения.</ResponseField>
<ResponseField name="minAmountOut" type="string">Минимум к получению (защита от проскальзывания).</ResponseField>
<ResponseField name="providerKind" type="string">Модель исполнения: `dex` (same-chain), `instant_swap` (cross-chain) или `cex`.</ResponseField>
<ResponseField name="failReason" type="string | null">Причина ошибки при `status: "failed"`, иначе `null`.</ResponseField>
<ResponseField name="createdAt" type="string">Дата создания заявки (ISO 8601, UTC).</ResponseField>
<ResponseField name="settledAt" type="string | null">Дата успешного завершения. `null`, пока не `settled`.</ResponseField>

## Проскальзывание и срок жизни котировки

* `maxSlippageBps` задаёт, насколько фактический выход может оказаться ниже ожидаемого. Из него
  считается `minAmountOut` — жёсткая граница: если рынок уйдёт сильнее, своп не исполнится и заявка
  станет `failed` (для DEX это enforce'ится самим роутером on-chain).
* `ttlSeconds` в котировке — справочное время её актуальности. При создании заявки платформа берёт
  **свежую** котировку заново, поэтому котировку не нужно «передавать» в `create` — достаточно тех же
  входных параметров.

## Частые вопросы

<AccordionGroup>
  <Accordion title="Нужно ли передавать котировку в запрос создания?">
    Нет. `POST /v1/public/conversions` принимает те же поля, что и `quote`, и берёт свежую котировку сам.
    Шаг `quote` нужен лишь для предпросмотра курса и проверки `executable`.
  </Accordion>

  <Accordion title="Почему создание вернуло VALIDATION_FAILED, хотя quote показал курс?">
    Вероятнее всего, котировка была неисполнимой (`executable: false`): нет настроенного драйвера или
    ключа провайдера, нет ликвидности, либо сумма ниже минимума или вне диапазона провайдера. Текст
    причины — в `error.message`. Создать заявку можно только по исполнимой котировке.
  </Accordion>

  <Accordion title="Почему заявка в статусе pending_approval, а не queued?">
    У оператора настроен порог второго подтверждения (four-eyes) для крупных по USD сумм, либо включено
    принудительное одобрение для выбранного провайдера. Заявка исполнится после ручного подтверждения
    оператором в Админке. Продолжайте опрашивать статус.
  </Accordion>

  <Accordion title="Почему executedAmountOut меньше quotedAmountOut?">
    Рынок мог сдвинуться в пределах допустимого проскальзывания между котировкой и исполнением.
    Гарантированный минимум — `minAmountOut`; ниже него своп не пройдёт.
  </Accordion>

  <Accordion title="Можно ли отменить конвертацию через API?">
    Нет. Отмена (`cancelled`) — операторское действие в Админке и возможна только до начала исполнения.
    Публичного эндпоинта отмены нет.
  </Accordion>

  <Accordion title="Как повторить неудачную конвертацию?">
    Создайте новую заявку (тот же `POST`) — платформа возьмёт актуальную котировку. Используйте новый
    `X-Idempotency-Key`, иначе вернётся прежняя завершившаяся заявка.
  </Accordion>
</AccordionGroup>

## Связанные страницы

* [Аутентификация](/authentication) — HMAC-подпись, заголовки, IP whitelist.
* [Идемпотентность](/idempotency) — `X-Idempotency-Key` и безопасные повторы.
* [Статусы конвертаций](/conversions/statuses) — жизненный цикл и причины ошибок.
* [Ошибки](/errors) — единый формат ответа и справочник кодов.
* [Лимиты запросов](/rate-limits) — per-site rate-limit.
