baluni-api
Version:
Api for baluni-cli
190 lines (189 loc) • 10.5 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 });
exports.route = exports.getBestQuote = exports.getBestQuoteForSwapPath = exports.getAdjAmount = void 0;
const ethers_1 = require("ethers");
const constants_1 = require("../../constants"); // Assuming these constants are defined correctly
const utils_1 = require("ethers/lib/utils");
const ERC20_json_1 = __importDefault(require("../../abis/common/ERC20.json"));
const Quoter_json_1 = __importDefault(require("../../abis/uniswap/Quoter.json"));
const Factory_json_1 = __importDefault(require("../../abis/uniswap/Factory.json"));
const Pool_json_1 = __importDefault(require("../../abis/uniswap/Pool.json"));
const smart_order_router_1 = require("@uniswap/smart-order-router");
const sdk_core_1 = require("@uniswap/sdk-core");
const parseToken_1 = require("./parseToken");
const router_sdk_1 = require("@uniswap/router-sdk");
const POOL_FEES = [100, 500, 3000, 10000];
const ZERO_ADDRESS = ethers_1.ethers.constants.AddressZero;
function logError(context, error) {
console.error(`Error in ${context}:`, error);
}
function getAdjAmount(_amount, _decimals) {
return (0, utils_1.parseUnits)(_amount, _decimals);
}
exports.getAdjAmount = getAdjAmount;
function findPoolAndFee(factoryContract, tokenIn, tokenOut) {
return __awaiter(this, void 0, void 0, function* () {
for (const poolFee of POOL_FEES) {
const poolAddress = yield getPoolAddress(factoryContract, tokenIn, tokenOut, poolFee);
if (poolAddress !== ZERO_ADDRESS)
return poolFee;
}
return 0;
});
}
function getPoolAddress(factoryContract, tokenIn, tokenOut, poolFee) {
return __awaiter(this, void 0, void 0, function* () {
const poolAddress = yield factoryContract.getPool(tokenIn, tokenOut, poolFee);
return poolAddress;
});
}
function getAmountOut(quoterContract, tokenIn, tokenOut, poolFee, amountIn, slippage) {
return __awaiter(this, void 0, void 0, function* () {
try {
const expectedAmountOut = yield quoterContract.callStatic.quoteExactInputSingle(tokenIn, tokenOut, poolFee, amountIn.toString(), 0);
return expectedAmountOut.mul(10000 - slippage).div(10000);
}
catch (error) {
logError('::API:: getAmountOut', error);
return ethers_1.BigNumber.from(0);
}
});
}
// Utilizza questa funzione per verificare la validità delle condizioni prima della chiamata
function isSwapPossible(factoryContract, tokenIn, tokenOut, poolFee) {
return __awaiter(this, void 0, void 0, function* () {
const poolAddress = yield factoryContract.getPool(tokenIn, tokenOut, poolFee);
return poolAddress !== ZERO_ADDRESS;
});
}
function getPoolData(poolContract, minRequiredLiquidity) {
return __awaiter(this, void 0, void 0, function* () {
const slot0 = yield poolContract.slot0();
const currentSqrtPriceX96 = slot0.sqrtPriceX96;
const currentTick = slot0.tick;
const tickSpacing = yield poolContract.tickSpacing();
const desiredTickLower = currentTick - (currentTick % tickSpacing);
const desiredTickUpper = desiredTickLower + tickSpacing;
const tickLowerData = yield poolContract.ticks(desiredTickLower);
const tickUpperData = yield poolContract.ticks(desiredTickUpper);
const liquidityInLowerTick = tickLowerData.liquidityGross;
const liquidityInUpperTick = tickUpperData.liquidityGross;
const isLiquiditySufficient = liquidityInLowerTick.gt(minRequiredLiquidity) &&
liquidityInUpperTick.gt(minRequiredLiquidity);
return isLiquiditySufficient;
});
}
function quoteSwapAmountOut(factoryContract, quoterContract, tokenIn, tokenOut, amountIn, slippageTolerance) {
return __awaiter(this, void 0, void 0, function* () {
const poolFee = yield findPoolAndFee(factoryContract, tokenIn, tokenOut);
if (poolFee === 0) {
console.log('Nessuna pool adatta trovata, evitando la chiamata che fallirebbe.');
return { amountOut: ethers_1.BigNumber.from(0), poolFee: 0 };
}
// Verifica se la swap è possibile prima di procedere
const swapPossible = yield isSwapPossible(factoryContract, tokenIn, tokenOut, poolFee);
if (!swapPossible) {
console.log('La swap non è possibile, evitando la chiamata che fallirebbe.');
return { amountOut: ethers_1.BigNumber.from(0), poolFee };
}
const amountOut = yield getAmountOut(quoterContract, tokenIn, tokenOut, poolFee, amountIn, slippageTolerance);
if (amountOut == ethers_1.BigNumber.from(0)) {
return { amountOut: ethers_1.BigNumber.from(0), poolFee };
}
const poolAddress = yield getPoolAddress(factoryContract, tokenIn, tokenOut, poolFee);
const poolContract = new ethers_1.Contract(poolAddress, Pool_json_1.default, quoterContract.provider);
const minimalLiquidity = getPoolData(poolContract, amountOut);
if (!minimalLiquidity) {
return { amountOut: ethers_1.BigNumber.from(0), poolFee };
}
return { amountOut, poolFee };
});
}
function getBestQuoteForSwapPath(factoryContract, quoterContract, tokenIn, tokenOut, amountIn, slippageTolerance, chainId) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
let bestQuote = yield quoteSwapAmountOut(factoryContract, quoterContract, tokenIn, tokenOut, amountIn, slippageTolerance);
let bestPath = [tokenIn, tokenOut];
// Check through intermediate tokens if direct path isn't the best
const intermediateTokens = [
(_a = constants_1.NATIVETOKENS[chainId]) === null || _a === void 0 ? void 0 : _a.WRAPPED,
constants_1.WETH[chainId],
constants_1.DAI[chainId],
constants_1.USDT[chainId],
constants_1.USDC[chainId],
].filter(token => token !== tokenIn && token !== tokenOut);
for (const intermediateToken of intermediateTokens) {
const toIntermediateQuote = yield quoteSwapAmountOut(factoryContract, quoterContract, tokenIn, intermediateToken, amountIn, slippageTolerance);
if (toIntermediateQuote.amountOut.isZero())
continue;
const fromIntermediateQuote = yield quoteSwapAmountOut(factoryContract, quoterContract, intermediateToken, tokenOut, toIntermediateQuote.amountOut, slippageTolerance);
if (fromIntermediateQuote.amountOut.gt(bestQuote.amountOut)) {
bestQuote = fromIntermediateQuote;
bestPath = [tokenIn, intermediateToken, tokenOut];
}
}
console.log('::DEBUG:: final bestQuote:', bestQuote);
console.log('::DEBUG:: final bestPath:', bestPath);
return {
amountOut: bestQuote.amountOut,
path: bestPath,
poolFees: [bestQuote.poolFee],
};
});
}
exports.getBestQuoteForSwapPath = getBestQuoteForSwapPath;
function getBestQuote(wallet, token0, token1, reverse, protocol, chainId, amount, slippage) {
return __awaiter(this, void 0, void 0, function* () {
const _protocol = constants_1.PROTOCOLS[chainId][protocol];
const quoter = String(_protocol.QUOTER);
const quoterContract = new ethers_1.Contract(quoter, Quoter_json_1.default, wallet);
const factory = String(_protocol.FACTORY);
const factoryContract = new ethers_1.Contract(factory, Factory_json_1.default, wallet);
const tokenAAddress = reverse ? token1 : token0;
const tokenAContract = new ethers_1.Contract(tokenAAddress, ERC20_json_1.default, wallet);
const tokenADecimals = yield tokenAContract.decimals();
const tokenBAddress = reverse ? token0 : token1;
const adjAmount = getAdjAmount(amount, tokenADecimals);
const bestQuote = yield getBestQuoteForSwapPath(factoryContract, quoterContract, tokenAAddress, tokenBAddress, adjAmount, slippage, chainId);
console.log('::DEBUG:: final bestQuote:', bestQuote);
return bestQuote;
});
}
exports.getBestQuote = getBestQuote;
function route(tradeRequest) {
return __awaiter(this, void 0, void 0, function* () {
const router = new smart_order_router_1.AlphaRouter({
chainId: tradeRequest.chainId,
provider: new ethers_1.ethers.providers.JsonRpcProvider(constants_1.NETWORKS[tradeRequest.chainId]),
});
const currencyAmount = (0, parseToken_1.parseToken)(tradeRequest.currencyAmount, tradeRequest.chainId);
const currency = (0, parseToken_1.parseToken)(tradeRequest.currency, tradeRequest.chainId);
const formatedSlippage = tradeRequest.slippage / 100; // 5000/100 = 50
const SLIPPAGE = new sdk_core_1.Percent(formatedSlippage, 10000); // Correct 15%
const routing = router.route(smart_order_router_1.CurrencyAmount.fromRawAmount(currencyAmount, Number(tradeRequest.amount)), currency, sdk_core_1.TradeType.EXACT_INPUT, {
slippageTolerance: SLIPPAGE,
type: smart_order_router_1.SwapType.SWAP_ROUTER_02, // SwapType.UNIVERSAL_ROUTER
recipient: tradeRequest.recipient,
deadline: Math.floor(Date.now() / 1000) + 360,
//deadlineOrPreviousBlockhash: Math.floor(Date.now() / 1000) + 360,
}, {
distributionPercent: 5,
maxSplits: 4,
protocols: [router_sdk_1.Protocol.V3, router_sdk_1.Protocol.V2, router_sdk_1.Protocol.MIXED],
});
return routing;
});
}
exports.route = route;