@moonwell-fi/moonwell-sdk
Version:
TypeScript Interface for Moonwell
870 lines • 41.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMorphoVaultsData = getMorphoVaultsData;
exports.getMorphoVaultsRewards = getMorphoVaultsRewards;
const dayjs_1 = __importDefault(require("dayjs"));
const utc_js_1 = __importDefault(require("dayjs/plugin/utc.js"));
const viem_1 = require("viem");
dayjs_1.default.extend(utc_js_1.default);
const amount_js_1 = require("../../../common/amount.js");
const index_js_1 = require("../../../environments/index.js");
const index_js_2 = require("../../../environments/utils/index.js");
const graphql_js_1 = require("../utils/graphql.js");
const math_js_1 = require("../utils/math.js");
const lunarIndexerTransform_js_1 = require("./lunarIndexerTransform.js");
function scaleV1MarketPositionByV2Ownership(v1VaultSupplied, v2RealAssets, v1VaultTotalAssets) {
if (v1VaultTotalAssets === 0n) {
return 0n;
}
return (0, math_js_1.mulDivDown)(v1VaultSupplied, v2RealAssets, v1VaultTotalAssets);
}
async function getMorphoVaultsDataFromIndexer(params) {
const { environments } = params;
const environmentsWithVaults = environments.filter((environment) => Object.keys(environment.vaults).length > 0 && environment.lunarIndexerUrl);
const environmentsVaultsSettlements = await Promise.allSettled(environmentsWithVaults.map(async (environment) => {
const lunarIndexerUrl = environment.lunarIndexerUrl;
try {
const [tokenMap, vaultsResponse] = await Promise.all([
(0, lunarIndexerTransform_js_1.fetchTokenMap)(lunarIndexerUrl, environment.chainId),
(0, lunarIndexerTransform_js_1.fetchVaultsFromIndexer)(lunarIndexerUrl, environment.chainId, params.includeRewards ? { includeRewards: true } : undefined),
]);
let vaults = (0, lunarIndexerTransform_js_1.transformVaultsFromIndexer)(vaultsResponse.results, environment, tokenMap);
const vaultByKey = new Map(vaults.map((v) => [v.vaultKey, v]));
vaults = vaults.map((vault) => {
const v1VaultKey = (0, lunarIndexerTransform_js_1.getV1VaultKey)(environment, vault.vaultKey);
if (!v1VaultKey)
return vault;
const v1Vault = vaultByKey.get(v1VaultKey);
if (!v1Vault) {
return vault;
}
return {
...vault,
totalSupply: v1Vault.totalSupply,
totalSupplyUsd: v1Vault.totalSupplyUsd,
vaultSupply: v1Vault.vaultSupply,
totalLiquidity: v1Vault.totalLiquidity,
totalLiquidityUsd: v1Vault.totalLiquidityUsd,
underlyingPrice: v1Vault.underlyingPrice,
};
});
if (params.vaults) {
const requestedVaults = params.vaults.map((id) => id.toLowerCase());
vaults = vaults.filter((vault) => requestedVaults.includes(vault.vaultToken.address.toLowerCase()));
}
const vaultKeyOrder = Object.keys(environment.config.vaults);
vaults.sort((a, b) => {
const indexA = vaultKeyOrder.indexOf(a.vaultKey);
const indexB = vaultKeyOrder.indexOf(b.vaultKey);
return ((indexA === -1 ? Number.POSITIVE_INFINITY : indexA) -
(indexB === -1 ? Number.POSITIVE_INFINITY : indexB));
});
return vaults;
}
catch (error) {
console.warn(`Failed to fetch vaults from Lunar Indexer for chain ${environment.chainId}, falling back to on-chain:`, error);
environment.onError?.(error, {
source: "vaults",
chainId: environment.chainId,
});
throw { environment, error };
}
}));
const allVaults = environmentsVaultsSettlements.flatMap((settlement) => settlement.status === "fulfilled" ? settlement.value : []);
const failedEnvironments = environmentsVaultsSettlements
.filter((s) => s.status === "rejected")
.map((s) => s.reason?.environment)
.filter((env) => env !== undefined);
let fallbackVaults = [];
if (failedEnvironments.length > 0) {
console.warn(`Falling back to on-chain for ${failedEnvironments.length} environment(s)`);
fallbackVaults = await getMorphoVaultsDataFromOnChain({
...params,
environments: failedEnvironments,
});
}
for (const vault of allVaults) {
const environment = environments.find((env) => env.chainId === vault.chainId);
if (!environment)
continue;
const vaultConfig = environment.config.vaults[vault.vaultKey];
if (!vaultConfig?.multiReward)
continue;
try {
const [distributorTotalSupply, vaultTotalSupply, vaultTotalAssets] = await Promise.all([
getTotalSupplyData(environment, vaultConfig.multiReward),
getTotalSupplyData(environment, vault.vaultToken.address),
getTotalAssetsData(environment, vault.vaultToken.address),
]);
const stakedAssets = (0, math_js_1.toAssetsDown)(distributorTotalSupply, vaultTotalAssets, vaultTotalSupply);
vault.totalStaked = new amount_js_1.Amount(stakedAssets, vault.underlyingToken.decimals);
vault.totalStakedUsd = vault.totalStaked.value * vault.underlyingPrice;
}
catch (error) {
console.warn(`Failed to fetch staking data for vault ${vault.vaultKey}:`, error);
}
}
if (params.includeRewards) {
for (const vault of allVaults) {
const environment = environments.find((env) => env.chainId === vault.chainId);
if (!environment)
continue;
const vaultConfig = environment.config.vaults[vault.vaultKey];
if (!vaultConfig?.multiReward)
continue;
try {
const homeEnvironment = Object.values(index_js_1.publicEnvironments).find((e) => e.custom?.governance?.chainIds?.includes(environment.chainId)) || environment;
const viewsContract = environment.contracts.views;
const homeViewsContract = homeEnvironment.contracts.views;
const data = await Promise.all([
viewsContract?.read.getAllMarketsInfo(),
homeViewsContract?.read.getNativeTokenPrice(),
homeViewsContract?.read.getGovernanceTokenPrice(),
]);
const [allMarkets, nativeTokenPriceRaw, governanceTokenPriceRaw] = data;
const governanceTokenPrice = new amount_js_1.Amount(governanceTokenPriceRaw || 0n, 18);
const nativeTokenPrice = new amount_js_1.Amount(nativeTokenPriceRaw || 0n, 18);
let tokenPrices = allMarkets
?.map((marketInfo) => {
const marketFound = (0, index_js_2.findMarketByAddress)(environment, marketInfo.market);
if (marketFound) {
return {
token: marketFound.underlyingToken,
tokenPrice: new amount_js_1.Amount(marketInfo.underlyingPrice, 36 - marketFound.underlyingToken.decimals),
};
}
else {
return;
}
})
.filter((token) => !!token) || [];
if (environment.custom?.governance?.token) {
tokenPrices = [
...tokenPrices,
{
token: environment.config.tokens[environment.custom.governance.token],
tokenPrice: governanceTokenPrice,
},
];
}
tokenPrices = [
...tokenPrices,
{
token: (0, index_js_2.findTokenByAddress)(environment, viem_1.zeroAddress),
tokenPrice: nativeTokenPrice,
},
];
const rewards = await getRewardsData(environment, vaultConfig.multiReward);
const distributorTotalSupply = await getTotalSupplyData(environment, vaultConfig.multiReward);
rewards
.filter((reward) => reward?.periodFinish &&
dayjs_1.default.utc().isBefore(dayjs_1.default.unix(Number(reward.periodFinish))))
.forEach((reward) => {
const token = Object.values(environment.config.tokens).find((token) => token.address === reward?.token);
if (!token || !reward?.rewardRate)
return;
const market = tokenPrices.find((m) => m?.token.address === reward.token);
const rewardPriceUsd = market?.tokenPrice.value ?? 0;
const rewardsPerYear = new amount_js_1.Amount(reward.rewardRate, market?.token.decimals ?? 18)
.value *
math_js_1.SECONDS_PER_YEAR *
rewardPriceUsd;
vault.stakingRewards.push({
apr: (rewardsPerYear /
(new amount_js_1.Amount(distributorTotalSupply, vault.vaultToken.decimals)
.value *
vault.underlyingPrice)) *
100,
token: token,
});
});
vault.stakingRewardsApr = vault.stakingRewards.reduce((acc, curr) => acc + curr.apr, 0);
vault.totalStakingApr = vault.stakingRewardsApr + vault.baseApy;
}
catch (error) {
console.warn(`Failed to fetch staking rewards for vault ${vault.vaultKey}:`, error);
}
}
}
return [...allVaults, ...fallbackVaults];
}
async function getMorphoVaultsData(params) {
const { environments } = params;
const hasLunarIndexer = environments.some((env) => env.lunarIndexerUrl);
if (hasLunarIndexer) {
return getMorphoVaultsDataFromIndexer(params);
}
return getMorphoVaultsDataFromOnChain(params);
}
async function getMorphoVaultsDataFromOnChain(params) {
const { environments } = params;
const environmentsWithVaults = environments.filter((environment) => Object.keys(environment.vaults).length > 0 &&
environment.contracts.morphoViews);
const environmentsVaultsInfoSettlements = await Promise.allSettled(environmentsWithVaults.map(async (environment) => {
const v1VaultsAddresses = Object.entries(environment.config.vaults)
.filter(([_, config]) => !config.version || config.version === 1)
.map(([key, _]) => environment.vaults[key]?.address)
.filter((address) => address !== undefined)
.filter((address) => params.vaults
? params.vaults
.map((id) => id.toLowerCase())
.includes(address.toLowerCase())
: true);
const v2VaultsAddresses = Object.entries(environment.config.vaults)
.filter(([_, config]) => config.version === 2)
.map(([key, _]) => environment.vaults[key]?.address)
.filter((address) => address !== undefined)
.filter((address) => params.vaults
? params.vaults
.map((id) => id.toLowerCase())
.includes(address.toLowerCase())
: true);
const queryPromises = [];
if (v1VaultsAddresses.length > 0 && environment.contracts.morphoViews) {
queryPromises.push((async () => {
try {
const vaultInfo = await environment.contracts.morphoViews?.read.getVaultsInfo([
v1VaultsAddresses,
]);
return vaultInfo;
}
catch (error) {
return undefined;
}
})());
}
if (v2VaultsAddresses.length > 0 && environment.contracts.morphoViewsV2) {
queryPromises.push((async () => {
try {
const vaultInfoV2 = await environment.contracts.morphoViewsV2?.read.getVaultsInfo([
v2VaultsAddresses,
]);
const underlyingVaultAddresses = vaultInfoV2
?.flatMap((v2Vault) => v2Vault.adapters?.map((adapter) => adapter.underlyingVault))
.filter((addr) => addr && addr !== viem_1.zeroAddress) || [];
const uniqueUnderlyingAddresses = [
...new Set(underlyingVaultAddresses),
];
const underlyingVaultsData = new Map();
if (uniqueUnderlyingAddresses.length > 0 &&
environment.contracts.morphoViews) {
const underlyingInfo = await environment.contracts.morphoViews.read.getVaultsInfo([
uniqueUnderlyingAddresses,
]);
underlyingInfo?.forEach((vaultData, index) => {
underlyingVaultsData.set(uniqueUnderlyingAddresses[index].toLowerCase(), vaultData);
});
}
const transformedVaults = vaultInfoV2?.map((v2Vault) => {
const adapter = v2Vault.adapters?.[0];
if (!adapter) {
return {
vault: v2Vault.vault,
totalSupply: v2Vault.totalSupply,
totalAssets: v2Vault.totalAssets,
underlyingPrice: v2Vault.underlyingPrice,
fee: 0n,
timelock: 0n,
markets: [],
};
}
const underlyingVaultData = underlyingVaultsData.get(adapter.underlyingVault.toLowerCase());
let markets = [];
if (underlyingVaultData?.markets) {
markets = underlyingVaultData.markets.map((v1Market) => {
const scaledVaultSupplied = scaleV1MarketPositionByV2Ownership(v1Market.vaultSupplied, adapter.realAssets, adapter.underlyingVaultTotalAssets);
return {
marketId: v1Market.marketId,
marketCollateral: v1Market.marketCollateral,
marketCollateralName: v1Market.marketCollateralName,
marketCollateralSymbol: v1Market.marketCollateralSymbol,
marketLltv: v1Market.marketLltv,
marketApy: v1Market.marketApy,
marketLiquidity: v1Market.marketLiquidity,
vaultSupplied: scaledVaultSupplied,
};
});
}
else {
markets =
(adapter.underlyingMarkets || []).map((underlyingMarket) => ({
marketId: underlyingMarket.marketId,
marketCollateral: underlyingMarket.collateralToken,
marketCollateralName: underlyingMarket.collateralName,
marketCollateralSymbol: underlyingMarket.collateralSymbol,
marketLltv: underlyingMarket.marketLltv,
marketApy: underlyingMarket.marketSupplyApy,
marketLiquidity: underlyingMarket.marketLiquidity,
vaultSupplied: 0n,
})) || [];
}
return {
vault: v2Vault.vault,
totalSupply: v2Vault.totalSupply,
totalAssets: v2Vault.totalAssets,
underlyingPrice: v2Vault.underlyingPrice,
fee: adapter.underlyingVaultFee,
timelock: adapter.underlyingVaultTimelock,
markets,
};
});
return transformedVaults;
}
catch (error) {
return undefined;
}
})());
}
const queryResults = await Promise.all(queryPromises);
const results = queryResults
.filter((r) => r !== undefined)
.flat();
const vaultKeys = Object.keys(environment.config.vaults);
const sortedResults = results.sort((a, b) => {
const aIndex = vaultKeys.findIndex((key) => environment.vaults[key]?.address.toLowerCase() ===
a.vault.toLowerCase());
const bIndex = vaultKeys.findIndex((key) => environment.vaults[key]?.address.toLowerCase() ===
b.vault.toLowerCase());
return aIndex - bIndex;
});
return sortedResults;
}));
const environmentsVaultsInfo = environmentsVaultsInfoSettlements.map((s) => {
if (s.status === "fulfilled") {
return s.value;
}
return [];
});
const result = await environmentsWithVaults.reduce(async (aggregatorPromise, environment, environmentIndex) => {
const aggregator = await aggregatorPromise;
const environmentVaultsInfo = environmentsVaultsInfo[environmentIndex];
if (!environmentVaultsInfo) {
return aggregator;
}
const v2VaultsToFetch = environmentVaultsInfo.filter((vaultInfo) => {
const vaultKey = Object.keys(environment.config.tokens).find((key) => environment.config.tokens[key].address.toLowerCase() ===
vaultInfo.vault.toLowerCase());
const vaultConfig = environment.config.vaults[vaultKey];
return vaultConfig?.version === 2;
});
const v2ApyDataMap = new Map();
if (v2VaultsToFetch.length > 0) {
const v2ApySettlements = await Promise.allSettled(v2VaultsToFetch.map((vaultInfo) => (0, graphql_js_1.getVaultV2Apy)(environment, vaultInfo.vault, environment.chainId)));
v2ApySettlements.forEach((settlement, index) => {
if (settlement.status === "fulfilled" && settlement.value) {
v2ApyDataMap.set(v2VaultsToFetch[index].vault.toLowerCase(), settlement.value);
}
});
}
const settled = await Promise.allSettled(environmentVaultsInfo.map(async (vaultInfo) => {
const vaultKey = Object.keys(environment.config.tokens).find((key) => {
return (environment.config.tokens[key].address.toLowerCase() ===
vaultInfo.vault.toLowerCase());
});
const vaultToken = environment.config.tokens[vaultKey];
const vaultConfig = environment.config.vaults[vaultKey];
const underlyingToken = environment.config.tokens[vaultConfig.underlyingToken];
const underlyingPrice = new amount_js_1.Amount(vaultInfo.underlyingPrice, 18);
const vaultSupply = new amount_js_1.Amount(vaultInfo.totalSupply, vaultToken.decimals);
const totalSupply = new amount_js_1.Amount(vaultInfo.totalAssets, underlyingToken.decimals);
const totalSupplyUsd = totalSupply.value * underlyingPrice.value;
const performanceFee = new amount_js_1.Amount(vaultInfo.fee, 18).value;
const timelock = Number(vaultInfo.timelock) / (60 * 60);
let ratio = 0n;
let baseApy = 0;
let v2ApyData;
if (vaultConfig.version === 2) {
v2ApyData = v2ApyDataMap.get(vaultInfo.vault.toLowerCase());
if (v2ApyData) {
baseApy = v2ApyData.avgNetApy * 100;
}
}
const markets = vaultInfo.markets.map((vaultMarket) => {
ratio += (0, math_js_1.wMulDown)(vaultMarket.marketApy, vaultMarket.vaultSupplied);
const totalSupplied = new amount_js_1.Amount(vaultMarket.vaultSupplied, underlyingToken.decimals);
const totalSuppliedUsd = totalSupplied.value * underlyingPrice.value;
const allocation = totalSupplied.value / totalSupply.value;
const marketLoanToValue = new amount_js_1.Amount(vaultMarket.marketLltv, 18).value * 100;
const marketApy = new amount_js_1.Amount(vaultMarket.marketApy, 18).value * 100;
let marketLiquidity = new amount_js_1.Amount(vaultMarket.marketLiquidity, underlyingToken.decimals);
let marketLiquidityUsd = marketLiquidity.value * underlyingPrice.value;
if (vaultMarket.marketCollateral === viem_1.zeroAddress) {
marketLiquidity = totalSupplied;
marketLiquidityUsd = totalSuppliedUsd;
}
const mapping = {
marketId: vaultMarket.marketId,
allocation,
marketApy,
marketCollateral: {
address: vaultMarket.marketCollateral,
decimals: 0,
name: vaultMarket.marketCollateralName,
symbol: vaultMarket.marketCollateralSymbol,
},
marketLiquidity,
marketLiquidityUsd,
marketLoanToValue,
totalSupplied,
totalSuppliedUsd,
rewards: [],
};
return mapping;
});
if (vaultConfig.version === 2) {
const totalAllocation = markets.reduce((sum, m) => sum + m.allocation, 0);
if (totalAllocation > 0) {
for (const market of markets) {
market.allocation = market.allocation / totalAllocation;
}
}
}
if (vaultConfig.version !== 2) {
const avgSupplyApy = (0, math_js_1.mulDivDown)(ratio, math_js_1.WAD - vaultInfo.fee, vaultInfo.totalAssets === 0n ? 1n : vaultInfo.totalAssets);
baseApy = new amount_js_1.Amount(avgSupplyApy, 18).value * 100;
}
let totalLiquidity;
let totalLiquidityUsd;
if (vaultConfig.version === 2 && v2ApyData) {
totalLiquidity = new amount_js_1.Amount(BigInt(v2ApyData.liquidity || "0"), underlyingToken.decimals);
totalLiquidityUsd = v2ApyData.liquidityUsd || 0;
}
else {
totalLiquidity = new amount_js_1.Amount(markets.reduce((acc, curr) => acc + curr.marketLiquidity.exponential, 0n), underlyingToken.decimals);
totalLiquidityUsd = markets.reduce((acc, curr) => acc + curr.marketLiquidityUsd, 0);
if (totalLiquidity.value > totalSupply.value) {
totalLiquidity = totalSupply;
totalLiquidityUsd = totalSupplyUsd;
}
}
let totalStaked = new amount_js_1.Amount(0n, underlyingToken.decimals);
let totalStakedUsd = 0;
if (vaultConfig.multiReward) {
const [distributorTotalSupply, vaultTotalSupply, vaultTotalAssets,] = await Promise.all([
getTotalSupplyData(environment, vaultConfig.multiReward),
getTotalSupplyData(environment, vaultToken.address),
getTotalAssetsData(environment, vaultToken.address),
]);
const stakedAssets = (0, math_js_1.toAssetsDown)(distributorTotalSupply, vaultTotalAssets, vaultTotalSupply);
totalStaked = new amount_js_1.Amount(stakedAssets, underlyingToken.decimals);
totalStakedUsd = totalStaked.value * underlyingPrice.value;
}
const mapping = {
chainId: environment.chainId,
vaultKey: vaultKey,
version: vaultConfig.version || 1,
deprecated: vaultConfig.deprecated === true,
vaultToken,
underlyingToken,
underlyingPrice: underlyingPrice.value,
baseApy,
totalApy: baseApy,
rewardsApy: 0,
stakingRewardsApr: 0,
totalStakingApr: baseApy,
curators: [],
performanceFee,
timelock,
totalLiquidity,
totalLiquidityUsd,
totalSupplyUsd,
totalSupply,
vaultSupply,
totalStaked,
totalStakedUsd,
markets: markets,
rewards: [],
stakingRewards: [],
};
return mapping;
}));
let vaults = settled.flatMap((s) => s.status === "fulfilled" ? s.value : []);
const onChainVaultByKey = new Map(vaults.map((v) => [v.vaultKey, v]));
vaults = vaults.map((vault) => {
const v1VaultKey = (0, lunarIndexerTransform_js_1.getV1VaultKey)(environment, vault.vaultKey);
if (!v1VaultKey)
return vault;
const v1Vault = onChainVaultByKey.get(v1VaultKey);
if (!v1Vault) {
return vault;
}
return {
...vault,
totalSupply: v1Vault.totalSupply,
totalSupplyUsd: v1Vault.totalSupplyUsd,
vaultSupply: v1Vault.vaultSupply,
totalLiquidity: v1Vault.totalLiquidity,
totalLiquidityUsd: v1Vault.totalLiquidityUsd,
underlyingPrice: v1Vault.underlyingPrice,
};
});
return {
...(await aggregator),
[environment.chainId]: vaults,
};
}, Promise.resolve({}));
if (params.includeRewards === true) {
const flatList = Object.values(result).flat();
for (const vault of flatList) {
const environment = params.environments.find((environment) => environment.chainId === vault.chainId);
if (!environment) {
continue;
}
const homeEnvironment = Object.values(index_js_1.publicEnvironments).find((e) => e.custom?.governance?.chainIds?.includes(environment.chainId)) || environment;
const viewsContract = environment.contracts.views;
const homeViewsContract = homeEnvironment.contracts.views;
const data = await Promise.all([
viewsContract?.read.getAllMarketsInfo(),
homeViewsContract?.read.getNativeTokenPrice(),
homeViewsContract?.read.getGovernanceTokenPrice(),
]);
const [allMarkets, nativeTokenPriceRaw, governanceTokenPriceRaw] = data;
const governanceTokenPrice = new amount_js_1.Amount(governanceTokenPriceRaw || 0n, 18);
const nativeTokenPrice = new amount_js_1.Amount(nativeTokenPriceRaw || 0n, 18);
let tokenPrices = allMarkets
?.map((marketInfo) => {
const marketFound = (0, index_js_2.findMarketByAddress)(environment, marketInfo.market);
if (marketFound) {
return {
token: marketFound.underlyingToken,
tokenPrice: new amount_js_1.Amount(marketInfo.underlyingPrice, 36 - marketFound.underlyingToken.decimals),
};
}
else {
return;
}
})
.filter((token) => !!token) || [];
if (environment.custom?.governance?.token) {
tokenPrices = [
...tokenPrices,
{
token: environment.config.tokens[environment.custom.governance.token],
tokenPrice: governanceTokenPrice,
},
];
}
tokenPrices = [
...tokenPrices,
{
token: (0, index_js_2.findTokenByAddress)(environment, viem_1.zeroAddress),
tokenPrice: nativeTokenPrice,
},
];
const vaultConfig = environment?.config.vaults[vault.vaultKey];
if (!environment || !vaultConfig || !vaultConfig.multiReward) {
continue;
}
const rewards = await getRewardsData(environment, vaultConfig.multiReward);
const distributorTotalSupply = await getTotalSupplyData(environment, vaultConfig.multiReward);
rewards
.filter((reward) => reward?.periodFinish &&
dayjs_1.default.utc().isBefore(dayjs_1.default.unix(Number(reward.periodFinish))))
.forEach((reward) => {
const token = Object.values(environment.config.tokens).find((token) => token.address === reward?.token);
if (!token || !reward?.rewardRate)
return;
const market = tokenPrices.find((m) => m?.token.address === reward.token);
const rewardPriceUsd = market?.tokenPrice.value ?? 0;
const rewardsPerYear = new amount_js_1.Amount(reward.rewardRate, market?.token.decimals ?? 18).value *
math_js_1.SECONDS_PER_YEAR *
rewardPriceUsd;
vault.stakingRewards.push({
apr: (rewardsPerYear /
(new amount_js_1.Amount(distributorTotalSupply, vault.vaultToken.decimals)
.value *
vault.underlyingPrice)) *
100,
token: token,
});
});
vault.stakingRewardsApr = vault.stakingRewards.reduce((acc, curr) => acc + curr.apr, 0);
vault.totalStakingApr = vault.stakingRewardsApr + vault.baseApy;
}
const vaults = Object.values(result)
.flat()
.filter((vault) => {
const environment = params.environments.find((environment) => environment.chainId === vault.chainId);
return environment?.custom.morpho?.minimalDeployment === false;
});
const rewards = await getMorphoVaultsRewards(params.environments[0], vaults, params.currentChainRewardsOnly);
vaults.forEach((vault) => {
const vaultRewards = rewards.find((reward) => reward.vaultToken.address === vault.vaultToken.address &&
reward.chainId === vault.chainId);
vault.rewards =
vaultRewards?.rewards
.filter((reward) => reward.marketId === undefined)
.map((reward) => ({
asset: reward.asset,
supplyApr: reward.supplyApr,
supplyAmount: reward.supplyAmount,
borrowApr: reward.borrowApr,
borrowAmount: reward.borrowAmount,
})) || [];
vault.markets.forEach((market) => {
const marketRewards = vaultRewards?.rewards
.filter((reward) => reward.marketId === market.marketId)
.map((reward) => ({
asset: reward.asset,
supplyApr: reward.supplyApr,
supplyAmount: reward.supplyAmount,
borrowApr: reward.borrowApr,
borrowAmount: reward.borrowAmount,
})) || [];
market.rewards = marketRewards;
market.rewards.forEach((reward) => {
const supplyApr = reward.supplyApr * market.allocation;
const supplyAmount = reward.supplyAmount * market.allocation;
const vaultReward = vault.rewards.find((r) => r.asset.address === reward.asset.address);
if (vaultReward) {
vaultReward.supplyApr += supplyApr;
vaultReward.supplyAmount += supplyAmount;
}
else {
vault.rewards.push({
asset: reward.asset,
supplyApr,
supplyAmount,
borrowApr: 0,
borrowAmount: 0,
});
}
});
});
vault.rewardsApy = vault.rewards.reduce((acc, curr) => acc + curr.supplyApr, 0);
vault.totalApy = vault.rewardsApy + vault.baseApy;
});
}
return environments.flatMap((environment) => {
return result[environment.chainId] || [];
});
}
const getRewardsData = async (environment, multiRewardsAddress) => {
if (!environment.custom.multiRewarder) {
return [];
}
const multiRewardAbi = (0, viem_1.parseAbi)([
"function rewardData(address token) view returns (address, uint256, uint256, uint256, uint256, uint256)",
]);
const multiRewardContract = (0, viem_1.getContract)({
address: multiRewardsAddress,
abi: multiRewardAbi,
client: environment.publicClient,
});
const rewards = await Promise.all(environment.custom.multiRewarder.map(async (multiRewarder) => {
const tokenAddress = environment.tokens[multiRewarder.rewardToken].address;
if (!tokenAddress) {
return;
}
try {
const rewardData = await multiRewardContract.read.rewardData([
tokenAddress,
]);
return {
rewardRate: BigInt(rewardData[3]),
token: tokenAddress,
periodFinish: BigInt(rewardData[2]),
};
}
catch {
return { rewardRate: 0n, token: tokenAddress };
}
}));
return rewards.filter(Boolean);
};
const getTotalSupplyData = async (environment, multiRewardsAddress) => {
if (!environment.custom.multiRewarder) {
return 0n;
}
const multiRewardAbi = (0, viem_1.parseAbi)([
"function totalSupply() view returns (uint256)",
]);
const multiRewardContract = (0, viem_1.getContract)({
address: multiRewardsAddress,
abi: multiRewardAbi,
client: environment.publicClient,
});
try {
const totalSupply = await multiRewardContract.read.totalSupply();
return BigInt(totalSupply);
}
catch {
return 0n;
}
};
const getTotalAssetsData = async (environment, multiRewardsAddress) => {
if (!environment.custom.multiRewarder) {
return 0n;
}
const multiRewardAbi = (0, viem_1.parseAbi)([
"function totalAssets() view returns (uint256)",
]);
const multiRewardContract = (0, viem_1.getContract)({
address: multiRewardsAddress,
abi: multiRewardAbi,
client: environment.publicClient,
});
try {
const totalAssets = await multiRewardContract.read.totalAssets();
return BigInt(totalAssets);
}
catch {
return 0n;
}
};
async function getMorphoVaultsRewards(environment, vaults, currentChainRewardsOnly) {
const query = `
{
vaults(
where: { address_in: [${vaults.map((vault) => `"${vault.vaultToken.address}"`).join(",")}], chainId_in: [${vaults.map((vault) => vault.chainId).join(",")}] }
) {
items {
chain {
id
}
id
address
asset {
priceUsd
}
state {
rewards {
asset {
address
symbol
decimals
name
chain {
id
}
}
supplyApr
amountPerSuppliedToken
}
}
}
}
marketPositions(
where: { userAddress_in: [${vaults.map((vault) => `"${vault.vaultToken.address}"`).join(",")}], chainId_in: [${vaults.map((vault) => vault.chainId).join(",")}] }
) {
items {
user {
address
}
market {
morphoBlue {
chain {
id
}
}
uniqueKey
loanAsset {
priceUsd
}
state {
rewards {
asset {
address
symbol
decimals
name
chain {
id
}
}
supplyApr
amountPerSuppliedToken
}
}
}
}
}
} `;
const result = await (0, graphql_js_1.getGraphQL)(environment, query);
if (result) {
try {
const marketsRewards = result.marketPositions.items.flatMap((item) => {
const rewards = (item.market.state?.rewards || []).map((reward) => {
const tokenAmountPer1000 = (Number.parseFloat(reward.amountPerSuppliedToken) /
item.market.loanAsset.priceUsd) *
1000;
const tokenDecimals = 10 ** reward.asset.decimals;
const amount = Number(tokenAmountPer1000) / tokenDecimals;
return {
chainId: reward.asset.chain.id,
vaultId: item.user.address,
marketId: item.market.uniqueKey,
asset: reward.asset,
supplyApr: (reward.supplyApr || 0) * 100,
supplyAmount: amount,
borrowApr: 0,
borrowAmount: 0,
};
});
return rewards;
});
const vaultsRewards = result.vaults.items.flatMap((item) => {
return (item.state?.rewards || []).map((reward) => {
const tokenAmountPer1000 = (Number.parseFloat(reward.amountPerSuppliedToken) /
item.asset.priceUsd) *
1000;
const tokenDecimals = 10 ** reward.asset.decimals;
const amount = Number(tokenAmountPer1000) / tokenDecimals;
return {
chainId: reward.asset.chain.id,
vaultId: item.address,
marketId: undefined,
asset: reward.asset,
supplyApr: (reward.supplyApr || 0) * 100,
supplyAmount: amount,
borrowApr: 0,
borrowAmount: 0,
};
});
});
const rewards = [...marketsRewards, ...vaultsRewards];
return vaults.map((vault) => {
return {
chainId: vault.chainId,
vaultToken: vault.vaultToken,
rewards: rewards
.filter((reward) => reward.vaultId.toLowerCase() ===
vault.vaultToken.address.toLowerCase() &&
(reward.chainId === vault.chainId || !currentChainRewardsOnly))
.map((reward) => {
return {
marketId: reward.marketId,
asset: reward.asset,
supplyApr: reward.supplyApr,
supplyAmount: reward.supplyAmount,
borrowApr: reward.borrowApr,
borrowAmount: reward.borrowAmount,
};
}),
};
});
}
catch (ex) {
return vaults.map((vault) => {
return {
chainId: vault.chainId,
vaultToken: vault.vaultToken,
rewards: [],
};
});
}
}
else {
return vaults.map((vault) => {
return {
chainId: vault.chainId,
vaultToken: vault.vaultToken,
rewards: [],
};
});
}
}
//# sourceMappingURL=common.js.map