UNPKG

@moonwell-fi/moonwell-sdk

Version:

TypeScript Interface for Moonwell

236 lines 9.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MerklApiError = void 0; exports.getUserMorphoRewardsData = getUserMorphoRewardsData; exports.getUserMorphoStakingRewardsData = getUserMorphoStakingRewardsData; 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"); class MerklApiError extends Error { constructor(params) { super(params.message, params.cause !== undefined ? { cause: params.cause } : undefined); Object.defineProperty(this, "status", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "statusText", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "url", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "chainId", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.name = "MerklApiError"; this.url = params.url; this.chainId = params.chainId; this.status = params.status; this.statusText = params.statusText; } } exports.MerklApiError = MerklApiError; async function getUserMorphoRewardsData(params) { const merklRewards = await getMerklRewardsData(params.environment, params.account, { throwOnError: params.throwOnExternalApiError ?? false }); const isFullDeployment = params.environment.custom.morpho?.minimalDeployment === false; const vaultCampaignIds = isFullDeployment ? new Set(Object.values(index_js_1.publicEnvironments).flatMap((environment) => Object.values(environment.config.vaults ?? {}) .map((vault) => vault.campaignId) .filter((id) => id !== undefined))) : null; const sumBreakdowns = (breakdowns, field) => breakdowns.reduce((acc, curr) => vaultCampaignIds === null || vaultCampaignIds.has(curr.campaignId) ? acc + BigInt(curr[field]) : acc, 0n); 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 amount = vaultCampaignIds ? sumBreakdowns(reward.breakdowns, "amount") : BigInt(reward.amount); const claimed = vaultCampaignIds ? sumBreakdowns(reward.breakdowns, "claimed") : BigInt(reward.claimed); const pending = vaultCampaignIds ? sumBreakdowns(reward.breakdowns, "pending") : BigInt(reward.pending); const claimableNow = new amount_js_1.Amount(amount - claimed, rewardToken.decimals); const claimableNowUsd = claimableNow.value * (reward.token.price ?? 0); const claimableFuture = new amount_js_1.Amount(pending, rewardToken.decimals); const claimableFutureUsd = claimableFuture.value * (reward.token.price ?? 0); merklResult.push({ type: "merkl-reward", chainId: chainData.chain.id, account: params.account, rewardToken, claimableNow, claimableNowUsd, claimableFuture, claimableFutureUsd, }); } } return merklResult; } async function getUserMorphoStakingRewardsData(params) { const vaultsWithStaking = Object.values(params.environment.config.vaults).filter((vault) => Boolean(vault.multiReward)); if (!vaultsWithStaking.length) { return []; } 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 [allMarkets, nativeTokenPriceRaw, governanceTokenPriceRaw] = await Promise.all([ viewsContract?.read.getAllMarketsInfo(), homeViewsContract?.read.getNativeTokenPrice(), homeViewsContract?.read.getGovernanceTokenPrice(), ]); 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, }, ]; const rewards = await Promise.all(vaultsWithStaking.map(async (vault) => { if (!vault.multiReward) return []; const vaultRewards = await getRewardsEarnedData(params.environment, params.account, vault.multiReward); 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 getMerklRewardsData(environment, account, options = { throwOnError: false }) { const url = `https://api.merkl.xyz/v4/users/${account}/rewards?chainId=${environment.chainId}&test=false&breakdownPage=0&reloadChainId=${environment.chainId}`; let response; try { response = await fetch(url, { headers: fetch_headers_js_1.MOONWELL_FETCH_JSON_HEADERS }); } catch (error) { if (options.throwOnError) { throw new MerklApiError({ message: `Merkl API network error for chain ${environment.chainId}`, url, chainId: environment.chainId, cause: error, }); } console.error(`[getMerklRewardsData:network] chain=${environment.chainId} url=${url}`, error); return []; } if (!response.ok) { const message = `Merkl API request failed for chain ${environment.chainId}: ${response.status} ${response.statusText}`; if (options.throwOnError) { throw new MerklApiError({ message, url, chainId: environment.chainId, status: response.status, statusText: response.statusText, }); } console.warn(`${message} (url=${url})`); return []; } try { return (await response.json()); } catch (error) { if (options.throwOnError) { throw new MerklApiError({ message: `Merkl API response parse error for chain ${environment.chainId}`, url, chainId: environment.chainId, cause: error, }); } console.error(`[getMerklRewardsData:parse] chain=${environment.chainId} url=${url}`, error); return []; } } //# sourceMappingURL=common.js.map