UNPKG

@robertprp/intents-sdk

Version:

Shogun Network Intent-based cross-chain swaps SDK

254 lines 10.2 kB
import { ChainID, isEvmChain } from '../../chains.js'; import { JupiterQuoteProvider } from './jupiter.js'; import {} from '@jup-ag/api'; import { ParaswapQuoteProvider } from './paraswap.js'; import { CROSS_CHAIN_TOKENS } from './stablecoins-tokens.js'; import { AftermathQuoteProvider } from './aftermath.js'; import { calculateAmounts } from '../defillama.js'; import { LiquidSwapQuoteProvider } from './liquidswap.js'; export class QuoteProvider { static async getQuoteFromDefillama(params) { const defillamaQuote = await calculateAmounts({ amountIn: params.amount, tokenIn: params.tokenIn, tokenOut: params.tokenOut, destChainId: params.destChainId, srcChainId: params.sourceChainId, }); return { amountInUsd: 0, estimatedAmountOutUsd: 0, estimatedAmountOutUsdReduced: 0, estimatedAmountInAsMinStablecoinAmount: defillamaQuote.minStablecoinsAmount, estimatedAmountOut: defillamaQuote.amountOut, estimatedAmountOutReduced: defillamaQuote.amountOut, }; } static async getQuoteWithDefillamaFallback(params) { try { const quote = await this.getQuoteFromRouters(params); return quote; } catch (e) { const quote = await this.getQuoteFromDefillama(params); return quote; } } static async getQuote(params) { return this.getQuoteWithDefillamaFallback(params); } static async getQuoteFromRouters(params) { const bridgeTokenSymbol = QuoteProvider.DEFAULT_BRIDGE_TOKEN; const sourceBridgeToken = CROSS_CHAIN_TOKENS[bridgeTokenSymbol]?.[params.sourceChainId]; const destBridgeToken = CROSS_CHAIN_TOKENS[bridgeTokenSymbol]?.[params.destChainId]; const sourceSingleChainQuoteParams = { chainId: params.sourceChainId, amount: params.amount, tokenIn: params.tokenIn, tokenOut: sourceBridgeToken, }; const sourceQuote = await this.getSingleChainQuote(sourceSingleChainQuoteParams); const destSingleChainQuoteParams = { chainId: params.destChainId, amount: sourceQuote.amountOut, tokenIn: destBridgeToken, tokenOut: params.tokenOut, }; const destQuote = await this.getSingleChainQuote(destSingleChainQuoteParams); // Use weighted reduction instead of fixed percentage const weightedReductionFactor = this.calculateWeightedReductionFactorForUsd(destQuote.amountOutUsd); const estimatedAmountOutReduced = BigInt(Math.floor(Number(destQuote.amountOut) * weightedReductionFactor)); const estimatedAmountOutUsdReduced = destQuote.amountOutUsd * weightedReductionFactor; const reducedAmountInUsd = this.calculateWeightedReductionFactorForUsd(sourceQuote.amountInUsd); const estimatedAmountInAsMinStablecoinAmount = BigInt(Math.floor(reducedAmountInUsd * 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS)); return { estimatedAmountOut: destQuote.amountOut, estimatedAmountOutUsd: destQuote.amountOutUsd, amountInUsd: sourceQuote.amountInUsd, estimatedAmountInAsMinStablecoinAmount, estimatedAmountOutReduced, estimatedAmountOutUsdReduced, }; } static async getSingleChainQuote(params) { if (params.tokenIn.toLowerCase() === params.tokenOut.toLowerCase()) { let amountUsd = 0; if (params.tokenIn.toLowerCase() === CROSS_CHAIN_TOKENS.USDC[params.chainId].toLowerCase()) { amountUsd = Number(params.amount) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS; } return { amountIn: params.amount, amountOut: params.amount, amountOutUsd: amountUsd, amountInUsd: amountUsd, slippage: 0, priceImpact: 0, provider: getProviderNameByChainId(params.chainId), rawQuote: null, }; } if (params.chainId === ChainID.Hyperliquid) { const liquidSwapQuote = await QuoteProvider.getLiquidSwapQuote(params); return QuoteProvider.transformLiquidSwapQuote(liquidSwapQuote); } if (params.chainId === ChainID.Solana) { const jupiterQuote = await QuoteProvider.getJupiterQuote(params); return QuoteProvider.transformJupiterQuote(jupiterQuote); } if (params.chainId === ChainID.Sui) { const quote = await QuoteProvider.getAftermathQuote(params); return QuoteProvider.transformAftermathQuote({ quote, request: params }); } if (isEvmChain(params.chainId)) { const paraswapQuote = await QuoteProvider.getParaswapQuote(params); return QuoteProvider.transformParaswapQuote(paraswapQuote); } throw new Error(`Unsupported chain for quote: ${params.chainId}`); } static transformLiquidSwapQuote(liquidSwapQuote) { return { amountIn: BigInt(liquidSwapQuote.amountIn), amountOut: BigInt(liquidSwapQuote.amountOut), amountOutUsd: 0, amountInUsd: 0, slippage: 0, priceImpact: Number(liquidSwapQuote.averagePriceImpact), provider: 'liquidswap', rawQuote: liquidSwapQuote, }; } /** * Calculates the weighted reduction factor for an amount in USD. * For amounts <= $5, the reduction factor is 0.65. */ static calculateWeightedReductionFactorForUsd(amountUsd) { if (amountUsd <= 5) { return 0.50; } else if (amountUsd <= 10) { return 0.65; } else if (amountUsd <= 20) { return 0.77; } else if (amountUsd <= 30) { return 0.90; } else { return 0.95; } } static async getLiquidSwapQuote(params) { const liquidSwapQuoter = new LiquidSwapQuoteProvider(); return liquidSwapQuoter.getQuote({ tokenIn: params.tokenIn, tokenOut: params.tokenOut, amountIn: params.amount.toString(), }); } static async getJupiterQuote(params) { const jupiterQuoter = new JupiterQuoteProvider(); return jupiterQuoter.getQuote({ amount: params.amount, tokenIn: params.tokenIn, tokenOut: params.tokenOut, swapMode: 'ExactIn', slippageBps: params.slippageBps, }); } static async getParaswapQuote(params) { const paraswapQuoter = new ParaswapQuoteProvider(params.chainId); return paraswapQuoter.getSwapEstimation({ amount: BigInt(params.amount), srcToken: params.tokenIn, destToken: params.tokenOut, side: 'SELL', }); } static async getAftermathQuote(params) { const aftermathQuoter = new AftermathQuoteProvider(); return aftermathQuoter.getQuote({ amountIn: params.amount, tokenIn: params.tokenIn, tokenOut: params.tokenOut, }); } static transformAftermathQuote({ quote, request, }) { let amountUsd = 0; if (request.tokenIn === CROSS_CHAIN_TOKENS.USDC[request.chainId]) { amountUsd = Number(quote.coinIn.amount) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS; } if (request.tokenOut === CROSS_CHAIN_TOKENS.USDC[request.chainId]) { amountUsd = Number(quote.coinOut.amount) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS; } return { amountIn: quote.coinIn.amount, amountOut: quote.coinOut.amount, amountOutUsd: amountUsd, amountInUsd: amountUsd, slippage: quote.slippage ?? 0, priceImpact: 0, provider: 'aftermath', rawQuote: quote, }; } static transformParaswapQuote(paraswapQuote) { return { amountIn: BigInt(paraswapQuote.amountIn), amountOut: BigInt(paraswapQuote.amountOut), amountOutUsd: paraswapQuote.amountOutUsd, amountInUsd: paraswapQuote.amountInUsd, slippage: 0, // Calculate from paraswap data if available priceImpact: paraswapQuote.quote.maxImpact ? Number(paraswapQuote.quote.maxImpact) * 100 : 0, provider: 'paraswap', rawQuote: paraswapQuote, }; } static transformJupiterQuote({ quote }) { const amountIn = Number(quote.inAmount); const amountOut = Number(quote.outAmount); let amountUsd = 0; const priceImpact = quote.priceImpactPct ? Number(quote.priceImpactPct) * 100 : 0; if (quote.outputMint === CROSS_CHAIN_TOKENS.USDC[ChainID.Solana]) { amountUsd = amountOut / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS; } if (quote.inputMint === CROSS_CHAIN_TOKENS.USDC[ChainID.Solana]) { amountUsd = amountIn / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS; } return { amountIn: BigInt(amountIn), amountOut: BigInt(amountOut), amountOutUsd: amountUsd, amountInUsd: amountUsd, slippage: quote.slippageBps ? quote.slippageBps / 100 : 0, priceImpact, provider: 'jupiter', rawQuote: quote, }; } } Object.defineProperty(QuoteProvider, "DEFAULT_BRIDGE_TOKEN", { enumerable: true, configurable: true, writable: true, value: 'USDC' }); Object.defineProperty(QuoteProvider, "DEFAULT_BRIDGE_TOKEN_DECIMALS", { enumerable: true, configurable: true, writable: true, value: 6 }); function getProviderNameByChainId(chainId) { switch (chainId) { case ChainID.Sui: return 'aftermath'; case ChainID.Solana: return 'jupiter'; case ChainID.Hyperliquid: return 'liquidswap'; default: return 'paraswap'; } } //# sourceMappingURL=aggregator.js.map