@moonwell-fi/moonwell-sdk
Version:
TypeScript Interface for Moonwell
168 lines (146 loc) • 5.01 kB
text/typescript
import { base } from "viem/chains";
import type { MoonwellClient } from "../../client/createMoonwellClient.js";
import {
Amount,
DAYS_PER_YEAR,
SECONDS_PER_DAY,
getEnvironmentsFromArgs,
} from "../../common/index.js";
import type { NetworkParameterType } from "../../common/types.js";
import {
type Chain,
type Environment,
type TokensType,
publicEnvironments,
} from "../../environments/index.js";
import type { StakingInfo } from "../../types/staking.js";
import { getMerklStakingApr } from "./common.js";
export type GetStakingInfoParameters<
environments,
network extends Chain | undefined,
> = NetworkParameterType<environments, network>;
export type GetStakingInfoReturnType = Promise<StakingInfo[]>;
export async function getStakingInfo<
environments,
Network extends Chain | undefined,
>(
client: MoonwellClient,
args?: GetStakingInfoParameters<environments, Network>,
): GetStakingInfoReturnType {
const environments = getEnvironmentsFromArgs(client, args);
const envsWithStaking = environments.filter(
(env) => env.config.contracts.stakingToken,
);
const envStakingInfoSettlements = await Promise.allSettled(
envsWithStaking.map(async (environment) => {
const homeEnvironment =
(Object.values(publicEnvironments) as Environment[]).find((e) =>
e.custom?.governance?.chainIds?.includes(environment.chainId),
) || environment;
const isBase = environment.chainId === base.id;
const settlements = await Promise.allSettled([
environment.contracts.views?.read.getStakingInfo(),
homeEnvironment.contracts.views?.read.getGovernanceTokenPrice(),
...(isBase
? [
environment.contracts.views?.read.getStakingInfo({
blockNumber: BigInt(34149943),
}),
]
: []),
]);
return settlements.map((s) =>
s.status === "fulfilled" ? s.value : undefined,
);
}),
);
const envStakingInfo = envStakingInfoSettlements
.filter((s) => s.status === "fulfilled")
.map(
(s) =>
(
s as PromiseFulfilledResult<
(
| bigint
| {
cooldown: bigint;
unstakeWindow: bigint;
distributionEnd: bigint;
totalSupply: bigint;
emissionPerSecond: bigint;
lastUpdateTimestamp: bigint;
index: bigint;
}
| undefined
)[]
>
).value,
)
.filter((val) => val !== undefined);
const baseEnv = envsWithStaking.find((env) => env.chainId === base.id);
const stakingTokenKey = baseEnv?.config.contracts.stakingToken;
const baseStkToken =
baseEnv != null && stakingTokenKey != null
? baseEnv.config.tokens[stakingTokenKey]
: undefined;
const baseStakingApr =
baseStkToken != null ? await getMerklStakingApr(baseStkToken.address) : 0;
const result = envsWithStaking.flatMap((curr, index) => {
const token =
curr.config.tokens[
curr.config.contracts.governanceToken as keyof TokensType<typeof curr>
]!;
const stakingToken =
curr.config.tokens[
curr.config.contracts.stakingToken as keyof TokensType<typeof curr>
]!;
const envStakingInfoData = envStakingInfo[index]![0]!;
const envGovernanceTokenPriceData = envStakingInfo[index]![1];
const envStakingInfoDataAfterX28Proposal = envStakingInfo[index]![2]!;
const isBase = curr.chainId === base.id;
if (
!envStakingInfoData ||
(isBase && !envStakingInfoDataAfterX28Proposal)
) {
return [];
}
const {
cooldown,
distributionEnd,
emissionPerSecond: emissionPerSecondRaw,
totalSupply: totalSupplyRaw,
unstakeWindow,
} = envStakingInfoData as {
cooldown: bigint;
distributionEnd: bigint;
emissionPerSecond: bigint;
totalSupply: bigint;
unstakeWindow: bigint;
};
//Quick workaround to get governance token price from some other environment
const governanceTokenPrice = new Amount(
(envGovernanceTokenPriceData ?? 0n) as bigint,
18,
);
const totalSupply = new Amount(totalSupplyRaw, 18);
const emissionPerSecond = new Amount(emissionPerSecondRaw, 18);
const emissionPerYear =
emissionPerSecond.value * SECONDS_PER_DAY * DAYS_PER_YEAR;
const apr =
((emissionPerYear + totalSupply.value) / totalSupply.value - 1) * 100;
const stakingInfo: StakingInfo = {
apr: isBase ? baseStakingApr : apr,
chainId: curr.chainId,
cooldown: Number(cooldown),
distributionEnd: Number(distributionEnd),
token,
tokenPrice: governanceTokenPrice.value,
stakingToken,
totalSupply,
totalSupplyUSD: totalSupply.value * governanceTokenPrice.value,
unstakeWindow: Number(unstakeWindow),
};
return stakingInfo;
});
return result;
}