UNPKG

@moonwell-fi/moonwell-sdk

Version:

TypeScript Interface for Moonwell

416 lines 18.8 kB
import { Amount } from "../../../common/amount.js"; import { getGraphQL, getSubgraph } from "../utils/graphql.js"; export async function getMorphoMarketsData(params) { const { environments } = params; const environmentsWithMarkets = environments.filter((environment) => Object.keys(environment.config.morphoMarkets).length > 0 && environment.contracts.morphoViews); const environmentsMarketsInfo = await Promise.all(environmentsWithMarkets.map((environment) => { const marketsIds = Object.values(environment.config.morphoMarkets) .map((item) => item.id) .filter((id) => params.markets ? params.markets .map((id) => id.toLowerCase()) .includes(id.toLowerCase()) : true); return environment.contracts.morphoViews?.read.getMorphoBlueMarketsInfo([ marketsIds, ]); })); const environmentPublicAllocatorSharedLiquidity = await Promise.all(environmentsWithMarkets.map((environment) => { const marketsIds = Object.values(environment.config.morphoMarkets) .map((item) => item.id) .filter((id) => params.markets ? params.markets .map((id) => id.toLowerCase()) .includes(id.toLowerCase()) : true); return getMorphoMarketPublicAllocatorSharedLiquidity(environment, marketsIds); })); const result = environmentsWithMarkets.reduce((aggregator, environment, environmentIndex) => { const environmentMarketsInfo = environmentsMarketsInfo[environmentIndex]; const markets = environmentMarketsInfo.map((marketInfo) => { const marketKey = Object.keys(environment.config.morphoMarkets).find((item) => environment.config.morphoMarkets[item].id.toLowerCase() === marketInfo.marketId.toLowerCase()); const marketConfig = Object.values(environment.config.morphoMarkets).find((item) => item.id.toLowerCase() === marketInfo.marketId.toLowerCase()); const loanToken = environment.config.tokens[marketConfig.loanToken]; const collateralToken = environment.config.tokens[marketConfig.collateralToken]; const oraclePrice = new Amount(BigInt(marketInfo.oraclePrice), 36 + loanToken.decimals - collateralToken.decimals).value; let collateralTokenPrice = new Amount(marketInfo.collateralPrice, 18) .value; let loanTokenPrice = new Amount(marketInfo.loanPrice, 18).value; if (collateralTokenPrice === 0 && loanTokenPrice > 0) { collateralTokenPrice = loanTokenPrice * oraclePrice; } if (loanTokenPrice === 0 && collateralTokenPrice > 0) { loanTokenPrice = collateralTokenPrice / oraclePrice; } const publicAllocatorSharedLiquidity = environmentPublicAllocatorSharedLiquidity[environmentIndex]?.find((item) => item.marketId === marketInfo.marketId); const performanceFee = new Amount(marketInfo.fee, 18).value; const loanToValue = new Amount(marketInfo.lltv, 18).value; const totalSupplyInLoanToken = new Amount(BigInt(marketInfo.totalSupplyAssets), loanToken.decimals); const totalSupply = new Amount(Number(totalSupplyInLoanToken.value / oraclePrice), collateralToken.decimals); const totalBorrows = new Amount(marketInfo.totalBorrowAssets, loanToken.decimals); // Supply APR is used only for vaults, zeroing it for now to avoid confusion // const supplyApy = new Amount(marketInfo.supplyApy, 18).value * 100; const borrowApy = new Amount(marketInfo.borrowApy, 18).value * 100; const availableLiquidity = publicAllocatorSharedLiquidity?.reallocatableLiquidityAssets || new Amount(0, 18); const availableLiquidityUsd = availableLiquidity?.value * loanTokenPrice; const collateralAssets = publicAllocatorSharedLiquidity?.collateralAssets || new Amount(0, 18); const mapping = { chainId: environment.chainId, marketId: marketInfo.marketId, marketKey, deprecated: marketConfig.deprecated === true, loanToValue, performanceFee, loanToken, loanTokenPrice, collateralToken, collateralTokenPrice, collateralAssets, totalSupply, totalSupplyUsd: totalSupply.value * collateralTokenPrice, totalSupplyInLoanToken, totalBorrows, totalBorrowsUsd: totalBorrows.value * loanTokenPrice, baseBorrowApy: borrowApy, totalBorrowApr: borrowApy, baseSupplyApy: 0, //supplyApy, totalSupplyApr: 0, //supplyApy, rewardsSupplyApy: 0, rewardsBorrowApy: 0, availableLiquidity, availableLiquidityUsd, marketParams: { loanToken: marketInfo.loanToken, collateralToken: marketInfo.collateralToken, irm: marketInfo.irm, lltv: marketInfo.lltv, oracle: marketInfo.oracle, }, rewards: [], publicAllocatorSharedLiquidity: publicAllocatorSharedLiquidity?.publicAllocatorSharedLiquidity || [], }; return mapping; }); return { ...aggregator, [environment.chainId]: markets, }; }, {}); if (params.includeRewards) { const markets = Object.values(result) .flat() .filter((market) => { const environment = params.environments.find((environment) => environment.chainId === market.chainId); return environment?.custom.morpho?.minimalDeployment === false; }); const rewards = await getMorphoMarketRewards(markets); markets.forEach((market) => { const marketReward = rewards.find((reward) => reward.marketId === market.marketId && reward.chainId === market.chainId); if (marketReward) { market.rewards = marketReward.rewards; // market.collateralAssets = marketReward.collateralAssets; // market.publicAllocatorSharedLiquidity = // marketReward.publicAllocatorSharedLiquidity; // market.availableLiquidity = marketReward.reallocatableLiquidityAssets; // market.availableLiquidityUsd = // marketReward.reallocatableLiquidityAssets.value * // market.loanTokenPrice; } market.rewardsSupplyApy = market.rewards.reduce((acc, curr) => acc + curr.supplyApr, 0); market.rewardsBorrowApy = market.rewards.reduce((acc, curr) => acc + curr.borrowApr, 0); market.totalSupplyApr = market.rewardsSupplyApy + market.baseSupplyApy; market.totalBorrowApr = market.rewardsBorrowApy + market.baseBorrowApy; }); } return environments.flatMap((environment) => { return result[environment.chainId] || []; }); } async function getMorphoMarketPublicAllocatorSharedLiquidity(environment, markets) { if (markets.length === 0) { return []; } const query = ` { metaMorphos( where: {hasPublicAllocator: true, markets_: {market_in: [${markets.map((market) => `"${market.toLowerCase()}"`).join(",")}]}} ) { id name lastTotalAssets markets { market { id } enabled cap } publicAllocator { id fee markets { id flowCapIn flowCapOut market { market { id oracle { oracleAddress } irm lltv totalBorrow totalSupply totalCollateral inputToken { symbol decimals id } borrowedToken { symbol decimals id } } } } } account { positions(where:{side:SUPPLIER}) { market { id } balance } } } } `; const result = await getSubgraph(environment, query); if (result) { const returnValue = []; for (const market of markets) { let inputTokenDecimals = 0; let outputTokenDecimals = 0; let collateralAssets = 0; let reallocatableLiquidityAssets = 0; const r = []; const marketRemainingLiquidity = {}; const vaults = result.metaMorphos.filter((item) => item.publicAllocator.markets.some((vaultMarket) => vaultMarket.market.market.id.toLowerCase() === market.toLowerCase()) && item.account.positions.some((position) => position.market.id.toLowerCase() === market.toLowerCase() && Number(position.balance) > 0)); for (const vault of vaults) { const otherMarkets = vault.publicAllocator.markets.filter((publicAllocatorMarket) => publicAllocatorMarket.market.market.id.toLowerCase() !== market.toLowerCase()); const thisMarket = vault.publicAllocator.markets.find((publicAllocatorMarket) => publicAllocatorMarket.market.market.id.toLowerCase() === market.toLowerCase()); const thisMarketCaps = vault.markets.find((marketCaps) => marketCaps.market.id.toLowerCase() === market.toLowerCase()); const thisMarketCap = Number(thisMarketCaps?.cap || 0); //Should I check if the cap is enabled? const suppliedToMarket = vault.account.positions.find((position) => position.market.id.toLowerCase() === market.toLowerCase()); const suppliedToMarketAmount = Number(suppliedToMarket?.balance || 0); const thisMarketCapRemaining = thisMarketCap - suppliedToMarketAmount; let maxIn = Math.min(Number(thisMarket?.flowCapIn), thisMarketCapRemaining); collateralAssets = Number(thisMarket?.market.market.totalCollateral || 0); inputTokenDecimals = thisMarket?.market.market.inputToken.decimals || 0; outputTokenDecimals = thisMarket?.market.market.borrowedToken.decimals || 0; const otherMarketsLiquidity = []; for (const otherMarket of otherMarkets) { const vaultSuppliedToMarket = vault.account.positions.find((position) => position.market.id.toLowerCase() === otherMarket.market.market.id.toLowerCase()); if (vaultSuppliedToMarket) { const vaultSuppliedAmount = Number(vaultSuppliedToMarket.balance); const maxOut = Number(otherMarket.flowCapOut); const liquidity = Number(otherMarket.market.market.totalSupply) - Number(otherMarket.market.market.totalBorrow); if (vaultSuppliedAmount > 0 && maxOut > 0 && liquidity > 0) { otherMarketsLiquidity.push({ marketId: otherMarket.market.market.id, amount: Math.min(liquidity, vaultSuppliedAmount, maxOut), liquidity, vault: { address: vault.id, name: vault.name, publicAllocatorConfig: { fee: Number(vault.publicAllocator.fee), flowCaps: vault.publicAllocator.markets.map((publicAllocatorMarket) => ({ maxIn: Number(publicAllocatorMarket.flowCapIn), maxOut: Number(publicAllocatorMarket.flowCapOut), market: { uniqueKey: publicAllocatorMarket.market.market.id, }, })), }, }, allocationMarket: { uniqueKey: otherMarket.market.market.id, loanAsset: { address: otherMarket.market.market.borrowedToken.id, }, collateralAsset: { address: otherMarket.market.market.inputToken.id, }, oracleAddress: otherMarket.market.market.oracle.oracleAddress, irmAddress: otherMarket.market.market.irm, lltv: otherMarket.market.market.lltv, }, }); } } } for (const otherMarket of otherMarketsLiquidity .filter((item) => item.amount > 0) .sort((a, b) => b.amount - a.amount)) { const marketLiquidity = marketRemainingLiquidity[otherMarket.marketId] || otherMarket.liquidity; if (maxIn > 0 && marketLiquidity > 0) { const assets = Math.min(marketLiquidity, otherMarket.amount, maxIn); maxIn = maxIn - assets; marketRemainingLiquidity[otherMarket.marketId] = marketLiquidity - assets; reallocatableLiquidityAssets += assets; r.push({ assets, vault: otherMarket.vault, allocationMarket: otherMarket.allocationMarket, }); } } } returnValue.push({ chainId: environment.chainId, marketId: market.toLowerCase(), collateralAssets: new Amount(BigInt(collateralAssets), inputTokenDecimals), reallocatableLiquidityAssets: new Amount(BigInt(reallocatableLiquidityAssets), outputTokenDecimals), publicAllocatorSharedLiquidity: r, }); } return returnValue; } else { return []; } } async function getMorphoMarketRewards(markets) { if (markets.length === 0) { return []; } const query = ` { markets(where: { uniqueKey_in: [${markets.map((market) => `"${market.marketId.toLowerCase()}"`).join(",")}], chainId_in: [${markets.map((market) => market.chainId).join(",")}] }) { items { morphoBlue { chain { id } } reallocatableLiquidityAssets publicAllocatorSharedLiquidity { assets vault { address name publicAllocatorConfig { fee flowCaps { maxIn maxOut market { uniqueKey } } } } allocationMarket { uniqueKey loanAsset { address } collateralAsset { address } oracleAddress irmAddress lltv } } collateralAsset { decimals } loanAsset { decimals priceUsd } state { collateralAssets rewards { asset { address symbol decimals name } supplyApr borrowApr amountPerBorrowedToken amountPerSuppliedToken } } uniqueKey } } } `; const result = await getGraphQL(query); if (result) { const markets = result.markets.items.map((item) => { const mapping = { chainId: item.morphoBlue.chain.id, marketId: item.uniqueKey, reallocatableLiquidityAssets: new Amount(BigInt(item.reallocatableLiquidityAssets), item.loanAsset.decimals), collateralAssets: new Amount(BigInt(item.state.collateralAssets), item.collateralAsset.decimals), publicAllocatorSharedLiquidity: item.publicAllocatorSharedLiquidity.map((item) => ({ assets: Number(item.assets), vault: { address: item.vault.address, name: item.vault.name, publicAllocatorConfig: item.vault.publicAllocatorConfig, }, allocationMarket: item.allocationMarket, })), rewards: item.state?.rewards.map((reward) => { const tokenDecimals = 10 ** reward.asset.decimals; //Supply APR is used only for vaults, zeroing it for now to avoid confusion //const tokenAmountPer1000 = ((parseFloat(reward.amountPerSuppliedToken) / item.loanAsset.priceUsd) * 1000) || "0" //const amount = (Number(tokenAmountPer1000) / tokenDecimals) const borrowTokenAmountPer1000 = (Number.parseFloat(reward.amountPerBorrowedToken) / item.loanAsset.priceUsd) * 1000; const borrowAmount = borrowTokenAmountPer1000 / tokenDecimals; return { marketId: item.uniqueKey, asset: reward.asset, supplyApr: 0, //(reward.supplyApr || 0) * 100, supplyAmount: 0, //amount, borrowApr: (reward.borrowApr || 0) * 100 * -1, borrowAmount: borrowAmount, }; }), }; return mapping; }); return markets; } else { return []; } } //# sourceMappingURL=common.js.map