deploy: POST /api/wallets + full swagger
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
9
apps/api/src/routes/vault.routes.ts
Normal file
9
apps/api/src/routes/vault.routes.ts
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user