initiukyghv
This commit is contained in:
@@ -21,11 +21,43 @@ import { WalletModel } from '../models/wallet.model';
|
||||
import type { ChainCode } from '../lib/address-validators';
|
||||
import { proxiedFetch } from '../lib/outbound-proxy';
|
||||
import { getTokensForChains } from '../lib/token-registry';
|
||||
import { computeAppFee } from '../lib/app-fee';
|
||||
import { formatSmallestUnits } from '../lib/amount-units';
|
||||
|
||||
const router = Router();
|
||||
const LIFI_API_URL = 'https://li.quest/v1';
|
||||
const LIFI_TIMEOUT_MS = 20_000;
|
||||
|
||||
/**
|
||||
* Вычисляет нашу app-комиссию 0.7% (в токене + USDT) из LiFi-ответа (action.fromToken + fromAmount,
|
||||
* estimate.fromAmountUSD). Чисто информативно (показать в quote) — сбор не трогаем (0.7% берётся
|
||||
* атомарно в /bridge/execute), поэтому LiFi fee-параметры НЕ инжектим (иначе двойное списание).
|
||||
*/
|
||||
function computeJumperAppCommission(body: any): {
|
||||
ratePercent: number;
|
||||
inToken: { symbol: string | null; amount: string | null };
|
||||
usd: number | null;
|
||||
} | null {
|
||||
const act = body?.action;
|
||||
const est = body?.estimate;
|
||||
const fromToken = act?.fromToken;
|
||||
const fromAmount = act?.fromAmount ?? est?.fromAmount;
|
||||
if (!fromToken || fromAmount == null) return null;
|
||||
let amountToken: string | null = null;
|
||||
try {
|
||||
const dec = Number(fromToken.decimals ?? 0);
|
||||
const feeSmallest = computeAppFee(String(fromAmount)); // fromAmount × 0.7%
|
||||
amountToken = formatSmallestUnits(feeSmallest.toString(), dec);
|
||||
} catch { amountToken = null; }
|
||||
const fromUsd = Number(est?.fromAmountUSD);
|
||||
const usd = Number.isFinite(fromUsd) ? Math.round(fromUsd * 0.007 * 1e6) / 1e6 : null;
|
||||
return {
|
||||
ratePercent: 0.7,
|
||||
inToken: { symbol: fromToken.symbol ?? null, amount: amountToken },
|
||||
usd,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* LiFi chainIds → наш ChainCode. LiFi использует custom IDs для не-EVM:
|
||||
* - SOL: 1151111081099710 (КАРДИНАЛЬНО отличается от Relay's 792703809)
|
||||
@@ -213,6 +245,17 @@ async function proxyJumperRequest(req: Request, res: Response, _next: NextFuncti
|
||||
}
|
||||
|
||||
try {
|
||||
// /quote — доинжектим вычисляемую appCommission (наша 0.7% в токене + USDT) для видимости.
|
||||
if (jumperPath === '/quote') {
|
||||
try {
|
||||
const obj = JSON.parse(text);
|
||||
obj.appCommission = computeJumperAppCommission(obj);
|
||||
res.send(JSON.stringify(obj));
|
||||
} catch {
|
||||
res.send(text); // не JSON — passthrough
|
||||
}
|
||||
return;
|
||||
}
|
||||
const filtered = filterJumperMetadata(jumperPath, text);
|
||||
if (filtered) {
|
||||
res.json(filtered);
|
||||
@@ -426,7 +469,7 @@ async function handleQuoteBest(req: Request, res: Response): Promise<void> {
|
||||
// Step 1 — NearIntents only.
|
||||
const nearRes = await tryLiFiQuote({ key: 'allowBridges', value: 'near' });
|
||||
if (nearRes.ok && nearRes.body && (nearRes.body.estimate || nearRes.body.action)) {
|
||||
res.status(200).json({ ...nearRes.body, _source: 'near' });
|
||||
res.status(200).json({ ...nearRes.body, _source: 'near', appCommission: computeJumperAppCommission(nearRes.body) });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -435,7 +478,7 @@ async function handleQuoteBest(req: Request, res: Response): Promise<void> {
|
||||
// Step 2 — fallback на любой best route.
|
||||
const bestRes = await tryLiFiQuote();
|
||||
if (bestRes.ok && bestRes.body && (bestRes.body.estimate || bestRes.body.action)) {
|
||||
res.status(200).json({ ...bestRes.body, _source: 'best' });
|
||||
res.status(200).json({ ...bestRes.body, _source: 'best', appCommission: computeJumperAppCommission(bestRes.body) });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,40 @@ import { WalletModel } from '../models/wallet.model';
|
||||
import type { ChainCode } from '../lib/address-validators';
|
||||
import { indexRelayExecuteResponse } from '../lib/relay-trusted-cache';
|
||||
import { proxiedFetch } from '../lib/outbound-proxy';
|
||||
import { getDecimalsByContract, parseHumanAmount } from '../lib/amount-units';
|
||||
import { getDecimalsByContract, parseHumanAmount, formatSmallestUnits } from '../lib/amount-units';
|
||||
import { computeAppFee } from '../lib/app-fee';
|
||||
|
||||
const router = Router();
|
||||
const RELAY_API_URL = 'https://api.relay.link';
|
||||
const RELAY_TIMEOUT_MS = 20_000;
|
||||
|
||||
/**
|
||||
* Вычисляет нашу app-комиссию 0.7% (в токене + USDT) из Relay-ответа `details.currencyIn`.
|
||||
* Чисто информативно (показать в quote/cost-estimate) — сбор комиссии не трогаем (отдельная /app-fee tx),
|
||||
* поэтому НЕ инжектим Relay appFees (иначе было бы двойное списание).
|
||||
*/
|
||||
function computeRelayAppCommission(full: any): {
|
||||
ratePercent: number;
|
||||
inToken: { symbol: string | null; amount: string | null };
|
||||
usd: number | null;
|
||||
} | null {
|
||||
const cIn = full?.details?.currencyIn;
|
||||
if (!cIn || cIn.amount == null) return null;
|
||||
let amountToken: string | null = null;
|
||||
try {
|
||||
const dec = Number(cIn.currency?.decimals ?? 0);
|
||||
const feeSmallest = computeAppFee(String(cIn.amount)); // amount × 0.7%
|
||||
amountToken = formatSmallestUnits(feeSmallest.toString(), dec);
|
||||
} catch { amountToken = null; }
|
||||
const inUsd = Number(cIn.amountUsd);
|
||||
const usd = Number.isFinite(inUsd) ? Math.round(inUsd * 0.007 * 1e6) / 1e6 : null;
|
||||
return {
|
||||
ratePercent: 0.7,
|
||||
inToken: { symbol: cIn.currency?.symbol ?? null, amount: amountToken },
|
||||
usd,
|
||||
};
|
||||
}
|
||||
|
||||
// chainId → ChainCode. Relay использует EVM chainIds + custom большие для не-EVM.
|
||||
const RELAY_CHAINID_TO_CHAIN: Record<number, ChainCode> = {
|
||||
1: 'ETH',
|
||||
@@ -235,6 +263,8 @@ async function proxyRelayRequest(req: Request, res: Response, next: NextFunction
|
||||
app: fees.app ?? null,
|
||||
total: { amountUsd: totalUsd },
|
||||
},
|
||||
// Наша app-комиссия 0.7% в токене + USDT (информативно; собирается отдельной /app-fee tx).
|
||||
appCommission: computeRelayAppCommission(full),
|
||||
rate: full?.details?.rate ?? null,
|
||||
priceImpactPct: full?.details?.totalImpact?.percent ?? null,
|
||||
priceImpactUsd: full?.details?.totalImpact?.usd ?? null,
|
||||
@@ -250,6 +280,19 @@ async function proxyRelayRequest(req: Request, res: Response, next: NextFunction
|
||||
return;
|
||||
}
|
||||
|
||||
// /quote — доинжектим вычисляемую appCommission (наша 0.7% в токене + USDT) для видимости в Swagger.
|
||||
// Остальное (steps[], fees, details) проходит без изменений. Сбор комиссии не меняется.
|
||||
if (relayPath === '/quote') {
|
||||
try {
|
||||
const obj = JSON.parse(text);
|
||||
obj.appCommission = computeRelayAppCommission(obj);
|
||||
res.send(JSON.stringify(obj));
|
||||
} catch {
|
||||
res.send(text); // не JSON — passthrough как есть
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Send raw text если это валидный JSON, иначе обернём
|
||||
try {
|
||||
res.send(text);
|
||||
|
||||
@@ -885,6 +885,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"appCommission": {
|
||||
"type": "object",
|
||||
"nullable": true,
|
||||
"description": "Наша app-комиссия 0.7% (в токене + USDT). Информативно; собирается отдельной /app-fee tx. Считается из details.currencyIn. Есть и в /relay/quote.",
|
||||
"properties": {
|
||||
"ratePercent": { "type": "number", "example": 0.7 },
|
||||
"inToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"symbol": { "type": "string", "nullable": true, "example": "USDT" },
|
||||
"amount": { "type": "string", "nullable": true, "example": "0.7", "description": "0.7% во входном токене" }
|
||||
}
|
||||
},
|
||||
"usd": { "type": "number", "nullable": true, "example": 0.7, "description": "та же 0.7% в USDT (≈USD)" }
|
||||
}
|
||||
},
|
||||
"rate": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
@@ -2862,7 +2878,7 @@
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Quote с steps[], fees, details, breakdown"
|
||||
"description": "Quote с steps[], fees, details, breakdown + appCommission (наша 0.7% в токене+USDT, информативно — считается из details.currencyIn)"
|
||||
},
|
||||
"502": {
|
||||
"description": "Relay upstream error (приложен upstream JSON для деталей)"
|
||||
@@ -3921,7 +3937,7 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "LiFi quote + _source field (near|best)"
|
||||
"description": "LiFi quote + _source (near|best) + appCommission (наша 0.7% в токене + USDT; информативно, считается из action.fromToken/estimate.fromAmountUSD; сбор — атомарно в /bridge/execute). Пример appCommission: { ratePercent: 0.7, inToken: { symbol: \"USDT\", amount: \"0.7\" }, usd: 0.7 }"
|
||||
},
|
||||
"400": {
|
||||
"description": "Missing required params"
|
||||
|
||||
Reference in New Issue
Block a user