deploy: POST /api/wallets + full swagger

This commit is contained in:
ZOMBIIIIIII
2026-05-03 20:01:58 +03:00
parent 59a7d1d9ca
commit 295c3a9d6d
27 changed files with 1994 additions and 430 deletions

View File

@@ -56,6 +56,12 @@ async function getQuote(req: Request, res: Response) {
return;
}
const parsedSlippage = parseInt(String(slippageBps), 10);
if (!Number.isFinite(parsedSlippage) || parsedSlippage < 1 || parsedSlippage > 500) {
res.status(400).json({ success: false, error: 'slippageBps must be 1-500 (max 5%)' });
return;
}
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), JUPITER_TIMEOUT_MS);

View File

@@ -193,15 +193,55 @@ async function createTransaction(req: Request, res: Response) {
}
}
// Whitelist contracts and functions accepted via /triggersmartcontract.
// Defence in depth: иначе клиент мог бы вызвать любой контракт (e.g. drain pool).
const ALLOWED_TRC_CONTRACTS = new Set<string>([
USDT_CONTRACT, // USDT TRC20
'TKzxdSv2FZKQrEqkKVgp5DcwEXBEKMg2Ax', // SunSwap router
'TX8E6X7X1FWYRYuYR2LTvS7zm1KchcVs5E', // FeeSwapRouter_TRX
'TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR', // WTRX
]);
const ALLOWED_TRC_FUNCTIONS = new Set<string>([
'transfer(address,uint256)',
'approve(address,uint256)',
'balanceOf(address)',
'allowance(address,address)',
'swapExactETHForTokens(uint256,address[],address,uint256)',
'swapExactTokensForETH(uint256,uint256,address[],address,uint256)',
'swapNativeWithFee(bytes)',
'swapTokenWithFee(address,uint256,bytes)',
'getAmountsOut(uint256,address[])',
]);
/**
* POST /api/tron/triggersmartcontract
* Proxies to TronGrid /wallet/triggersmartcontract — builds unsigned transaction
* Proxies to TronGrid /wallet/triggersmartcontract — builds unsigned transaction.
* Whitelisted contracts + function selectors only.
*/
async function triggerSmartContract(req: Request, res: Response) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), TRON_TIMEOUT_MS);
try {
const body = req.body ?? {};
const contractAddress = String(body.contract_address || '');
const functionSelector = String(body.function_selector || '');
const ownerAddress = String(body.owner_address || '');
if (!ALLOWED_TRC_CONTRACTS.has(contractAddress)) {
res.status(403).json({ success: false, error: 'Contract address not allowed' });
return;
}
if (!ALLOWED_TRC_FUNCTIONS.has(functionSelector)) {
res.status(403).json({ success: false, error: 'Function selector not allowed' });
return;
}
if (!TRON_ADDRESS_RE.test(ownerAddress)) {
res.status(400).json({ success: false, error: 'Invalid owner_address' });
return;
}
const headers: Record<string, string> = {
'Content-Type': 'application/json',
Accept: 'application/json',

View File

@@ -0,0 +1,9 @@
import { Router } from 'express';
import { VaultController } from '../controllers/vault.controller';
const router = Router();
router.get('/', VaultController.getVault);
router.put('/', VaultController.putVault);
export default router;

View File

@@ -4,5 +4,10 @@ import { WalletController } from '../controllers/wallet.controller';
const router = Router();
router.get('/', WalletController.getWallets);
router.post('/', WalletController.createWallets);
router.get('/:chain/balance', WalletController.getChainBalance);
router.get('/:chain/transactions', WalletController.getChainTransactions);
router.post('/:chain/send', WalletController.sendFromChain);
export default router;