init
This commit is contained in:
@@ -552,6 +552,16 @@
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -680,6 +690,29 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -903,7 +936,8 @@
|
||||
"chain",
|
||||
"symbol",
|
||||
"name",
|
||||
"contract"
|
||||
"contract",
|
||||
"decimals"
|
||||
],
|
||||
"properties": {
|
||||
"chain": {
|
||||
@@ -930,6 +964,13 @@
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -950,29 +991,83 @@
|
||||
"chain": "ETH",
|
||||
"symbol": "ETH",
|
||||
"name": "Ethereum",
|
||||
"contract": null
|
||||
"contract": null,
|
||||
"decimals": 18
|
||||
},
|
||||
{
|
||||
"chain": "ETH",
|
||||
"symbol": "USDT",
|
||||
"name": "Tether USD",
|
||||
"contract": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
|
||||
"contract": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
||||
"decimals": 6
|
||||
},
|
||||
{
|
||||
"chain": "BSC",
|
||||
"symbol": "BNB",
|
||||
"name": "BNB",
|
||||
"contract": null
|
||||
"contract": null,
|
||||
"decimals": 18
|
||||
},
|
||||
{
|
||||
"chain": "TRX",
|
||||
"symbol": "USDT",
|
||||
"name": "Tether USD",
|
||||
"contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1482,7 +1577,7 @@
|
||||
"/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 без этих ограничений.",
|
||||
"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"
|
||||
],
|
||||
@@ -1738,6 +1833,88 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/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)",
|
||||
@@ -2724,7 +2901,7 @@
|
||||
"/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 по одной сети.",
|
||||
"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"
|
||||
],
|
||||
@@ -2744,6 +2921,16 @@
|
||||
]
|
||||
},
|
||||
"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": {
|
||||
@@ -2762,6 +2949,492 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user