Files
cryptowallet/apps/api/swagger.json
2026-05-14 21:40:36 +03:00

2768 lines
90 KiB
JSON
Raw 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 устарел)."
}
}
},
"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
}
}
}
}
},
"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"
],
"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"
}
}
},
"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
},
{
"chain": "ETH",
"symbol": "USDT",
"name": "Tether USD",
"contract": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
},
{
"chain": "BSC",
"symbol": "BNB",
"name": "BNB",
"contract": null
},
{
"chain": "TRX",
"symbol": "USDT",
"name": "Tether USD",
"contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
}
]
}
}
}
}
},
"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 без этих ограничений.",
"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/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\nOptional ?chain=ETH|BSC|BTC|TRX|SOL — filter по одной сети.",
"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)."
}
],
"responses": {
"200": {
"description": "Token list",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokensListResponse"
}
}
}
},
"400": {
"description": "Invalid chain parameter"
}
}
}
}
}
}