baluni-api
Version:
Api for baluni-cli
351 lines (350 loc) • 16 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// server.ts
const express_1 = __importDefault(require("express"));
const constants_1 = require("./constants");
const uniswap_1 = require("./uniswap");
const ethers_1 = require("ethers");
const vault_1 = require("./yearn/vault");
const fetchToken_1 = require("./utils/uniswap/fetchToken");
const cors_1 = __importDefault(require("cors"));
const odos_1 = require("./odos");
const body_parser_1 = __importDefault(require("body-parser"));
const smart_order_router_1 = require("@uniswap/smart-order-router");
const sdk_1 = require("@uniswap/sdk");
const sdk_core_1 = require("@uniswap/sdk-core");
const parseToken_1 = require("./utils/uniswap/parseToken");
const app = (0, express_1.default)();
const port = 3001;
const CONFIGURATIONS = {
protocols: constants_1.PROTOCOLS,
oracle: constants_1.ORACLE,
nativeTokens: constants_1.NATIVETOKENS,
networks: constants_1.NETWORKS,
};
app.use((0, cors_1.default)());
app.use(body_parser_1.default.json());
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/:chainId/uni-v3/tokens', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
try {
const response = yield fetch(constants_1.TOKENS_URL);
const data = yield response.json();
const { chainId } = req.params;
const filteredTokens = data.tokens.filter((token) => token.chainId === Number(chainId));
res.json(filteredTokens);
}
catch (error) {
res.status(500).json({ message: 'Failed to fetch tokens', error: error });
}
}));
app.get('/:chainId/uni-v3/tokens/:tokenSymbol', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { chainId, tokenSymbol } = req.params;
if (!chainId || !tokenSymbol) {
return res
.status(400)
.json({ error: 'Missing chainId or tokenName query parameter' });
}
try {
const response = yield fetch(constants_1.TOKENS_URL);
const data = yield response.json();
const matchingTokens = data.tokens.filter((token) => token.chainId === Number(chainId) &&
token.symbol.toLowerCase() === tokenSymbol.toString().toLowerCase());
if (matchingTokens.length === 0) {
return res.status(404).json({ error: 'Token not found' });
}
res.json(matchingTokens[0]); // Returns the first matching token, assuming names are unique per chainId
}
catch (error) {
res.status(500).json({ message: 'Failed to fetch tokens', error: error });
}
}));
app.get('/:chainId/yearn-v3/vaults/:tokenSymbol', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { tokenSymbol, chainId } = req.params;
const { strategyType, boosted } = req.query;
const apiURL = `https://ydaemon.yearn.fi/${chainId}/vaults/all`;
try {
const response = yield fetch(apiURL);
const data = yield response.json();
let filteredVaults = data.filter(vault => {
var _a;
const matchesSymbol = vault.token.symbol.toLowerCase() === tokenSymbol.toLowerCase();
const isVersion3 = ((_a = vault.version) === null || _a === void 0 ? void 0 : _a.startsWith('3.0')) ||
vault.name.includes('3.0') ||
vault.symbol.includes('3.0');
let matchesStrategyType = true;
let matchesBoosted = true;
if (strategyType === 'multi') {
matchesStrategyType = vault.kind === 'Multi Strategy';
}
else if (strategyType === 'single') {
matchesStrategyType = vault.kind !== 'Multi Strategy';
}
// Check if boosted filter is applied
if (boosted === 'true') {
matchesBoosted = vault.boosted === true;
}
return (matchesSymbol && isVersion3 && matchesStrategyType && matchesBoosted);
});
if (filteredVaults.length === 0) {
return res
.status(404)
.json({ error: 'Vault not found for the given criteria' });
}
const vault = filteredVaults[0];
res.json({
vaultAddress: vault.address,
vaultName: vault.name,
vaultSymbol: vault.symbol,
tokenAddress: vault.token.address,
tokenName: vault.token.name,
tokenSymbol: vault.token.symbol,
strategyType: vault.kind,
version: vault.version,
boosted: vault.boosted, // Include boosted status in the response
});
}
catch (error) {
console.error('Failed to fetch Yearn Finance vaults:', error);
res.status(500).json({ error: 'Failed to fetch Yearn Finance vaults' });
}
}));
app.get('/:chainId/yearn-v3/vaults', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { chainId } = req.params;
const apiURL = `https://ydaemon.yearn.fi/${chainId}/vaults/all`;
try {
const response = yield fetch(apiURL);
const data = yield response.json();
res.json(data.map(vault => ({
vaultAddress: vault.address,
vaultName: vault.name,
vaultSymbol: vault.symbol,
tokenAddress: vault.token.address,
tokenName: vault.token.name,
tokenSymbol: vault.token.symbol,
})));
}
catch (error) {
console.error('Failed to fetch Yearn Finance vaults:', error);
res.status(500).json({ error: 'Failed to fetch Yearn Finance vaults.' });
}
}));
app.get('/config/:chainId/:protocolName/:contractName', (req, res) => {
var _a, _b, _c;
const { chainId, contractName, protocolName } = req.params;
const config = (_c = (_b = (_a = CONFIGURATIONS['protocols']) === null || _a === void 0 ? void 0 : _a[chainId]) === null || _b === void 0 ? void 0 : _b[protocolName]) === null || _c === void 0 ? void 0 : _c[contractName.toUpperCase()];
if (!config) {
return res
.status(404)
.json({ error: 'Configuration not found for the given parameters' });
}
res.json({ chainId, contractName, address: config });
});
app.get('/:chainId/uni-v3/swap/:address/:token0/:token1/:reverse/:amount/:slippage', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { address, token0, token1, reverse, chainId, amount, slippage } = req.params;
try {
const tokenAAddress = yield (0, fetchToken_1.fetchTokenAddressByName)(token0, Number(chainId));
const tokenBAddress = yield (0, fetchToken_1.fetchTokenAddressByName)(token1, Number(chainId));
const wallet = new ethers_1.ethers.Wallet(process.env.PRIVATE_KEY, new ethers_1.ethers.providers.JsonRpcProvider(constants_1.NETWORKS[chainId]));
const swapResult = yield (0, uniswap_1.buildSwap)(wallet, address, tokenAAddress, tokenBAddress, Boolean(reverse), 'uni-v3', chainId, amount, Number(slippage));
console.log('Swap result:', swapResult);
res.json({
Approvals: swapResult.Approvals,
Calldatas: swapResult.Calldatas,
TokensReturn: swapResult.TokensReturn,
});
}
catch (error) {
console.error('Error during swap operation:', error);
res.status(500).json({ error: 'Error during swap operation' });
}
}));
app.get('/:chainId/yearn-v3/deposit/:tokenSymbol/:strategy/:boosted/:amount/:receiver/', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
try {
const { tokenSymbol, strategy, amount, receiver, chainId, boosted } = req.params;
// Ora `config` è del tipo corretto
const filteredVaults = yield fetchYearnVaultsData(Number(chainId));
filteredVaults
.filter(vault => {
var _a;
const matchesSymbol = vault.token.symbol.toLowerCase() === tokenSymbol.toLowerCase();
const isVersion3 = ((_a = vault.version) === null || _a === void 0 ? void 0 : _a.startsWith('3.0')) ||
vault.name.includes('3.0') ||
vault.symbol.includes('3.0');
let matchesStrategyType = true;
let matchesBoosted = true;
if (strategy === 'multi') {
matchesStrategyType = vault.kind === 'Multi Strategy';
}
else if (strategy === 'single') {
matchesStrategyType = vault.kind !== 'Multi Strategy';
}
if (boosted === 'true') {
matchesBoosted = vault.boosted === true;
}
return (matchesSymbol && isVersion3 && matchesStrategyType && matchesBoosted);
})
.map(vault => vault.address);
const vaultAddress = filteredVaults[0];
const tokenAddress = yield (0, fetchToken_1.fetchTokenAddressByName)(tokenSymbol, Number(chainId));
const walletInstance = new ethers_1.ethers.Wallet(process.env.PRIVATE_KEY, new ethers_1.ethers.providers.JsonRpcProvider(constants_1.NETWORKS[chainId]));
let adjAmount;
if (tokenSymbol === 'USDC' ||
tokenSymbol === 'USDT' ||
tokenSymbol === 'USDC.E') {
adjAmount = ethers_1.ethers.utils.parseUnits(amount, 6);
}
else if (tokenSymbol === 'WBTC') {
adjAmount = ethers_1.ethers.utils.parseUnits(amount, 8);
}
else {
adjAmount = ethers_1.ethers.utils.parseUnits(amount, 18);
}
const result = yield (0, vault_1.depositToYearn)(walletInstance, tokenAddress, vaultAddress.address, adjAmount, receiver, chainId);
res.json(result);
}
catch (error) {
console.error(error);
res.status(500).json({ error: error.toString() });
}
}));
app.get(':chainId/yearn-v3/redeem/:tokenSymbol/:strategy/:boosted/:amount/:receiver/', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
try {
const { tokenSymbol, strategy, amount, receiver, chainId, boosted } = req.params;
let adjAmount;
if (tokenSymbol === 'USDC' ||
tokenSymbol === 'USDT' ||
tokenSymbol === 'USDC.E') {
adjAmount = ethers_1.ethers.utils.parseUnits(amount, 6);
}
else if (tokenSymbol === 'WBTC') {
adjAmount = ethers_1.ethers.utils.parseUnits(amount, 8);
}
else {
adjAmount = ethers_1.ethers.utils.parseUnits(amount, 18);
}
const filteredVaults = yield fetchYearnVaultsData(Number(chainId));
filteredVaults
.filter(vault => {
var _a;
const matchesSymbol = vault.token.symbol.toLowerCase() === tokenSymbol.toLowerCase();
const isVersion3 = ((_a = vault.version) === null || _a === void 0 ? void 0 : _a.startsWith('3.0')) ||
vault.name.includes('3.0') ||
vault.symbol.includes('3.0');
let matchesStrategyType = true;
let matchesBoosted = true;
if (strategy === 'multi') {
matchesStrategyType = vault.kind === 'Multi Strategy';
}
else if (strategy === 'single') {
matchesStrategyType = vault.kind !== 'Multi Strategy';
}
if (boosted === 'true') {
matchesBoosted = vault.boosted === true;
}
return (matchesSymbol && isVersion3 && matchesStrategyType && matchesBoosted);
})
.map(vault => vault.address);
const vaultAddress = filteredVaults[0];
const walletInstance = new ethers_1.ethers.Wallet(process.env.PRIVATE_KEY, new ethers_1.ethers.providers.JsonRpcProvider(constants_1.NETWORKS[chainId]));
const result = yield (0, vault_1.redeemFromYearn)(walletInstance, vaultAddress.address, adjAmount, receiver, chainId);
res.json(result);
}
catch (error) {
console.error(error);
res.status(500).json({ error: error.toString() });
}
}));
app.post('/:chainId/odos/swap', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { address, inputTokens, outputTokens, slippage } = req.body;
const { chainId } = req.params;
try {
const inputTokensArray = inputTokens.map((token) => ({
tokenAddress: token.tokenAddress,
amount: token.amount,
}));
const outputTokensArray = outputTokens.map((token) => ({
tokenAddress: token.tokenAddress,
proportion: token.proportion,
}));
const wallet = new ethers_1.ethers.Wallet(process.env.PRIVATE_KEY, new ethers_1.ethers.providers.JsonRpcProvider(constants_1.NETWORKS[chainId]));
const swapResult = yield (0, odos_1.buildSwapOdos)(wallet, address, chainId, inputTokensArray, outputTokensArray, Number(slippage), 3844415834, // referralCode
false, // disableRFQs
true // compact
);
console.log('Swap result:', swapResult);
res.json({
Approvals: swapResult.Approvals,
Calldatas: swapResult.Calldatas,
TokensReturn: swapResult.TokensReturn,
});
}
catch (error) {
console.error('Error during swap operation:', error);
res.status(500).json({ error: 'Error during swap operation' });
}
}));
app.post('/route', (req, res) => {
const reqBody = req.body;
const router = new smart_order_router_1.AlphaRouter({
chainId: reqBody.chainId,
provider: new ethers_1.ethers.providers.JsonRpcProvider(constants_1.NETWORKS[reqBody.chainId]),
});
const currencyAmount = (0, parseToken_1.parseToken)(reqBody.currencyAmount, reqBody.chainId);
const currency = (0, parseToken_1.parseToken)(reqBody.currency, reqBody.chainId);
const tradeType = reqBody.tradeType || sdk_1.TradeType.EXACT_INPUT;
const SLIPPAGE = new sdk_core_1.Percent(50, 10000); // Correct 15%
router
.route(smart_order_router_1.CurrencyAmount.fromRawAmount(currencyAmount, reqBody.amount), currency, tradeType, {
slippageTolerance: SLIPPAGE,
type: smart_order_router_1.SwapType.UNIVERSAL_ROUTER,
recipient: ethers_1.ethers.constants.AddressZero,
deadlineOrPreviousBlockhash: Math.floor(Date.now() / 1000) + 360,
}, {
minSplits: 3,
})
.then(route => {
if (!route) {
res.status(404);
res.send('No route found');
return;
}
res.json(route);
})
.catch(err => {
res.status(500);
res.send('Internal server error');
console.log(err);
});
});
// HELPER FUNCTIONS
function fetchYearnVaultsData(chainId) {
return __awaiter(this, void 0, void 0, function* () {
try {
const apiURL = `https://ydaemon.yearn.fi/${chainId}/vaults/all`;
const response = yield fetch(apiURL);
const data = yield response.json();
return data;
}
catch (error) {
console.error('Failed to fetch Yearn Finance vaults:', error);
return [];
}
});
}
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});