@moonwell-fi/moonwell-sdk
Version:
TypeScript Interface for Moonwell
236 lines • 9.74 kB
JavaScript
;
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