UNPKG

@admin-jigsaw/jigsaw-sdk

Version:

Returns predefined data for Jigsaw platform and exposes functionality to retrieve the necessary data

190 lines (189 loc) 9.29 kB
import { createPublicClient, extractChain, formatEther, getAddress, http, zeroAddress, } from "viem"; import { mainnet, sonic } from "viem/chains"; import { strategyManagerAbi } from "./contracts/strategyManagerAbi"; import { strategyAbi } from "./contracts/strategyAbi"; import { generatePendleRemoveLiquidityDataString, getPendleMarketByStrategy, } from "./utils/pendle.utils"; import { removeLiquiditySingleToken } from "./lib/pendle"; import { getProtocolByStrategy } from "./utils/protocols.utils"; import { getChainConfig } from "./constants/chains"; import { PROTOCOLS_DATA } from "./constants/strategiesSharedConstants"; export class JigsawClient { constructor(rpcUrl, chainId) { const chain = extractChain({ chains: [mainnet, sonic], id: chainId, // Allow any chainId, will throw an error if unsupported }); const transport = rpcUrl ? http(rpcUrl) : http(); this.client = createPublicClient({ chain, transport }); } /** * Fetches user strategies based on holding address and optional asset symbol. * @param holdingAddress The address of the user's holding contract. * @param assetSymbol Optional filter for asset symbol. * @returns A list of strategies. */ async getUserStrategies(holdingAddress, assetSymbol) { try { const chainConfig = getChainConfig(this.client.chain?.id || mainnet.id); const data = await this.client.readContract({ address: chainConfig.constants.strategyManagerAddress, abi: strategyManagerAbi, functionName: "getHoldingToStrategy", args: [holdingAddress], }); if (!Array.isArray(data)) return []; // If no asset symbol specified, return all strategy addresses if (!assetSymbol) { return data; } // Filter strategies based on the symbol return data.filter((strategyAddress) => { const lowerSymbol = assetSymbol.toLowerCase(); if (chainConfig.strategies === null) return false; // Check AAVE strategies if (chainConfig.strategies.aave?.aaveStrategies) { const aaveStrategy = Object.entries(chainConfig.strategies.aave.aaveStrategies).find(([symbol, strat]) => symbol.toLowerCase() === lowerSymbol && getAddress(strat.stratAddress) === getAddress(strategyAddress)); if (aaveStrategy) return true; } // Check DINERO strategies if (chainConfig.strategies.dinero) { const dineroStrategy = Object.entries(chainConfig.strategies.dinero).find(([symbol, strat]) => symbol.toLowerCase() === lowerSymbol && getAddress(strat.stratAddress) === getAddress(strategyAddress)); if (dineroStrategy) return true; } // Check RESERVOIR strategies if (chainConfig.strategies.reservoir) { const reservoirStrategy = Object.entries(chainConfig.strategies.reservoir).find(([symbol, strat]) => symbol.toLowerCase() === lowerSymbol && getAddress(strat.stratAddress) === getAddress(strategyAddress)); if (reservoirStrategy) return true; } // Check PENDLE strategies if (chainConfig.strategies.pendle?.strategies) { const pendleStrategy = Object.entries(chainConfig.strategies.pendle.strategies).find(([symbol, strategies]) => { if (symbol.toLowerCase() !== lowerSymbol) return false; return strategies.some((strategy) => getAddress(strategy.stratAddress) === getAddress(strategyAddress)); }); if (pendleStrategy) return true; } return false; }); } catch (error) { console.error("Failed to fetch user strategies:", error); return []; } } /** * Fetches liquidation call data for user strategies, optionally filtered by asset symbol. * @param holdingAddress User's holding contract address. * @param assetSymbol Optional filter for asset symbol. * @param pendleSlippage Optional slippage parameter for Pendle strategies. * @returns Object with arrays of strategy addresses and corresponding call data strings. */ async getUserLiquidationInfo(holdingAddress, assetSymbol, pendleSlippage) { try { // Get all strategies for this holding address const strategies = await this.getUserStrategies(holdingAddress, assetSymbol); if (!strategies.length) { return { strategies: [], strategiesData: [] }; } // Generate calldata for each strategy, maintaining array correspondence const callDataPromises = strategies.map(async (strategyAddress) => { try { const protocolName = getProtocolByStrategy(strategyAddress, this.client.chain?.id || mainnet.id); // Only generate callData for Pendle strategies if (protocolName === PROTOCOLS_DATA.PENDLE.name) { const multicallResults = await this.client.multicall({ contracts: [ { address: strategyAddress, abi: strategyAbi, functionName: "recipients", args: [holdingAddress], }, { address: strategyAddress, abi: strategyAbi, functionName: "tokenIn", args: [], }, ], }); const totalSharesRaw = multicallResults[0]?.result ? (multicallResults[0].result[1] ?? BigInt(0)) : BigInt(0); const tokenAddress = multicallResults[1]?.result ?? null; const pendleMarket = getPendleMarketByStrategy(strategyAddress, this.client.chain?.id || mainnet.id); if (!pendleMarket || !tokenAddress) return ""; const resp = await removeLiquiditySingleToken(tokenAddress, totalSharesRaw.toString(), pendleMarket, holdingAddress, pendleSlippage, this.client.chain?.id || mainnet.id); return await generatePendleRemoveLiquidityDataString(resp); } // For non-Pendle strategies, return empty string but maintain array correspondence return zeroAddress; } catch (error) { console.error(`Error processing strategy ${strategyAddress}:`, error); // Return empty string on error, but maintain array correspondence return zeroAddress; } }); // Resolve all promises while maintaining strategy-callData correspondence const strategiesData = await Promise.all(callDataPromises); return { strategies, strategiesData, }; } catch (error) { console.error("Failed to fetch liquidation info:", error); return { strategies: [], strategiesData: [] }; } } /** * Fetches the current gas fee in Ether. * @returns The gas price as a formatted string. */ async getCurrentGasFee() { try { const gasPrice = await this.client.getGasPrice(); const priceInEth = formatEther(gasPrice); console.log(`Current gas price is ${priceInEth}`); return priceInEth; } catch (error) { console.error("Failed to fetch gas price:", error); return "0"; } } /** * Retrieves information about a specific strategy. * @param strategyAddress Strategy contract address. * @returns StrategyInfo object or null. */ async getStrategyInfo(strategyAddress) { try { const chainConfig = getChainConfig(this.client.chain?.id || mainnet.id); const strategyInfo = (await this.client.readContract({ address: chainConfig.constants.strategyManagerAddress, abi: strategyManagerAbi, functionName: "strategyInfo", args: [strategyAddress], })); return strategyInfo ?? null; } catch (error) { console.error("Failed to fetch strategy info:", error); return null; } } }