@moonwell-fi/moonwell-sdk
Version:
TypeScript Interface for Moonwell
351 lines • 16.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUserMorphoRewardsData = getUserMorphoRewardsData;
exports.getUserMorphoStakingRewardsData = getUserMorphoStakingRewardsData;
const lodash_1 = __importDefault(require("lodash"));
const { uniq } = lodash_1.default;
const viem_1 = require("viem");
const amount_js_1 = require("../../../common/amount.js");
const fetch_headers_js_1 = require("../../../common/fetch-headers.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");
async function getUserMorphoRewardsData(params) {
const merklRewards = await getMerklRewardsData(params.environment, params.account);
if (params.environment.custom.morpho?.minimalDeployment === false) {
const morphoRewards = await getMorphoRewardsData(params.environment.chainId, params.account);
const morphoAssets = await getMorphoAssetsData(morphoRewards.map((r) => r.asset.address));
const morphoResult = morphoRewards.map((r) => {
const asset = morphoAssets.find((a) => a.address.toLowerCase() === r.asset.address.toLowerCase());
if (!asset) {
return undefined;
}
const rewardToken = {
address: asset.address,
decimals: asset.decimals,
symbol: asset.symbol,
name: asset.name,
};
switch (r.type) {
case "uniform-reward": {
const claimableNow = new amount_js_1.Amount(BigInt(r.amount?.claimable_now || 0), rewardToken.decimals);
const claimableNowUsd = claimableNow.value * (asset.priceUsd || 0);
const claimableFuture = new amount_js_1.Amount(BigInt(r.amount?.claimable_next || 0), rewardToken.decimals);
const claimableFutureUsd = claimableFuture.value * (asset.priceUsd || 0);
const uniformReward = {
type: "uniform-reward",
chainId: r.asset.chain_id,
account: r.user,
rewardToken,
claimableNow,
claimableNowUsd,
claimableFuture,
claimableFutureUsd,
};
return uniformReward;
}
case "market-reward": {
const claimableNow = new amount_js_1.Amount(BigInt(r.for_supply?.claimable_now || 0), rewardToken.decimals);
const claimableNowUsd = claimableNow.value * (asset.priceUsd || 0);
const claimableFuture = new amount_js_1.Amount(BigInt(r.for_supply?.claimable_next || 0), rewardToken.decimals);
const claimableFutureUsd = claimableFuture.value * (asset.priceUsd || 0);
const collateralClaimableNow = new amount_js_1.Amount(BigInt(r.for_collateral?.claimable_now || 0), rewardToken.decimals);
const collateralClaimableNowUsd = collateralClaimableNow.value * (asset.priceUsd || 0);
const collateralClaimableFuture = new amount_js_1.Amount(BigInt(r.for_collateral?.claimable_next || 0), rewardToken.decimals);
const collateralClaimableFutureUsd = collateralClaimableFuture.value * (asset.priceUsd || 0);
const borrowClaimableNow = new amount_js_1.Amount(BigInt(r.for_borrow?.claimable_now || 0), rewardToken.decimals);
const borrowClaimableNowUsd = borrowClaimableNow.value * (asset.priceUsd || 0);
const borrowClaimableFuture = new amount_js_1.Amount(BigInt(r.for_borrow?.claimable_next || 0), rewardToken.decimals);
const borrowClaimableFutureUsd = borrowClaimableFuture.value * (asset.priceUsd || 0);
if (r.reallocated_from) {
const vaultReward = {
type: "vault-reward",
chainId: r.program.chain_id,
account: r.user,
vaultId: r.reallocated_from,
rewardToken,
claimableNow,
claimableNowUsd,
claimableFuture,
claimableFutureUsd,
};
return vaultReward;
}
else {
const marketReward = {
type: "market-reward",
chainId: r.program.chain_id,
account: r.user,
marketId: r.program.market_id || "",
rewardToken,
collateralRewards: {
claimableNow: collateralClaimableNow,
claimableNowUsd: collateralClaimableNowUsd,
claimableFuture: collateralClaimableFuture,
claimableFutureUsd: collateralClaimableFutureUsd,
},
borrowRewards: {
claimableNow: borrowClaimableNow,
claimableNowUsd: borrowClaimableNowUsd,
claimableFuture: borrowClaimableFuture,
claimableFutureUsd: borrowClaimableFutureUsd,
},
};
return marketReward;
}
}
case "vault-reward": {
const claimableNow = new amount_js_1.Amount(BigInt(r.for_supply?.claimable_now || 0), rewardToken.decimals);
const claimableNowUsd = claimableNow.value * (asset.priceUsd || 0);
const claimableFuture = new amount_js_1.Amount(BigInt(r.for_supply?.claimable_next || 0), rewardToken.decimals);
const claimableFutureUsd = claimableFuture.value * (asset.priceUsd || 0);
const vaultReward = {
type: "vault-reward",
chainId: r.program.chain_id,
account: r.user,
vaultId: r.program.vault,
rewardToken,
claimableNow,
claimableNowUsd,
claimableFuture,
claimableFutureUsd,
};
return vaultReward;
}
}
});
const merklResult = [];
for (const chainData of merklRewards) {
for (const reward of chainData.rewards) {
const morphoAsset = morphoAssets.find((a) => a.address.toLowerCase() === reward.token.address.toLowerCase());
const rewardToken = {
address: reward.token.address,
decimals: morphoAsset?.decimals ?? reward.token.decimals,
symbol: morphoAsset?.symbol ?? reward.token.symbol,
name: morphoAsset?.name ?? reward.token.symbol,
};
const claimableNow = new amount_js_1.Amount(BigInt(reward.amount) - BigInt(reward.claimed), rewardToken.decimals);
const claimableNowUsd = claimableNow.value *
(morphoAsset?.priceUsd ?? reward.token.price ?? 0);
const claimableFuture = new amount_js_1.Amount(BigInt(reward.pending), rewardToken.decimals);
const claimableFutureUsd = claimableFuture.value *
(morphoAsset?.priceUsd ?? reward.token.price ?? 0);
const merklReward = {
type: "merkl-reward",
chainId: chainData.chain.id,
account: params.account,
rewardToken,
claimableNow,
claimableNowUsd,
claimableFuture,
claimableFutureUsd,
};
merklResult.push(merklReward);
}
}
const allResults = [
...morphoResult.filter((r) => r !== undefined),
...merklResult,
];
return allResults;
}
const merklResult = [];
for (const chainData of merklRewards) {
for (const reward of chainData.rewards) {
const rewardToken = {
address: reward.token.address,
decimals: reward.token.decimals,
symbol: reward.token.symbol,
name: reward.token.symbol,
};
const claimableNow = new amount_js_1.Amount(BigInt(reward.amount) - BigInt(reward.claimed), rewardToken.decimals);
const claimableNowUsd = claimableNow.value * (reward.token.price ?? 0);
const claimableFuture = new amount_js_1.Amount(BigInt(reward.pending), rewardToken.decimals);
const claimableFutureUsd = claimableFuture.value * (reward.token.price ?? 0);
const merklReward = {
type: "merkl-reward",
chainId: chainData.chain.id,
account: params.account,
rewardToken,
claimableNow,
claimableNowUsd,
claimableFuture,
claimableFutureUsd,
};
merklResult.push(merklReward);
}
}
return merklResult;
}
async function getUserMorphoStakingRewardsData(params) {
const vaultsWithStaking = Object.values(params.environment.config.vaults).filter((vault) => Boolean(vault.multiReward));
if (!vaultsWithStaking.length) {
return [];
}
const rewards = await Promise.all(vaultsWithStaking.map(async (vault) => {
if (!vault.multiReward)
return [];
const vaultRewards = await getRewardsEarnedData(params.environment, params.account, vault.multiReward);
const homeEnvironment = Object.values(index_js_1.publicEnvironments).find((e) => e.custom?.governance?.chainIds?.includes(params.environment.chainId)) || params.environment;
const viewsContract = params.environment.contracts.views;
const homeViewsContract = homeEnvironment.contracts.views;
const userData = await Promise.all([
viewsContract?.read.getAllMarketsInfo(),
homeViewsContract?.read.getNativeTokenPrice(),
homeViewsContract?.read.getGovernanceTokenPrice(),
]);
const [allMarkets, nativeTokenPriceRaw, governanceTokenPriceRaw] = userData;
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)(params.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 (params.environment.custom?.governance?.token) {
tokenPrices = [
...tokenPrices,
{
token: params.environment.config.tokens[params.environment.custom.governance.token],
tokenPrice: governanceTokenPrice,
},
];
}
tokenPrices = [
...tokenPrices,
{
token: (0, index_js_2.findTokenByAddress)(params.environment, viem_1.zeroAddress),
tokenPrice: nativeTokenPrice,
},
];
return vaultRewards
.filter((reward) => {
return reward !== undefined && reward.amount.value > 0;
})
.map((reward) => {
const market = tokenPrices.find((m) => m?.token.address === reward.token.address);
const priceUsd = market?.tokenPrice.value ?? 0;
return {
...reward,
chainId: params.environment.chainId,
amountUsd: reward.amount.value * priceUsd,
};
});
}));
return rewards.flat();
}
const getRewardsEarnedData = async (environment, userAddress, multiRewardsAddress) => {
if (!environment.custom.multiRewarder) {
return [];
}
const multiRewardAbi = (0, viem_1.parseAbi)([
"function earned(address account, address token) view returns (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 token = environment.config.tokens[multiRewarder.rewardToken];
if (!token) {
return;
}
try {
const earned = await multiRewardContract.read.earned([
userAddress,
token.address,
]);
return { amount: new amount_js_1.Amount(BigInt(earned), token.decimals), token };
}
catch {
return { amount: new amount_js_1.Amount(0n, token.decimals), token };
}
}));
return rewards.filter(Boolean);
};
async function getMorphoRewardsData(chainId, account) {
const rewardsRequest = await fetch(`https://rewards.morpho.org/v1/users/${account}/rewards?chain_id=${chainId}&trusted=true&exclude_merkl_programs=true`, {
headers: fetch_headers_js_1.MOONWELL_FETCH_JSON_HEADERS,
});
const rewards = await rewardsRequest.json();
return (rewards.data || []);
}
async function getMorphoAssetsData(addresses) {
const rewardsRequest = await (0, graphql_js_1.getGraphQL)(`
query {
assets(where: { address_in:[${uniq(addresses)
.map((a) => `"${a.toLowerCase()}"`)
.join(",")}]}) {
items {
address
symbol
priceUsd
name
decimals
}
}
}
`);
if (rewardsRequest) {
return rewardsRequest.assets.items;
}
return [];
}
async function getMerklRewardsData(environment, account) {
try {
const chainIdsPromises = Object.values(environment.config.vaults).map(async (vault) => {
try {
const response = await fetch(`https://api.merkl.xyz/v4/opportunities?identifier=${environment.config.tokens[vault.vaultToken]?.address}&chainId=${environment.chainId}&status=LIVE`, {
headers: fetch_headers_js_1.MOONWELL_FETCH_JSON_HEADERS,
});
if (!response.ok) {
console.warn(`Failed to fetch opportunities: ${response.status} ${response.statusText}`);
return [];
}
const data = await response.json();
return data.flatMap((opportunity) => opportunity.rewardsRecord.breakdowns.map((breakdown) => breakdown.token.chainId));
}
catch (error) {
console.warn(`Error fetching opportunities for vault ${vault.vaultToken}:`, error);
return [];
}
});
const chainIds = [...new Set((await Promise.all(chainIdsPromises)).flat())];
const rewardsPromises = chainIds.map(async (chainId) => {
try {
const response = await fetch(`https://api.merkl.xyz/v4/users/${account}/rewards?chainId=${chainId}&test=false&breakdownPage=0&reloadChainId=${chainId}`, {
headers: fetch_headers_js_1.MOONWELL_FETCH_JSON_HEADERS,
});
if (!response.ok) {
console.warn(`Merkl API request failed: ${response.status} ${response.statusText}`);
return [];
}
return (await response.json());
}
catch (error) {
console.warn(`Error fetching Merkl rewards for chain ${chainId}:`, error);
return [];
}
});
const allRewards = await Promise.all(rewardsPromises);
return allRewards.flat();
}
catch (error) {
console.error("Error in getMerklRewardsData:", error);
return [];
}
}
//# sourceMappingURL=common.js.map