Files
cryptowallet/apps/api/swagger.json
ZOMBIIIIIII e86ff7c063 init
2026-05-28 13:51:30 +03:00

3441 lines
116 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"openapi": "3.0.0",
"info": {
"title": "CryptoWallet API",
"version": "5.0.0",
"description": "Multi-chain custodial wallet API (ETH/BSC/BTC/TRX/SOL). Сервер генерит mnemonic, шифрует AES-256-GCM (master-key из HashiCorp Vault), хранит её и сам подписывает транзакции. Auth via JWT (cookie/Bearer), issued by external auth-service (BITOK)."
},
"servers": [
{
"url": "/api",
"description": "API root"
}
],
"tags": [
{
"name": "System",
"description": "Health & service info"
},
{
"name": "Wallets",
"description": "Custodial wallet lifecycle"
},
{
"name": "Wallet Ops",
"description": "Per-chain balance / transactions / send"
},
{
"name": "BTC",
"description": "Bitcoin RPC proxy (Blockstream)"
},
{
"name": "TRON",
"description": "TRON RPC proxy (TronGrid)"
},
{
"name": "Solana",
"description": "Solana swap proxy (Jupiter)"
},
{
"name": "TRON Swap",
"description": "TRON swap proxy (SunSwap + FeeSwapRouter)"
},
{
"name": "BSC",
"description": "BSC swap proxy (PancakeSwap V2)"
},
{
"name": "Relay",
"description": "Cross-chain bridges (Relay Protocol)"
},
{
"name": "Prices",
"description": "USD-цены (CoinGecko + KeyDB cache 5 мин)"
}
],
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
},
"cookieAuth": {
"type": "apiKey",
"in": "cookie",
"name": "access_token"
}
},
"schemas": {
"Error": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": false
},
"error": {
"type": "string"
}
}
},
"HealthResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "ok"
}
}
}
}
},
"Chain": {
"type": "string",
"enum": [
"ETH",
"BTC",
"SOL",
"TRX",
"BSC"
]
},
"Wallet": {
"type": "object",
"properties": {
"chain": {
"$ref": "#/components/schemas/Chain"
},
"address": {
"type": "string"
},
"derivationPath": {
"type": "string",
"description": "BIP32 path"
}
}
},
"WalletsResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Wallet"
}
}
}
},
"MnemonicResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"mnemonic": {
"type": "string",
"description": "BIP39 mnemonic (12 words)"
}
}
}
}
},
"TxBroadcastResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"txid": {
"type": "string",
"description": "Идентификатор отправленной транзакции"
},
"chain": {
"$ref": "#/components/schemas/Chain"
}
}
}
}
},
"FormattedAmount": {
"type": "object",
"description": "Сумма с метаданными формата + USD-цена. Поля `usdPrice`/`usdValue` всегда присутствуют, но могут быть `null` если symbol не в registry или upstream price oracle (CoinGecko) недоступен.",
"required": [
"raw",
"formatted",
"decimals",
"usdPrice",
"usdValue"
],
"properties": {
"raw": {
"type": "string",
"description": "Smallest units (wei/sat/sun/lamports), string-encoded BigInt",
"example": "1500000000000000000"
},
"formatted": {
"type": "string",
"description": "Human-readable decimal",
"example": "1.5"
},
"decimals": {
"type": "integer",
"description": "Decimals of the chain/token",
"example": 18
},
"usdPrice": {
"type": "number",
"nullable": true,
"description": "Цена 1 целой единицы в USD по данным CoinGecko (cache 5 мин, KeyDB). `null` если symbol не в registry или upstream недоступен.",
"example": 3210.45
},
"usdValue": {
"type": "number",
"nullable": true,
"description": "Совокупная стоимость holding'а в USD = `Number(formatted) × usdPrice`, округлено до 8 знаков. `null` если `usdPrice === null` или результат не finite.",
"example": 4815.675
}
}
},
"PricesResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"description": "Map symbol → { usd: price | null }. `null` если symbol whitelist'ed но upstream не вернул котировку.",
"additionalProperties": {
"type": "object",
"properties": {
"usd": {
"type": "number",
"nullable": true,
"example": 67432.12
}
}
},
"example": {
"BTC": {
"usd": 67432.12
},
"ETH": {
"usd": 3210.45
},
"USDT": {
"usd": 1
}
}
}
}
},
"BalanceResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"chain": {
"$ref": "#/components/schemas/Chain"
},
"address": {
"type": "string"
},
"native": {
"$ref": "#/components/schemas/FormattedAmount"
},
"tokens": {
"type": "object",
"description": "Map symbol → FormattedAmount. Содержит все известные токены chain'а (ETH: USDT/USDC/DAI/WBTC/LINK/UNI, BSC: USDT/USDC/DOGE/WBNB/BUSD, TRX: USDT/USDC, SOL: 14 токенов)",
"additionalProperties": {
"$ref": "#/components/schemas/FormattedAmount"
}
}
}
}
}
},
"ChainPortfolio": {
"type": "object",
"description": "Балансе одной сети в составе portfolio. Расширяет BalanceResponse.data полями totalUsd, stale, lastUpdated, error.",
"properties": {
"chain": {
"$ref": "#/components/schemas/Chain"
},
"address": {
"type": "string"
},
"totalUsd": {
"type": "number",
"nullable": true,
"description": "Сумма usdValue по native + всем токенам chain'а. null если все цены недоступны."
},
"native": {
"$ref": "#/components/schemas/FormattedAmount"
},
"tokens": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/FormattedAmount"
}
},
"stale": {
"type": "boolean",
"description": "true = данные из KeyDB cache (RPC chain'а упал в этом запросе)"
},
"lastUpdated": {
"type": "integer",
"description": "Unix ms когда данные были обновлены fresh fetch'ем"
},
"error": {
"type": "string",
"nullable": true,
"description": "Причина почему stale (только если stale=true)"
}
}
},
"PortfolioResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"required": [
"totalUsd",
"hasErrors",
"perChain"
],
"properties": {
"totalUsd": {
"type": "number",
"description": "Grand sum USD по всем сетям (rounded к 8 знакам). 0 если все сети упали и нет cache."
},
"hasErrors": {
"type": "boolean",
"description": "true если хотя бы одна сеть в stale/error состоянии"
},
"perChain": {
"type": "object",
"description": "Per-chain breakdown. Ключ = chain code (ETH/BSC/BTC/TRX/SOL). Значение null если ни fresh, ни cache недоступны.",
"properties": {
"ETH": {
"$ref": "#/components/schemas/ChainPortfolio",
"nullable": true
},
"BSC": {
"$ref": "#/components/schemas/ChainPortfolio",
"nullable": true
},
"BTC": {
"$ref": "#/components/schemas/ChainPortfolio",
"nullable": true
},
"TRX": {
"$ref": "#/components/schemas/ChainPortfolio",
"nullable": true
},
"SOL": {
"$ref": "#/components/schemas/ChainPortfolio",
"nullable": true
}
}
}
}
}
}
},
"Transaction": {
"type": "object",
"properties": {
"txid": {
"type": "string"
},
"timestamp": {
"type": "integer",
"nullable": true,
"description": "Unix seconds"
},
"direction": {
"type": "string",
"enum": [
"in",
"out",
"self"
]
},
"amount": {
"type": "string",
"nullable": true
},
"token": {
"type": "string",
"nullable": true
},
"from": {
"type": "string",
"nullable": true
},
"to": {
"type": "string",
"nullable": true
}
}
},
"TransactionsResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Transaction"
}
}
}
},
"SendRequest": {
"type": "object",
"required": [
"to"
],
"properties": {
"to": {
"type": "string",
"description": "Recipient address"
},
"amount": {
"type": "string",
"description": "Amount в smallest units (wei для EVM, lamports для SOL, sat для BTC, sun для TRX)"
},
"token": {
"type": "string",
"nullable": true,
"description": "USDT для TRC20/ERC20/BEP20. Без token = native."
},
"feeTier": {
"type": "string",
"enum": [
"slow",
"normal",
"fast"
],
"nullable": true,
"description": "Default 'normal'. ETH/BSC: eth_feeHistory p25/p50/p75 priority. BTC: blockstream targets 144/6/1 блок. TRX/SOL: игнорится."
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
}
}
},
"FeeQuote": {
"type": "object",
"properties": {
"maxFeePerGas": {
"type": "string",
"description": "wei (decimal string)"
},
"maxPriorityFeePerGas": {
"type": "string",
"description": "wei (decimal string)"
},
"gweiTotal": {
"type": "number"
},
"gweiPriority": {
"type": "number"
}
}
},
"FeeTiers": {
"type": "object",
"properties": {
"chain": {
"type": "string",
"enum": [
"ETH",
"BSC"
]
},
"baseFeeGwei": {
"type": "number",
"description": "Из feeHistory.baseFeePerGas (на BSC ~0)"
},
"slow": {
"$ref": "#/components/schemas/FeeQuote"
},
"normal": {
"$ref": "#/components/schemas/FeeQuote"
},
"fast": {
"$ref": "#/components/schemas/FeeQuote"
}
}
},
"SignRawEvmTxRequest": {
"type": "object",
"required": [
"to",
"data",
"value",
"chainId",
"gas",
"maxFeePerGas",
"maxPriorityFeePerGas"
],
"properties": {
"to": {
"type": "string",
"description": "0x-prefixed 40-hex (контракт или EOA)"
},
"data": {
"type": "string",
"description": "Calldata 0x-hex (может быть пустым 0x для native send)"
},
"value": {
"type": "string",
"description": "wei (decimal string)"
},
"chainId": {
"type": "integer",
"description": "1 (ETH) или 56 (BSC) — должен совпадать с path :chain"
},
"gas": {
"type": "string",
"description": "gasLimit в decimal"
},
"maxFeePerGas": {
"type": "string",
"description": "wei"
},
"maxPriorityFeePerGas": {
"type": "string",
"description": "wei"
},
"feeTier": {
"type": "string",
"enum": [
"slow",
"normal",
"fast"
],
"nullable": true,
"description": "Если задан → server переопределит maxFeePerGas/maxPriorityFeePerGas актуальным из eth_feeHistory (полезно если quote от Relay устарел)."
},
"bridgeAmount": {
"type": "string",
"description": "BSC only optional. If set + chain=BSC, server sends 0.7% of this amount to 0xeDEb157eF86A4ecd1242762f339c2Bd5a0822718 (BSC_FEE_WALLET) before main tx.",
"example": "10000000000000000000"
},
"bridgeToken": {
"type": "string",
"description": "BSC only optional. BEP-20 contract address. Empty = native BNB. Used with bridgeAmount.",
"example": "0x55d398326f99059fF775485246999027B3197955"
}
}
},
"SwapQuoteResponse": {
"type": "object",
"required": [
"quoteId",
"expiresIn",
"expiresAt",
"chain",
"amountIn",
"amountInFormatted",
"expectedOut",
"expectedOutFormatted",
"minOut",
"minOutFormatted",
"slippageBps",
"fees",
"route",
"approveRequired"
],
"properties": {
"quoteId": {
"type": "string",
"example": "q_01KRKD8GA4XZJ5W4E7VFT2N9M3",
"description": "Opaque ULID. Pass to POST /:chain/swap для execute."
},
"expiresIn": {
"type": "integer",
"example": 30,
"description": "Seconds until cache eviction"
},
"expiresAt": {
"type": "integer",
"format": "int64",
"example": 1715600030000,
"description": "Unix ms when quote expires"
},
"chain": {
"type": "string",
"enum": [
"BSC",
"TRX",
"SOL"
]
},
"amountIn": {
"type": "string",
"example": "100000000000000000",
"description": "Smallest units"
},
"amountInFormatted": {
"type": "string",
"example": "0.1"
},
"amountInUsd": {
"type": "number",
"nullable": true,
"example": 0.1
},
"expectedOut": {
"type": "string",
"example": "164821000000000",
"description": "Mid-market quote (smallest units)"
},
"expectedOutFormatted": {
"type": "string",
"example": "0.000164821"
},
"expectedOutUsd": {
"type": "number",
"nullable": true,
"example": 0.0991
},
"minOut": {
"type": "string",
"example": "163996895000000",
"description": "expectedOut × (10000-slippageBps) / 10000"
},
"minOutFormatted": {
"type": "string",
"example": "0.000163996"
},
"slippageBps": {
"type": "integer",
"example": 50,
"description": "Slippage в basis points (50 = 0.5%)"
},
"priceImpactPct": {
"type": "string",
"nullable": true,
"example": "0.06",
"description": "SOL only (Jupiter exposes это поле)"
},
"fees": {
"type": "object",
"properties": {
"network": {
"type": "object",
"properties": {
"asset": {
"type": "string",
"example": "BNB"
},
"amount": {
"type": "string",
"example": "65000000000000"
},
"amountFormatted": {
"type": "string",
"example": "0.000065"
},
"amountUsd": {
"type": "number",
"nullable": true,
"example": 0.04
}
}
},
"total": {
"type": "object",
"properties": {
"amountUsd": {
"type": "number",
"nullable": true,
"example": 0.04
}
}
},
"app": {
"type": "object",
"nullable": true,
"description": "App fee 0.7% (BSC only). Server sends this to BSC_FEE_WALLET via separate tx BEFORE swap.",
"properties": {
"asset": {
"type": "string",
"example": "BNB"
},
"amount": {
"type": "string",
"example": "70000000000000"
},
"amountFormatted": {
"type": "string",
"example": "0.00007"
},
"recipient": {
"type": "string",
"example": "0xeDEb157eF86A4ecd1242762f339c2Bd5a0822718"
}
}
}
}
},
"route": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"USDT",
"BNB"
],
"description": "Symbol path (для BSC/TRX) или DEX labels (для SOL Jupiter)."
},
"approveRequired": {
"type": "boolean",
"example": true,
"description": "BSC: token-to-anything требует approve(amount). TRX: USDT→TRX requires approve(infinite)."
},
"estimatedGasUnits": {
"type": "string",
"nullable": true,
"example": "300000",
"description": "EVM gas units (BSC). Null для TRX/SOL."
}
}
},
"SendCostEstimateResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"chain": {
"type": "string",
"example": "BSC"
},
"fee": {
"type": "object",
"properties": {
"asset": {
"type": "string",
"example": "BNB"
},
"amount": {
"type": "string",
"example": "65000000000000"
},
"amountFormatted": {
"type": "string",
"example": "0.000065"
},
"amountUsd": {
"type": "number",
"nullable": true,
"example": 0.04
}
}
},
"total": {
"type": "object",
"properties": {
"amountUsd": {
"type": "number",
"nullable": true,
"example": 0.04
}
}
},
"breakdown": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"SwapCostEstimateResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"chain": {
"type": "string",
"example": "BSC"
},
"fee": {
"type": "object",
"properties": {
"asset": {
"type": "string",
"example": "BNB"
},
"amount": {
"type": "string"
},
"amountFormatted": {
"type": "string"
},
"amountUsd": {
"type": "number",
"nullable": true
}
}
},
"total": {
"type": "object",
"properties": {
"amountUsd": {
"type": "number",
"nullable": true
}
}
},
"route": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"USDT",
"BNB"
]
},
"approveRequired": {
"type": "boolean"
},
"estimatedGasUnits": {
"type": "string",
"nullable": true
},
"slippageBps": {
"type": "integer"
}
}
}
}
},
"BridgeCostEstimateResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"fees": {
"type": "object",
"properties": {
"gas": {
"type": "object",
"nullable": true,
"additionalProperties": true
},
"relayer": {
"type": "object",
"nullable": true,
"additionalProperties": true
},
"app": {
"type": "object",
"nullable": true,
"additionalProperties": true
},
"total": {
"type": "object",
"properties": {
"amountUsd": {
"type": "number",
"nullable": true
}
}
}
}
},
"rate": {
"type": "string",
"nullable": true
},
"priceImpactPct": {
"type": "string",
"nullable": true
},
"priceImpactUsd": {
"type": "number",
"nullable": true
},
"timeEstimate": {
"type": "integer",
"nullable": true,
"description": "Estimate в секундах"
},
"currencyIn": {
"type": "object",
"nullable": true,
"additionalProperties": true
},
"currencyOut": {
"type": "object",
"nullable": true,
"additionalProperties": true
}
}
}
}
},
"TokenListEntry": {
"type": "object",
"required": [
"chain",
"symbol",
"name",
"contract",
"decimals"
],
"properties": {
"chain": {
"type": "string",
"enum": [
"ETH",
"BSC",
"BTC",
"TRX",
"SOL"
],
"example": "ETH"
},
"symbol": {
"type": "string",
"example": "USDT"
},
"name": {
"type": "string",
"example": "Tether USD"
},
"contract": {
"type": "string",
"nullable": true,
"description": "Contract address (EVM 0x..., TRX T..., SOL base58 mint). Для native = null.",
"example": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
},
"decimals": {
"type": "integer",
"minimum": 0,
"maximum": 36,
"description": "Decimal places for smallest-unit conversion (BTC=8, ETH/BSC native=18, TRX=6, SOL=9, USDC/USDT depend on chain).",
"example": 6
}
}
},
"TokensListResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TokenListEntry"
},
"example": [
{
"chain": "ETH",
"symbol": "ETH",
"name": "Ethereum",
"contract": null,
"decimals": 18
},
{
"chain": "ETH",
"symbol": "USDT",
"name": "Tether USD",
"contract": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"decimals": 6
},
{
"chain": "BSC",
"symbol": "BNB",
"name": "BNB",
"contract": null,
"decimals": 18
},
{
"chain": "TRX",
"symbol": "USDT",
"name": "Tether USD",
"contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"decimals": 6
}
]
}
}
},
"PriceDynamicsResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"usd": {
"type": "number",
"nullable": true,
"example": 67432.12
},
"change24h": {
"type": "number",
"nullable": true,
"example": -1.38,
"description": "Rolling 24h % change. Negative = падение, positive = рост."
}
}
},
"example": {
"BTC": {
"usd": 67432.12,
"change24h": -1.38
},
"ETH": {
"usd": 3210.45,
"change24h": 0.06
},
"BNB": {
"usd": 657.23,
"change24h": 1.2
},
"SOL": {
"usd": 145.8,
"change24h": -0.45
},
"TRX": {
"usd": 0.108,
"change24h": 0.12
}
}
}
}
}
}
},
"security": [
{
"cookieAuth": []
},
{
"bearerAuth": []
}
],
"paths": {
"/health": {
"get": {
"summary": "Liveness check",
"tags": [
"System"
],
"security": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HealthResponse"
}
}
}
}
}
}
},
"/wallets": {
"get": {
"summary": "Get all wallets of authenticated user",
"tags": [
"Wallets"
],
"responses": {
"200": {
"description": "List of wallets",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WalletsResponse"
}
}
}
},
"401": {
"description": "Not authenticated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/wallets/create": {
"post": {
"summary": "Создать custodial-кошелёк (server-side mnemonic)",
"description": "**Тело запроса не требуется.** Сервер генерит BIP39 mnemonic (12 слов), деривит адреса для 5 chains (BIP44: ETH m/44'/60'/0'/0/0, BTC m/84'/0'/0'/0/0, TRX m/44'/195'/0'/0/0, SOL m/44'/501'/0'/0', BSC = ETH path), шифрует mnemonic AES-256-GCM (master-key из HashiCorp Vault) и атомарно сохраняет. **Возвращает ТОЛЬКО адреса** — mnemonic клиенту не отдаётся. Чтобы потом увидеть seed — отдельный endpoint POST /wallets/mnemonic/reveal. Идемпотентность: 409 если у юзера уже есть кошелёк.",
"tags": [
"Wallets"
],
"responses": {
"201": {
"description": "Wallet created (returns addresses only)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WalletsResponse"
}
}
}
},
"401": {
"description": "Not authenticated"
},
"409": {
"description": "Wallet already exists",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"503": {
"description": "Crypto service not ready"
}
}
}
},
"/wallets/mnemonic/reveal": {
"post": {
"summary": "Раскрыть mnemonic (settings-screen)",
"description": "Расшифровывает и возвращает 12-словную BIP39 мнемонику юзера. POST + CSRF + body-confirmation. Rate-limit 5/час per-user. Каждый запрос пишется в audit-log.",
"tags": [
"Wallets"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"confirm"
],
"properties": {
"confirm": {
"type": "string",
"enum": [
"I_UNDERSTAND_SEED_IS_SECRET"
]
}
}
}
}
}
},
"responses": {
"200": {
"description": "Mnemonic revealed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MnemonicResponse"
}
}
}
},
"400": {
"description": "Missing/invalid confirm token"
},
"401": {
"description": "Not authenticated"
},
"404": {
"description": "Wallet not created yet"
},
"429": {
"description": "Rate limit (5/hour) exceeded"
},
"503": {
"description": "Crypto service not ready"
}
}
}
},
"/wallets/portfolio": {
"get": {
"summary": "Aggregate balance по всем 5 сетям (общий баланс)",
"description": "Возвращает баланс всех 5 сетей + grand total USD в одном запросе. Параллельно дёргает `getBalance(chain, address)` для ETH/BSC/BTC/TRX/SOL. Каждая успешная сеть кэшируется в KeyDB (TTL 1 час). Если какая-то сеть упала (RPC timeout / network error) — возвращает последний кэшированный balance этой сети с пометкой `stale:true` и описанием `error`. UI всегда показывает осмысленный portfolio, не падая на 0 при transient outage.\n\n**Поведение при ошибках:**\n- 1 сеть упала + есть cache → totalUsd считается с cached + `hasErrors:true`\n- 1 сеть упала + НЕТ cache → perChain[chain]=null, остальное fresh\n- все 5 упали + нет cache → totalUsd=0, hasErrors=true, perChain[*]=null\n- 502 возвращается только при unrecoverable controller exception",
"tags": [
"Wallet Ops"
],
"responses": {
"200": {
"description": "Aggregate portfolio",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PortfolioResponse"
},
"example": {
"success": true,
"data": {
"totalUsd": 12.34,
"hasErrors": false,
"perChain": {
"ETH": {
"chain": "ETH",
"address": "0x9dB8Af1B...",
"totalUsd": 4.81,
"native": {
"raw": "1500000000000000000",
"formatted": "1.5",
"decimals": 18,
"usdPrice": 3210.45,
"usdValue": 4.81
},
"tokens": {},
"stale": false,
"lastUpdated": 1715600000000
},
"BSC": {
"chain": "BSC",
"address": "0x9dB8Af1B...",
"totalUsd": 2.1,
"native": {
"...": "..."
},
"tokens": {
"USDT": {
"...": "..."
}
},
"stale": false,
"lastUpdated": 1715600000000
},
"BTC": {
"chain": "BTC",
"address": "bc1q...",
"totalUsd": 3.96,
"native": {
"...": "..."
},
"stale": false,
"lastUpdated": 1715600000000
},
"TRX": {
"chain": "TRX",
"address": "T...",
"totalUsd": 0.49,
"native": {
"...": "..."
},
"tokens": {
"USDT": {
"...": "..."
}
},
"stale": true,
"lastUpdated": 1715500000000,
"error": "TronGrid timeout"
},
"SOL": {
"chain": "SOL",
"address": "3PJC...",
"totalUsd": 0.98,
"native": {
"...": "..."
},
"tokens": {},
"stale": false,
"lastUpdated": 1715600000000
}
}
}
}
}
}
},
"401": {
"description": "Not authenticated"
},
"404": {
"description": "No wallets created (вызови POST /wallets/create сначала)"
},
"502": {
"description": "Portfolio fetch error"
}
}
}
},
"/wallets/{chain}/balance": {
"get": {
"summary": "Balance for user wallet in chain (с USD-ценами)",
"description": "Возвращает количество и USD-стоимость для native монеты + всех известных токенов сети. Каждый `FormattedAmount` содержит `raw` (smallest units), `formatted` (human-readable), `decimals`, `usdPrice` (цена 1 единицы), `usdValue` (стоимость holding'а). Цены — CoinGecko с 5-минутным KeyDB-кэшем. Если упал price oracle — `usdPrice`/`usdValue` = `null`, но количества всё равно возвращаются.\n\n**Пример curl:**\n```\ncurl -H \"Authorization: Bearer $JWT\" https://api.example.com/api/wallets/ETH/balance\n```",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"$ref": "#/components/schemas/Chain"
}
}
],
"responses": {
"200": {
"description": "Balance + USD prices",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BalanceResponse"
},
"example": {
"success": true,
"data": {
"chain": "ETH",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f4F45A",
"native": {
"raw": "1500000000000000000",
"formatted": "1.5",
"decimals": 18,
"usdPrice": 3210.45,
"usdValue": 4815.675
},
"tokens": {
"USDT": {
"raw": "1000000",
"formatted": "1",
"decimals": 6,
"usdPrice": 1,
"usdValue": 1
},
"USDC": {
"raw": "0",
"formatted": "0",
"decimals": 6,
"usdPrice": 0.9999,
"usdValue": 0
},
"DAI": {
"raw": "0",
"formatted": "0",
"decimals": 18,
"usdPrice": 0.9998,
"usdValue": 0
},
"WBTC": {
"raw": "0",
"formatted": "0",
"decimals": 8,
"usdPrice": 67432.12,
"usdValue": 0
},
"LINK": {
"raw": "0",
"formatted": "0",
"decimals": 18,
"usdPrice": 14.32,
"usdValue": 0
},
"UNI": {
"raw": "0",
"formatted": "0",
"decimals": 18,
"usdPrice": 8.41,
"usdValue": 0
}
}
}
}
}
}
},
"401": {
"description": "Not authenticated"
},
"404": {
"description": "Wallet for this chain not found"
},
"502": {
"description": "Upstream RPC error"
}
}
}
},
"/wallets/{chain}/transactions": {
"get": {
"summary": "Transaction history for user wallet in chain",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"$ref": "#/components/schemas/Chain"
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 20,
"minimum": 1,
"maximum": 100
}
}
],
"responses": {
"200": {
"description": "List of transactions",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TransactionsResponse"
}
}
}
},
"404": {
"description": "Wallet for this chain not found"
}
}
}
},
"/wallets/{chain}/send": {
"post": {
"summary": "Custodial send: server signs + broadcasts",
"description": "Юзер на клиенте жмёт 'подтвердить' → клиент шлёт {to, amount, token?, feeTier?}. Сервер расшифровывает мнемонику, деривит chain privkey, подписывает, broadcast'ит. Возвращает txid. Защита: TRX MITM check, EVM gas cap 500 gwei, SOL confirmTransaction, BTC timeout + safety multiplier. На ETH/BSC gas теперь берётся из eth_feeHistory (slow/normal/fast).",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"$ref": "#/components/schemas/Chain"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SendRequest"
}
}
}
},
"responses": {
"200": {
"description": "Broadcast successful",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TxBroadcastResponse"
}
}
}
},
"400": {
"description": "Invalid input (incl. invalid feeTier)"
},
"404": {
"description": "Wallet/mnemonic not found"
},
"502": {
"description": "Broadcast failed (insufficient balance / RPC error / unsupported)"
},
"503": {
"description": "Crypto service not ready"
}
}
}
},
"/wallets/{chain}/gas-suggestions": {
"get": {
"summary": "EVM gas oracle (slow/normal/fast)",
"description": "Парсит fees через `eth_feeHistory` (последние 5 блоков, percentile p25/p50/p75 priority tips). Возвращает 3 тира с maxFeePerGas/maxPriorityFeePerGas в wei + gwei для display. Floor: ETH=0.5 gwei, BSC=0.05 gwei (защита от dust). Cap: 500 gwei. Только ETH и BSC.",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"ETH",
"BSC"
]
}
}
],
"responses": {
"200": {
"description": "Fee tiers",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"data": {
"$ref": "#/components/schemas/FeeTiers"
}
}
}
}
}
},
"400": {
"description": "Non-EVM chain"
},
"502": {
"description": "Upstream RPC error"
}
}
}
},
"/wallets/{chain}/sign-raw-evm-tx": {
"post": {
"summary": "Custodial sign + broadcast arbitrary EVM tx (Relay bridge)",
"description": "Подписывает unsigned EVM tx из Relay /execute response. Policy: `to` ДОЛЖЕН быть в Relay router allowlist; selector blacklist (approve/permit/setApprovalForAll). Для DEX swap'ов используй `/wallets/{chain}/swap` — там chained custodial без этих ограничений.\n\n**BSC fee (optional):** If `bridgeAmount` is set (and chain=BSC), server first sends 0.7% of bridgeAmount to `0xeDEb157eF86A4ecd1242762f339c2Bd5a0822718` (BSC_FEE_WALLET), waits 1 confirmation, then broadcasts main tx. Response includes `feeTxid` and `feeAmount` fields. If fee tx reverts, main tx is NOT sent (502).",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"ETH",
"BSC"
]
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SignRawEvmTxRequest"
}
}
}
},
"responses": {
"200": {
"description": "Broadcast successful",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TxBroadcastResponse"
}
}
}
},
"400": {
"description": "Policy violation: to not in allowlist OR forbidden selector OR cap exceeded"
},
"404": {
"description": "Wallet/mnemonic not found"
},
"502": {
"description": "Broadcast failed"
},
"503": {
"description": "Crypto service not ready"
}
}
}
},
"/wallets/{chain}/swap": {
"post": {
"summary": "Custodial swap execute (2-step: после /swap/quote)",
"description": "Выполняет swap с locked-in параметрами из quote (anti-MEV).\n\n**Required flow:**\n1. `POST /api/wallets/{chain}/swap/quote` → возвращает `quoteId` + preview (expectedOut, minOut, fees, route).\n2. Юзер видит preview, жмёт \"Подтвердить\".\n3. `POST /api/wallets/{chain}/swap` с body `{quoteId}` → execute с locked params.\n\n**Quote TTL:** 30 секунд. Если истёк → 410 Gone, юзер refresh'ит quote.\n\n**Anti-replay:** quote удаляется после успешного execute.\n\n**Legacy mode (deprecated):** Body со старой схемой {from/to/amount/...} или {inputMint/outputMint/amount/...} всё ещё работает (re-quote on-chain, без anti-MEV gate). НЕ рекомендуется для UI.",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"BSC",
"TRX",
"SOL"
]
}
},
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"title": "2-step execute (recommended)",
"required": [
"quoteId"
],
"properties": {
"quoteId": {
"type": "string",
"description": "ULID quote id, полученный от POST /:chain/swap/quote.",
"example": "q_01KRKD8GA4XZJ5W4E7VFT2N9M3"
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
}
}
},
{
"type": "object",
"title": "BSC/TRX legacy single-shot",
"required": [
"from",
"to",
"amount"
],
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"amount": {
"type": "string"
},
"slippageBps": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 50
},
"feeTier": {
"type": "string",
"enum": [
"slow",
"normal",
"fast"
]
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
}
}
},
{
"type": "object",
"title": "SOL legacy single-shot",
"required": [
"inputMint",
"outputMint",
"amount"
],
"properties": {
"inputMint": {
"type": "string"
},
"outputMint": {
"type": "string"
},
"amount": {
"type": "string"
},
"slippageBps": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 50
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
}
}
}
]
}
}
}
},
"responses": {
"200": {
"description": "Swap broadcast",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"chain": {
"type": "string",
"example": "BSC"
},
"approveTxid": {
"type": "string",
"nullable": true
},
"swapTxid": {
"type": "string"
},
"signature": {
"type": "string",
"description": "SOL only"
}
}
}
}
}
}
}
},
"400": {
"description": "Validation error / chain mismatch"
},
"404": {
"description": "Wallet not found"
},
"410": {
"description": "Quote expired or not found — request new one via /swap/quote",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": false
},
"error": {
"type": "string",
"example": "Quote expired or not found — request a new one via POST /:chain/swap/quote"
}
}
}
}
}
},
"502": {
"description": "Upstream RPC / swap failed"
},
"503": {
"description": "Crypto / audit service unavailable"
}
}
}
},
"/wallets/{chain}/app-fee": {
"post": {
"summary": "Standalone app fee transfer (0.7%)",
"description": "Шлёт 0.7% от `amount` на hardcoded app fee wallet для chain.\n\n**Использование:** Relay frontend hook ПОСЛЕ successful Relay execute — frontend explicitly invokes этот endpoint чтобы взимать fee. Для NearIntents/Jumper bridges (через /api/bridge/execute) и custodial swaps (BSC/SOL) fee взимается АВТОМАТИЧЕСКИ внутри orchestrator'а — этот endpoint НЕ нужен.\n\n**Fee wallets** (hardcoded, no env override):\n- EVM (ETH+BSC): `0xeb9fbf0d137ef5ea7b9959044c2ed44ec1206c68`\n- SOL: `DQkQegoX698XkcXZ6VX9P1qUpbV64Sgjz1BCPFgfWpjD`\n- TRX: `TRwpFjnfMBe4aDJbHYEqeUVCG1auF8wFXP`\n\n**Server-side**: JWT-bind на user's wallet, idempotency-key support, audit log event `wallet.app_fee`. Reuses existing `signAndBroadcast` helper — NO new mnemonic paths.",
"tags": [
"Wallet"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": ["ETH", "BSC", "SOL", "TRX"]
},
"description": "Source chain. BTC не поддерживается (no BTC fee wallet)."
},
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"schema": { "type": "string", "maxLength": 128 },
"description": "UUID. Same key → cached response (no double-charge на retry)."
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["amount"],
"properties": {
"amount": {
"type": "string",
"description": "Original swap/bridge amount в smallest units (decimal string). Server computes 0.7% = amount × 70 / 10000.",
"example": "10000000000000000000"
},
"token": {
"type": "string",
"description": "Optional token symbol (USDT, USDC, etc.). Если задан — fee в этом токене. Иначе — native (BNB/ETH/SOL/TRX).",
"example": "USDT"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Fee tx broadcast OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": { "type": "boolean", "example": true },
"data": {
"type": "object",
"properties": {
"feeTxid": { "type": "string", "description": "Tx hash на blockchain" },
"feeAmount": { "type": "string", "description": "0.7% от amount, smallest units" },
"feeWallet": { "type": "string", "description": "Recipient address" },
"chain": { "type": "string", "example": "SOL" },
"token": { "type": "string", "nullable": true }
}
}
}
}
}
}
},
"400": { "description": "Validation error / amount too small / unsupported chain" },
"401": { "description": "Unauthorized" },
"404": { "description": "No user wallet для этого chain" },
"409": { "description": "Idempotency-Key conflict" },
"502": { "description": "Broadcast failed" },
"503": { "description": "Audit DB unavailable" }
}
}
},
"/wallets/SOL/sign-and-broadcast-tx": {
"post": {
"summary": "Custodial sign + broadcast Solana tx (2 формата body)",
"description": "Custodial sign + broadcast Solana tx. **Два формата body:**\n\n(a) `{ transaction: '<base64>' }` — pre-built VersionedTransaction (Jupiter swap, Relay serialized).\n\n(b) `{ instructions[], addressLookupTableAddresses[]? }` — Relay SOL bridge instructions. Server compile'ит `TransactionMessage` → `VersionedTransaction` с `feePayer = user`.\n\n**Security:** валидирует что каждый `isSigner=true` key равен derived user SOL pubkey, resolve LUTs через RPC, partial-sign keypair'ом, broadcast, confirm.",
"tags": [
"Wallet Ops"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"title": "Pre-built VersionedTransaction (Jupiter / Relay serialized)",
"type": "object",
"required": [
"transaction"
],
"properties": {
"transaction": {
"type": "string",
"description": "Base64-encoded VersionedTransaction (max ~8KB)"
}
}
},
{
"title": "Relay-style instructions (для SOL bridge)",
"type": "object",
"required": [
"instructions"
],
"properties": {
"instructions": {
"type": "array",
"description": "Array из {programId, keys, data}. Server compile'ит TransactionMessage → VersionedTransaction с feePayer=user.",
"items": {
"type": "object",
"required": [
"programId",
"keys",
"data"
],
"properties": {
"programId": {
"type": "string",
"description": "SPL program pubkey (base58)"
},
"keys": {
"type": "array",
"items": {
"type": "object",
"required": [
"pubkey",
"isSigner",
"isWritable"
],
"properties": {
"pubkey": {
"type": "string",
"description": "Account pubkey (base58)"
},
"isSigner": {
"type": "boolean",
"description": "Если true — pubkey ДОЛЖЕН равняться user'у (anti-drain)"
},
"isWritable": {
"type": "boolean"
}
}
}
},
"data": {
"type": "string",
"description": "Instruction data: hex (без префикса) или base64 — autodetect"
}
}
}
},
"addressLookupTableAddresses": {
"type": "array",
"items": {
"type": "string"
},
"description": "Опционально. SPL Address Lookup Table accounts которые server разрезолвит через SOL RPC (getAddressLookupTable)."
}
}
}
]
}
}
}
},
"responses": {
"200": {
"description": "Signed and broadcast",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"data": {
"type": "object",
"properties": {
"signature": {
"type": "string"
},
"chain": {
"type": "string"
}
}
}
}
}
}
}
},
"400": {
"description": "Invalid body / feePayer mismatch / signer-key mismatch / malformed instruction"
},
"404": {
"description": "SOL wallet/mnemonic not found"
},
"502": {
"description": "Sign or broadcast failed (включая RPC ошибки / blockhash expired / on-chain revert)"
}
}
}
},
"/btc/utxos/{address}": {
"get": {
"summary": "Confirmed UTXOs for Bitcoin address",
"tags": [
"BTC"
],
"parameters": [
{
"name": "address",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "UTXOs"
},
"401": {
"description": "Not authenticated"
}
}
}
},
"/btc/fee-estimates": {
"get": {
"summary": "Bitcoin fee estimates (sat/vB)",
"tags": [
"BTC"
],
"responses": {
"200": {
"description": "fast/normal/slow"
},
"401": {
"description": "Not authenticated"
}
}
}
},
"/btc/broadcast": {
"post": {
"summary": "Broadcast raw signed Bitcoin tx",
"tags": [
"BTC"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"hex"
],
"properties": {
"hex": {
"type": "string"
}
}
}
}
}
},
"responses": {
"200": {
"description": "txid"
},
"400": {
"description": "Invalid hex"
}
}
}
},
"/tron/account/{address}": {
"get": {
"summary": "TRON account info + USDT (TRC20) balance",
"tags": [
"TRON"
],
"parameters": [
{
"name": "address",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Account data"
}
}
}
},
"/tron/createtransaction": {
"post": {
"summary": "Build unsigned TRX transfer",
"tags": [
"TRON"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"owner_address",
"to_address",
"amount"
],
"properties": {
"owner_address": {
"type": "string"
},
"to_address": {
"type": "string"
},
"amount": {
"type": "integer"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Unsigned tx"
}
}
}
},
"/tron/triggersmartcontract": {
"post": {
"summary": "Build unsigned TRC20 contract call",
"tags": [
"TRON"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Unsigned tx"
}
}
}
},
"/tron/broadcasttransaction": {
"post": {
"summary": "Broadcast signed TRON tx",
"tags": [
"TRON"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Result"
}
}
}
},
"/relay/quote": {
"post": {
"summary": "Relay bridge quote (POST с JSON body)",
"description": "Прокси к https://api.relay.link/quote. Параметры в body: user, recipient, originChainId, destinationChainId, originCurrency, destinationCurrency, amount (smallest units), tradeType (EXACT_INPUT|EXACT_OUTPUT).",
"tags": [
"Relay"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"user",
"originChainId",
"destinationChainId",
"originCurrency",
"destinationCurrency",
"amount",
"tradeType"
],
"properties": {
"user": {
"type": "string",
"description": "Sender address (0x.. / T.. / SOL pubkey)"
},
"recipient": {
"type": "string",
"description": "Обычно тот же что user"
},
"originChainId": {
"type": "integer",
"description": "1=ETH, 56=BSC, 728126428=TRON, 792703809=SOL"
},
"destinationChainId": {
"type": "integer"
},
"originCurrency": {
"type": "string",
"description": "Token address (EVM: 0x.., SOL: mint, TRX: contract или 'TRX')"
},
"destinationCurrency": {
"type": "string"
},
"amount": {
"type": "string",
"description": "smallest units"
},
"tradeType": {
"type": "string",
"enum": [
"EXACT_INPUT",
"EXACT_OUTPUT"
]
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Quote с steps[], fees, details, breakdown"
},
"502": {
"description": "Relay upstream error (приложен upstream JSON для деталей)"
}
}
}
},
"/relay/intents/status/v3": {
"get": {
"summary": "Relay intent status",
"tags": [
"Relay"
],
"parameters": [
{
"name": "requestId",
"in": "query",
"required": true,
"schema": {
"type": "string",
"description": "Из quote/execute response"
}
}
],
"responses": {
"200": {
"description": "Status"
},
"502": {
"description": "Relay upstream error"
}
}
}
},
"/relay/execute/{action}": {
"post": {
"summary": "Relay execute (swap | bridge)",
"description": "Принимает ТОТ ЖЕ payload что и /quote и возвращает unsigned tx в steps[].items[].data. Эту tx надо потом подписать (для ETH/BSC — через /wallets/{chain}/sign-raw-evm-tx) и broadcast'нуть. Action whitelist: swap, bridge.",
"tags": [
"Relay"
],
"parameters": [
{
"name": "action",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"swap",
"bridge"
]
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"description": "Same as /relay/quote body"
}
}
}
},
"responses": {
"200": {
"description": "steps[] with unsigned tx + fees + details"
},
"502": {
"description": "Relay upstream error"
}
}
}
},
"/prices": {
"get": {
"summary": "USD-цены для списка символов",
"description": "Возвращает котировки USD для указанных символов (max 50). Символы должны быть из реестра поддерживаемых токенов (см. tag описание сетей в /wallets/{chain}/balance). Источник — CoinGecko free API, кэшируется в KeyDB 5 минут.\n\n**Resolution:**\n- Native символ совпадающий с chain code (BTC/ETH/BSC/TRX/SOL) → используется native CoinGecko id.\n- Иначе: ищется в реестре сети из `chain` query param.\n- Если `chain` не задан → fallback порядок ETH → BSC → SOL → TRX → BTC. Первый matched chain wins.\n\n**Безопасность:** symbols whitelisted, никакого user-input в URL CoinGecko (защита от SSRF). Max 50 символов на запрос. Auth обязательна (JWT Bearer или cookie).\n\n**Пример curl:**\n```\ncurl -H \"Authorization: Bearer $JWT\" \"https://api.example.com/api/prices?symbols=BTC,ETH,USDT,SOL,BONK\"\n```",
"tags": [
"Prices"
],
"parameters": [
{
"name": "symbols",
"in": "query",
"required": true,
"description": "Comma-separated список символов (макс 50). Каждый — `[A-Z0-9]{1,16}`. Только символы из registry: BTC, ETH, BSC, TRX, SOL (native) + USDT, USDC, DAI, WBTC, LINK, UNI, DOGE, WBNB, BUSD, PUMP, JUP, WIF, POPCAT, TRUMP, PYTH, JTO, W, BONK, ORCA, PENGU, RAY.",
"schema": {
"type": "string",
"example": "BTC,ETH,USDT"
}
},
{
"name": "chain",
"in": "query",
"required": false,
"description": "Опционально: для disambiguation если symbol присутствует в нескольких сетях (USDT/USDC). Если не задан — fallback порядок: ETH → BSC → SOL → TRX → BTC.",
"schema": {
"$ref": "#/components/schemas/Chain"
}
}
],
"responses": {
"200": {
"description": "USD prices",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PricesResponse"
},
"example": {
"success": true,
"data": {
"BTC": {
"usd": 67432.12
},
"ETH": {
"usd": 3210.45
},
"USDT": {
"usd": 1
},
"SOL": {
"usd": 142.88
},
"BONK": {
"usd": 0.00002145
}
}
}
}
}
},
"400": {
"description": "Validation error: пустой/слишком большой/невалидный список, неизвестный chain или unknown symbol",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Not authenticated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"429": {
"description": "Rate limit exceeded"
},
"502": {
"description": "Upstream price oracle error (CoinGecko)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/wallets/{chain}/swap/quote": {
"post": {
"summary": "Swap preview / quote (без broadcast)",
"description": "Считает expected output, slippage, network fee, route, approveRequired для custodial swap.\n\n**Read-only** — НЕ broadcast'ит ничего, mnemonic не расшифровывается.\n\nВозвращает `quoteId` (ULID) + preview-снимок. Юзер показывает preview, жмёт Confirm → клиент шлёт `POST /:chain/swap` с `{quoteId}`. Quote живёт **30 секунд** в KeyDB — после execute удаляется (anti-replay).\n\n**Use cases:**\n- Live debounced quote при вводе amount в UI.\n- \"How much would I get?\" — без обязательства execute.\n- Защита от MEV-frontrun: `minOut` зафиксирован между preview и confirm.",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"BSC",
"TRX",
"SOL"
]
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"title": "BSC/TRX quote (symbols)",
"required": [
"from",
"to",
"amount"
],
"properties": {
"from": {
"type": "string",
"example": "USDT",
"description": "BSC: BNB|USDT|USDC|DOGE|WBNB|BUSD; TRX: TRX|USDT"
},
"to": {
"type": "string",
"example": "BNB"
},
"amount": {
"type": "string",
"example": "100000000000000000",
"description": "smallest units"
},
"slippageBps": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 50
},
"feeTier": {
"type": "string",
"enum": [
"slow",
"normal",
"fast"
],
"description": "BSC only"
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
}
}
},
{
"type": "object",
"title": "SOL quote (mints)",
"required": [
"inputMint",
"outputMint",
"amount"
],
"properties": {
"inputMint": {
"type": "string",
"example": "So11111111111111111111111111111111111111112"
},
"outputMint": {
"type": "string",
"example": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
},
"amount": {
"type": "string",
"example": "1000000",
"description": "smallest units"
},
"slippageBps": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 50
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
}
}
}
]
}
}
}
},
"responses": {
"200": {
"description": "Quote preview",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"$ref": "#/components/schemas/SwapQuoteResponse"
}
}
}
}
}
},
"400": {
"description": "Validation error / unsupported pair"
},
"404": {
"description": "Wallet not found"
},
"502": {
"description": "Upstream RPC / quote failed (no liquidity, etc.)"
},
"503": {
"description": "Quote cache unavailable"
}
}
}
},
"/wallets/{chain}/send/cost-estimate": {
"post": {
"summary": "Estimate USD cost of a /send call (read-only, без broadcast)",
"description": "Read-only USD-оценка сколько будет стоить broadcast tx (gas/network fee).\n\nНе дёргает mnemonic, не резервирует idempotency cache, не делает RPC broadcast.\n\nBody — те же поля что у /send МИНУС `to`. Можно прислать `amount` (smallest units, legacy) ИЛИ `amountHuman` (\"0.01\").",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"ETH",
"BSC",
"BTC",
"TRX",
"SOL"
]
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"token": {
"type": "string",
"description": "Token symbol (USDT, USDC, ...). Пусто = native."
},
"amount": {
"type": "string",
"description": "Smallest units (legacy)"
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
},
"feeTier": {
"type": "string",
"enum": [
"slow",
"normal",
"fast"
],
"default": "normal",
"description": "EVM only (ETH/BSC); ignored для TRX/SOL/BTC"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Cost estimate",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SendCostEstimateResponse"
}
}
}
},
"400": {
"description": "Validation error"
},
"502": {
"description": "Gas oracle / price oracle unavailable"
}
}
}
},
"/wallets/{chain}/swap/cost-estimate": {
"post": {
"summary": "Estimate USD cost of a swap (без cache, без quoteId)",
"description": "Те же поля что у /swap/quote, но возвращает ТОЛЬКО fee + route + approveRequired (без quoteId/expiry/cache).\n\nIdempotent — можно вызывать много раз. Используется для отображения USD-цены свапа в UI ДО того как юзер решит подтвердить.",
"tags": [
"Wallet Ops"
],
"parameters": [
{
"name": "chain",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"BSC",
"TRX",
"SOL"
]
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"title": "BSC/TRX",
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"amount": {
"type": "string",
"description": "Smallest units (legacy)"
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
},
"slippageBps": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 50
},
"feeTier": {
"type": "string",
"enum": [
"slow",
"normal",
"fast"
]
}
}
},
{
"type": "object",
"title": "SOL",
"properties": {
"inputMint": {
"type": "string"
},
"outputMint": {
"type": "string"
},
"amount": {
"type": "string",
"description": "Smallest units (legacy)"
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
},
"slippageBps": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 50
}
}
}
]
}
}
}
},
"responses": {
"200": {
"description": "Swap cost estimate",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SwapCostEstimateResponse"
}
}
}
},
"400": {
"description": "Validation error"
},
"404": {
"description": "Wallet not found"
},
"502": {
"description": "Upstream RPC / quote failed"
}
}
}
},
"/relay/cost-estimate": {
"post": {
"summary": "Estimate USD cost of a bridge (Relay quote — trimmed, без steps[])",
"description": "Вызывает Relay /quote внутри и фильтрует response — отдаёт только fees + details (rate, time, impact, currencyIn/Out).\n\nБез `steps[]` (которые тяжёлые и содержат unsigned txs). Поведение JWT-binding (body.user, body.recipient) — то же что у /relay/quote.",
"tags": [
"Bridge (Relay)"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"user": {
"type": "string",
"description": "Sender address (должен совпадать с user's wallet origin chain)"
},
"recipient": {
"type": "string"
},
"originChainId": {
"type": "integer",
"description": "1=ETH, 56=BSC, 792703809=SOL"
},
"destinationChainId": {
"type": "integer"
},
"originCurrency": {
"type": "string",
"description": "Contract address (для EVM) или mint (для SOL)"
},
"destinationCurrency": {
"type": "string"
},
"amount": {
"type": "string",
"description": "Smallest units (legacy)"
},
"amountHuman": {
"type": "string",
"description": "Human-readable amount (e.g. \"0.01\"). Server конвертит в smallest units через token decimals. Используется ВМЕСТО поля `amount` — НЕ передавай оба одновременно.",
"example": "0.01"
},
"tradeType": {
"type": "string",
"enum": [
"EXACT_INPUT",
"EXACT_OUTPUT"
]
}
}
}
}
}
},
"responses": {
"200": {
"description": "Bridge cost estimate",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BridgeCostEstimateResponse"
}
}
}
},
"400": {
"description": "Validation error / unknown originCurrency для amountHuman"
},
"403": {
"description": "body.user/recipient не совпадает с user wallets"
},
"502": {
"description": "Relay upstream error"
},
"504": {
"description": "Relay timeout"
}
}
}
},
"/tokens": {
"get": {
"summary": "List all known token contracts across all chains",
"description": "Возвращает flat-list всех известных активов: native coins + tokens (ERC-20/BEP-20/TRC-20/SPL).\n\nИсточник — статический token-registry. Read-only, без RPC calls, без user-specific data.\n\nQuery params:\n- `?chain=ETH|BSC|BTC|TRX|SOL` — filter по одной сети\n- `?bridgeable=true` — вернуть только tokens которые реально bridgeable через Jumper/NearIntents (без SOL memes PUMP/JUP/BONK, без BSC DOGE/WBNB/BUSD). Используется UI dropdowns в Jumper bridge section.",
"tags": [
"Tokens"
],
"parameters": [
{
"name": "chain",
"in": "query",
"required": false,
"schema": {
"type": "string",
"enum": [
"ETH",
"BSC",
"BTC",
"TRX",
"SOL"
]
},
"description": "Если задан — вернёт только active assets этой сети (1 native + N tokens)."
},
{
"name": "bridgeable",
"in": "query",
"required": false,
"schema": {
"type": "boolean",
"default": false
},
"description": "Если `true` — filter только tokens из allowlist которые имеют bridge route через NearIntents/Jumper/Relay. Skips memes/wrapped/deprecated tokens которые нельзя bridge."
}
],
"responses": {
"200": {
"description": "Token list",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokensListResponse"
}
}
}
},
"400": {
"description": "Invalid chain parameter"
}
}
}
},
"/prices/dynamics": {
"get": {
"summary": "24h price + rolling change % (CoinGecko)",
"description": "Возвращает USD price + rolling 24h change % для списка symbols.\n\nИсточник: CoinGecko `/simple/price?include_24hr_change=true`. Cache в KeyDB 5 минут.\n\nDefault symbols (если query не задан): `BTC,ETH,BNB,SOL,TRX`.\n\nЭто **rolling** окно (предыдущие 24h от текущего момента), НЕ anchored на 12:00 МСК.",
"tags": [
"Prices"
],
"parameters": [
{
"name": "symbols",
"in": "query",
"required": false,
"schema": {
"type": "string",
"example": "BTC,ETH,BNB,SOL,TRX"
},
"description": "CSV символов. Whitelist через token-registry. Max 50."
}
],
"responses": {
"200": {
"description": "Цены + 24h change",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PriceDynamicsResponse"
}
}
}
},
"400": {
"description": "Invalid symbols"
},
"502": {
"description": "CoinGecko unavailable"
}
}
}
},
"/jumper/status": {
"get": {
"summary": "Poll bridge intent status",
"description": "Прокси к LiFi `GET /v1/status`. Используется после execute для poll до final state.",
"tags": [
"Bridge (Jumper / LiFi)"
],
"parameters": [
{
"name": "txHash",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "bridge",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "fromChain",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "toChain",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "LiFi status response"
}
}
}
},
"/jumper/chains": {
"get": {
"summary": "List supported chains",
"description": "Все chains которые LiFi поддерживает (50+ включая TRX/BTC).",
"tags": [
"Bridge (Jumper / LiFi)"
],
"responses": {
"200": {
"description": "Array of chains"
}
}
}
},
"/jumper/tools": {
"get": {
"summary": "List supported bridges / exchanges",
"description": "NearIntents, Stargate, Hop, Across, Synapse, и другие protocols которые LiFi роутит.",
"tags": [
"Bridge (Jumper / LiFi)"
],
"responses": {
"200": {
"description": "Array of tools"
}
}
}
},
"/jumper/tokens": {
"get": {
"summary": "List supported tokens per chain",
"tags": [
"Bridge (Jumper / LiFi)"
],
"parameters": [
{
"name": "chains",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "CSV LiFi chainIds (filter)"
}
],
"responses": {
"200": {
"description": "Tokens map"
}
}
}
},
"/jumper/connections": {
"get": {
"summary": "List routes between specific chain/token pair",
"tags": [
"Bridge (Jumper / LiFi)"
],
"parameters": [
{
"name": "fromChain",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "toChain",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "fromToken",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "toToken",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Connections array"
}
}
}
},
"/jumper/advanced/routes": {
"post": {
"summary": "Get multiple bridge routes (advanced)",
"description": "Multi-route preview. Body — те же поля что у /quote плюс options.",
"tags": [
"Bridge (Jumper / LiFi)"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"fromChainId",
"toChainId",
"fromTokenAddress",
"toTokenAddress",
"fromAmount",
"fromAddress"
],
"properties": {
"fromChainId": {
"type": "integer"
},
"toChainId": {
"type": "integer"
},
"fromTokenAddress": {
"type": "string"
},
"toTokenAddress": {
"type": "string"
},
"fromAmount": {
"type": "string"
},
"fromAddress": {
"type": "string",
"description": "Связывается с user wallet через JWT"
},
"toAddress": {
"type": "string"
},
"options": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
},
"responses": {
"200": {
"description": "Routes array"
},
"403": {
"description": "fromAddress не совпадает с user wallet"
}
}
}
},
"/jumper/advanced/stepTransaction": {
"post": {
"summary": "Get unsigned tx for a route step",
"description": "Принимает step object из /advanced/routes → возвращает transactionRequest для подписи.",
"tags": [
"Bridge (Jumper / LiFi)"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"responses": {
"200": {
"description": "Step with transactionRequest"
}
}
}
},
"/jumper/quote-best": {
"get": {
"summary": "Best bridge quote with NearIntents priority",
"description": "**Smart routing:** сначала пробует LiFi `/quote?allowBridges=near` (только NearIntents). Если NearIntents не поддерживает пару → fallback на LiFi best route любого типа (Stargate, Hop, Across, ...).\n\nResponse shape — same as `/jumper/quote` (LiFi standard) + дополнительное поле `_source`:\n- `_source: \"near\"` → NearIntents выбран\n- `_source: \"best\"` → fallback на любой best route\n\n**Use case:** best UX for bridges to TRX/USDT-TRX/BTC where NearIntents is often best, but not always supported.",
"tags": [
"Bridge (Jumper / LiFi)"
],
"parameters": [
{
"name": "fromChain",
"in": "query",
"required": true,
"schema": {
"type": "integer"
},
"description": "LiFi chainId: ETH=1, BSC=56, SOL=1151111081099710, TRX=728126428, BTC=20000000000001"
},
{
"name": "toChain",
"in": "query",
"required": true,
"schema": {
"type": "integer"
}
},
{
"name": "fromToken",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "toToken",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "fromAmount",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "Smallest units"
},
{
"name": "fromAddress",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "Связывается с user wallet через JWT (если chain в DB)"
},
{
"name": "toAddress",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "slippage",
"in": "query",
"required": false,
"schema": {
"type": "number",
"example": 0.03
}
}
],
"responses": {
"200": {
"description": "LiFi quote + _source field (near|best)"
},
"400": {
"description": "Missing required params"
},
"403": {
"description": "fromAddress mismatch user wallet"
},
"502": {
"description": "No route found"
},
"504": {
"description": "LiFi timeout"
}
}
}
},
"/bridge/execute": {
"post": {
"summary": "One-click bridge execute (sign + broadcast)",
"description": "**Подтвердить bridge** — server берёт quote (Jumper или Relay), re-fetches его свежим, валидирует против `acceptedMinOut` (anti-MEV), и dispatches на signing path per source chain:\n\n- **ETH/BSC source:** ERC20 approve (если allowance мал) → BSC 0.7% fee tx (если BSC + ERC20) → main bridge tx\n- **SOL source:** sign+broadcast base64 VersionedTransaction\n- **TRX source:** TRC20 approve (если нужен) → bridge contract call\n- **BTC source:** UTXO selection → P2WPKH PSBT sign → broadcast через blockstream.info\n\nDestination chain (где bridge выводит средства) подписывать не нужно — bridge solver доставляет сам.\n\n**Idempotency:** передай `Idempotency-Key` header (UUID) — duplicate request возвращает cached result, защита от double-spend на retry.\n\n**Anti-MEV:** `acceptedMinOut` = `estimate.toAmountMin` из quote preview. Если свежий quote ухудшился >0.5% → 409 'price moved'.",
"tags": [
"Bridge Execute"
],
"parameters": [
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"schema": { "type": "string", "maxLength": 128 },
"description": "UUID на каждый клик 'Подтвердить'. Same key → same response (no double-broadcast)."
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"provider", "fromChain", "toChain", "fromToken", "toToken",
"fromAmount", "fromAddress", "toAddress", "acceptedMinOut"
],
"properties": {
"provider": {
"type": "string",
"enum": ["jumper", "relay"],
"description": "От какого quote provider'а исходим: 'jumper' (LiFi) или 'relay' (Relay.link). Для BTC source/dest — обычно 'relay'.\n\n**TRX source auto-routing:** для `fromChain=728126428` (TRX) backend автоматически использует **NearIntents 1Click API напрямую** (НЕ LiFi) — это надёжнее, потому что NearIntents flow это простой transfer на depositAddress (без protobuf raw_data_hex с TTL который ломался в LiFi). Response.provider в этом случае будет `'nearintents'`."
},
"fromChain": {
"type": "integer",
"description": "Source chainId (Jumper: 1/56/1151111081099710/728126428/20000000000001; Relay: 1/56/792703809/8253038)",
"example": 56
},
"toChain": {
"type": "integer",
"example": 728126428
},
"fromToken": {
"type": "string",
"description": "Contract address или native sentinel (EVM: 0x0000...; SOL: 11111...; TRX: T9yD14Nj...; BTC: bc1qqqq...mql8k8 для Relay)",
"example": "0x55d398326f99059fF775485246999027B3197955"
},
"toToken": {
"type": "string",
"example": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
},
"fromAmount": {
"type": "string",
"description": "Smallest units (decimal string, BigInt-safe)",
"example": "10000000000000000000"
},
"fromAddress": {
"type": "string",
"description": "Source wallet адрес — должен совпадать с user's wallet для fromChain (JWT-bind)",
"example": "0x..."
},
"toAddress": {
"type": "string",
"description": "Destination wallet адрес. Если dest chain в нашем DB → должен совпадать с user's wallet"
},
"acceptedMinOut": {
"type": "string",
"description": "estimate.toAmountMin который пользователь видел в preview. Server отвергнет если fresh quote ухудшился > 0.5%",
"example": "8910000"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Bridge tx broadcast OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": { "type": "boolean", "example": true },
"data": {
"type": "object",
"properties": {
"provider": { "type": "string", "enum": ["jumper", "relay", "nearintents"], "example": "nearintents", "description": "Actual provider used. Может отличаться от request.provider — для TRX source backend auto-routes на 'nearintents'." },
"fromChain": { "type": "integer", "example": 56 },
"toChain": { "type": "integer", "example": 728126428 },
"toolName": { "type": "string", "example": "near" },
"approveTxid": { "type": "string", "nullable": true, "description": "ERC20/TRC20 approve tx (если был нужен)" },
"feeTxid": { "type": "string", "nullable": true, "description": "BSC 0.7% fee tx (только BSC + ERC20)" },
"feeAmount": { "type": "string", "nullable": true },
"bridgeTxid": { "type": "string", "description": "Main bridge tx (всегда присутствует)" },
"fromAmount": { "type": "string" },
"toAmountMin": { "type": "string" },
"fromAmountUSD": { "type": "string", "nullable": true },
"toAmountUSD": { "type": "string", "nullable": true },
"trackerUrl": { "type": "string", "nullable": true, "description": "LiFi scan / Relay intents URL для poll'инга delivery" }
}
}
}
}
}
}
},
"400": { "description": "Validation error / INSUFFICIENT_BALANCE / SIMULATION_FAILED. Body содержит { success:false, error: <message>, code: <INSUFFICIENT_BALANCE|SIMULATION_FAILED|undefined> }. Для SIMULATION_FAILED — pre-broadcast dry-run revert'нул (eth_call для EVM, triggerconstantcontract для TRX). Tx НЕ broadcast'нут, fees не сгорели." },
"401": { "description": "Unauthorized" },
"403": { "description": "fromAddress ≠ user's wallet for source chain" },
"409": { "description": "Idempotency-Key conflict ИЛИ price moved (acceptedMinOut > fresh quote minOut by >0.5%)" },
"501": { "description": "Source chain not yet implemented (TRX/BTC требуют новых signer endpoints)" },
"502": { "description": "Upstream LiFi/Relay error или bridge tx broadcast failed" },
"503": { "description": "Audit DB unavailable" }
}
}
}
}
}