@moonwell-fi/moonwell-sdk 0.22.0 → 0.24.0
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 +28 -0
- package/_cjs/actions/axiosWithRetry.js +10 -11
- package/_cjs/actions/axiosWithRetry.js.map +1 -1
- package/_cjs/actions/core/getUserBalances.js +11 -24
- package/_cjs/actions/core/getUserBalances.js.map +1 -1
- package/_cjs/actions/core/markets/common.js +30 -35
- package/_cjs/actions/core/markets/common.js.map +1 -1
- package/_cjs/actions/core/markets/getMarkets.js +7 -3
- package/_cjs/actions/core/markets/getMarkets.js.map +1 -1
- package/_cjs/actions/core/user-positions/common.js +56 -60
- package/_cjs/actions/core/user-positions/common.js.map +1 -1
- package/_cjs/actions/core/user-positions/getUserPositions.js +9 -6
- package/_cjs/actions/core/user-positions/getUserPositions.js.map +1 -1
- package/_cjs/actions/governance/proposals/common.js +0 -5
- package/_cjs/actions/governance/proposals/common.js.map +1 -1
- package/_cjs/actions/index.js +3 -1
- package/_cjs/actions/index.js.map +1 -1
- package/_cjs/actions/morpho/markets/common.js +128 -26
- package/_cjs/actions/morpho/markets/common.js.map +1 -1
- package/_cjs/actions/morpho/markets/getMorphoMarket.js +1 -0
- package/_cjs/actions/morpho/markets/getMorphoMarket.js.map +1 -1
- package/_cjs/actions/morpho/markets/getMorphoMarkets.js +1 -0
- package/_cjs/actions/morpho/markets/getMorphoMarkets.js.map +1 -1
- package/_cjs/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js +46 -0
- package/_cjs/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js.map +1 -0
- package/_cjs/actions/morpho/user-positions/common.js +26 -24
- package/_cjs/actions/morpho/user-positions/common.js.map +1 -1
- package/_cjs/actions/morpho/user-positions/getMorphoMarketUserPositions.js +7 -8
- package/_cjs/actions/morpho/user-positions/getMorphoMarketUserPositions.js.map +1 -1
- package/_cjs/actions/morpho/user-positions/getMorphoVaultUserPositions.js +7 -8
- package/_cjs/actions/morpho/user-positions/getMorphoVaultUserPositions.js.map +1 -1
- package/_cjs/actions/retry.js +43 -13
- package/_cjs/actions/retry.js.map +1 -1
- package/_cjs/client/createActions.js +2 -0
- package/_cjs/client/createActions.js.map +1 -1
- package/_cjs/client/createMoonwellClient.js.map +1 -1
- package/_cjs/common/error.js +37 -1
- package/_cjs/common/error.js.map +1 -1
- package/_cjs/common/index.js +2 -1
- package/_cjs/common/index.js.map +1 -1
- package/_cjs/common/readAcrossEnvironments.js +17 -0
- package/_cjs/common/readAcrossEnvironments.js.map +1 -0
- package/_cjs/environments/index.js.map +1 -1
- package/_cjs/environments/types/config.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_cjs/index.js +3 -1
- package/_cjs/index.js.map +1 -1
- package/_esm/actions/axiosWithRetry.js +11 -13
- package/_esm/actions/axiosWithRetry.js.map +1 -1
- package/_esm/actions/core/getUserBalances.js +23 -24
- package/_esm/actions/core/getUserBalances.js.map +1 -1
- package/_esm/actions/core/markets/common.js +34 -35
- package/_esm/actions/core/markets/common.js.map +1 -1
- package/_esm/actions/core/markets/getMarkets.js +7 -3
- package/_esm/actions/core/markets/getMarkets.js.map +1 -1
- package/_esm/actions/core/user-positions/common.js +65 -63
- package/_esm/actions/core/user-positions/common.js.map +1 -1
- package/_esm/actions/core/user-positions/getUserPositions.js +14 -6
- package/_esm/actions/core/user-positions/getUserPositions.js.map +1 -1
- package/_esm/actions/governance/proposals/common.js +0 -2
- package/_esm/actions/governance/proposals/common.js.map +1 -1
- package/_esm/actions/index.js +1 -0
- package/_esm/actions/index.js.map +1 -1
- package/_esm/actions/morpho/markets/common.js +136 -27
- package/_esm/actions/morpho/markets/common.js.map +1 -1
- package/_esm/actions/morpho/markets/getMorphoMarket.js +1 -0
- package/_esm/actions/morpho/markets/getMorphoMarket.js.map +1 -1
- package/_esm/actions/morpho/markets/getMorphoMarkets.js +1 -0
- package/_esm/actions/morpho/markets/getMorphoMarkets.js.map +1 -1
- package/_esm/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js +48 -0
- package/_esm/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js.map +1 -0
- package/_esm/actions/morpho/user-positions/common.js +31 -24
- package/_esm/actions/morpho/user-positions/common.js.map +1 -1
- package/_esm/actions/morpho/user-positions/getMorphoMarketUserPositions.js +12 -8
- package/_esm/actions/morpho/user-positions/getMorphoMarketUserPositions.js.map +1 -1
- package/_esm/actions/morpho/user-positions/getMorphoVaultUserPositions.js +12 -8
- package/_esm/actions/morpho/user-positions/getMorphoVaultUserPositions.js.map +1 -1
- package/_esm/actions/retry.js +45 -15
- package/_esm/actions/retry.js.map +1 -1
- package/_esm/client/createActions.js +2 -0
- package/_esm/client/createActions.js.map +1 -1
- package/_esm/client/createMoonwellClient.js.map +1 -1
- package/_esm/common/error.js +49 -0
- package/_esm/common/error.js.map +1 -1
- package/_esm/common/index.js +1 -1
- package/_esm/common/index.js.map +1 -1
- package/_esm/common/readAcrossEnvironments.js +30 -0
- package/_esm/common/readAcrossEnvironments.js.map +1 -0
- package/_esm/environments/index.js.map +1 -1
- package/_esm/environments/types/config.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/index.js +1 -0
- package/_esm/index.js.map +1 -1
- package/_types/actions/axiosWithRetry.d.ts +1 -0
- package/_types/actions/axiosWithRetry.d.ts.map +1 -1
- package/_types/actions/core/getUserBalances.d.ts.map +1 -1
- package/_types/actions/core/markets/common.d.ts.map +1 -1
- package/_types/actions/core/markets/getMarkets.d.ts.map +1 -1
- package/_types/actions/core/user-positions/common.d.ts.map +1 -1
- package/_types/actions/core/user-positions/getUserPositions.d.ts +5 -0
- package/_types/actions/core/user-positions/getUserPositions.d.ts.map +1 -1
- package/_types/actions/governance/proposals/common.d.ts.map +1 -1
- package/_types/actions/index.d.ts +1 -0
- package/_types/actions/index.d.ts.map +1 -1
- package/_types/actions/morpho/markets/common.d.ts +7 -10
- package/_types/actions/morpho/markets/common.d.ts.map +1 -1
- package/_types/actions/morpho/markets/getMorphoMarket.d.ts +2 -0
- package/_types/actions/morpho/markets/getMorphoMarket.d.ts.map +1 -1
- package/_types/actions/morpho/markets/getMorphoMarkets.d.ts +2 -0
- package/_types/actions/morpho/markets/getMorphoMarkets.d.ts.map +1 -1
- package/_types/actions/morpho/markets/getMorphoMarketsSharedLiquidity.d.ts +17 -0
- package/_types/actions/morpho/markets/getMorphoMarketsSharedLiquidity.d.ts.map +1 -0
- package/_types/actions/morpho/user-positions/common.d.ts.map +1 -1
- package/_types/actions/morpho/user-positions/getMorphoMarketUserPositions.d.ts +5 -0
- package/_types/actions/morpho/user-positions/getMorphoMarketUserPositions.d.ts.map +1 -1
- package/_types/actions/morpho/user-positions/getMorphoVaultUserPositions.d.ts +5 -0
- package/_types/actions/morpho/user-positions/getMorphoVaultUserPositions.d.ts.map +1 -1
- package/_types/actions/retry.d.ts +5 -4
- package/_types/actions/retry.d.ts.map +1 -1
- package/_types/client/createActions.d.ts +2 -0
- package/_types/client/createActions.d.ts.map +1 -1
- package/_types/client/createMoonwellClient.d.ts +2 -5
- package/_types/client/createMoonwellClient.d.ts.map +1 -1
- package/_types/common/error.d.ts +31 -0
- package/_types/common/error.d.ts.map +1 -1
- package/_types/common/index.d.ts +2 -2
- package/_types/common/index.d.ts.map +1 -1
- package/_types/common/readAcrossEnvironments.d.ts +23 -0
- package/_types/common/readAcrossEnvironments.d.ts.map +1 -0
- package/_types/environments/index.d.ts +2 -2
- package/_types/environments/index.d.ts.map +1 -1
- package/_types/environments/types/config.d.ts +8 -8
- package/_types/environments/types/config.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/index.d.ts +3 -1
- package/_types/index.d.ts.map +1 -1
- package/_types/types/morphoMarket.d.ts +9 -0
- package/_types/types/morphoMarket.d.ts.map +1 -1
- package/actions/axiosWithRetry.ts +19 -13
- package/actions/core/getUserBalances.ts +25 -31
- package/actions/core/markets/common.ts +44 -43
- package/actions/core/markets/getMarkets.ts +9 -5
- package/actions/core/user-positions/common.ts +107 -111
- package/actions/core/user-positions/getUserPositions.ts +11 -10
- package/actions/governance/proposals/common.ts +0 -3
- package/actions/index.ts +5 -0
- package/actions/morpho/markets/common.ts +289 -125
- package/actions/morpho/markets/getMorphoMarket.ts +3 -0
- package/actions/morpho/markets/getMorphoMarkets.ts +3 -0
- package/actions/morpho/markets/getMorphoMarketsSharedLiquidity.ts +93 -0
- package/actions/morpho/user-positions/common.ts +48 -42
- package/actions/morpho/user-positions/getMorphoMarketUserPositions.ts +14 -15
- package/actions/morpho/user-positions/getMorphoVaultUserPositions.ts +14 -15
- package/actions/retry.ts +53 -18
- package/client/createActions.ts +13 -0
- package/client/createMoonwellClient.ts +2 -4
- package/common/error.ts +45 -0
- package/common/index.ts +2 -2
- package/common/readAcrossEnvironments.ts +45 -0
- package/environments/index.ts +6 -1
- package/environments/types/config.ts +10 -8
- package/errors/version.ts +1 -1
- package/index.ts +3 -0
- package/package.json +1 -1
- package/types/morphoMarket.ts +10 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
|
|
2
|
+
import { getEnvironmentFromArgs } from "../../../common/index.js";
|
|
3
|
+
import type { NetworkParameterType } from "../../../common/types.js";
|
|
4
|
+
import type { Chain } from "../../../environments/index.js";
|
|
5
|
+
import type {
|
|
6
|
+
MorphoMarket,
|
|
7
|
+
MorphoMarketSharedLiquidity,
|
|
8
|
+
} from "../../../types/morphoMarket.js";
|
|
9
|
+
import {
|
|
10
|
+
computeSharedLiquidityFromLunar,
|
|
11
|
+
fetchSharedLiquidityFromLunar,
|
|
12
|
+
} from "./common.js";
|
|
13
|
+
|
|
14
|
+
export type GetMorphoMarketsSharedLiquidityParameters<
|
|
15
|
+
environments,
|
|
16
|
+
network extends Chain | undefined,
|
|
17
|
+
> = NetworkParameterType<environments, network> & {
|
|
18
|
+
/** Pass the full chain's base market result to retain allocation parameters. */
|
|
19
|
+
markets: readonly MorphoMarket[];
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
/** Overall deadline, including retries and backoff. Defaults to 15 seconds. */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type GetMorphoMarketsSharedLiquidityReturnType = Promise<
|
|
26
|
+
MorphoMarketSharedLiquidity[]
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
/** Fetch allocator liquidity independently, without refetching base market data.
|
|
30
|
+
* Rejects on failure so callers can retain the previous successful snapshot and
|
|
31
|
+
* distinguish unavailable liquidity from a successful empty allocation list. */
|
|
32
|
+
export async function getMorphoMarketsSharedLiquidity<
|
|
33
|
+
environments,
|
|
34
|
+
Network extends Chain | undefined,
|
|
35
|
+
>(
|
|
36
|
+
client: MoonwellClient,
|
|
37
|
+
args: GetMorphoMarketsSharedLiquidityParameters<environments, Network>,
|
|
38
|
+
): GetMorphoMarketsSharedLiquidityReturnType {
|
|
39
|
+
const environment = getEnvironmentFromArgs(client, args);
|
|
40
|
+
if (!environment?.lunarIndexerUrl) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
"Independent shared liquidity requires a configured Lunar Indexer environment",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (args.markets.some((market) => market.chainId !== environment.chainId)) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"Shared-liquidity markets must belong to the requested chain",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (args.markets.length === 0) return [];
|
|
51
|
+
|
|
52
|
+
const marketParamsMap = new Map(
|
|
53
|
+
args.markets.map((market) => [
|
|
54
|
+
market.marketId.toLowerCase(),
|
|
55
|
+
{
|
|
56
|
+
oracle: market.marketParams.oracle,
|
|
57
|
+
irm: market.marketParams.irm,
|
|
58
|
+
lltv: market.marketParams.lltv.toString(),
|
|
59
|
+
// Display token metadata can use the native-token zero address for ETH.
|
|
60
|
+
// Allocator transactions need the actual ERC20 addresses from Morpho.
|
|
61
|
+
loanToken: {
|
|
62
|
+
address: market.marketParams.loanToken,
|
|
63
|
+
decimals: market.loanToken.decimals,
|
|
64
|
+
},
|
|
65
|
+
collateralToken: {
|
|
66
|
+
address: market.marketParams.collateralToken,
|
|
67
|
+
decimals: market.collateralToken.decimals,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
]),
|
|
71
|
+
);
|
|
72
|
+
try {
|
|
73
|
+
const data = await fetchSharedLiquidityFromLunar(
|
|
74
|
+
environment.lunarIndexerUrl,
|
|
75
|
+
environment.chainId,
|
|
76
|
+
args,
|
|
77
|
+
);
|
|
78
|
+
return computeSharedLiquidityFromLunar(
|
|
79
|
+
data,
|
|
80
|
+
args.markets.map((market) => market.marketId),
|
|
81
|
+
marketParamsMap,
|
|
82
|
+
environment.chainId,
|
|
83
|
+
);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (!args.signal?.aborted) {
|
|
86
|
+
environment.onError?.(error, {
|
|
87
|
+
source: "morpho-shared-liquidity",
|
|
88
|
+
chainId: environment.chainId,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -59,50 +59,56 @@ export async function getMorphoMarketUserPositionsData(params: {
|
|
|
59
59
|
account: Address;
|
|
60
60
|
markets?: string[] | undefined;
|
|
61
61
|
}): Promise<MorphoMarketUserPosition[]> {
|
|
62
|
+
// A chain without a Morpho deployment has no positions to read. Both the
|
|
63
|
+
// plural action and the singular getMorphoMarketUserPosition come through
|
|
64
|
+
// here, so the guard lives at the source rather than in each caller.
|
|
65
|
+
const morphoViews = params.environment.contracts.morphoViews;
|
|
66
|
+
if (!morphoViews) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
const markets = Object.values(params.environment.config.morphoMarkets).filter(
|
|
63
71
|
(market) => (params.markets ? params.markets.includes(market.id) : true),
|
|
64
72
|
);
|
|
65
73
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
return [];
|
|
107
|
-
}
|
|
74
|
+
// A failed read must propagate: swallowing it here made a Base RPC outage
|
|
75
|
+
// look like "user has no isolated positions" (MOO-884).
|
|
76
|
+
const userMarketPositions = await morphoViews.read.getMorphoBlueUserBalances([
|
|
77
|
+
markets.map((market) => market.id),
|
|
78
|
+
params.account,
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
return markets.map((market, index) => {
|
|
82
|
+
const position = userMarketPositions[index];
|
|
83
|
+
|
|
84
|
+
const loanToken = params.environment.config.tokens[market.loanToken];
|
|
85
|
+
|
|
86
|
+
const collateralToken =
|
|
87
|
+
params.environment.config.tokens[market.collateralToken];
|
|
88
|
+
|
|
89
|
+
const supplied = new Amount(
|
|
90
|
+
position.collateralAssets,
|
|
91
|
+
collateralToken.decimals,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const borrowed = new Amount(position.loanAssets, loanToken.decimals);
|
|
95
|
+
|
|
96
|
+
const borrowedShares = new Amount(
|
|
97
|
+
position.loanShares,
|
|
98
|
+
loanToken.decimals + 6,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const result: MorphoMarketUserPosition = {
|
|
102
|
+
chainId: params.environment.chainId,
|
|
103
|
+
account: params.account,
|
|
104
|
+
marketId: market.id,
|
|
105
|
+
loanToken,
|
|
106
|
+
collateralToken,
|
|
107
|
+
supplied,
|
|
108
|
+
borrowed,
|
|
109
|
+
borrowedShares,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return result;
|
|
113
|
+
});
|
|
108
114
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Address } from "viem";
|
|
2
2
|
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
|
|
3
3
|
import { getEnvironmentsFromArgs } from "../../../common/index.js";
|
|
4
|
+
import { readAcrossEnvironments } from "../../../common/readAcrossEnvironments.js";
|
|
4
5
|
import type { OptionalNetworkParameterType } from "../../../common/types.js";
|
|
5
6
|
import type { Chain } from "../../../environments/index.js";
|
|
6
7
|
import type { MorphoMarketUserPosition } from "../../../types/morphoUserPosition.js";
|
|
@@ -17,6 +18,11 @@ export type GetMorphoMarketUserPositionsReturnType = Promise<
|
|
|
17
18
|
MorphoMarketUserPosition[]
|
|
18
19
|
>;
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Rejects with a `ChainReadError` when the read fails on any requested chain,
|
|
23
|
+
* so a failed RPC never looks like an empty position list. The error lists the
|
|
24
|
+
* failed chains and carries the positions from the chains that succeeded.
|
|
25
|
+
*/
|
|
20
26
|
export async function getMorphoMarketUserPositions<
|
|
21
27
|
environments,
|
|
22
28
|
Network extends Chain | undefined,
|
|
@@ -26,20 +32,13 @@ export async function getMorphoMarketUserPositions<
|
|
|
26
32
|
): GetMorphoMarketUserPositionsReturnType {
|
|
27
33
|
const environments = getEnvironmentsFromArgs(client, args);
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
environments
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
35
|
+
return readAcrossEnvironments({
|
|
36
|
+
environments,
|
|
37
|
+
source: "getMorphoMarketUserPositions",
|
|
38
|
+
read: (environment) =>
|
|
39
|
+
getMorphoMarketUserPositionsData({
|
|
40
|
+
environment,
|
|
41
|
+
account: args.userAddress,
|
|
37
42
|
}),
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const result = settled.flatMap((s) =>
|
|
41
|
-
s.status === "fulfilled" ? s.value : [],
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
return result;
|
|
43
|
+
});
|
|
45
44
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Address, Chain } from "viem";
|
|
2
2
|
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
|
|
3
3
|
import { getEnvironmentsFromArgs } from "../../../common/index.js";
|
|
4
|
+
import { readAcrossEnvironments } from "../../../common/readAcrossEnvironments.js";
|
|
4
5
|
import type { OptionalNetworkParameterType } from "../../../common/types.js";
|
|
5
6
|
import type { MorphoVaultUserPosition } from "../../../types/morphoUserPosition.js";
|
|
6
7
|
import { getMorphoVaultUserPositionsData } from "./common.js";
|
|
@@ -16,6 +17,11 @@ export type GetMorphoVaultUserPositionsReturnType = Promise<
|
|
|
16
17
|
MorphoVaultUserPosition[]
|
|
17
18
|
>;
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Rejects with a `ChainReadError` when the read fails on any requested chain,
|
|
22
|
+
* so a failed RPC never looks like an empty position list. The error lists the
|
|
23
|
+
* failed chains and carries the positions from the chains that succeeded.
|
|
24
|
+
*/
|
|
19
25
|
export async function getMorphoVaultUserPositions<
|
|
20
26
|
environments,
|
|
21
27
|
Network extends Chain | undefined,
|
|
@@ -25,20 +31,13 @@ export async function getMorphoVaultUserPositions<
|
|
|
25
31
|
): GetMorphoVaultUserPositionsReturnType {
|
|
26
32
|
const environments = getEnvironmentsFromArgs(client, args);
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
environments
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
34
|
+
return readAcrossEnvironments({
|
|
35
|
+
environments,
|
|
36
|
+
source: "getMorphoVaultUserPositions",
|
|
37
|
+
read: (environment) =>
|
|
38
|
+
getMorphoVaultUserPositionsData({
|
|
39
|
+
environment,
|
|
40
|
+
account: args.userAddress,
|
|
36
41
|
}),
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const result = settled.flatMap((s) =>
|
|
40
|
-
s.status === "fulfilled" ? s.value : [],
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
return result;
|
|
42
|
+
});
|
|
44
43
|
}
|
package/actions/retry.ts
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* where the existing `environment.onError(...)` wiring picks it up
|
|
17
17
|
*/
|
|
18
18
|
import axios, {
|
|
19
|
-
type AxiosError,
|
|
20
19
|
type AxiosInstance,
|
|
20
|
+
type GenericAbortSignal,
|
|
21
21
|
type InternalAxiosRequestConfig,
|
|
22
22
|
} from "axios";
|
|
23
23
|
|
|
@@ -25,14 +25,30 @@ export interface RetryOptions {
|
|
|
25
25
|
maxAttempts?: number;
|
|
26
26
|
initialDelay?: number;
|
|
27
27
|
maxDelay?: number;
|
|
28
|
+
signal?: GenericAbortSignal | undefined;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
31
32
|
const DEFAULT_INITIAL_DELAY_MS = 250;
|
|
32
33
|
const DEFAULT_MAX_DELAY_MS = 5_000;
|
|
33
34
|
|
|
34
|
-
async function sleep(ms: number): Promise<void> {
|
|
35
|
-
return new Promise((resolve) =>
|
|
35
|
+
async function sleep(ms: number, signal?: GenericAbortSignal): Promise<void> {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
if (signal?.aborted) {
|
|
38
|
+
reject(new axios.CanceledError());
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const onAbort = (): void => {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
signal?.removeEventListener?.("abort", onAbort);
|
|
44
|
+
reject(new axios.CanceledError());
|
|
45
|
+
};
|
|
46
|
+
const timer = setTimeout(() => {
|
|
47
|
+
signal?.removeEventListener?.("abort", onAbort);
|
|
48
|
+
resolve();
|
|
49
|
+
}, ms);
|
|
50
|
+
signal?.addEventListener?.("abort", onAbort, { once: true });
|
|
51
|
+
});
|
|
36
52
|
}
|
|
37
53
|
|
|
38
54
|
function backoffDelay(
|
|
@@ -51,6 +67,7 @@ function backoffDelay(
|
|
|
51
67
|
* - Non-axios errors (parse errors, code bugs) → do NOT retry
|
|
52
68
|
*/
|
|
53
69
|
export function isRetriableError(error: unknown): boolean {
|
|
70
|
+
if (axios.isCancel(error)) return false;
|
|
54
71
|
if (!axios.isAxiosError(error)) return false;
|
|
55
72
|
if (!error.response) return true;
|
|
56
73
|
return error.response.status >= 500;
|
|
@@ -59,33 +76,49 @@ export function isRetriableError(error: unknown): boolean {
|
|
|
59
76
|
/**
|
|
60
77
|
* Attach a retry-on-failure interceptor to an axios instance. Every request
|
|
61
78
|
* made through this instance is retried up to `maxAttempts` times when the
|
|
62
|
-
* failure is retriable.
|
|
63
|
-
*
|
|
79
|
+
* failure is retriable. Axios merges into a new config on every request, so
|
|
80
|
+
* the attempt counter must travel with that config rather than its identity.
|
|
64
81
|
*/
|
|
65
82
|
export function attachRetryInterceptor(
|
|
66
83
|
instance: AxiosInstance,
|
|
67
|
-
options: RetryOptions = {},
|
|
84
|
+
options: Omit<RetryOptions, "signal"> = {},
|
|
68
85
|
): void {
|
|
69
86
|
const {
|
|
70
87
|
maxAttempts = DEFAULT_MAX_ATTEMPTS,
|
|
71
88
|
initialDelay = DEFAULT_INITIAL_DELAY_MS,
|
|
72
89
|
maxDelay = DEFAULT_MAX_DELAY_MS,
|
|
73
90
|
} = options;
|
|
74
|
-
|
|
91
|
+
interface RetryConfig extends InternalAxiosRequestConfig {
|
|
92
|
+
moonwellRetryAttempts?: number | undefined;
|
|
93
|
+
}
|
|
75
94
|
|
|
76
95
|
instance.interceptors.response.use(
|
|
77
|
-
(response) =>
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
(response) => {
|
|
97
|
+
const config: RetryConfig = response.config;
|
|
98
|
+
config.moonwellRetryAttempts = undefined;
|
|
99
|
+
return response;
|
|
100
|
+
},
|
|
101
|
+
async (error: unknown) => {
|
|
102
|
+
if (!axios.isAxiosError(error)) throw error;
|
|
103
|
+
const config: RetryConfig | undefined = error.config;
|
|
80
104
|
if (!config) throw error;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
const previousAttempts = config.moonwellRetryAttempts ?? 0;
|
|
106
|
+
if (!isRetriableError(error) || previousAttempts >= maxAttempts - 1) {
|
|
107
|
+
config.moonwellRetryAttempts = undefined;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
85
110
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
111
|
+
config.moonwellRetryAttempts = previousAttempts + 1;
|
|
112
|
+
try {
|
|
113
|
+
await sleep(
|
|
114
|
+
backoffDelay(previousAttempts, initialDelay, maxDelay),
|
|
115
|
+
config.signal,
|
|
116
|
+
);
|
|
117
|
+
return await instance.request(config);
|
|
118
|
+
} catch (retryError) {
|
|
119
|
+
config.moonwellRetryAttempts = undefined;
|
|
120
|
+
throw retryError;
|
|
121
|
+
}
|
|
89
122
|
},
|
|
90
123
|
);
|
|
91
124
|
}
|
|
@@ -103,10 +136,12 @@ export async function retry<T>(
|
|
|
103
136
|
maxAttempts = DEFAULT_MAX_ATTEMPTS,
|
|
104
137
|
initialDelay = DEFAULT_INITIAL_DELAY_MS,
|
|
105
138
|
maxDelay = DEFAULT_MAX_DELAY_MS,
|
|
139
|
+
signal,
|
|
106
140
|
} = options;
|
|
107
141
|
let attempt = 0;
|
|
108
142
|
let lastError: unknown;
|
|
109
143
|
while (attempt < maxAttempts) {
|
|
144
|
+
if (signal?.aborted) throw new axios.CanceledError();
|
|
110
145
|
try {
|
|
111
146
|
return await fn();
|
|
112
147
|
} catch (error) {
|
|
@@ -114,7 +149,7 @@ export async function retry<T>(
|
|
|
114
149
|
attempt++;
|
|
115
150
|
if (!isRetriableError(error)) throw error;
|
|
116
151
|
if (attempt >= maxAttempts) break;
|
|
117
|
-
await sleep(backoffDelay(attempt - 1, initialDelay, maxDelay));
|
|
152
|
+
await sleep(backoffDelay(attempt - 1, initialDelay, maxDelay), signal);
|
|
118
153
|
}
|
|
119
154
|
}
|
|
120
155
|
throw lastError;
|
package/client/createActions.ts
CHANGED
|
@@ -143,6 +143,11 @@ import {
|
|
|
143
143
|
type GetMorphoMarketsReturnType,
|
|
144
144
|
getMorphoMarkets,
|
|
145
145
|
} from "../actions/morpho/markets/getMorphoMarkets.js";
|
|
146
|
+
import {
|
|
147
|
+
type GetMorphoMarketsSharedLiquidityParameters,
|
|
148
|
+
type GetMorphoMarketsSharedLiquidityReturnType,
|
|
149
|
+
getMorphoMarketsSharedLiquidity,
|
|
150
|
+
} from "../actions/morpho/markets/getMorphoMarketsSharedLiquidity.js";
|
|
146
151
|
import {
|
|
147
152
|
type GetMorphoMarketUserPositionParameters,
|
|
148
153
|
type GetMorphoMarketUserPositionReturnType,
|
|
@@ -288,6 +293,12 @@ export type Actions<
|
|
|
288
293
|
args?: GetMorphoMarketsParameters<environments, chain>,
|
|
289
294
|
) => GetMorphoMarketsReturnType;
|
|
290
295
|
|
|
296
|
+
getMorphoMarketsSharedLiquidity: <
|
|
297
|
+
chain extends Chain | undefined = Chain | undefined,
|
|
298
|
+
>(
|
|
299
|
+
args: GetMorphoMarketsSharedLiquidityParameters<environments, chain>,
|
|
300
|
+
) => GetMorphoMarketsSharedLiquidityReturnType;
|
|
301
|
+
|
|
291
302
|
getMorphoMarketUserPosition: <
|
|
292
303
|
chain extends Chain | undefined = Chain | undefined,
|
|
293
304
|
>(
|
|
@@ -399,6 +410,8 @@ export const actions = <
|
|
|
399
410
|
getUserVotingPowers: (args) => getUserVotingPowers(client, args),
|
|
400
411
|
getMorphoMarket: (args) => getMorphoMarket(client, args),
|
|
401
412
|
getMorphoMarkets: (args) => getMorphoMarkets(client, args),
|
|
413
|
+
getMorphoMarketsSharedLiquidity: (args) =>
|
|
414
|
+
getMorphoMarketsSharedLiquidity(client, args),
|
|
402
415
|
getMorphoMarketUserPosition: (args) =>
|
|
403
416
|
getMorphoMarketUserPosition(client, args),
|
|
404
417
|
getMorphoMarketUserPositions: (args) =>
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type BaseEnvironment,
|
|
6
6
|
type Environment,
|
|
7
7
|
type EthereumEnvironment,
|
|
8
|
+
type OnErrorContext,
|
|
8
9
|
type OptimismEnvironment,
|
|
9
10
|
type PolygonEnvironment,
|
|
10
11
|
type SupportedChains,
|
|
@@ -64,10 +65,7 @@ export type NetworksConfig<networks> = {} extends networks
|
|
|
64
65
|
|
|
65
66
|
export const createMoonwellClient = <const networks>(config: {
|
|
66
67
|
networks: NetworksConfig<Narrow<networks>>;
|
|
67
|
-
onError?: (
|
|
68
|
-
error: unknown,
|
|
69
|
-
context: { source: string; chainId: number },
|
|
70
|
-
) => void;
|
|
68
|
+
onError?: (error: unknown, context: OnErrorContext) => void;
|
|
71
69
|
}) => {
|
|
72
70
|
const environments = Object.entries(
|
|
73
71
|
config.networks as NetworksConfig<SupportedChains>,
|
package/common/error.ts
CHANGED
|
@@ -28,3 +28,48 @@ export class HttpRequestError extends BaseError {
|
|
|
28
28
|
Object.setPrototypeOf(this, HttpRequestError.prototype);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
/** One failed per-chain read inside a multi-chain action. */
|
|
33
|
+
export type ChainReadFailure = {
|
|
34
|
+
chainId: number;
|
|
35
|
+
/** The original error from the RPC / contract read, unchanged. */
|
|
36
|
+
reason: unknown;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Thrown by multi-chain read actions (`getUserPositions`,
|
|
41
|
+
* `getMorphoMarketUserPositions`, `getMorphoVaultUserPositions`) when the read
|
|
42
|
+
* fails on one or more of the requested chains.
|
|
43
|
+
*
|
|
44
|
+
* `failures` lists every failed chain with its original error and `data`
|
|
45
|
+
* carries the results from the chains that succeeded, so a consumer can keep
|
|
46
|
+
* rendering the healthy chains while flagging the broken one. `errors` (from
|
|
47
|
+
* `AggregateError`) holds the same original errors in `failures` order.
|
|
48
|
+
*
|
|
49
|
+
* These failures are surfaced only through this rejection, not additionally
|
|
50
|
+
* through `onError`: a rejection is the consumer's to report, and reporting it
|
|
51
|
+
* on both paths doubled telemetry under query-library retries.
|
|
52
|
+
*/
|
|
53
|
+
export class ChainReadError<T = unknown> extends AggregateError {
|
|
54
|
+
override readonly name = "ChainReadError";
|
|
55
|
+
readonly source: string;
|
|
56
|
+
readonly failures: ChainReadFailure[];
|
|
57
|
+
readonly data: T[];
|
|
58
|
+
|
|
59
|
+
constructor(params: {
|
|
60
|
+
source: string;
|
|
61
|
+
failures: ChainReadFailure[];
|
|
62
|
+
data: T[];
|
|
63
|
+
}) {
|
|
64
|
+
const chainIds = params.failures
|
|
65
|
+
.map((failure) => failure.chainId)
|
|
66
|
+
.join(", ");
|
|
67
|
+
super(
|
|
68
|
+
params.failures.map((failure) => failure.reason),
|
|
69
|
+
`[moonwell-sdk] ${params.source}: read failed on chainIds ${chainIds}`,
|
|
70
|
+
);
|
|
71
|
+
this.source = params.source;
|
|
72
|
+
this.failures = params.failures;
|
|
73
|
+
this.data = params.data;
|
|
74
|
+
}
|
|
75
|
+
}
|
package/common/index.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { Environment } from "../environments/index.js";
|
|
|
5
5
|
|
|
6
6
|
dayjs.extend(utc);
|
|
7
7
|
export { Amount } from "./amount.js";
|
|
8
|
-
export { BaseError, HttpRequestError } from "./error.js";
|
|
9
|
-
export type { HttpRequestErrorType } from "./error.js";
|
|
8
|
+
export { BaseError, ChainReadError, HttpRequestError } from "./error.js";
|
|
9
|
+
export type { ChainReadFailure, HttpRequestErrorType } from "./error.js";
|
|
10
10
|
export { getBlockNumberAtTimestamp } from "./getBlockNumberAtTimestamp.js";
|
|
11
11
|
export type { MultichainReturnType } from "./types.js";
|
|
12
12
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Environment } from "../environments/index.js";
|
|
2
|
+
import { ChainReadError } from "./error.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Run a per-environment read on every environment and flatten the results.
|
|
6
|
+
*
|
|
7
|
+
* Unlike a bare `Promise.allSettled` that keeps only fulfilled entries, a
|
|
8
|
+
* rejected environment is never dropped silently: if any environment fails,
|
|
9
|
+
* the call rejects with a `ChainReadError` that lists every failed chain with
|
|
10
|
+
* its original error and carries the results of the chains that succeeded.
|
|
11
|
+
* For user positions a silently missing chain is indistinguishable from "no
|
|
12
|
+
* position", which drives wrong balances and borrow limits downstream; the
|
|
13
|
+
* partial `data` on the error lets a consumer keep rendering the healthy
|
|
14
|
+
* chains while flagging the broken one.
|
|
15
|
+
*
|
|
16
|
+
* Failures are surfaced only through the rejection, not additionally through
|
|
17
|
+
* `environment.onError`, so a consumer that retries the call is not told about
|
|
18
|
+
* the same outage once per attempt per chain.
|
|
19
|
+
*/
|
|
20
|
+
export async function readAcrossEnvironments<T>(params: {
|
|
21
|
+
environments: Environment[];
|
|
22
|
+
source: string;
|
|
23
|
+
read: (environment: Environment) => Promise<T[]>;
|
|
24
|
+
}): Promise<T[]> {
|
|
25
|
+
const { environments, source, read } = params;
|
|
26
|
+
|
|
27
|
+
const settled = await Promise.allSettled(
|
|
28
|
+
environments.map((environment) => read(environment)),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const data = settled.flatMap((result) =>
|
|
32
|
+
result.status === "fulfilled" ? result.value : [],
|
|
33
|
+
);
|
|
34
|
+
const failures = settled.flatMap((result, index) =>
|
|
35
|
+
result.status === "rejected"
|
|
36
|
+
? [{ chainId: environments[index].chainId, reason: result.reason }]
|
|
37
|
+
: [],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (failures.length > 0) {
|
|
41
|
+
throw new ChainReadError({ source, failures, data });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return data;
|
|
45
|
+
}
|
package/environments/index.ts
CHANGED
|
@@ -49,7 +49,11 @@ import {
|
|
|
49
49
|
} from "./definitions/polygon/environment.js";
|
|
50
50
|
|
|
51
51
|
import { optimism } from "viem/chains";
|
|
52
|
-
import type {
|
|
52
|
+
import type {
|
|
53
|
+
Environment,
|
|
54
|
+
OnErrorContext,
|
|
55
|
+
TokenConfig,
|
|
56
|
+
} from "./types/config.js";
|
|
53
57
|
|
|
54
58
|
export {
|
|
55
59
|
arbitrum,
|
|
@@ -68,6 +72,7 @@ export type {
|
|
|
68
72
|
GovernanceToken,
|
|
69
73
|
GovernanceTokenInfo,
|
|
70
74
|
GovernanceTokensType,
|
|
75
|
+
OnErrorContext,
|
|
71
76
|
Prettify,
|
|
72
77
|
SupportedChains,
|
|
73
78
|
SupportedChainsIds,
|
|
@@ -227,6 +227,14 @@ export type MorphoMarketsConfig<markets, tokens> = {} extends markets
|
|
|
227
227
|
export type ContractsConfig<contracts, tokens> = {} extends contracts
|
|
228
228
|
? {}
|
|
229
229
|
: { [name in keyof ContractConfig<tokens>]?: ContractConfig<tokens>[name] };
|
|
230
|
+
|
|
231
|
+
/** Context passed to `onError` callbacks. `token` is set for per-token read failures. */
|
|
232
|
+
export type OnErrorContext = {
|
|
233
|
+
source: string;
|
|
234
|
+
chainId: number;
|
|
235
|
+
token?: Address;
|
|
236
|
+
};
|
|
237
|
+
|
|
230
238
|
export type CustomConfig<custom> = {} extends custom
|
|
231
239
|
? {}
|
|
232
240
|
: { [name in keyof CustomConfigType]?: CustomConfigType[name] };
|
|
@@ -276,10 +284,7 @@ export const createEnvironmentConfig = <
|
|
|
276
284
|
indexerUrl?: string;
|
|
277
285
|
governanceIndexerUrl: string;
|
|
278
286
|
lunarIndexerUrl?: string;
|
|
279
|
-
onError?: (
|
|
280
|
-
error: unknown,
|
|
281
|
-
context: { source: string; chainId: number },
|
|
282
|
-
) => void;
|
|
287
|
+
onError?: (error: unknown, context: OnErrorContext) => void;
|
|
283
288
|
tokens: TokensConfig<tokens>;
|
|
284
289
|
markets: MarketsConfig<markets, tokens>;
|
|
285
290
|
vaults: VaultsConfig<vaults, tokens>;
|
|
@@ -481,10 +486,7 @@ export type Environment<
|
|
|
481
486
|
indexerUrl?: string;
|
|
482
487
|
governanceIndexerUrl: string;
|
|
483
488
|
lunarIndexerUrl?: string;
|
|
484
|
-
onError?: (
|
|
485
|
-
error: unknown,
|
|
486
|
-
context: { source: string; chainId: number },
|
|
487
|
-
) => void;
|
|
489
|
+
onError?: (error: unknown, context: OnErrorContext) => void;
|
|
488
490
|
tokens: {
|
|
489
491
|
[name in keyof tokens]: TokenContractReturnType;
|
|
490
492
|
};
|
package/errors/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.
|
|
1
|
+
export const version = '0.24.0'
|
package/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ export {
|
|
|
5
5
|
|
|
6
6
|
export { MerklApiError } from "./actions/morpho/user-rewards/common.js";
|
|
7
7
|
export { MorphoUserRewardsAggregateError } from "./actions/morpho/user-rewards/getMorphoUserRewards.js";
|
|
8
|
+
export { ChainReadError } from "./common/error.js";
|
|
9
|
+
export type { ChainReadFailure } from "./common/error.js";
|
|
8
10
|
|
|
9
11
|
export type {
|
|
10
12
|
MarketConfig,
|
|
@@ -18,6 +20,7 @@ export type { Discussion } from "./types/discussion.js";
|
|
|
18
20
|
export type { Market, MarketReward, MarketSnapshot } from "./types/market.js";
|
|
19
21
|
export type {
|
|
20
22
|
MorphoMarket,
|
|
23
|
+
MorphoMarketSharedLiquidity,
|
|
21
24
|
MorphoMarketParamsType,
|
|
22
25
|
PublicAllocatorSharedLiquidityType,
|
|
23
26
|
} from "./types/morphoMarket.js";
|