Copy class QuoteLibrary {
constructor(config) {
this.contract = new ethers.Contract(
config.contractAddress,
config.abi,
config.provider
);
this.supportedDEXs = config.supportedDEXs || [];
this.cache = new Map();
this.cacheTimeout = config.cacheTimeout || 30000; // 30 seconds
}
async getQuote(tokenIn, tokenOut, amountIn, options = {}) {
const cacheKey = `${tokenIn}-${tokenOut}-${amountIn}`;
// Check cache first
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
}
try {
const quote = await this.contract.getQuote(
tokenIn,
tokenOut,
amountIn,
options.dexData || "0x"
);
// Cache the result
this.cache.set(cacheKey, {
data: quote,
timestamp: Date.now()
});
return this.formatQuoteResult(quote);
} catch (error) {
throw new Error(`Failed to get quote: ${error.message}`);
}
}
async getCrossChainQuote(fromChain, toChain, tokenIn, tokenOut, amountIn) {
try {
const quote = await this.contract.getCrossChainQuote(
fromChain,
toChain,
tokenIn,
tokenOut,
amountIn
);
return this.formatCrossChainQuote(quote);
} catch (error) {
throw new Error(`Failed to get cross-chain quote: ${error.message}`);
}
}
async getBestRoute(tokenIn, tokenOut, amountIn, maxSlippage = 50) {
try {
const route = await this.contract.getBestRoute(
tokenIn,
tokenOut,
amountIn,
maxSlippage
);
return this.formatOptimalRoute(route);
} catch (error) {
throw new Error(`Failed to get best route: ${error.message}`);
}
}
async compareAllDEXs(tokenIn, tokenOut, amountIn) {
try {
const quotes = await this.contract.getMultipleQuotes(
tokenIn,
tokenOut,
amountIn,
this.supportedDEXs
);
return quotes
.map(quote => this.formatQuoteResult(quote))
.sort((a, b) => b.amountOut - a.amountOut);
} catch (error) {
throw new Error(`Failed to compare DEXs: ${error.message}`);
}
}
formatQuoteResult(quote) {
return {
amountOut: ethers.formatUnits(quote.amountOut, 18),
priceImpact: Number(quote.priceImpact) / 100, // Convert to percentage
gasEstimate: Number(quote.gasEstimate),
path: quote.path,
dexUsed: quote.dexUsed,
timestamp: Number(quote.timestamp),
isExpired: Date.now() / 1000 - Number(quote.timestamp) > 300 // 5 minutes
};
}
formatCrossChainQuote(quote) {
return {
amountOut: ethers.formatUnits(quote.amountOut, 18),
totalFees: ethers.formatEther(quote.totalFees),
estimatedTime: Number(quote.estimatedTime),
priceImpact: Number(quote.priceImpact) / 100,
route: quote.route.map(step => ({
chain: step.chain,
tokenIn: step.tokenIn,
tokenOut: step.tokenOut,
amountIn: ethers.formatUnits(step.amountIn, 18),
amountOut: ethers.formatUnits(step.amountOut, 18),
dex: step.dex,
stepType: ['SWAP', 'BRIDGE', 'WRAP'][step.stepType]
})),
minAmountOut: ethers.formatUnits(quote.minAmountOut, 18)
};
}
formatOptimalRoute(route) {
return {
quote: this.formatQuoteResult(route.quote),
steps: route.steps.map(step => ({
dex: step.dex,
tokenIn: step.tokenIn,
tokenOut: step.tokenOut,
amountIn: ethers.formatUnits(step.amountIn, 18),
minAmountOut: ethers.formatUnits(step.minAmountOut, 18),
swapData: step.swapData
})),
totalGasCost: ethers.formatEther(route.totalGasCost),
isDirectSwap: route.isDirectSwap,
intermediateTokens: route.intermediateTokens
};
}
clearCache() {
this.cache.clear();
}
getCacheStats() {
return {
size: this.cache.size,
keys: Array.from(this.cache.keys())
};
}
}