UNPKG

@moonwell-fi/moonwell-sdk

Version:

TypeScript Interface for Moonwell

493 lines 24.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchLiquidStakingRewards = exports.getMarketsData = void 0; const viem_1 = require("viem"); const index_js_1 = require("../../../common/index.js"); const fetch_headers_js_1 = require("../../../common/fetch-headers.js"); const index_js_2 = require("../../../environments/index.js"); const index_js_3 = require("../../../environments/utils/index.js"); const getMarketsData = async (environment) => { const isMoonriver = environment.chainId === 1285; if (environment.lunarIndexerUrl && !isMoonriver) { try { return await fetchMarketsFromLunar(environment); } catch (error) { console.warn(`[getMarketsData] Lunar Indexer failed for chain ${environment.chainId}, falling back to on-chain:`, error); environment.onError?.(error, { source: "markets", chainId: environment.chainId, }); } } const homeEnvironment = Object.values(index_js_2.publicEnvironments).find((e) => e.custom?.governance?.chainIds?.includes(environment.chainId)) || environment; const viewsContract = environment.contracts.views; const homeViewsContract = homeEnvironment.contracts.views; const [protocolInfoResult, allMarketsInfoResult, nativePriceResult, govPriceResult,] = await Promise.allSettled([ viewsContract?.read.getProtocolInfo(), viewsContract?.read.getAllMarketsInfo(), homeViewsContract?.read.getNativeTokenPrice(), homeViewsContract?.read.getGovernanceTokenPrice(), ]); if (allMarketsInfoResult.status === "rejected") { console.debug("[mToken fallback] getAllMarketsInfo failed, using per-mToken fallback:", allMarketsInfoResult.reason); const seizePaused = protocolInfoResult.status === "fulfilled" && protocolInfoResult.value !== undefined ? protocolInfoResult.value.seizePaused : false; const transferPaused = protocolInfoResult.status === "fulfilled" && protocolInfoResult.value !== undefined ? protocolInfoResult.value.transferPaused : false; return await getMarketsFromMTokenFallback(environment, seizePaused, transferPaused); } const { seizePaused, transferPaused } = protocolInfoResult.status === "fulfilled" && protocolInfoResult.value !== undefined ? protocolInfoResult.value : { seizePaused: false, transferPaused: false }; const allMarketsInfo = allMarketsInfoResult.value; if (!allMarketsInfo) { environment.onError?.(new Error("getAllMarketsInfo returned undefined"), { source: "markets-onchain-missing-views", chainId: environment.chainId, }); return []; } const nativeTokenPriceRaw = nativePriceResult.status === "fulfilled" && nativePriceResult.value !== undefined ? nativePriceResult.value : 0n; const governanceTokenPriceRaw = govPriceResult.status === "fulfilled" && govPriceResult.value !== undefined ? govPriceResult.value : 0n; const governanceTokenPrice = new index_js_1.Amount(governanceTokenPriceRaw, 18); const nativeTokenPrice = new index_js_1.Amount(nativeTokenPriceRaw, 18); const markets = []; const tokenPrices = allMarketsInfo .map((marketInfo) => { const marketFound = (0, index_js_3.findMarketByAddress)(environment, marketInfo.market); if (marketFound) { return { token: marketFound.underlyingToken, tokenPrice: new index_js_1.Amount(marketInfo.underlyingPrice, 36 - marketFound.underlyingToken.decimals), }; } else { return; } }) .filter((token) => !!token); for (const marketInfo of allMarketsInfo) { const marketFound = (0, index_js_3.findMarketByAddress)(environment, marketInfo.market); if (marketFound) { const { marketConfig, marketToken, underlyingToken, marketKey } = marketFound; let badDebt = new index_js_1.Amount(0n, underlyingToken.decimals); if (marketConfig.badDebt === true) { try { const badDebtResult = await environment.markets[marketKey]?.read.badDebt(); badDebt = new index_js_1.Amount(badDebtResult, underlyingToken.decimals); } catch (error) { } } const supplyCaps = new index_js_1.Amount(marketInfo.supplyCap, underlyingToken.decimals); const borrowCaps = new index_js_1.Amount(marketInfo.borrowCap, underlyingToken.decimals); const collateralFactor = new index_js_1.Amount(marketInfo.collateralFactor, 18) .value; const underlyingPrice = new index_js_1.Amount(marketInfo.underlyingPrice, 36 - underlyingToken.decimals).value; const marketTotalSupply = new index_js_1.Amount(marketInfo.totalSupply, marketToken.decimals); const totalBorrows = new index_js_1.Amount(marketInfo.totalBorrows, underlyingToken.decimals); const totalReserves = new index_js_1.Amount(marketInfo.totalReserves, underlyingToken.decimals); const cash = new index_js_1.Amount(marketInfo.cash, underlyingToken.decimals); const exchangeRate = new index_js_1.Amount(marketInfo.exchangeRate, 10 + underlyingToken.decimals).value; const reserveFactor = new index_js_1.Amount(marketInfo.reserveFactor, 18).value; const borrowRate = new index_js_1.Amount(marketInfo.borrowRate, 18); const supplyRate = new index_js_1.Amount(marketInfo.supplyRate, 18); const totalSupply = new index_js_1.Amount(marketTotalSupply.value * exchangeRate, underlyingToken.decimals); const badDebtUsd = badDebt.value * underlyingPrice; const totalSupplyUsd = totalSupply.value * underlyingPrice; const totalBorrowsUsd = totalBorrows.value * underlyingPrice; const totalReservesUsd = totalReserves.value * underlyingPrice; const supplyCapsUsd = supplyCaps.value * underlyingPrice; const borrowCapsUsd = borrowCaps.value * underlyingPrice; const baseSupplyApy = (0, index_js_1.calculateApy)(supplyRate.value); const baseBorrowApy = (0, index_js_1.calculateApy)(borrowRate.value); const market = { marketKey, chainId: environment.chainId, seizePaused, transferPaused, mintPaused: marketInfo.mintPaused, borrowPaused: marketInfo.borrowPaused, deprecated: marketConfig.deprecated === true, borrowCaps, borrowCapsUsd, cash, collateralFactor, exchangeRate, marketToken, reserveFactor, supplyCaps, supplyCapsUsd, badDebt, badDebtUsd, totalBorrows, totalBorrowsUsd, totalReserves, totalReservesUsd, totalSupply, totalSupplyUsd, underlyingPrice, underlyingToken, baseBorrowApy, baseSupplyApy, totalBorrowApr: 0, totalSupplyApr: 0, rewards: [], }; for (const incentive of marketInfo.incentives) { let { borrowIncentivesPerSec, supplyIncentivesPerSec, token: tokenAddress, } = incentive; const token = (0, index_js_3.findTokenByAddress)(environment, tokenAddress); if (token) { const isGovernanceToken = token.symbol === environment.custom?.governance?.token; const isNativeToken = token.address === viem_1.zeroAddress; const tokenPrice = tokenPrices.find((r) => r?.token.address === incentive.token)?.tokenPrice.value; const price = isNativeToken ? nativeTokenPrice.value : isGovernanceToken ? governanceTokenPrice.value : tokenPrice; if (price) { if (borrowIncentivesPerSec === 1n) { borrowIncentivesPerSec = 0n; } const supplyRewardsPerDayUsd = (0, index_js_1.perDay)(new index_js_1.Amount(supplyIncentivesPerSec, token.decimals).value) * price; const borrowRewardsPerDayUsd = (0, index_js_1.perDay)(new index_js_1.Amount(borrowIncentivesPerSec, token.decimals).value) * price; const supplyApr = totalSupplyUsd === 0 ? 0 : (supplyRewardsPerDayUsd / totalSupplyUsd) * index_js_1.DAYS_PER_YEAR * 100; const borrowApr = totalBorrowsUsd === 0 ? 0 : (borrowRewardsPerDayUsd / totalBorrowsUsd) * index_js_1.DAYS_PER_YEAR * 100 * -1; market.rewards.push({ liquidStakingApr: 0, borrowApr, supplyApr, token, }); } } } market.totalSupplyApr = market.rewards.reduce((prev, curr) => prev + curr.supplyApr, market.baseSupplyApy); market.totalBorrowApr = market.rewards.reduce((prev, curr) => prev + curr.borrowApr, market.baseBorrowApy); markets.push(market); } } return markets; }; exports.getMarketsData = getMarketsData; async function getMarketsFromMTokenFallback(environment, seizePaused, transferPaused) { const markets = []; for (const marketKey of Object.keys(environment.config.markets)) { const envAny = environment; const marketConfig = envAny.config.markets[marketKey]; if (!marketConfig) continue; const underlyingToken = envAny.config.tokens[marketConfig.underlyingToken]; const marketToken = envAny.config.tokens[marketConfig.marketToken]; if (!underlyingToken || !marketToken) continue; const mTokenContract = envAny.markets[marketKey]; if (!mTokenContract) continue; const [totalSupplyResult, totalBorrowsResult, totalReservesResult, cashResult, exchangeRateResult, supplyRateResult, borrowRateResult, reserveFactorResult,] = await Promise.allSettled([ mTokenContract.read.totalSupply(), mTokenContract.read.totalBorrows(), mTokenContract.read.totalReserves(), mTokenContract.read.getCash(), mTokenContract.read.exchangeRateStored(), mTokenContract.read.supplyRatePerTimestamp(), mTokenContract.read.borrowRatePerTimestamp(), mTokenContract.read.reserveFactorMantissa(), ]); const totalSupplyRaw = totalSupplyResult.status === "fulfilled" ? totalSupplyResult.value : 0n; const totalBorrowsRaw = totalBorrowsResult.status === "fulfilled" ? totalBorrowsResult.value : 0n; const totalReservesRaw = totalReservesResult.status === "fulfilled" ? totalReservesResult.value : 0n; const cashRaw = cashResult.status === "fulfilled" ? cashResult.value : 0n; const exchangeRateRaw = exchangeRateResult.status === "fulfilled" ? exchangeRateResult.value : 10n ** BigInt(10 + underlyingToken.decimals); const supplyRateRaw = supplyRateResult.status === "fulfilled" ? supplyRateResult.value : 0n; const borrowRateRaw = borrowRateResult.status === "fulfilled" ? borrowRateResult.value : 0n; const reserveFactorRaw = reserveFactorResult.status === "fulfilled" ? reserveFactorResult.value : 0n; const exchangeRate = new index_js_1.Amount(exchangeRateRaw, 10 + underlyingToken.decimals).value; const marketTotalSupply = new index_js_1.Amount(totalSupplyRaw, marketToken.decimals); const totalSupply = new index_js_1.Amount(marketTotalSupply.value * exchangeRate, underlyingToken.decimals); const totalBorrows = new index_js_1.Amount(totalBorrowsRaw, underlyingToken.decimals); const totalReserves = new index_js_1.Amount(totalReservesRaw, underlyingToken.decimals); const cash = new index_js_1.Amount(cashRaw, underlyingToken.decimals); const supplyRate = new index_js_1.Amount(supplyRateRaw, 18); const borrowRate = new index_js_1.Amount(borrowRateRaw, 18); const reserveFactor = new index_js_1.Amount(reserveFactorRaw, 18).value; const baseSupplyApy = (0, index_js_1.calculateApy)(supplyRate.value); const baseBorrowApy = (0, index_js_1.calculateApy)(borrowRate.value); const market = { marketKey, chainId: environment.chainId, seizePaused, transferPaused, mintPaused: true, borrowPaused: true, deprecated: marketConfig.deprecated === true, borrowCaps: new index_js_1.Amount(0n, underlyingToken.decimals), borrowCapsUsd: 0, cash, collateralFactor: 0, exchangeRate, marketToken, reserveFactor, supplyCaps: new index_js_1.Amount(0n, underlyingToken.decimals), supplyCapsUsd: 0, badDebt: new index_js_1.Amount(0n, underlyingToken.decimals), badDebtUsd: 0, totalBorrows, totalBorrowsUsd: 0, totalReserves, totalReservesUsd: 0, totalSupply, totalSupplyUsd: 0, underlyingPrice: 0, underlyingToken, baseBorrowApy, baseSupplyApy, totalBorrowApr: baseBorrowApy, totalSupplyApr: baseSupplyApy, rewards: [], }; markets.push(market); } return markets; } async function fetchMarketsFromLunar(environment) { if (!environment.lunarIndexerUrl) { throw new Error("Lunar Indexer URL not configured"); } const { createLunarIndexerClient, DEFAULT_LUNAR_TIMEOUT_MS } = await Promise.resolve().then(() => __importStar(require("../../lunar-indexer-client.js"))); const client = createLunarIndexerClient({ baseUrl: environment.lunarIndexerUrl, timeout: DEFAULT_LUNAR_TIMEOUT_MS, }); const lunarMarketsResponse = await client.listMarkets(environment.chainId); const lunarMarkets = lunarMarketsResponse.results; const needsRpcPrices = lunarMarkets.some((market) => market.incentives.some((incentive) => incentive.priceUsd === null || incentive.supplyApr === null || incentive.borrowApr === null)); let governanceTokenPrice; let nativeTokenPrice; if (needsRpcPrices) { const homeEnvironment = Object.values(index_js_2.publicEnvironments).find((e) => e.custom?.governance?.chainIds?.includes(environment.chainId)) || environment; const [nativeTokenPriceRaw, governanceTokenPriceRaw] = await Promise.all([ homeEnvironment.contracts.views?.read.getNativeTokenPrice(), homeEnvironment.contracts.views?.read.getGovernanceTokenPrice(), ]); if (!nativeTokenPriceRaw || !governanceTokenPriceRaw) { throw new Error("Failed to fetch native or governance token prices from home chain"); } governanceTokenPrice = new index_js_1.Amount(governanceTokenPriceRaw, 18); nativeTokenPrice = new index_js_1.Amount(nativeTokenPriceRaw, 18); } const markets = []; for (const lunarMarket of lunarMarkets) { const marketFound = (0, index_js_3.findMarketByAddress)(environment, lunarMarket.address); if (!marketFound) { continue; } const { marketConfig, marketToken, underlyingToken, marketKey } = marketFound; const totalSupply = new index_js_1.Amount(BigInt(Math.floor(Number(lunarMarket.totalSupply) * 10 ** underlyingToken.decimals)), underlyingToken.decimals); const totalBorrows = new index_js_1.Amount(BigInt(Math.floor(Number(lunarMarket.totalBorrows) * 10 ** underlyingToken.decimals)), underlyingToken.decimals); const totalReserves = new index_js_1.Amount(BigInt(Math.floor(Number(lunarMarket.totalReserves) * 10 ** underlyingToken.decimals)), underlyingToken.decimals); const cash = new index_js_1.Amount(BigInt(Math.floor(Number(lunarMarket.cash) * 10 ** underlyingToken.decimals)), underlyingToken.decimals); const badDebt = new index_js_1.Amount(BigInt(Math.floor(Number(lunarMarket.badDebt) * 10 ** underlyingToken.decimals)), underlyingToken.decimals); const supplyCaps = new index_js_1.Amount(BigInt(Math.floor(Number(lunarMarket.supplyCap) * 10 ** underlyingToken.decimals)), underlyingToken.decimals); const borrowCaps = new index_js_1.Amount(BigInt(Math.floor(Number(lunarMarket.borrowCap) * 10 ** underlyingToken.decimals)), underlyingToken.decimals); const reserveFactor = new index_js_1.Amount(BigInt(lunarMarket.reserveFactor), 18) .value; const market = { marketKey, chainId: environment.chainId, seizePaused: lunarMarket.seizePaused, transferPaused: lunarMarket.transferPaused, mintPaused: lunarMarket.mintPaused, borrowPaused: lunarMarket.borrowPaused, deprecated: marketConfig.deprecated === true, borrowCaps, borrowCapsUsd: Number(lunarMarket.borrowCap) * Number(lunarMarket.priceUsd), cash, collateralFactor: Number(lunarMarket.collateralFactor), exchangeRate: Number(lunarMarket.exchangeRate), marketToken, reserveFactor, supplyCaps, supplyCapsUsd: Number(lunarMarket.supplyCap) * Number(lunarMarket.priceUsd), badDebt, badDebtUsd: Number(lunarMarket.badDebtUsd), totalBorrows, totalBorrowsUsd: Number(lunarMarket.totalBorrowsUsd), totalReserves, totalReservesUsd: Number(lunarMarket.totalReservesUsd), totalSupply, totalSupplyUsd: Number(lunarMarket.totalSupplyUsd), underlyingPrice: Number(lunarMarket.priceUsd), underlyingToken, baseBorrowApy: Number(lunarMarket.baseBorrowApy), baseSupplyApy: Number(lunarMarket.baseSupplyApy), totalBorrowApr: 0, totalSupplyApr: 0, rewards: [], }; for (const incentive of lunarMarket.incentives) { const token = (0, index_js_3.findTokenByAddress)(environment, incentive.token); if (!token) { continue; } let supplyApr; let borrowApr; const isBorrowPlaceholder = BigInt(incentive.borrowIncentivesPerSec) === 1n; if (incentive.priceUsd !== null && incentive.supplyApr !== null && incentive.borrowApr !== null) { supplyApr = Number(incentive.supplyApr); borrowApr = isBorrowPlaceholder ? 0 : -Number(incentive.borrowApr); } else { const isGovernanceToken = token.symbol === environment.custom?.governance?.token; const isNativeToken = token.address === viem_1.zeroAddress; const price = isNativeToken ? nativeTokenPrice?.value : isGovernanceToken ? governanceTokenPrice?.value : undefined; if (!price) { continue; } const borrowIncentivesPerSec = isBorrowPlaceholder ? 0n : BigInt(incentive.borrowIncentivesPerSec); const supplyIncentivesPerSec = BigInt(incentive.supplyIncentivesPerSec); const supplyRewardsPerDayUsd = (0, index_js_1.perDay)(new index_js_1.Amount(supplyIncentivesPerSec, token.decimals).value) * price; const borrowRewardsPerDayUsd = (0, index_js_1.perDay)(new index_js_1.Amount(borrowIncentivesPerSec, token.decimals).value) * price; supplyApr = Number(lunarMarket.totalSupplyUsd) === 0 ? 0 : (supplyRewardsPerDayUsd / Number(lunarMarket.totalSupplyUsd)) * index_js_1.DAYS_PER_YEAR * 100; borrowApr = Number(lunarMarket.totalBorrowsUsd) === 0 ? 0 : (borrowRewardsPerDayUsd / Number(lunarMarket.totalBorrowsUsd)) * index_js_1.DAYS_PER_YEAR * 100 * -1; } market.rewards.push({ liquidStakingApr: 0, borrowApr, supplyApr, token, }); } market.totalSupplyApr = market.rewards.reduce((prev, curr) => prev + curr.supplyApr, market.baseSupplyApy); market.totalBorrowApr = market.rewards.reduce((prev, curr) => prev + curr.borrowApr, market.baseBorrowApy); markets.push(market); } return markets; } const fetchFromGenericCacheApi = async (uri) => { const response = await fetch("https://generic-api-cache.moonwell.workers.dev/", { method: "POST", body: `{"uri":"${uri}","cacheDuration":"300"}`, headers: { ...fetch_headers_js_1.MOONWELL_FETCH_JSON_HEADERS, "Content-Type": "text/plain", }, }); return response.json(); }; const fetchLiquidStakingRewards = async () => { const result = { cbETH: 0, rETH: 0, wstETH: 0, }; try { const cbETH = await fetchFromGenericCacheApi("https://api.exchange.coinbase.com/wrapped-assets/CBETH"); result.cbETH = Number(cbETH.apy) * 100; } catch (error) { result.cbETH = 0; } try { const rETH = await fetchFromGenericCacheApi("https://rocketpool.net/api/mainnet/payload"); result.rETH = Number(rETH.rethAPR); } catch (error) { result.rETH = 0; } try { const stETH = await fetchFromGenericCacheApi("https://eth-api.lido.fi/v1/protocol/steth/apr/last"); result.wstETH = stETH.data.apr; } catch (error) { result.wstETH = 0; } return result; }; exports.fetchLiquidStakingRewards = fetchLiquidStakingRewards; //# sourceMappingURL=common.js.map