UNPKG

@devasher/kuru-sdk

Version:

Ethers v6 SDK to interact with Kuru (forked from @kuru-labs/kuru-sdk)

179 lines 8.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PathFinder = void 0; // ============ External Imports ============ const ethers_1 = require("ethers"); // ============ Internal Imports ============ const market_1 = require("../market"); const OrderBook_json_1 = __importDefault(require("../../abi/OrderBook.json")); const KuruUtils_json_1 = __importDefault(require("../../abi/KuruUtils.json")); class PathFinder { static async findBestPath(providerOrSigner, tokenIn, tokenOut, amountIn, amountType = 'amountIn', poolFetcher, pools, estimatorContractAddress) { // Normalize input addresses to lowercase const normalizedTokenIn = tokenIn.toLowerCase(); const normalizedTokenOut = tokenOut.toLowerCase(); if (!pools) { if (!poolFetcher) { throw new Error('Either pools or poolFetcher must be provided'); } pools = await poolFetcher.getAllPools(normalizedTokenIn, normalizedTokenOut); } else { // Normalize pool addresses pools = pools.map((pool) => ({ ...pool, orderbook: pool.orderbook.toLowerCase(), baseToken: pool.baseToken.toLowerCase(), quoteToken: pool.quoteToken.toLowerCase(), })); } const routes = computeAllRoutes(normalizedTokenIn, normalizedTokenOut, pools); let bestRoute = { route: { path: [], tokenIn: '', tokenOut: '', }, isBuy: [], nativeSend: [], output: 0, priceImpact: 0, feeInBase: 0, }; let bestOutput = 0; const routeOutputs = await Promise.all(routes.map(async (route) => amountType === 'amountOut' ? await computeRouteInput(providerOrSigner, route, amountIn) : await computeRouteOutput(providerOrSigner, route, amountIn))); for (const routeOutput of routeOutputs) { if (routeOutput.output > bestOutput) { bestRoute = routeOutput; bestOutput = routeOutput.output; } } if (estimatorContractAddress) { bestRoute.priceImpact = await calculatePriceImpact(providerOrSigner, estimatorContractAddress, bestRoute, amountIn); } return bestRoute; } } exports.PathFinder = PathFinder; async function calculatePriceImpact(providerOrSigner, estimatorContractAddress, route, amountIn) { const estimatorContract = new ethers_1.ethers.Contract(estimatorContractAddress, KuruUtils_json_1.default.abi, providerOrSigner); const orderbookAddresses = route.route.path.map((pool) => pool.orderbook); const price = await estimatorContract.calculatePriceOverRoute(orderbookAddresses, route.isBuy); const priceInUnits = parseFloat((0, ethers_1.formatUnits)(price, 18)); const actualPrice = parseFloat((amountIn / route.output).toFixed(18)); return parseFloat(((100 * actualPrice) / priceInUnits - 100).toFixed(2)); } function computeAllRoutes(tokenIn, tokenOut, pools, currentPath = [], allPaths = [], startTokenIn = tokenIn, maxHops = 2) { for (const pool of pools) { if (currentPath.indexOf(pool) !== -1 || !involvesToken(pool, tokenIn)) continue; const outputToken = pool.baseToken === tokenIn ? pool.quoteToken : pool.baseToken; if (outputToken === tokenOut) { allPaths.push({ path: [...currentPath, pool], tokenIn: startTokenIn, tokenOut, }); } else if (maxHops > 1) { computeAllRoutes(outputToken, tokenOut, pools, [...currentPath, pool], allPaths, startTokenIn, maxHops - 1); } } return allPaths; } async function computeRouteInput(providerOrSigner, route, amountOut, marketParamsMap) { let currentToken = route.tokenIn; let output = amountOut; let feeInBase = amountOut; let isBuy = []; let nativeSend = []; let priceImpact = 0; for (const pool of route.path) { const orderbookAddress = pool.orderbook; // Get market parameters from map if available, otherwise fetch them let poolMarketParams = marketParamsMap === null || marketParamsMap === void 0 ? void 0 : marketParamsMap.get(orderbookAddress); if (!poolMarketParams) { poolMarketParams = await market_1.ParamFetcher.getMarketParams(providerOrSigner, orderbookAddress); } const orderbook = new ethers_1.ethers.Contract(orderbookAddress, OrderBook_json_1.default.abi, providerOrSigner); const l2Book = await orderbook.getL2Book({ from: ethers_1.ZeroAddress, }); const vaultParams = await orderbook.getVaultParams({ from: ethers_1.ZeroAddress, }); currentToken === ethers_1.ZeroAddress ? nativeSend.push(true) : nativeSend.push(false); if (currentToken === pool.baseToken) { // If the current token is the base token, we are selling base for quote output = await market_1.CostEstimator.estimateRequiredBaseForSell(providerOrSigner, orderbookAddress, poolMarketParams, output, l2Book, vaultParams); currentToken = pool.quoteToken; // Update current token to quote token isBuy.push(false); } else { // If the current token is the quote token, we are buying base with quote output = await market_1.CostEstimator.estimateRequiredQuoteForBuy(providerOrSigner, orderbookAddress, poolMarketParams, output, l2Book, vaultParams); currentToken = pool.baseToken; // Update current token to base token isBuy.push(true); } const takerFeesBps = Number(poolMarketParams.takerFeeBps.toString()); feeInBase = (feeInBase * takerFeesBps) / 10000; } return { route, output, nativeSend, isBuy, feeInBase, priceImpact, }; } async function computeRouteOutput(providerOrSigner, route, amountIn, marketParamsMap) { let currentToken = route.tokenIn; let output = amountIn; let feeInBase = amountIn; let isBuy = []; let nativeSend = []; let priceImpact = 1; for (const pool of route.path) { const orderbookAddress = pool.orderbook; // Get market parameters from map if available, otherwise fetch them let poolMarketParams = marketParamsMap === null || marketParamsMap === void 0 ? void 0 : marketParamsMap.get(orderbookAddress); if (!poolMarketParams) { poolMarketParams = await market_1.ParamFetcher.getMarketParams(providerOrSigner, orderbookAddress); } currentToken === ethers_1.ZeroAddress ? nativeSend.push(true) : nativeSend.push(false); if (currentToken === pool.baseToken) { // If the current token is the base token, we are selling base for quote output = (await market_1.CostEstimator.estimateMarketSell(providerOrSigner, orderbookAddress, poolMarketParams, output)).output; currentToken = pool.quoteToken; // Update current token to quote token isBuy.push(false); } else { // If the current token is the quote token, we are buying base with quote output = (await market_1.CostEstimator.estimateMarketBuy(providerOrSigner, orderbookAddress, poolMarketParams, output)).output; currentToken = pool.baseToken; // Update current token to base token isBuy.push(true); } const takerFeesBps = Number(poolMarketParams.takerFeeBps.toString()); feeInBase = (feeInBase * takerFeesBps) / 10000; } return { route, output, nativeSend, isBuy, priceImpact, feeInBase, }; } function involvesToken(pool, token) { // Make comparison case insensitive const normalizedToken = token.toLowerCase(); return pool.baseToken.toLowerCase() === normalizedToken || pool.quoteToken.toLowerCase() === normalizedToken; } //# sourceMappingURL=path.js.map