initalll
This commit is contained in:
@@ -51,6 +51,14 @@
|
||||
{
|
||||
"name": "Prices",
|
||||
"description": "USD-цены (CoinGecko + KeyDB cache 5 мин)"
|
||||
},
|
||||
{
|
||||
"name": "Staking",
|
||||
"description": "Custodial-стейкинг (ETH → Lido, SOL → native StakeProgram). Каждое сырое amount-поле (…Wei/…Lamports) имеет спутник …Human с человекочитаемым числом."
|
||||
},
|
||||
{
|
||||
"name": "Liquidity",
|
||||
"description": "Uniswap v3 LP — предоставление ликвидности (ETH). Каждое сырое amount-поле имеет спутник …Human (по decimals токена)."
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
@@ -1080,6 +1088,567 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/wallets/{chain}/stake/quote": {
|
||||
"post": {
|
||||
"summary": "Stake preview (Lido ETH): ожидаемый stETH, app-fee, APR",
|
||||
"description": "Превью перед стейком. Считает app-fee 0.7%, сумму после fee, ожидаемый stETH (~1:1) и текущий Lido APR (public API, кэш 10 мин; null если недоступен). Ничего не подписывает. Только ETH.",
|
||||
"tags": [
|
||||
"Staking"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "chain",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ETH",
|
||||
"SOL"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"type": "string",
|
||||
"description": "Сумма в wei (строка целых цифр)",
|
||||
"example": "100000000000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Quote",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amountWei": {
|
||||
"type": "string"
|
||||
},
|
||||
"amountWeiHuman": {
|
||||
"type": "string",
|
||||
"description": "human-форма (18 dec), напр. 0.1"
|
||||
},
|
||||
"appFeeWei": {
|
||||
"type": "string"
|
||||
},
|
||||
"appFeeWeiHuman": {
|
||||
"type": "string"
|
||||
},
|
||||
"stakeAmountWei": {
|
||||
"type": "string"
|
||||
},
|
||||
"stakeAmountWeiHuman": {
|
||||
"type": "string"
|
||||
},
|
||||
"expectedStEthWei": {
|
||||
"type": "string"
|
||||
},
|
||||
"expectedStEthWeiHuman": {
|
||||
"type": "string"
|
||||
},
|
||||
"aprPercent": {
|
||||
"type": "number",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid amount / unsupported chain"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/stake": {
|
||||
"post": {
|
||||
"summary": "Custodial stake (ETH → Lido, SOL → native StakeProgram): один вызов",
|
||||
"description": "Сервер отнимает app-fee 0.7%, затем стейкает: ETH → Lido submit(referral) payable (amount в wei); SOL → создаёт stake-аккаунт + delegate на валидатора (amount в lamports). Возвращает txid'ы (для SOL — ещё stakeAccount). JWT-bind: адрес = кошелёк юзера для chain. Поддерживает header Idempotency-Key (анти-дабл-спенд). Chain: ETH или SOL.",
|
||||
"tags": [
|
||||
"Staking"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "chain",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ETH",
|
||||
"SOL"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Idempotency-Key",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Опциональный ключ идемпотентности (анти-дабл-спенд на retry)"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"type": "string",
|
||||
"description": "Сумма в wei (строка целых цифр)",
|
||||
"example": "100000000000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Staked",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"feeTxid": {
|
||||
"type": "string"
|
||||
},
|
||||
"stakeTxid": {
|
||||
"type": "string"
|
||||
},
|
||||
"stakeAmountWei": {
|
||||
"type": "string"
|
||||
},
|
||||
"appFeeWei": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input / amount too small"
|
||||
},
|
||||
"403": {
|
||||
"description": "No ETH wallet for user"
|
||||
},
|
||||
"404": {
|
||||
"description": "Mnemonic not found"
|
||||
},
|
||||
"409": {
|
||||
"description": "Idempotency conflict"
|
||||
},
|
||||
"502": {
|
||||
"description": "Broadcast failed (insufficient balance / RPC error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/stake/positions": {
|
||||
"get": {
|
||||
"summary": "Stake positions (Lido ETH): stETH-баланс + APR",
|
||||
"description": "Читает live из чейна: stETH balanceOf(адрес юзера) = текущая позиция, плюс Lido APR. Без записи в БД. Только ETH.",
|
||||
"tags": [
|
||||
"Staking"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "chain",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ETH",
|
||||
"SOL"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Positions",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"protocol": {
|
||||
"type": "string"
|
||||
},
|
||||
"chain": {
|
||||
"type": "string"
|
||||
},
|
||||
"stEthBalanceWei": {
|
||||
"type": "string"
|
||||
},
|
||||
"stEthBalanceWeiHuman": {
|
||||
"type": "string"
|
||||
},
|
||||
"aprPercent": {
|
||||
"type": "number",
|
||||
"nullable": true
|
||||
},
|
||||
"withdrawalRequests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"requestId": { "type": "string" },
|
||||
"amountStEthWei": { "type": "string" },
|
||||
"amountStEthWeiHuman": { "type": "string" },
|
||||
"isFinalized": { "type": "boolean" },
|
||||
"isClaimed": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "No ETH wallet for user"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/unstake": {
|
||||
"post": {
|
||||
"summary": "Custodial unstake (ETH Lido / SOL native)",
|
||||
"description": "Вывод БЕЗ комиссии. ETH: тело {amount, mode}. mode=\"swap\" (по умолч.) — мгновенно stETH→ETH через Curve, один вызов (slippage сервер ставит автоматически ~0.3%); mode=\"queue\" — Lido Withdrawal Queue (request → потом /unstake/claim). SOL: тело {stakeAccount} — если active → deactivate; если inactive → withdraw (между ними cooldown сети 1-2 эпохи). JWT-bind + Idempotency-Key.",
|
||||
"tags": ["Staking"],
|
||||
"parameters": [
|
||||
{ "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH", "SOL"] } },
|
||||
{ "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": { "type": "string", "description": "ETH: stETH в wei", "example": "100000000000000000" },
|
||||
"mode": { "type": "string", "enum": ["swap", "queue"], "description": "ETH only; default swap" },
|
||||
"stakeAccount": { "type": "string", "description": "SOL only: адрес stake-аккаунта" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Unstake submitted", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object" } } } } } },
|
||||
"400": { "description": "Invalid input" },
|
||||
"403": { "description": "No wallet for user" },
|
||||
"502": { "description": "Broadcast failed" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/unstake/claim": {
|
||||
"post": {
|
||||
"summary": "Claim Lido queue withdrawal (ETH only)",
|
||||
"description": "Клеймит финализированный withdrawal request (mode=queue). requestId берётся из GET /wallets/ETH/stake/positions (поле withdrawalRequests[].requestId, когда isFinalized=true).",
|
||||
"tags": ["Staking"],
|
||||
"parameters": [
|
||||
{ "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH"] } },
|
||||
{ "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": { "type": "object", "required": ["requestId"], "properties": { "requestId": { "type": "string", "example": "12345" } } } } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Claimed", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object" } } } } } },
|
||||
"400": { "description": "Invalid / not finalized" },
|
||||
"502": { "description": "Broadcast failed" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/lp/pools": {
|
||||
"get": {
|
||||
"summary": "Uniswap v3 LP: куриные пулы (TVL, цена, APR)",
|
||||
"description": "Список курируемых пулов: token0/token1 + символы + decimals, fee-tier, текущая цена (token1 per token0, human), текущий tick, TVL/volume/APR из subgraph (null если недоступен). Цену отсюда используй для задания диапазона в /lp/quote.",
|
||||
"tags": ["Liquidity"],
|
||||
"parameters": [ { "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH"] } } ],
|
||||
"responses": {
|
||||
"200": { "description": "Pools", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "array", "items": { "type": "object" } } } } } } },
|
||||
"403": { "description": "No ETH wallet" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/lp/quote": {
|
||||
"post": {
|
||||
"summary": "LP preview: тики + amount0/amount1 + APR + наша комиссия 0.7% (native+USD) + проверка баланса",
|
||||
"description": "По [priceLower, priceUpper] и amount0Desired/amount1Desired считает тики, фактические суммы, slippage-мины, APR. ДОБАВЛЕНО: нашу app-комиссию 0.7% по каждой ноге (в токене + USD + ETH-эквивалент, всё в обычных числах) и проверку баланса (хватает ли token0/token1 на взнос И ETH на газ — с учётом useNativeEth для WETH-ноги). Баланс считается по кошельку юзера (JWT). Ничего не подписывает.",
|
||||
"tags": ["Liquidity"],
|
||||
"parameters": [ { "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH"] } } ],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"required": ["pool", "priceLower", "priceUpper", "amount0Desired", "amount1Desired"],
|
||||
"properties": {
|
||||
"pool": { "type": "string", "description": "адрес куриного пула" },
|
||||
"priceLower": { "type": "number" },
|
||||
"priceUpper": { "type": "number" },
|
||||
"amount0Desired": { "type": "string", "description": "smallest units token0" },
|
||||
"amount1Desired": { "type": "string", "description": "smallest units token1" },
|
||||
"slippageBps": { "type": "integer", "description": "0..5000 (default 100)" },
|
||||
"useNativeEth": { "type": "boolean", "description": "WETH-нога покрывается нативным ETH (авто-wrap) — влияет на проверку баланса (проверяет ETH вместо WETH)" }
|
||||
}
|
||||
} } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Quote (+ комиссия + баланс)", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object", "properties": {
|
||||
"poolAddress": { "type": "string" },
|
||||
"tickLower": { "type": "integer" },
|
||||
"tickUpper": { "type": "integer" },
|
||||
"amount0DesiredHuman": { "type": "string" },
|
||||
"amount1DesiredHuman": { "type": "string" },
|
||||
"liquidity": { "type": "string" },
|
||||
"aprPercent": { "type": "number", "nullable": true },
|
||||
"appFee0Human": { "type": "string", "description": "0.7% от token0-ноги (в токене, обычное число)" },
|
||||
"appFee1Human": { "type": "string", "description": "0.7% от token1-ноги" },
|
||||
"appFee0Usd": { "type": "number", "nullable": true },
|
||||
"appFee1Usd": { "type": "number", "nullable": true },
|
||||
"appFeeTotalUsd": { "type": "number", "nullable": true },
|
||||
"appFeeTotalEth": { "type": "string", "nullable": true, "description": "суммарная комиссия в монете сети (ETH)" },
|
||||
"balance": {
|
||||
"type": "object",
|
||||
"description": "есть только если у юзера есть ETH-кошелёк",
|
||||
"properties": {
|
||||
"token0": { "type": "object", "properties": { "symbol": { "type": "string" }, "have": { "type": "string", "nullable": true }, "need": { "type": "string" }, "sufficient": { "type": "boolean", "nullable": true }, "shortfall": { "type": "string" } } },
|
||||
"token1": { "type": "object", "properties": { "symbol": { "type": "string" }, "have": { "type": "string", "nullable": true }, "need": { "type": "string" }, "sufficient": { "type": "boolean", "nullable": true }, "shortfall": { "type": "string" } } },
|
||||
"gas": { "type": "object", "properties": { "symbol": { "type": "string" }, "have": { "type": "string", "nullable": true }, "needReserve": { "type": "string" }, "sufficient": { "type": "boolean", "nullable": true } } },
|
||||
"sufficient": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
} } } } } } },
|
||||
"400": { "description": "Invalid input / pool not curated" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/lp/add": {
|
||||
"post": {
|
||||
"summary": "LP add: fee + approve×2 + NFPM.mint(), один вызов",
|
||||
"description": "Сервер отнимает app-fee 0.7% по каждому токену, аппрувит оба на NonfungiblePositionManager и минтит позицию в заданном диапазоне. Возвращает tokenId + txids. Оба токена — ERC20 (WETH как ERC20). JWT-bind + Idempotency-Key.",
|
||||
"tags": ["Liquidity"],
|
||||
"parameters": [
|
||||
{ "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH"] } },
|
||||
{ "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"required": ["pool", "priceLower", "priceUpper", "amount0Desired", "amount1Desired"],
|
||||
"properties": {
|
||||
"pool": { "type": "string" },
|
||||
"priceLower": { "type": "number" },
|
||||
"priceUpper": { "type": "number" },
|
||||
"amount0Desired": { "type": "string" },
|
||||
"amount1Desired": { "type": "string" },
|
||||
"slippageBps": { "type": "integer" },
|
||||
"useNativeEth": { "type": "boolean", "description": "если пул с WETH-ногой — завернуть нативный ETH в WETH автоматически (нужен ETH на балансе вместо WETH)" }
|
||||
}
|
||||
} } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Minted", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object" } } } } } },
|
||||
"400": { "description": "Invalid input" },
|
||||
"403": { "description": "No ETH wallet" },
|
||||
"502": { "description": "Broadcast failed" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/lp/remove": {
|
||||
"post": {
|
||||
"summary": "LP remove: decreaseLiquidity + collect (+ burn), один вызов",
|
||||
"description": "Убирает percent% ликвидности позиции tokenId, собирает тело + накопленные комиссии юзеру (multicall). При percent=100 — также burn NFT. Проверка ownerOf(tokenId)=юзер. JWT-bind + Idempotency-Key.",
|
||||
"tags": ["Liquidity"],
|
||||
"parameters": [
|
||||
{ "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH"] } },
|
||||
{ "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"required": ["tokenId"],
|
||||
"properties": {
|
||||
"tokenId": { "type": "string" },
|
||||
"percent": { "type": "number", "description": "(0,100], default 100" }
|
||||
}
|
||||
} } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Removed", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object" } } } } } },
|
||||
"400": { "description": "Invalid input / not owner" },
|
||||
"502": { "description": "Broadcast failed" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/lp/positions": {
|
||||
"get": {
|
||||
"summary": "LP positions: диапазон, тек. стоимость, накопленные комиссии",
|
||||
"description": "Читает live из чейна: NFT-позиции юзера в куриных пулах. Поля: tokenId, диапазон (тики+цены), liquidity, текущие amount0/amount1, накопленные комиссии (feesOwed0/1), inRange. Без БД.",
|
||||
"tags": ["Liquidity"],
|
||||
"parameters": [ { "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH"] } } ],
|
||||
"responses": {
|
||||
"200": { "description": "Positions", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object" } } } } } },
|
||||
"403": { "description": "No ETH wallet" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/approvals": {
|
||||
"get": {
|
||||
"summary": "Скан открытых ERC20-allowance кошелька (видимость drain-векторов)",
|
||||
"description": "Read-only: читает on-chain allowance(owner, spender) по known-токенам (USDT/USDC/DAI/WBTC/WETH…) × known-спендерам (Relay Router v1/v2, Relay Depository, Permit2, Uniswap NFPM). Возвращает только НЕнулевые allowance с флагом `unlimited` (allowance > 2^200, т.е. MAX_UINT256). Открытый unlimited-approval = открытая дверь, через которую солвер может вывести будущие поступления токена — отзови его через POST /approvals/revoke.",
|
||||
"tags": ["Wallet Ops"],
|
||||
"parameters": [ { "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH", "BSC"] } } ],
|
||||
"responses": {
|
||||
"200": { "description": "Открытые allowance", "content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": { "type": "boolean" },
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chain": { "type": "string" },
|
||||
"owner": { "type": "string" },
|
||||
"approvals": { "type": "array", "items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": { "type": "string", "example": "USDT" },
|
||||
"tokenAddress": { "type": "string" },
|
||||
"spender": { "type": "string", "example": "Relay Router v2" },
|
||||
"spenderAddress": { "type": "string" },
|
||||
"allowance": { "type": "string", "description": "smallest units" },
|
||||
"allowanceHuman": { "type": "string", "description": "'unlimited' либо человекочитаемое количество" },
|
||||
"unlimited": { "type": "boolean" }
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
} } } },
|
||||
"400": { "description": "Approvals только ETH/BSC" },
|
||||
"403": { "description": "Нет кошелька для chain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/approvals/revoke": {
|
||||
"post": {
|
||||
"summary": "Custodial-отзыв ERC20-approval (approve(spender, 0))",
|
||||
"description": "Подписывает и броадкастит `token.approve(spender, 0)` кошельком пользователя (custodial — ключ на сервере). **amount захардкожен в 0** — endpoint может ТОЛЬКО снизить allowance, никогда не выдать (не attack surface). Останавливает утечку поступлений через повисший unlimited-approval (напр. USDT → Relay Router v2). Если allowance уже 0 — отвечает `alreadyZero:true` без транзакции (газ не тратится). JWT-bind + Idempotency-Key + audit.",
|
||||
"tags": ["Wallet Ops"],
|
||||
"parameters": [
|
||||
{ "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH", "BSC"] } },
|
||||
{ "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"required": ["token", "spender"],
|
||||
"properties": {
|
||||
"token": { "type": "string", "description": "ERC20-контракт, у которого отзываем approval", "example": "0xdAC17F958D2ee523a2206206994597C13D831ec7" },
|
||||
"spender": { "type": "string", "description": "адрес спендера (напр. Relay Router v2)", "example": "0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f" }
|
||||
}
|
||||
} } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Отозвано (txid) либо allowance уже был 0 (alreadyZero)", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object", "properties": { "chain": { "type": "string" }, "token": { "type": "string" }, "spender": { "type": "string" }, "txid": { "type": "string", "nullable": true }, "alreadyZero": { "type": "boolean" }, "previousAllowance": { "type": "string", "nullable": true } } } } } } } },
|
||||
"400": { "description": "Невалидный token/spender или chain ≠ ETH/BSC" },
|
||||
"403": { "description": "Нет кошелька для chain" },
|
||||
"502": { "description": "Broadcast failed" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/approvals/revoke-all": {
|
||||
"post": {
|
||||
"summary": "«Отвязка от всего» — отозвать ВСЕ открытые approval одним действием",
|
||||
"description": "Сервер сканирует все открытые (ненулевые) allowance кошелька (Relay v1/v2, Depository, Permit2, Uniswap NFPM × известные токены) и подписывает `approve(spender, 0)` по каждой открытой двери — последовательно (ждёт 1 conf, корректный nonce). **amount всегда 0** — только отзыв, не выдача. По умолчанию отзывает ВСЕ ненулевые; `onlyUnlimited:true` — лишь unlimited/oversized (реальный drain-вектор). Если открытых дверей нет — `total:0` без транзакций. При нехватке газа отзывает сколько успел, остаток помечает `skipped` (пополни нативку и повтори). Нужно ~0.0003 native газа на каждую дверь. JWT-bind + Idempotency-Key + audit.",
|
||||
"tags": ["Wallet Ops"],
|
||||
"parameters": [
|
||||
{ "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH", "BSC"] } },
|
||||
{ "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": false,
|
||||
"content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"onlyUnlimited": { "type": "boolean", "description": "true → отозвать только unlimited/oversized approval. По умолчанию false (все ненулевые).", "default": false }
|
||||
}
|
||||
} } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Результат отзыва по каждой двери", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object", "properties": {
|
||||
"chain": { "type": "string" },
|
||||
"owner": { "type": "string" },
|
||||
"total": { "type": "integer", "description": "сколько открытых дверей нашлось" },
|
||||
"revoked": { "type": "array", "items": { "type": "object", "properties": { "token": { "type": "string" }, "spender": { "type": "string" }, "tokenAddress": { "type": "string" }, "spenderAddress": { "type": "string" }, "txid": { "type": "string" }, "previousAllowance": { "type": "string" } } } },
|
||||
"failed": { "type": "array", "items": { "type": "object", "properties": { "token": { "type": "string" }, "spender": { "type": "string" }, "error": { "type": "string" } } } },
|
||||
"skipped": { "type": "array", "items": { "type": "object", "properties": { "token": { "type": "string" }, "spender": { "type": "string" }, "reason": { "type": "string" } } } }
|
||||
} } } } } } },
|
||||
"400": { "description": "chain ≠ ETH/BSC" },
|
||||
"403": { "description": "Нет кошелька для chain" },
|
||||
"502": { "description": "Scan failed / fatal" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"summary": "Liveness check",
|
||||
@@ -2816,6 +3385,61 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wallets/{chain}/op-cost": {
|
||||
"post": {
|
||||
"summary": "Стоимость операции (свап/бридж): наша 0.7% в native+USD + проверка баланса токена/газа",
|
||||
"description": "Read-only. По входному токену и сумме возвращает (всё в ОБЫЧНЫХ числах, не decimals): нашу комиссию 0.7% в самом токене, эквивалент в native-монете сети и в USD; и проверку баланса — хватает ли входного токена на операцию И нативной монеты на газ. Без mnemonic/broadcast/БД, JWT-bound (только свой кошелёк). Сетевую/gas-комиссию даёт swap/cost-estimate либо relay quote — здесь не дублируется. Если входной токен = native, токен-нога считается по нативному балансу, а газ-нога требует сумму + резерв.",
|
||||
"tags": ["Wallet Ops"],
|
||||
"parameters": [
|
||||
{ "name": "chain", "in": "path", "required": true, "schema": { "type": "string", "enum": ["ETH", "BSC", "SOL", "TRX"] } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": { "type": "string", "description": "Символ входного токена (USDT/USDC/…). Пусто или символ нативной монеты = нативная (ETH/BNB/SOL/TRX).", "example": "USDT" },
|
||||
"amount": { "type": "string", "description": "Сумма в smallest units (legacy). Используй ЛИБО amount, ЛИБО amountHuman." },
|
||||
"amountHuman": { "type": "string", "description": "Сумма в обычном виде (напр. \"10\").", "example": "10" }
|
||||
}
|
||||
} } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Стоимость операции + достаточность баланса (human-числа)", "content": { "application/json": { "schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": { "type": "boolean" },
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chain": { "type": "string" },
|
||||
"input": { "type": "object", "properties": { "token": { "type": "string" }, "amount": { "type": "string" } } },
|
||||
"appCommission": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ratePercent": { "type": "number", "example": 0.7 },
|
||||
"inToken": { "type": "object", "properties": { "symbol": { "type": "string" }, "amount": { "type": "string", "example": "0.07" } } },
|
||||
"inNative": { "type": "object", "properties": { "symbol": { "type": "string", "example": "ETH" }, "amount": { "type": "string", "nullable": true, "example": "0.0000218" } } },
|
||||
"usd": { "type": "number", "nullable": true, "example": 0.07 }
|
||||
}
|
||||
},
|
||||
"balance": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": { "type": "object", "properties": { "symbol": { "type": "string" }, "have": { "type": "string", "nullable": true }, "need": { "type": "string" }, "sufficient": { "type": "boolean", "nullable": true }, "shortfall": { "type": "string" } } },
|
||||
"gas": { "type": "object", "properties": { "symbol": { "type": "string" }, "have": { "type": "string", "nullable": true }, "needReserve": { "type": "string" }, "sufficient": { "type": "boolean", "nullable": true } } }
|
||||
}
|
||||
},
|
||||
"sufficient": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
}
|
||||
} } } },
|
||||
"400": { "description": "Невалидный token/amount или chain не ETH/BSC/SOL/TRX" },
|
||||
"404": { "description": "Нет кошелька для chain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/relay/cost-estimate": {
|
||||
"post": {
|
||||
"summary": "Estimate USD cost of a bridge (Relay quote — trimmed, без steps[])",
|
||||
|
||||
Reference in New Issue
Block a user