@moonwell-fi/moonwell-sdk 0.12.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/_cjs/actions/axiosWithRetry.js +25 -0
- package/_cjs/actions/axiosWithRetry.js.map +1 -0
- package/_cjs/actions/beam/getBeamTokenLimits.js +2 -5
- package/_cjs/actions/beam/getBeamTokenLimits.js.map +1 -1
- package/_cjs/actions/beam/getBeamTokenRoutes.js +3 -6
- package/_cjs/actions/beam/getBeamTokenRoutes.js.map +1 -1
- package/_cjs/actions/core/markets/getMarketSnapshots.js +2 -5
- package/_cjs/actions/core/markets/getMarketSnapshots.js.map +1 -1
- package/_cjs/actions/core/user-positions/getUserPositionSnapshots.js +2 -5
- package/_cjs/actions/core/user-positions/getUserPositionSnapshots.js.map +1 -1
- package/_cjs/actions/governance/getCirculatingSupplySnapshots.js +3 -6
- package/_cjs/actions/governance/getCirculatingSupplySnapshots.js.map +1 -1
- package/_cjs/actions/governance/getDelegates.js +2 -5
- package/_cjs/actions/governance/getDelegates.js.map +1 -1
- package/_cjs/actions/governance/getDiscussions.js +3 -3
- package/_cjs/actions/governance/getDiscussions.js.map +1 -1
- package/_cjs/actions/governance/getStakingSnapshots.js +2 -5
- package/_cjs/actions/governance/getStakingSnapshots.js.map +1 -1
- package/_cjs/actions/governance/getUserVotingPowers.js +12 -4
- package/_cjs/actions/governance/getUserVotingPowers.js.map +1 -1
- package/_cjs/actions/governance/governor-api-client.js +10 -13
- package/_cjs/actions/governance/governor-api-client.js.map +1 -1
- package/_cjs/actions/governance/proposals/common.js +2 -1
- package/_cjs/actions/governance/proposals/common.js.map +1 -1
- package/_cjs/actions/governance/snapshot/common.js +2 -5
- package/_cjs/actions/governance/snapshot/common.js.map +1 -1
- package/_cjs/actions/lunar-indexer-client.js +4 -0
- package/_cjs/actions/lunar-indexer-client.js.map +1 -1
- package/_cjs/actions/morpho/markets/common.js +2 -5
- package/_cjs/actions/morpho/markets/common.js.map +1 -1
- package/_cjs/actions/morpho/markets/lunarIndexerTransform.js +5 -8
- package/_cjs/actions/morpho/markets/lunarIndexerTransform.js.map +1 -1
- package/_cjs/actions/retry.js +63 -0
- package/_cjs/actions/retry.js.map +1 -0
- package/_cjs/common/getBlockNumberAtTimestamp.js +42 -0
- package/_cjs/common/getBlockNumberAtTimestamp.js.map +1 -0
- package/_cjs/common/index.js +3 -1
- package/_cjs/common/index.js.map +1 -1
- package/_cjs/environments/types/config.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_esm/actions/axiosWithRetry.js +32 -0
- package/_esm/actions/axiosWithRetry.js.map +1 -0
- package/_esm/actions/beam/getBeamTokenLimits.js +2 -2
- package/_esm/actions/beam/getBeamTokenLimits.js.map +1 -1
- package/_esm/actions/beam/getBeamTokenRoutes.js +3 -3
- package/_esm/actions/beam/getBeamTokenRoutes.js.map +1 -1
- package/_esm/actions/core/markets/getMarketSnapshots.js +2 -2
- package/_esm/actions/core/markets/getMarketSnapshots.js.map +1 -1
- package/_esm/actions/core/user-positions/getUserPositionSnapshots.js +2 -2
- package/_esm/actions/core/user-positions/getUserPositionSnapshots.js.map +1 -1
- package/_esm/actions/governance/getCirculatingSupplySnapshots.js +3 -3
- package/_esm/actions/governance/getCirculatingSupplySnapshots.js.map +1 -1
- package/_esm/actions/governance/getDelegates.js +2 -2
- package/_esm/actions/governance/getDelegates.js.map +1 -1
- package/_esm/actions/governance/getDiscussions.js +3 -3
- package/_esm/actions/governance/getDiscussions.js.map +1 -1
- package/_esm/actions/governance/getStakingSnapshots.js +2 -2
- package/_esm/actions/governance/getStakingSnapshots.js.map +1 -1
- package/_esm/actions/governance/getUserVotingPowers.js +13 -5
- package/_esm/actions/governance/getUserVotingPowers.js.map +1 -1
- package/_esm/actions/governance/governor-api-client.js +10 -10
- package/_esm/actions/governance/governor-api-client.js.map +1 -1
- package/_esm/actions/governance/proposals/common.js +2 -1
- package/_esm/actions/governance/proposals/common.js.map +1 -1
- package/_esm/actions/governance/snapshot/common.js +2 -2
- package/_esm/actions/governance/snapshot/common.js.map +1 -1
- package/_esm/actions/lunar-indexer-client.js +6 -0
- package/_esm/actions/lunar-indexer-client.js.map +1 -1
- package/_esm/actions/morpho/markets/common.js +2 -2
- package/_esm/actions/morpho/markets/common.js.map +1 -1
- package/_esm/actions/morpho/markets/lunarIndexerTransform.js +5 -5
- package/_esm/actions/morpho/markets/lunarIndexerTransform.js.map +1 -1
- package/_esm/actions/retry.js +90 -0
- package/_esm/actions/retry.js.map +1 -0
- package/_esm/common/getBlockNumberAtTimestamp.js +56 -0
- package/_esm/common/getBlockNumberAtTimestamp.js.map +1 -0
- package/_esm/common/index.js +1 -0
- package/_esm/common/index.js.map +1 -1
- package/_esm/environments/types/config.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_types/actions/axiosWithRetry.d.ts +16 -0
- package/_types/actions/axiosWithRetry.d.ts.map +1 -0
- package/_types/actions/beam/getBeamTokenLimits.d.ts.map +1 -1
- package/_types/actions/beam/getBeamTokenRoutes.d.ts.map +1 -1
- package/_types/actions/core/markets/getMarketSnapshots.d.ts.map +1 -1
- package/_types/actions/core/user-positions/getUserPositionSnapshots.d.ts.map +1 -1
- package/_types/actions/governance/getCirculatingSupplySnapshots.d.ts.map +1 -1
- package/_types/actions/governance/getDelegates.d.ts.map +1 -1
- package/_types/actions/governance/getDiscussions.d.ts.map +1 -1
- package/_types/actions/governance/getStakingSnapshots.d.ts.map +1 -1
- package/_types/actions/governance/getUserVotingPowers.d.ts +13 -1
- package/_types/actions/governance/getUserVotingPowers.d.ts.map +1 -1
- package/_types/actions/governance/governor-api-client.d.ts.map +1 -1
- package/_types/actions/governance/proposals/common.d.ts.map +1 -1
- package/_types/actions/governance/snapshot/common.d.ts.map +1 -1
- package/_types/actions/lunar-indexer-client.d.ts.map +1 -1
- package/_types/actions/morpho/markets/common.d.ts.map +1 -1
- package/_types/actions/morpho/markets/lunarIndexerTransform.d.ts.map +1 -1
- package/_types/actions/retry.d.ts +45 -0
- package/_types/actions/retry.d.ts.map +1 -0
- package/_types/client/createMoonwellClient.d.ts +2 -2
- package/_types/common/getBlockNumberAtTimestamp.d.ts +15 -0
- package/_types/common/getBlockNumberAtTimestamp.d.ts.map +1 -0
- package/_types/common/index.d.ts +1 -0
- package/_types/common/index.d.ts.map +1 -1
- package/_types/environments/definitions/base/environment.d.ts +21 -3
- package/_types/environments/definitions/base/environment.d.ts.map +1 -1
- package/_types/environments/definitions/moonbeam/custom.d.ts +1 -1
- package/_types/environments/definitions/moonbeam/environment.d.ts +1 -1
- package/_types/environments/index.d.ts +119 -20
- package/_types/environments/index.d.ts.map +1 -1
- package/_types/environments/types/config.d.ts +27 -41
- package/_types/environments/types/config.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/actions/axiosWithRetry.ts +42 -0
- package/actions/beam/getBeamTokenLimits.ts +2 -2
- package/actions/beam/getBeamTokenRoutes.ts +3 -3
- package/actions/core/markets/getMarketSnapshots.ts +2 -2
- package/actions/core/user-positions/getUserPositionSnapshots.ts +2 -2
- package/actions/governance/getCirculatingSupplySnapshots.ts +3 -3
- package/actions/governance/getDelegates.ts +2 -2
- package/actions/governance/getDiscussions.ts +6 -5
- package/actions/governance/getStakingSnapshots.ts +2 -2
- package/actions/governance/getUserVotingPowers.ts +43 -8
- package/actions/governance/governor-api-client.ts +12 -10
- package/actions/governance/proposals/common.ts +2 -1
- package/actions/governance/snapshot/common.ts +2 -2
- package/actions/lunar-indexer-client.ts +8 -0
- package/actions/morpho/markets/common.ts +2 -2
- package/actions/morpho/markets/lunarIndexerTransform.ts +6 -5
- package/actions/retry.ts +121 -0
- package/common/getBlockNumberAtTimestamp.ts +59 -0
- package/common/index.ts +1 -0
- package/environments/types/config.ts +34 -207
- package/errors/version.ts +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop-in axios method wrappers with retry built in.
|
|
3
|
+
*
|
|
4
|
+
* Use these instead of `axios.get` / `axios.post` for outbound HTTP calls in
|
|
5
|
+
* action files. The retry policy lives in `./retry.ts`.
|
|
6
|
+
*
|
|
7
|
+
* Why a wrapper instead of `retry(() => axios.get(...))` at each callsite?
|
|
8
|
+
* TypeScript's control-flow narrowing (e.g. after `if (!env.url) return [];`)
|
|
9
|
+
* doesn't propagate into nested closures, so inline `retry(() => axios.get(env.url))`
|
|
10
|
+
* makes `env.url` `string | undefined` again. Passing the URL as a function
|
|
11
|
+
* argument keeps the narrowing intact.
|
|
12
|
+
*/
|
|
13
|
+
import axios, { type AxiosRequestConfig, type AxiosResponse } from "axios";
|
|
14
|
+
|
|
15
|
+
import { retry } from "./retry.js";
|
|
16
|
+
|
|
17
|
+
// Only forward args the caller actually passed — calling axios.get(url, undefined)
|
|
18
|
+
// is observably different from axios.get(url) for tests that assert call arity.
|
|
19
|
+
|
|
20
|
+
export function getWithRetry<T = unknown>(
|
|
21
|
+
url: string,
|
|
22
|
+
config?: AxiosRequestConfig,
|
|
23
|
+
): Promise<AxiosResponse<T>> {
|
|
24
|
+
if (config !== undefined) {
|
|
25
|
+
return retry(() => axios.get<T>(url, config));
|
|
26
|
+
}
|
|
27
|
+
return retry(() => axios.get<T>(url));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function postWithRetry<T = unknown, D = unknown>(
|
|
31
|
+
url: string,
|
|
32
|
+
data?: D,
|
|
33
|
+
config?: AxiosRequestConfig<D>,
|
|
34
|
+
): Promise<AxiosResponse<T>> {
|
|
35
|
+
if (config !== undefined) {
|
|
36
|
+
return retry(() => axios.post<T, AxiosResponse<T>, D>(url, data, config));
|
|
37
|
+
}
|
|
38
|
+
if (data !== undefined) {
|
|
39
|
+
return retry(() => axios.post<T, AxiosResponse<T>, D>(url, data));
|
|
40
|
+
}
|
|
41
|
+
return retry(() => axios.post<T, AxiosResponse<T>, D>(url));
|
|
42
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import type { HttpRequestError } from "../../common/index.js";
|
|
3
2
|
import { Amount } from "../../common/index.js";
|
|
4
3
|
import type { BeamTokenLimits, BeamTokenRoutes } from "../../types/beam.js";
|
|
4
|
+
import { getWithRetry } from "../axiosWithRetry.js";
|
|
5
5
|
|
|
6
6
|
export type GetBeamTokenLimitsErrorType = HttpRequestError;
|
|
7
7
|
|
|
@@ -36,7 +36,7 @@ export async function getBeamTokenLimits(
|
|
|
36
36
|
direction === "withdraw" ? path.chainId : route.chainId;
|
|
37
37
|
|
|
38
38
|
try {
|
|
39
|
-
const acrossLimitsResponse = await
|
|
39
|
+
const acrossLimitsResponse = await getWithRetry<{
|
|
40
40
|
minDeposit: string;
|
|
41
41
|
maxDeposit: string;
|
|
42
42
|
maxDepositInstant: string;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import { zeroAddress } from "viem";
|
|
3
2
|
import type { MoonwellClient } from "../../client/createMoonwellClient.js";
|
|
4
3
|
import type { HttpRequestError } from "../../common/index.js";
|
|
5
4
|
import { getEnvironmentsFromArgs } from "../../common/index.js";
|
|
6
5
|
import type { TokenConfig } from "../../environments/index.js";
|
|
7
6
|
import type { BeamTokenInfo, BeamTokenRoutes } from "../../types/beam.js";
|
|
7
|
+
import { getWithRetry } from "../axiosWithRetry.js";
|
|
8
8
|
|
|
9
9
|
export type GetBeamTokenRoutesErrorType = HttpRequestError;
|
|
10
10
|
|
|
@@ -18,7 +18,7 @@ export async function getBeamTokenRoutes(
|
|
|
18
18
|
): GetBeamTokenRoutesReturnType {
|
|
19
19
|
const environments = getEnvironmentsFromArgs(client, undefined, false);
|
|
20
20
|
|
|
21
|
-
const acrossRoutesResponse = await
|
|
21
|
+
const acrossRoutesResponse = await getWithRetry<
|
|
22
22
|
{
|
|
23
23
|
originChainId: number;
|
|
24
24
|
originToken: string;
|
|
@@ -30,7 +30,7 @@ export async function getBeamTokenRoutes(
|
|
|
30
30
|
}[]
|
|
31
31
|
>("https://across.to/api/available-routes");
|
|
32
32
|
|
|
33
|
-
const biconomyInfoResponse = await
|
|
33
|
+
const biconomyInfoResponse = await getWithRetry<{
|
|
34
34
|
version: string;
|
|
35
35
|
node: string;
|
|
36
36
|
supportedChains: {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
|
|
3
2
|
import {
|
|
4
3
|
type SnapshotPeriod,
|
|
@@ -12,6 +11,7 @@ import { buildMarketId } from "../../../common/lunar-indexer-helpers.js";
|
|
|
12
11
|
import type { NetworkParameterType } from "../../../common/types.js";
|
|
13
12
|
import type { Chain, Environment } from "../../../environments/index.js";
|
|
14
13
|
import type { MarketSnapshot } from "../../../types/market.js";
|
|
14
|
+
import { postWithRetry } from "../../axiosWithRetry.js";
|
|
15
15
|
import {
|
|
16
16
|
DEFAULT_LUNAR_TIMEOUT_MS,
|
|
17
17
|
createLunarIndexerClient,
|
|
@@ -203,7 +203,7 @@ async function fetchCoreMarketSnapshotsFromPonder(
|
|
|
203
203
|
let endCursor: string | undefined;
|
|
204
204
|
|
|
205
205
|
while (hasNextPage) {
|
|
206
|
-
const result = await
|
|
206
|
+
const result = await postWithRetry<{
|
|
207
207
|
data: {
|
|
208
208
|
marketDailySnapshots: {
|
|
209
209
|
items: MarketDailyData[];
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import type { Address } from "viem";
|
|
3
2
|
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
|
|
4
3
|
import {
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
import type { NetworkParameterType } from "../../../common/types.js";
|
|
13
12
|
import type { Chain, Environment } from "../../../environments/index.js";
|
|
14
13
|
import type { UserPositionSnapshot } from "../../../types/userPosition.js";
|
|
14
|
+
import { postWithRetry } from "../../axiosWithRetry.js";
|
|
15
15
|
import {
|
|
16
16
|
DEFAULT_LUNAR_TIMEOUT_MS,
|
|
17
17
|
createLunarIndexerClient,
|
|
@@ -205,7 +205,7 @@ async function fetchUserPositionSnapshotsFromPonder(
|
|
|
205
205
|
let endCursor: string | undefined;
|
|
206
206
|
|
|
207
207
|
while (hasNextPage) {
|
|
208
|
-
const result = await
|
|
208
|
+
const result = await postWithRetry<{
|
|
209
209
|
data: {
|
|
210
210
|
accountDailySnapshots: {
|
|
211
211
|
items: UserDailyData[];
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import type { MoonwellClient } from "../../client/createMoonwellClient.js";
|
|
3
2
|
import { getEnvironmentFromArgs } from "../../common/index.js";
|
|
4
3
|
import type { OptionalNetworkParameterType } from "../../common/types.js";
|
|
5
4
|
import type { Chain, Environment } from "../../environments/index.js";
|
|
6
5
|
import type { CirculatingSupplySnapshot } from "../../types/circulatingSupply.js";
|
|
6
|
+
import { getWithRetry, postWithRetry } from "../axiosWithRetry.js";
|
|
7
7
|
|
|
8
8
|
export type GetCirculatingSupplySnapshotsParameters<
|
|
9
9
|
environments,
|
|
@@ -46,7 +46,7 @@ async function fetchCirculatingSupplyFromLunar(
|
|
|
46
46
|
params.cursor = cursor;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const response = await
|
|
49
|
+
const response = await getWithRetry<
|
|
50
50
|
LunarPaginatedResponse<LunarCirculatingSupplySnapshot>
|
|
51
51
|
>(`${lunarIndexerUrl}/api/v1/staking/circulating-supply/${chainId}`, {
|
|
52
52
|
params,
|
|
@@ -118,7 +118,7 @@ async function fetchCirculatingSupplyFromPonder(
|
|
|
118
118
|
): Promise<CirculatingSupplySnapshot[]> {
|
|
119
119
|
if (!environment.indexerUrl) return [];
|
|
120
120
|
try {
|
|
121
|
-
const response = await
|
|
121
|
+
const response = await postWithRetry<{
|
|
122
122
|
data: {
|
|
123
123
|
circulatingSupplyDailySnapshots: {
|
|
124
124
|
items: {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import { isAddress } from "viem";
|
|
3
2
|
import { base, moonbeam, optimism } from "viem/chains";
|
|
4
3
|
import type { MoonwellClient } from "../../client/createMoonwellClient.js";
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
} from "../../environments/index.js";
|
|
9
8
|
import * as logger from "../../logger/console.js";
|
|
10
9
|
import type { Delegate } from "../../types/delegate.js";
|
|
10
|
+
import { getWithRetry } from "../axiosWithRetry.js";
|
|
11
11
|
import { fetchAllVoters } from "./governor-api-client.js";
|
|
12
12
|
|
|
13
13
|
export type GetDelegatesReturnType = Promise<Delegate[]>;
|
|
@@ -119,7 +119,7 @@ const getForumProfiles = async () => {
|
|
|
119
119
|
|
|
120
120
|
const getUsersPaginated = async (page = 0) => {
|
|
121
121
|
try {
|
|
122
|
-
const response = await
|
|
122
|
+
const response = await getWithRetry<{
|
|
123
123
|
directory_items: {
|
|
124
124
|
user: {
|
|
125
125
|
id: number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import lodash from "lodash";
|
|
3
2
|
import type { MoonwellClient } from "../../client/createMoonwellClient.js";
|
|
4
3
|
import { HttpRequestError } from "../../common/index.js";
|
|
5
4
|
import * as logger from "../../logger/console.js";
|
|
6
5
|
import type { Discussion } from "../../types/discussion.js";
|
|
6
|
+
import { getWithRetry } from "../axiosWithRetry.js";
|
|
7
7
|
|
|
8
8
|
const { isEqual, uniqWith } = lodash;
|
|
9
9
|
|
|
@@ -43,7 +43,7 @@ export async function getDiscussions(
|
|
|
43
43
|
"Starting to get discussions...",
|
|
44
44
|
);
|
|
45
45
|
|
|
46
|
-
const moonwellProposalsResult = await
|
|
46
|
+
const moonwellProposalsResult = await getWithRetry<ForumTopicRequestResponse>(
|
|
47
47
|
"https://forum.moonwell.fi/c/proposals/moonwell-improvement-proposals/9/l/latest.json",
|
|
48
48
|
);
|
|
49
49
|
|
|
@@ -51,9 +51,10 @@ export async function getDiscussions(
|
|
|
51
51
|
throw new HttpRequestError(moonwellProposalsResult.statusText);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const communityProposalsResult =
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
const communityProposalsResult =
|
|
55
|
+
await getWithRetry<ForumTopicRequestResponse>(
|
|
56
|
+
"https://forum.moonwell.fi/c/proposals/community-proposal/19/l/latest.json",
|
|
57
|
+
);
|
|
57
58
|
|
|
58
59
|
if (
|
|
59
60
|
communityProposalsResult.status !== 200 ||
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import type { MoonwellClient } from "../../client/createMoonwellClient.js";
|
|
3
2
|
import {
|
|
4
3
|
type SnapshotPeriod,
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
import type { NetworkParameterType } from "../../common/types.js";
|
|
11
10
|
import type { Chain } from "../../environments/index.js";
|
|
12
11
|
import type { StakingSnapshot } from "../../types/staking.js";
|
|
12
|
+
import { postWithRetry } from "../axiosWithRetry.js";
|
|
13
13
|
import {
|
|
14
14
|
DEFAULT_LUNAR_TIMEOUT_MS,
|
|
15
15
|
createLunarIndexerClient,
|
|
@@ -129,7 +129,7 @@ async function fetchStakingSnapshotsFromPonder(
|
|
|
129
129
|
startTime?: number,
|
|
130
130
|
): Promise<StakingSnapshot[]> {
|
|
131
131
|
try {
|
|
132
|
-
const response = await
|
|
132
|
+
const response = await postWithRetry<{
|
|
133
133
|
data: {
|
|
134
134
|
stakingDailySnapshots: {
|
|
135
135
|
items: StakingSnapshot[];
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type Address, zeroAddress } from "viem";
|
|
2
2
|
import type { MoonwellClient } from "../../client/createMoonwellClient.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Amount,
|
|
5
|
+
getBlockNumberAtTimestamp,
|
|
6
|
+
getEnvironmentsFromArgs,
|
|
7
|
+
} from "../../common/index.js";
|
|
4
8
|
import type { OptionalNetworkParameterType } from "../../common/types.js";
|
|
5
9
|
import type { Chain, GovernanceToken } from "../../environments/index.js";
|
|
6
10
|
import type { UserVotingPowers } from "../../types/userVotingPowers.js";
|
|
@@ -15,8 +19,21 @@ export type GetUserVotingPowersParameters<
|
|
|
15
19
|
/** User address*/
|
|
16
20
|
userAddress: Address;
|
|
17
21
|
|
|
18
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Block number to read voting power at. Applied to every queried chain — only safe
|
|
24
|
+
* for single-chain governance tokens. For cross-chain tokens (e.g. WELL) prefer
|
|
25
|
+
* `snapshotTimestamp`, which resolves a correct per-chain block.
|
|
26
|
+
*/
|
|
19
27
|
blockNumber?: bigint;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Unix timestamp (seconds) at which to read voting power. When set, the SDK looks up
|
|
31
|
+
* the block on each queried chain whose timestamp is the latest one ≤ this value, and
|
|
32
|
+
* uses that per-chain block. Use this for cross-chain governance tokens; supplying a
|
|
33
|
+
* single block number across heterogeneous chains is unsafe because chain heights
|
|
34
|
+
* diverge. Takes priority over `blockNumber` when both are provided.
|
|
35
|
+
*/
|
|
36
|
+
snapshotTimestamp?: number;
|
|
20
37
|
};
|
|
21
38
|
|
|
22
39
|
export type GetUserVotingPowersReturnType = Promise<UserVotingPowers[]>;
|
|
@@ -28,7 +45,7 @@ export async function getUserVotingPowers<
|
|
|
28
45
|
client: MoonwellClient,
|
|
29
46
|
args: GetUserVotingPowersParameters<environments, Network>,
|
|
30
47
|
): GetUserVotingPowersReturnType {
|
|
31
|
-
const { governanceToken, userAddress, blockNumber } = args;
|
|
48
|
+
const { governanceToken, userAddress, blockNumber, snapshotTimestamp } = args;
|
|
32
49
|
|
|
33
50
|
const environments = getEnvironmentsFromArgs(client, args);
|
|
34
51
|
|
|
@@ -36,12 +53,30 @@ export async function getUserVotingPowers<
|
|
|
36
53
|
(env) => env.custom?.governance?.token === governanceToken,
|
|
37
54
|
);
|
|
38
55
|
|
|
56
|
+
const perChainBlockNumbers =
|
|
57
|
+
snapshotTimestamp !== undefined
|
|
58
|
+
? await Promise.all(
|
|
59
|
+
tokenEnvironments.map((env) =>
|
|
60
|
+
getBlockNumberAtTimestamp(
|
|
61
|
+
env.publicClient,
|
|
62
|
+
BigInt(snapshotTimestamp),
|
|
63
|
+
),
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
: undefined;
|
|
67
|
+
|
|
39
68
|
const environmentsUserVotingPowers = await Promise.all(
|
|
40
|
-
tokenEnvironments.map((environment) =>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
tokenEnvironments.map((environment, index) => {
|
|
70
|
+
const blockForChain = perChainBlockNumbers
|
|
71
|
+
? perChainBlockNumbers[index]
|
|
72
|
+
: blockNumber;
|
|
73
|
+
return environment.contracts.views?.read.getUserVotingPower(
|
|
74
|
+
[userAddress],
|
|
75
|
+
{
|
|
76
|
+
blockNumber: blockForChain,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
}),
|
|
45
80
|
);
|
|
46
81
|
|
|
47
82
|
return tokenEnvironments.map((environment, index) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import type { Environment } from "../../environments/index.js";
|
|
2
|
+
import { getWithRetry } from "../axiosWithRetry.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Base configuration for Governor API requests
|
|
@@ -108,7 +108,7 @@ export async function fetchProposals(
|
|
|
108
108
|
if (options?.cursor) params.append("cursor", options.cursor);
|
|
109
109
|
if (options?.chainId) params.append("chainId", options.chainId.toString());
|
|
110
110
|
|
|
111
|
-
const response = await
|
|
111
|
+
const response = await getWithRetry<PaginatedResponse<ApiProposal>>(
|
|
112
112
|
`${baseUrl}/api/v1/governor/proposals?${params.toString()}`,
|
|
113
113
|
);
|
|
114
114
|
|
|
@@ -152,7 +152,7 @@ export async function fetchProposal(
|
|
|
152
152
|
): Promise<ApiProposal> {
|
|
153
153
|
const baseUrl = getGovernorApiUrl(environment);
|
|
154
154
|
|
|
155
|
-
const response = await
|
|
155
|
+
const response = await getWithRetry<ApiProposal>(
|
|
156
156
|
`${baseUrl}/api/v1/governor/proposals/${proposalId}`,
|
|
157
157
|
);
|
|
158
158
|
|
|
@@ -177,7 +177,7 @@ export async function fetchProposalVotes(
|
|
|
177
177
|
if (options?.limit) params.append("limit", options.limit.toString());
|
|
178
178
|
if (options?.cursor) params.append("cursor", options.cursor);
|
|
179
179
|
|
|
180
|
-
const response = await
|
|
180
|
+
const response = await getWithRetry<PaginatedResponse<ApiVote>>(
|
|
181
181
|
`${baseUrl}/api/v1/governor/proposals/${proposalId}/votes?${params.toString()}`,
|
|
182
182
|
);
|
|
183
183
|
|
|
@@ -225,7 +225,9 @@ export async function fetchProposalStateChanges(
|
|
|
225
225
|
if (options?.limit) params.append("limit", options.limit.toString());
|
|
226
226
|
if (options?.cursor) params.append("cursor", options.cursor);
|
|
227
227
|
|
|
228
|
-
const response = await
|
|
228
|
+
const response = await getWithRetry<
|
|
229
|
+
PaginatedResponse<ApiProposalStateChange>
|
|
230
|
+
>(
|
|
229
231
|
`${baseUrl}/api/v1/governor/proposals/${proposalId}/state-changes?${params.toString()}`,
|
|
230
232
|
);
|
|
231
233
|
|
|
@@ -277,7 +279,7 @@ export async function fetchVoters(
|
|
|
277
279
|
const endpoint = `${baseUrl}/api/v1/governor/voters?${params.toString()}`;
|
|
278
280
|
|
|
279
281
|
try {
|
|
280
|
-
const response = await
|
|
282
|
+
const response = await getWithRetry<PaginatedResponse<ApiVoter>>(endpoint);
|
|
281
283
|
|
|
282
284
|
if (response.status !== 200 || !response.data) {
|
|
283
285
|
throw new Error(`Failed to fetch voters: ${response.statusText}`);
|
|
@@ -328,7 +330,7 @@ export async function fetchVoter(
|
|
|
328
330
|
): Promise<ApiVoter> {
|
|
329
331
|
const baseUrl = getGovernorApiUrl(environment);
|
|
330
332
|
|
|
331
|
-
const response = await
|
|
333
|
+
const response = await getWithRetry<ApiVoter>(
|
|
332
334
|
`${baseUrl}/api/v1/governor/voters/${address}`,
|
|
333
335
|
);
|
|
334
336
|
|
|
@@ -359,7 +361,7 @@ export async function fetchVoterProposals(
|
|
|
359
361
|
|
|
360
362
|
const endpoint = `${baseUrl}/api/v1/governor/voters/${address}/proposals?${params.toString()}`;
|
|
361
363
|
|
|
362
|
-
const response = await
|
|
364
|
+
const response = await getWithRetry<PaginatedResponse<ApiProposal>>(endpoint);
|
|
363
365
|
|
|
364
366
|
if (response.status !== 200 || !response.data) {
|
|
365
367
|
throw new Error(`Failed to fetch voter proposals: ${response.statusText}`);
|
|
@@ -407,7 +409,7 @@ export async function fetchVoterVotes(
|
|
|
407
409
|
|
|
408
410
|
const endpoint = `${baseUrl}/api/v1/governor/voters/${address}/votes?${params.toString()}`;
|
|
409
411
|
|
|
410
|
-
const response = await
|
|
412
|
+
const response = await getWithRetry<PaginatedResponse<ApiVote>>(endpoint);
|
|
411
413
|
|
|
412
414
|
if (response.status !== 200 || !response.data) {
|
|
413
415
|
throw new Error(`Failed to fetch voter votes: ${response.statusText}`);
|
|
@@ -450,7 +452,7 @@ export async function fetchUserVoteReceipt(
|
|
|
450
452
|
): Promise<ApiVoteReceipt[]> {
|
|
451
453
|
const baseUrl = getGovernorApiUrl(environment);
|
|
452
454
|
|
|
453
|
-
const response = await
|
|
455
|
+
const response = await getWithRetry<ApiVoteReceipt[]>(
|
|
454
456
|
`${baseUrl}/api/v1/governor/proposals/${proposalId}/vote/${voterAddress}`,
|
|
455
457
|
);
|
|
456
458
|
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type Proposal,
|
|
10
10
|
type ProposalState,
|
|
11
11
|
} from "../../../types/proposal.js";
|
|
12
|
+
import { postWithRetry } from "../../axiosWithRetry.js";
|
|
12
13
|
import type { ApiProposal } from "../governor-api-client.js";
|
|
13
14
|
|
|
14
15
|
export const WORMHOLE_CONTRACT = "0xc8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3";
|
|
@@ -656,7 +657,7 @@ export const getExtendedProposalData = async (params: {
|
|
|
656
657
|
try {
|
|
657
658
|
while (shouldContinue && page < MAX_PAGES) {
|
|
658
659
|
page++;
|
|
659
|
-
const response = await
|
|
660
|
+
const response = await postWithRetry<{
|
|
660
661
|
data: {
|
|
661
662
|
proposals: {
|
|
662
663
|
items: {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import {
|
|
3
2
|
type Environment,
|
|
4
3
|
base,
|
|
@@ -6,6 +5,7 @@ import {
|
|
|
6
5
|
supportedChains,
|
|
7
6
|
} from "../../../environments/index.js";
|
|
8
7
|
import type { SnapshotProposal } from "../../../types/snapshotProposal.js";
|
|
8
|
+
import { postWithRetry } from "../../axiosWithRetry.js";
|
|
9
9
|
import type { GetSnapshotProposalsReturnType } from "./getSnapshotProposals.js";
|
|
10
10
|
|
|
11
11
|
export const getSnapshotProposalData = async (params: {
|
|
@@ -32,7 +32,7 @@ export const getSnapshotProposalData = async (params: {
|
|
|
32
32
|
|
|
33
33
|
const pageSize = params.pagination?.size ? params.pagination.size : 10;
|
|
34
34
|
|
|
35
|
-
const response = await
|
|
35
|
+
const response = await postWithRetry<{
|
|
36
36
|
data: {
|
|
37
37
|
spaces: {
|
|
38
38
|
proposalsCount: number;
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import axios, { type AxiosInstance, type AxiosError } from "axios";
|
|
9
9
|
|
|
10
|
+
import { attachRetryInterceptor } from "./retry.js";
|
|
11
|
+
|
|
10
12
|
// ============================================================================
|
|
11
13
|
// Type Definitions
|
|
12
14
|
// ============================================================================
|
|
@@ -301,6 +303,12 @@ export class LunarIndexerClient {
|
|
|
301
303
|
"Content-Type": "application/json",
|
|
302
304
|
},
|
|
303
305
|
});
|
|
306
|
+
|
|
307
|
+
// Retry transient failures (5xx, network errors, timeouts) silently before
|
|
308
|
+
// surfacing to callers. 4xx (incl. 404) bypasses retries — see ./retry.ts.
|
|
309
|
+
attachRetryInterceptor(this.client);
|
|
310
|
+
attachRetryInterceptor(this.stakingClient);
|
|
311
|
+
attachRetryInterceptor(this.vaultsClient);
|
|
304
312
|
}
|
|
305
313
|
|
|
306
314
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import type { Address } from "viem";
|
|
3
2
|
import { Amount } from "../../../common/amount.js";
|
|
4
3
|
import type { MultichainReturnType } from "../../../common/types.js";
|
|
@@ -8,6 +7,7 @@ import type {
|
|
|
8
7
|
PublicAllocatorSharedLiquidityType,
|
|
9
8
|
} from "../../../types/morphoMarket.js";
|
|
10
9
|
import type { MorphoReward } from "../../../types/morphoReward.js";
|
|
10
|
+
import { getWithRetry } from "../../axiosWithRetry.js";
|
|
11
11
|
import { getGraphQL } from "../utils/graphql.js";
|
|
12
12
|
import {
|
|
13
13
|
type GetMorphoMarketsRewardsReturnType as LunarIndexerRewardsType,
|
|
@@ -49,7 +49,7 @@ async function fetchSharedLiquidityFromLunar(
|
|
|
49
49
|
lunarIndexerUrl: string,
|
|
50
50
|
chainId: number,
|
|
51
51
|
): Promise<LunarSharedLiquidityResponse> {
|
|
52
|
-
const response = await
|
|
52
|
+
const response = await getWithRetry<LunarSharedLiquidityResponse>(
|
|
53
53
|
`${lunarIndexerUrl}/api/v1/isolated/shared-liquidity/${chainId}`,
|
|
54
54
|
);
|
|
55
55
|
return response.data;
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* @module morpho/markets/lunarIndexerTransform
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import axios from "axios";
|
|
12
11
|
import type { Address } from "viem";
|
|
13
12
|
import { parseUnits } from "viem";
|
|
14
13
|
import { Amount } from "../../../common/amount.js";
|
|
@@ -20,6 +19,7 @@ import type {
|
|
|
20
19
|
PublicAllocatorSharedLiquidityType,
|
|
21
20
|
} from "../../../types/morphoMarket.js";
|
|
22
21
|
import type { MorphoReward } from "../../../types/morphoReward.js";
|
|
22
|
+
import { getWithRetry } from "../../axiosWithRetry.js";
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Lunar Indexer API Response Types
|
|
@@ -185,7 +185,7 @@ export async function fetchMarketsFromIndexer(
|
|
|
185
185
|
}
|
|
186
186
|
const queryString = params.toString();
|
|
187
187
|
const url = `${lunarIndexerUrl}/api/v1/isolated/markets/${chainId}${queryString ? `?${queryString}` : ""}`;
|
|
188
|
-
const response = await
|
|
188
|
+
const response = await getWithRetry<LunarIndexerMarketsResponse>(url);
|
|
189
189
|
return response.data;
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -198,7 +198,7 @@ export async function fetchMarketFromIndexer(
|
|
|
198
198
|
marketId: string,
|
|
199
199
|
): Promise<LunarIndexerMarket> {
|
|
200
200
|
const url = `${lunarIndexerUrl}/api/v1/isolated/market/${chainId}/${marketId.toLowerCase()}`;
|
|
201
|
-
const response = await
|
|
201
|
+
const response = await getWithRetry<LunarIndexerMarket>(url);
|
|
202
202
|
return response.data;
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -240,7 +240,7 @@ export async function fetchMarketSnapshotsFromIndexer(
|
|
|
240
240
|
queryString ? `?${queryString}` : ""
|
|
241
241
|
}`;
|
|
242
242
|
|
|
243
|
-
const response = await
|
|
243
|
+
const response = await getWithRetry<LunarIndexerMarketSnapshotsResponse>(url);
|
|
244
244
|
return response.data;
|
|
245
245
|
}
|
|
246
246
|
|
|
@@ -281,7 +281,8 @@ export async function fetchAccountMarketPortfolioFromIndexer(
|
|
|
281
281
|
queryString ? `?${queryString}` : ""
|
|
282
282
|
}`;
|
|
283
283
|
|
|
284
|
-
const response =
|
|
284
|
+
const response =
|
|
285
|
+
await getWithRetry<LunarIndexerAccountPortfolioResponse>(url);
|
|
285
286
|
return response.data;
|
|
286
287
|
}
|
|
287
288
|
|
package/actions/retry.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry helpers for SDK API calls.
|
|
3
|
+
*
|
|
4
|
+
* Two consumers:
|
|
5
|
+
* 1. `attachRetryInterceptor` — axios response interceptor used inside
|
|
6
|
+
* LunarIndexerClient. One install per axios instance covers every method.
|
|
7
|
+
* 2. `retry` — generic wrapper for ad-hoc `axios.post(...)` callsites in
|
|
8
|
+
* actions that don't go through a shared axios instance.
|
|
9
|
+
*
|
|
10
|
+
* Policy:
|
|
11
|
+
* - 3 attempts (initial + 2 retries) by default
|
|
12
|
+
* - Exponential backoff: 250ms → 500ms (capped at 5s)
|
|
13
|
+
* - Retry only network errors / timeouts / 5xx; never retry 4xx (incl. 404 —
|
|
14
|
+
* retrying a deterministic "not found" wastes time and amplifies log noise)
|
|
15
|
+
* - After retries exhaust, the last error propagates to the caller's try/catch
|
|
16
|
+
* where the existing `environment.onError(...)` wiring picks it up
|
|
17
|
+
*/
|
|
18
|
+
import axios, {
|
|
19
|
+
type AxiosError,
|
|
20
|
+
type AxiosInstance,
|
|
21
|
+
type InternalAxiosRequestConfig,
|
|
22
|
+
} from "axios";
|
|
23
|
+
|
|
24
|
+
export interface RetryOptions {
|
|
25
|
+
maxAttempts?: number;
|
|
26
|
+
initialDelay?: number;
|
|
27
|
+
maxDelay?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
31
|
+
const DEFAULT_INITIAL_DELAY_MS = 250;
|
|
32
|
+
const DEFAULT_MAX_DELAY_MS = 5_000;
|
|
33
|
+
|
|
34
|
+
async function sleep(ms: number): Promise<void> {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function backoffDelay(
|
|
39
|
+
attemptIndex: number,
|
|
40
|
+
initialDelay: number,
|
|
41
|
+
maxDelay: number,
|
|
42
|
+
): number {
|
|
43
|
+
return Math.min(initialDelay * 2 ** attemptIndex, maxDelay);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Decide whether an error represents a transient failure worth retrying.
|
|
48
|
+
* - Axios network errors (no `response`) → retry (server unreachable, timeout)
|
|
49
|
+
* - 5xx responses → retry (server hiccup)
|
|
50
|
+
* - 4xx responses (incl. 404) → do NOT retry (deterministic, won't change)
|
|
51
|
+
* - Non-axios errors (parse errors, code bugs) → do NOT retry
|
|
52
|
+
*/
|
|
53
|
+
export function isRetriableError(error: unknown): boolean {
|
|
54
|
+
if (!axios.isAxiosError(error)) return false;
|
|
55
|
+
if (!error.response) return true;
|
|
56
|
+
return error.response.status >= 500;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Attach a retry-on-failure interceptor to an axios instance. Every request
|
|
61
|
+
* made through this instance is retried up to `maxAttempts` times when the
|
|
62
|
+
* failure is retriable. Per-config state is tracked on a WeakMap so we don't
|
|
63
|
+
* pollute the public axios config shape.
|
|
64
|
+
*/
|
|
65
|
+
export function attachRetryInterceptor(
|
|
66
|
+
instance: AxiosInstance,
|
|
67
|
+
options: RetryOptions = {},
|
|
68
|
+
): void {
|
|
69
|
+
const {
|
|
70
|
+
maxAttempts = DEFAULT_MAX_ATTEMPTS,
|
|
71
|
+
initialDelay = DEFAULT_INITIAL_DELAY_MS,
|
|
72
|
+
maxDelay = DEFAULT_MAX_DELAY_MS,
|
|
73
|
+
} = options;
|
|
74
|
+
const attemptsByConfig = new WeakMap<InternalAxiosRequestConfig, number>();
|
|
75
|
+
|
|
76
|
+
instance.interceptors.response.use(
|
|
77
|
+
(response) => response,
|
|
78
|
+
async (error: AxiosError) => {
|
|
79
|
+
const config = error.config;
|
|
80
|
+
if (!config) throw error;
|
|
81
|
+
if (!isRetriableError(error)) throw error;
|
|
82
|
+
|
|
83
|
+
const previousAttempts = attemptsByConfig.get(config) ?? 0;
|
|
84
|
+
if (previousAttempts >= maxAttempts - 1) throw error;
|
|
85
|
+
|
|
86
|
+
attemptsByConfig.set(config, previousAttempts + 1);
|
|
87
|
+
await sleep(backoffDelay(previousAttempts, initialDelay, maxDelay));
|
|
88
|
+
return instance.request(config);
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generic wrapper for individual axios calls (e.g. raw `axios.post(...)` in
|
|
95
|
+
* action files that don't share a configured instance). Mirrors the
|
|
96
|
+
* interceptor's policy: retry transient failures, fail fast on 4xx.
|
|
97
|
+
*/
|
|
98
|
+
export async function retry<T>(
|
|
99
|
+
fn: () => Promise<T>,
|
|
100
|
+
options: RetryOptions = {},
|
|
101
|
+
): Promise<T> {
|
|
102
|
+
const {
|
|
103
|
+
maxAttempts = DEFAULT_MAX_ATTEMPTS,
|
|
104
|
+
initialDelay = DEFAULT_INITIAL_DELAY_MS,
|
|
105
|
+
maxDelay = DEFAULT_MAX_DELAY_MS,
|
|
106
|
+
} = options;
|
|
107
|
+
let attempt = 0;
|
|
108
|
+
let lastError: unknown;
|
|
109
|
+
while (attempt < maxAttempts) {
|
|
110
|
+
try {
|
|
111
|
+
return await fn();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
lastError = error;
|
|
114
|
+
attempt++;
|
|
115
|
+
if (!isRetriableError(error)) throw error;
|
|
116
|
+
if (attempt >= maxAttempts) break;
|
|
117
|
+
await sleep(backoffDelay(attempt - 1, initialDelay, maxDelay));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw lastError;
|
|
121
|
+
}
|