@moonwell-fi/moonwell-sdk 0.23.0 → 0.25.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 +36 -0
- package/_cjs/actions/axiosWithRetry.js +10 -11
- package/_cjs/actions/axiosWithRetry.js.map +1 -1
- package/_cjs/actions/core/getUserBalances.js +63 -32
- package/_cjs/actions/core/getUserBalances.js.map +1 -1
- package/_cjs/actions/core/markets/common.js +31 -42
- 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/getStakingInfo.js +1 -7
- package/_cjs/actions/governance/getStakingInfo.js.map +1 -1
- package/_cjs/actions/governance/getUserStakingInfo.js +1 -7
- package/_cjs/actions/governance/getUserStakingInfo.js.map +1 -1
- package/_cjs/actions/governance/getWellPrice.js +51 -1
- package/_cjs/actions/governance/getWellPrice.js.map +1 -1
- package/_cjs/actions/governance/proposals/common.js +34 -15
- package/_cjs/actions/governance/proposals/common.js.map +1 -1
- package/_cjs/actions/governance/proposals/getProposals.js +5 -4
- package/_cjs/actions/governance/proposals/getProposals.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 +155 -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/morpho/user-rewards/common.js +1 -7
- package/_cjs/actions/morpho/user-rewards/common.js.map +1 -1
- package/_cjs/actions/morpho/vaults/common.js +104 -87
- package/_cjs/actions/morpho/vaults/common.js.map +1 -1
- package/_cjs/actions/morpho/vaults/lunarIndexerTransform.js +31 -3
- package/_cjs/actions/morpho/vaults/lunarIndexerTransform.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/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/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 +72 -35
- package/_esm/actions/core/getUserBalances.js.map +1 -1
- package/_esm/actions/core/markets/common.js +36 -43
- 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/getStakingInfo.js +2 -8
- package/_esm/actions/governance/getStakingInfo.js.map +1 -1
- package/_esm/actions/governance/getUserStakingInfo.js +2 -8
- package/_esm/actions/governance/getUserStakingInfo.js.map +1 -1
- package/_esm/actions/governance/getWellPrice.js +67 -7
- package/_esm/actions/governance/getWellPrice.js.map +1 -1
- package/_esm/actions/governance/proposals/common.js +37 -12
- package/_esm/actions/governance/proposals/common.js.map +1 -1
- package/_esm/actions/governance/proposals/getProposals.js +7 -5
- package/_esm/actions/governance/proposals/getProposals.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 +168 -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/morpho/user-rewards/common.js +2 -8
- package/_esm/actions/morpho/user-rewards/common.js.map +1 -1
- package/_esm/actions/morpho/vaults/common.js +110 -91
- package/_esm/actions/morpho/vaults/common.js.map +1 -1
- package/_esm/actions/morpho/vaults/lunarIndexerTransform.js +36 -4
- package/_esm/actions/morpho/vaults/lunarIndexerTransform.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/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/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/getStakingInfo.d.ts.map +1 -1
- package/_types/actions/governance/getUserStakingInfo.d.ts.map +1 -1
- package/_types/actions/governance/getWellPrice.d.ts +9 -1
- package/_types/actions/governance/getWellPrice.d.ts.map +1 -1
- package/_types/actions/governance/proposals/common.d.ts.map +1 -1
- package/_types/actions/governance/proposals/getProposals.d.ts +10 -1
- package/_types/actions/governance/proposals/getProposals.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/morpho/user-rewards/common.d.ts.map +1 -1
- package/_types/actions/morpho/vaults/common.d.ts.map +1 -1
- package/_types/actions/morpho/vaults/lunarIndexerTransform.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/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/types/config.d.ts +11 -1
- 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 +98 -51
- package/actions/core/markets/common.ts +49 -51
- 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/getStakingInfo.ts +2 -9
- package/actions/governance/getUserStakingInfo.ts +2 -8
- package/actions/governance/getWellPrice.ts +99 -7
- package/actions/governance/proposals/common.ts +44 -19
- package/actions/governance/proposals/getProposals.ts +22 -5
- package/actions/index.ts +5 -0
- package/actions/morpho/markets/common.ts +324 -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/morpho/user-rewards/common.ts +2 -8
- package/actions/morpho/vaults/common.ts +161 -127
- package/actions/morpho/vaults/lunarIndexerTransform.ts +41 -6
- package/actions/retry.ts +53 -18
- package/client/createActions.ts +13 -0
- package/common/error.ts +45 -0
- package/common/index.ts +2 -2
- package/common/readAcrossEnvironments.ts +45 -0
- package/environments/types/config.ts +11 -1
- package/errors/version.ts +1 -1
- package/index.ts +3 -0
- package/package.json +1 -1
- package/types/morphoMarket.ts +10 -0
|
@@ -30,49 +30,74 @@ export type GetUserBalancesReturnType = Promise<UserBalance[]>;
|
|
|
30
30
|
* empty" and disabled the confirm button for 59 users (Sentry
|
|
31
31
|
* MOONWELL-FRONTEND-195). Callers settle these with `Promise.allSettled` and
|
|
32
32
|
* omit the rejected tokens, so a consumer that finds no entry knows the balance
|
|
33
|
-
* is UNKNOWN rather than zero.
|
|
34
|
-
* (source `user-balances-token-read`)
|
|
35
|
-
* degraded read.
|
|
33
|
+
* is UNKNOWN rather than zero. Failures are routed to `environment.onError`
|
|
34
|
+
* (source `user-balances-token-read`) once per chain by `getUserBalances`, so
|
|
35
|
+
* Sentry-wired consumers can see the degraded read.
|
|
36
36
|
*/
|
|
37
37
|
const getTokenBalance = async (
|
|
38
38
|
environment: Environment,
|
|
39
39
|
userAddress: Address,
|
|
40
40
|
tokenAddress: Address,
|
|
41
41
|
): Promise<{ amount: bigint; token: `0x${string}` }> => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
address: userAddress,
|
|
46
|
-
});
|
|
47
|
-
return { amount: BigInt(balance), token: tokenAddress };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const erc20Abi = parseAbi([
|
|
51
|
-
"function balanceOf(address owner) view returns (uint256)",
|
|
52
|
-
]);
|
|
53
|
-
|
|
54
|
-
const erc20Contract = getContract({
|
|
55
|
-
address: tokenAddress,
|
|
56
|
-
abi: erc20Abi,
|
|
57
|
-
client: environment.publicClient,
|
|
42
|
+
if (tokenAddress === zeroAddress) {
|
|
43
|
+
const balance = await environment.publicClient.getBalance({
|
|
44
|
+
address: userAddress,
|
|
58
45
|
});
|
|
59
|
-
|
|
60
|
-
const balance = await erc20Contract.read.balanceOf([userAddress]);
|
|
61
46
|
return { amount: BigInt(balance), token: tokenAddress };
|
|
62
|
-
} catch (error) {
|
|
63
|
-
environment.onError?.(error, {
|
|
64
|
-
source: "user-balances-token-read",
|
|
65
|
-
chainId: environment.chainId,
|
|
66
|
-
token: tokenAddress,
|
|
67
|
-
});
|
|
68
|
-
throw error;
|
|
69
47
|
}
|
|
48
|
+
|
|
49
|
+
const erc20Abi = parseAbi([
|
|
50
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const erc20Contract = getContract({
|
|
54
|
+
address: tokenAddress,
|
|
55
|
+
abi: erc20Abi,
|
|
56
|
+
client: environment.publicClient,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const balance = await erc20Contract.read.balanceOf([userAddress]);
|
|
60
|
+
return { amount: BigInt(balance), token: tokenAddress };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type TokenBalanceReads = {
|
|
64
|
+
balances: { amount: bigint; token: `0x${string}` }[];
|
|
65
|
+
failures: { token: Address; error: unknown }[];
|
|
66
|
+
total: number;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reads each token balance, keeping failures instead of reporting them one by
|
|
71
|
+
* one: viem batches these reads into one multicall, so a single failed request
|
|
72
|
+
* rejects every token on the chain.
|
|
73
|
+
*/
|
|
74
|
+
const readTokenBalances = async (
|
|
75
|
+
environment: Environment,
|
|
76
|
+
userAddress: Address,
|
|
77
|
+
tokenAddresses: Address[],
|
|
78
|
+
): Promise<TokenBalanceReads> => {
|
|
79
|
+
const results = await Promise.all(
|
|
80
|
+
tokenAddresses.map(async (token) => {
|
|
81
|
+
try {
|
|
82
|
+
return {
|
|
83
|
+
balance: await getTokenBalance(environment, userAddress, token),
|
|
84
|
+
};
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return { failure: { token, error } };
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
return {
|
|
91
|
+
balances: results.flatMap((r) => (r.balance ? [r.balance] : [])),
|
|
92
|
+
failures: results.flatMap((r) => (r.failure ? [r.failure] : [])),
|
|
93
|
+
total: tokenAddresses.length,
|
|
94
|
+
};
|
|
70
95
|
};
|
|
71
96
|
|
|
72
97
|
async function getTokenBalancesFromEnvironment(
|
|
73
98
|
environment: Environment,
|
|
74
99
|
userAddress: Address,
|
|
75
|
-
): Promise<
|
|
100
|
+
): Promise<TokenBalanceReads> {
|
|
76
101
|
// Try the views multicall first (single RPC for N tokens). Some chains ship a
|
|
77
102
|
// staking-only views contract (Ethereum's at the time of writing) that doesn't
|
|
78
103
|
// implement getTokensBalances and reverts on the call — in that case fall
|
|
@@ -87,7 +112,7 @@ async function getTokenBalancesFromEnvironment(
|
|
|
87
112
|
userAddress,
|
|
88
113
|
]);
|
|
89
114
|
|
|
90
|
-
return [...tokenBalancesFromView];
|
|
115
|
+
return { balances: [...tokenBalancesFromView], failures: [], total: 0 };
|
|
91
116
|
} catch (error) {
|
|
92
117
|
environment.onError?.(error, {
|
|
93
118
|
source: "user-balances-views-fallback",
|
|
@@ -96,14 +121,10 @@ async function getTokenBalancesFromEnvironment(
|
|
|
96
121
|
}
|
|
97
122
|
}
|
|
98
123
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
),
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
return tokenBalancesSettled.flatMap((s) =>
|
|
106
|
-
s.status === "fulfilled" ? s.value : [],
|
|
124
|
+
return readTokenBalances(
|
|
125
|
+
environment,
|
|
126
|
+
userAddress,
|
|
127
|
+
Object.values(environment.config.tokens).map((token) => token.address),
|
|
107
128
|
);
|
|
108
129
|
}
|
|
109
130
|
|
|
@@ -124,32 +145,58 @@ export async function getUserBalances<
|
|
|
124
145
|
),
|
|
125
146
|
);
|
|
126
147
|
|
|
127
|
-
const
|
|
128
|
-
|
|
148
|
+
const environmentsTokenReads = environmentsTokensBalancesSettled.map((s) =>
|
|
149
|
+
s.status === "fulfilled"
|
|
150
|
+
? s.value
|
|
151
|
+
: { balances: [], failures: [], total: 0 },
|
|
129
152
|
);
|
|
153
|
+
const environmentsTokensBalances = environmentsTokenReads.map((r) => [
|
|
154
|
+
...r.balances,
|
|
155
|
+
]);
|
|
130
156
|
|
|
131
157
|
// Fetch morpho staking balances
|
|
132
158
|
await Promise.all(
|
|
133
159
|
environments.map(async (env, index) => {
|
|
134
160
|
if (!env.config.vaults) return;
|
|
135
161
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
const vaultBalances = vaultBalancesSettled.flatMap((s) =>
|
|
145
|
-
s.status === "fulfilled" ? s.value : [],
|
|
162
|
+
const vaultReads = await readTokenBalances(
|
|
163
|
+
env,
|
|
164
|
+
userAddress,
|
|
165
|
+
Object.values(env.config.vaults).flatMap((vault) =>
|
|
166
|
+
vault.multiReward ? [vault.multiReward] : [],
|
|
167
|
+
),
|
|
146
168
|
);
|
|
147
169
|
|
|
148
170
|
const envBalances = environmentsTokensBalances[index] || [];
|
|
149
|
-
environmentsTokensBalances[index] = [
|
|
171
|
+
environmentsTokensBalances[index] = [
|
|
172
|
+
...envBalances,
|
|
173
|
+
...vaultReads.balances,
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const tokenReads = environmentsTokenReads[index];
|
|
177
|
+
if (tokenReads) {
|
|
178
|
+
tokenReads.failures.push(...vaultReads.failures);
|
|
179
|
+
tokenReads.total += vaultReads.total;
|
|
180
|
+
}
|
|
150
181
|
}),
|
|
151
182
|
);
|
|
152
183
|
|
|
184
|
+
// One report per chain for every token read that failed.
|
|
185
|
+
environments.forEach((env, index) => {
|
|
186
|
+
const reads = environmentsTokenReads[index];
|
|
187
|
+
const [firstFailure] = reads?.failures ?? [];
|
|
188
|
+
if (!reads || !firstFailure) return;
|
|
189
|
+
env.onError?.(firstFailure.error, {
|
|
190
|
+
source: "user-balances-token-read",
|
|
191
|
+
chainId: env.chainId,
|
|
192
|
+
...(reads.failures.length === 1 && { token: firstFailure.token }),
|
|
193
|
+
operation: "token-balance-read",
|
|
194
|
+
failedCount: reads.failures.length,
|
|
195
|
+
totalCount: reads.total,
|
|
196
|
+
items: reads.failures.map((f) => f.token),
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
153
200
|
const result = environments.flatMap((env, index) => {
|
|
154
201
|
const balances = environmentsTokensBalances[index] || [];
|
|
155
202
|
|
|
@@ -17,7 +17,10 @@ import {
|
|
|
17
17
|
findTokenByAddress,
|
|
18
18
|
} from "../../../environments/utils/index.js";
|
|
19
19
|
import type { Market } from "../../../types/market.js";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
getGovernanceTokenPriceFor,
|
|
22
|
+
getGovernanceTokenPriceOrZero,
|
|
23
|
+
} from "../../governance/getWellPrice.js";
|
|
21
24
|
|
|
22
25
|
export const getMarketsData = async (environment: Environment) => {
|
|
23
26
|
// Moonriver (chainId 1285) should always use on-chain data
|
|
@@ -530,13 +533,7 @@ async function fetchMarketsFromLunar(
|
|
|
530
533
|
|
|
531
534
|
const [nativeTokenPriceRaw, governanceTokenPriceRaw] = await Promise.all([
|
|
532
535
|
homeEnvironment.contracts.views?.read.getNativeTokenPrice(),
|
|
533
|
-
|
|
534
|
-
environment.onError?.(err, {
|
|
535
|
-
source: "governance-token-price",
|
|
536
|
-
chainId: environment.chainId,
|
|
537
|
-
});
|
|
538
|
-
return 0n;
|
|
539
|
-
}),
|
|
536
|
+
getGovernanceTokenPriceOrZero(environment),
|
|
540
537
|
]);
|
|
541
538
|
|
|
542
539
|
if (nativeTokenPriceRaw === undefined) {
|
|
@@ -876,54 +873,55 @@ async function fetchMarketsFromLunar(
|
|
|
876
873
|
}
|
|
877
874
|
|
|
878
875
|
const fetchFromGenericCacheApi = async <T>(uri: string): Promise<T> => {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
876
|
+
// AbortSignal.timeout is not available in every browser supported by SDK
|
|
877
|
+
// consumers. Keep the timeout local and release it after reading the body.
|
|
878
|
+
const controller = new AbortController();
|
|
879
|
+
const timer = setTimeout(() => controller.abort(), 5_000);
|
|
880
|
+
try {
|
|
881
|
+
const response = await fetch(
|
|
882
|
+
"https://generic-api-cache.moonwell.workers.dev/",
|
|
883
|
+
{
|
|
884
|
+
method: "POST",
|
|
885
|
+
body: `{"uri":"${uri}","cacheDuration":"300"}`,
|
|
886
|
+
signal: controller.signal,
|
|
887
|
+
headers: {
|
|
888
|
+
...MOONWELL_FETCH_JSON_HEADERS,
|
|
889
|
+
"Content-Type": "text/plain",
|
|
890
|
+
},
|
|
887
891
|
},
|
|
888
|
-
|
|
889
|
-
);
|
|
892
|
+
);
|
|
890
893
|
|
|
891
|
-
|
|
894
|
+
if (!response.ok)
|
|
895
|
+
throw new Error(`Staking APR request failed: ${response.status}`);
|
|
896
|
+
return await response.json();
|
|
897
|
+
} finally {
|
|
898
|
+
clearTimeout(timer);
|
|
899
|
+
}
|
|
892
900
|
};
|
|
893
901
|
|
|
894
|
-
export const fetchLiquidStakingRewards = async ()
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
902
|
+
export const fetchLiquidStakingRewards = async (): Promise<{
|
|
903
|
+
cbETH: number;
|
|
904
|
+
rETH: number;
|
|
905
|
+
wstETH: number;
|
|
906
|
+
}> => {
|
|
907
|
+
// Each provider can succeed independently, and each has a bounded request.
|
|
908
|
+
// Preserve the existing zero fallback for callers of this optional enrichment.
|
|
909
|
+
const [cbETH, rETH, wstETH] = await Promise.all([
|
|
910
|
+
fetchFromGenericCacheApi<{ apy: string }>(
|
|
903
911
|
"https://api.exchange.coinbase.com/wrapped-assets/CBETH",
|
|
904
|
-
)
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
try {
|
|
911
|
-
const rETH = await fetchFromGenericCacheApi<{ rethAPR: string }>(
|
|
912
|
+
)
|
|
913
|
+
.then((result) => Number(result.apy) * 100)
|
|
914
|
+
.catch(() => 0),
|
|
915
|
+
fetchFromGenericCacheApi<{ rethAPR: string }>(
|
|
912
916
|
"https://rocketpool.net/api/mainnet/payload",
|
|
913
|
-
)
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
try {
|
|
920
|
-
const stETH = await fetchFromGenericCacheApi<{ data: { apr: number } }>(
|
|
917
|
+
)
|
|
918
|
+
.then((result) => Number(result.rethAPR))
|
|
919
|
+
.catch(() => 0),
|
|
920
|
+
fetchFromGenericCacheApi<{ data: { apr: number } }>(
|
|
921
921
|
"https://eth-api.lido.fi/v1/protocol/steth/apr/last",
|
|
922
|
-
)
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
return result;
|
|
922
|
+
)
|
|
923
|
+
.then((result) => result.data.apr)
|
|
924
|
+
.catch(() => 0),
|
|
925
|
+
]);
|
|
926
|
+
return { cbETH, rETH, wstETH };
|
|
929
927
|
};
|
|
@@ -25,9 +25,14 @@ export async function getMarkets<
|
|
|
25
25
|
const environments = getEnvironmentsFromArgs(client, args);
|
|
26
26
|
const logId = logger.start("getMarkets", "Starting to get markets...");
|
|
27
27
|
|
|
28
|
-
const settlements = await Promise.
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const [settlements, liquidStakingRewards] = await Promise.all([
|
|
29
|
+
Promise.allSettled(
|
|
30
|
+
environments.map((environment) => getMarketsData(environment)),
|
|
31
|
+
),
|
|
32
|
+
args?.includeLiquidStakingRewards === true
|
|
33
|
+
? fetchLiquidStakingRewards()
|
|
34
|
+
: undefined,
|
|
35
|
+
]);
|
|
31
36
|
|
|
32
37
|
const result = settlements
|
|
33
38
|
.filter(
|
|
@@ -35,8 +40,7 @@ export async function getMarkets<
|
|
|
35
40
|
)
|
|
36
41
|
.map((s) => s.value);
|
|
37
42
|
|
|
38
|
-
if (
|
|
39
|
-
const liquidStakingRewards = await fetchLiquidStakingRewards();
|
|
43
|
+
if (liquidStakingRewards) {
|
|
40
44
|
for (const item of result.flat()) {
|
|
41
45
|
if (item.underlyingToken.symbol.toLowerCase() === "cbeth") {
|
|
42
46
|
item.rewards.push({
|
|
@@ -15,117 +15,113 @@ export const getUserPositionData = async (params: {
|
|
|
15
15
|
return [];
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
])
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
18
|
+
// Only getAllMarketsInfo is settled, because it has the mToken fallback
|
|
19
|
+
// below. The three user reads have no fallback: Promise.all lets their first
|
|
20
|
+
// rejection propagate instead of being treated as an empty list, which would
|
|
21
|
+
// misreport the user as having no position (MOO-885).
|
|
22
|
+
const [[allMarketsResult], balances, borrows, memberships] =
|
|
23
|
+
await Promise.all([
|
|
24
|
+
Promise.allSettled([viewsContract.read.getAllMarketsInfo()]),
|
|
25
|
+
viewsContract.read.getUserBalances([params.account]),
|
|
26
|
+
viewsContract.read.getUserBorrowsBalances([params.account]),
|
|
27
|
+
viewsContract.read.getUserMarketsMemberships([params.account]),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
// If getAllMarketsInfo failed (e.g. broken on-chain oracle), fall back to
|
|
31
|
+
// per-mToken exchange rate calls. The user balance/borrow/membership calls
|
|
32
|
+
// don't touch the oracle so they can still succeed. The fallback reports
|
|
33
|
+
// every USD value as 0, so the degraded read is surfaced through onError
|
|
34
|
+
// instead of passing as a healthy result.
|
|
35
|
+
if (allMarketsResult.status === "rejected") {
|
|
36
|
+
params.environment.onError?.(allMarketsResult.reason, {
|
|
37
|
+
source: "user-positions-oracle-fallback",
|
|
38
|
+
chainId: params.environment.chainId,
|
|
39
|
+
});
|
|
40
|
+
return getUserPositionsFromMTokenFallback(
|
|
41
|
+
params,
|
|
42
|
+
balances,
|
|
43
|
+
borrows,
|
|
44
|
+
memberships,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
const allMarkets = allMarketsResult.value;
|
|
49
|
+
|
|
50
|
+
const markets = allMarkets
|
|
51
|
+
?.map((marketInfo) => {
|
|
52
|
+
const market = findMarketByAddress(params.environment, marketInfo.market);
|
|
53
|
+
if (market) {
|
|
54
|
+
const { marketToken, underlyingToken } = market;
|
|
55
|
+
|
|
56
|
+
const underlyingPrice = new Amount(
|
|
57
|
+
marketInfo.underlyingPrice,
|
|
58
|
+
36 - underlyingToken.decimals,
|
|
59
|
+
).value;
|
|
60
|
+
const collateralFactor = new Amount(marketInfo.collateralFactor, 18)
|
|
61
|
+
.value;
|
|
62
|
+
const exchangeRate = new Amount(
|
|
63
|
+
marketInfo.exchangeRate,
|
|
64
|
+
10 + underlyingToken.decimals,
|
|
65
|
+
).value;
|
|
66
|
+
|
|
67
|
+
const marketCollateralEnabled =
|
|
68
|
+
memberships?.find((r) => r.token === marketInfo.market)
|
|
69
|
+
?.membership === true;
|
|
70
|
+
const marketBorrowedRaw =
|
|
71
|
+
borrows?.find((r) => r.token === marketInfo.market)?.amount || 0n;
|
|
72
|
+
const marketSuppliedRaw =
|
|
73
|
+
balances?.find((r) => r.token === marketInfo.market)?.amount || 0n;
|
|
74
|
+
|
|
75
|
+
const borrowed = new Amount(
|
|
76
|
+
marketBorrowedRaw,
|
|
77
|
+
market.underlyingToken.decimals,
|
|
78
|
+
);
|
|
79
|
+
const borrowedUsd = borrowed.value * underlyingPrice;
|
|
47
80
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
params.environment,
|
|
52
|
-
marketInfo.market,
|
|
81
|
+
const marketSupplied = new Amount(
|
|
82
|
+
marketSuppliedRaw,
|
|
83
|
+
marketToken.decimals,
|
|
53
84
|
);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
? new Amount(
|
|
95
|
-
supplied.value * collateralFactor,
|
|
96
|
-
underlyingToken.decimals,
|
|
97
|
-
)
|
|
98
|
-
: new Amount(0n, underlyingToken.decimals);
|
|
99
|
-
|
|
100
|
-
const collateralUsd = collateral.value * underlyingPrice;
|
|
101
|
-
|
|
102
|
-
const result: UserPosition = {
|
|
103
|
-
chainId: params.environment.chainId,
|
|
104
|
-
account: params.account,
|
|
105
|
-
market: market.marketToken,
|
|
106
|
-
collateralEnabled: marketCollateralEnabled,
|
|
107
|
-
borrowed,
|
|
108
|
-
borrowedUsd,
|
|
109
|
-
collateral,
|
|
110
|
-
collateralUsd,
|
|
111
|
-
supplied,
|
|
112
|
-
suppliedUsd,
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
return result;
|
|
116
|
-
} else {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
.filter((r) => r !== undefined)
|
|
121
|
-
.filter((r) =>
|
|
122
|
-
params.markets ? params.markets.includes(r!.market.address) : true,
|
|
123
|
-
) as UserPosition[];
|
|
124
|
-
|
|
125
|
-
return markets;
|
|
126
|
-
} catch {
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
85
|
+
|
|
86
|
+
const supplied = new Amount(
|
|
87
|
+
marketSupplied.value * exchangeRate,
|
|
88
|
+
underlyingToken.decimals,
|
|
89
|
+
);
|
|
90
|
+
const suppliedUsd = supplied.value * underlyingPrice;
|
|
91
|
+
|
|
92
|
+
const collateral = marketCollateralEnabled
|
|
93
|
+
? new Amount(
|
|
94
|
+
supplied.value * collateralFactor,
|
|
95
|
+
underlyingToken.decimals,
|
|
96
|
+
)
|
|
97
|
+
: new Amount(0n, underlyingToken.decimals);
|
|
98
|
+
|
|
99
|
+
const collateralUsd = collateral.value * underlyingPrice;
|
|
100
|
+
|
|
101
|
+
const result: UserPosition = {
|
|
102
|
+
chainId: params.environment.chainId,
|
|
103
|
+
account: params.account,
|
|
104
|
+
market: market.marketToken,
|
|
105
|
+
collateralEnabled: marketCollateralEnabled,
|
|
106
|
+
borrowed,
|
|
107
|
+
borrowedUsd,
|
|
108
|
+
collateral,
|
|
109
|
+
collateralUsd,
|
|
110
|
+
supplied,
|
|
111
|
+
suppliedUsd,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return result;
|
|
115
|
+
} else {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
.filter((r) => r !== undefined)
|
|
120
|
+
.filter((r) =>
|
|
121
|
+
params.markets ? params.markets.includes(r!.market.address) : true,
|
|
122
|
+
) as UserPosition[];
|
|
123
|
+
|
|
124
|
+
return markets;
|
|
129
125
|
};
|
|
130
126
|
|
|
131
127
|
/**
|
|
@@ -141,9 +137,9 @@ async function getUserPositionsFromMTokenFallback(
|
|
|
141
137
|
account: Address;
|
|
142
138
|
markets?: string[] | undefined;
|
|
143
139
|
},
|
|
144
|
-
balances: { amount: bigint; token: `0x${string}` }[],
|
|
145
|
-
borrows: { amount: bigint; token: `0x${string}` }[],
|
|
146
|
-
memberships: { membership: boolean; token: `0x${string}` }[],
|
|
140
|
+
balances: readonly { amount: bigint; token: `0x${string}` }[],
|
|
141
|
+
borrows: readonly { amount: bigint; token: `0x${string}` }[],
|
|
142
|
+
memberships: readonly { membership: boolean; token: `0x${string}` }[],
|
|
147
143
|
): Promise<UserPosition[]> {
|
|
148
144
|
const positions: UserPosition[] = [];
|
|
149
145
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -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 { UserPosition } from "../../../types/userPosition.js";
|
|
6
7
|
import { getUserPositionData } from "./common.js";
|
|
@@ -15,6 +16,11 @@ export type GetUserPositionsParameters<
|
|
|
15
16
|
|
|
16
17
|
export type GetUserPositionsReturnType = Promise<UserPosition[]>;
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Rejects with a `ChainReadError` when the read fails on any requested chain,
|
|
21
|
+
* so a failed RPC never looks like an empty position list. The error lists the
|
|
22
|
+
* failed chains and carries the positions from the chains that succeeded.
|
|
23
|
+
*/
|
|
18
24
|
export async function getUserPositions<
|
|
19
25
|
environments,
|
|
20
26
|
Network extends Chain | undefined,
|
|
@@ -26,18 +32,13 @@ export async function getUserPositions<
|
|
|
26
32
|
|
|
27
33
|
const environments = getEnvironmentsFromArgs(client, args);
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
environments
|
|
35
|
+
return readAcrossEnvironments({
|
|
36
|
+
environments,
|
|
37
|
+
source: "getUserPositions",
|
|
38
|
+
read: (environment) =>
|
|
31
39
|
getUserPositionData({
|
|
32
40
|
environment,
|
|
33
41
|
account: userAddress,
|
|
34
42
|
}),
|
|
35
|
-
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const result = settled.flatMap((s) =>
|
|
39
|
-
s.status === "fulfilled" ? s.value : [],
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
return result;
|
|
43
|
+
});
|
|
43
44
|
}
|
|
@@ -10,7 +10,7 @@ import type { NetworkParameterType } from "../../common/types.js";
|
|
|
10
10
|
import type { Chain, Environment } from "../../environments/index.js";
|
|
11
11
|
import type { StakingInfo } from "../../types/staking.js";
|
|
12
12
|
import { getMerklStakingApr } from "./common.js";
|
|
13
|
-
import {
|
|
13
|
+
import { getGovernanceTokenPriceOrZero } from "./getWellPrice.js";
|
|
14
14
|
|
|
15
15
|
export type GetStakingInfoParameters<
|
|
16
16
|
environments,
|
|
@@ -150,7 +150,7 @@ export async function getStakingInfo<
|
|
|
150
150
|
blockNumber: BigInt(34149943),
|
|
151
151
|
})
|
|
152
152
|
: Promise.resolve(undefined),
|
|
153
|
-
|
|
153
|
+
getGovernanceTokenPriceOrZero(environment, baseEnvironment),
|
|
154
154
|
]);
|
|
155
155
|
|
|
156
156
|
const viewsStaking =
|
|
@@ -181,13 +181,6 @@ export async function getStakingInfo<
|
|
|
181
181
|
|
|
182
182
|
const price = priceResult.status === "fulfilled" ? priceResult.value : 0n;
|
|
183
183
|
|
|
184
|
-
if (priceResult.status === "rejected") {
|
|
185
|
-
environment.onError?.(priceResult.reason, {
|
|
186
|
-
source: "governance-token-price",
|
|
187
|
-
chainId: environment.chainId,
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
184
|
return { stakingInfo, historicalStaking, price };
|
|
192
185
|
}),
|
|
193
186
|
);
|