@moonwell-fi/moonwell-sdk 0.12.1 → 0.12.2

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.
@@ -1,5 +1,3 @@
1
- import lodash from "lodash";
2
- const { uniq } = lodash;
3
1
  import { type Address, getContract, parseAbi, zeroAddress } from "viem";
4
2
  import { Amount } from "../../../common/amount.js";
5
3
  import { MOONWELL_FETCH_JSON_HEADERS } from "../../../common/fetch-headers.js";
@@ -14,258 +12,54 @@ import {
14
12
  } from "../../../environments/utils/index.js";
15
13
  import type { MorphoUserReward } from "../../../types/morphoUserReward.js";
16
14
  import type { MorphoUserStakingReward } from "../../../types/morphoUserStakingReward.js";
17
- import { getGraphQL } from "../utils/graphql.js";
18
15
 
19
16
  export async function getUserMorphoRewardsData(params: {
20
17
  environment: Environment;
21
18
  account: `0x${string}`;
22
19
  }): Promise<MorphoUserReward[]> {
20
+ // The Morpho URD distributions endpoint (rewards.morpho.org) was
21
+ // deprecated and now 301-redirects to a SPA, so JSON parsing fails.
22
+ // Surface only Merkl rewards.
23
+ const merklRewards = await getMerklRewardsData(
24
+ params.environment,
25
+ params.account,
26
+ );
27
+
23
28
  const isFullDeployment =
24
29
  params.environment.custom.morpho?.minimalDeployment === false;
25
30
 
26
- const emptyMorphoRewards: MorphoRewardsResponse[] = [];
27
- const [merklRewards, morphoRewards] = await Promise.all([
28
- getMerklRewardsData(params.environment, params.account),
29
- isFullDeployment
30
- ? getMorphoRewardsData(params.environment, params.account)
31
- : Promise.resolve(emptyMorphoRewards),
32
- ]);
33
-
34
- if (isFullDeployment) {
35
- // Process Morpho rewards (GraphQL query depends on morphoRewards result)
36
- const morphoAssets = await getMorphoAssetsData(
37
- params.environment,
38
- morphoRewards.map((r) => r.asset.address),
39
- );
40
-
41
- const morphoResult: (MorphoUserReward | undefined)[] = morphoRewards.map(
42
- (r) => {
43
- const asset = morphoAssets.find(
44
- (a) => a.address.toLowerCase() === r.asset.address.toLowerCase(),
45
- );
46
-
47
- if (!asset) {
48
- return undefined;
49
- }
50
-
51
- const rewardToken: TokenConfig = {
52
- address: asset.address,
53
- decimals: asset.decimals,
54
- symbol: asset.symbol,
55
- name: asset.name,
56
- };
57
-
58
- switch (r.type) {
59
- case "uniform-reward": {
60
- const claimableNow = new Amount(
61
- BigInt(r.amount?.claimable_now || 0),
62
- rewardToken.decimals,
63
- );
64
- const claimableNowUsd = claimableNow.value * (asset.priceUsd || 0);
65
- const claimableFuture = new Amount(
66
- BigInt(r.amount?.claimable_next || 0),
67
- rewardToken.decimals,
68
- );
69
- const claimableFutureUsd =
70
- claimableFuture.value * (asset.priceUsd || 0);
71
-
72
- const uniformReward: MorphoUserReward = {
73
- type: "uniform-reward",
74
- chainId: r.asset.chain_id,
75
- account: r.user,
76
- rewardToken,
77
- claimableNow,
78
- claimableNowUsd,
79
- claimableFuture,
80
- claimableFutureUsd,
81
- };
82
- return uniformReward;
83
- }
84
-
85
- case "market-reward": {
86
- const claimableNow = new Amount(
87
- BigInt(r.for_supply?.claimable_now || 0),
88
- rewardToken.decimals,
89
- );
90
- const claimableNowUsd = claimableNow.value * (asset.priceUsd || 0);
91
-
92
- const claimableFuture = new Amount(
93
- BigInt(r.for_supply?.claimable_next || 0),
94
- rewardToken.decimals,
95
- );
96
- const claimableFutureUsd =
97
- claimableFuture.value * (asset.priceUsd || 0);
98
-
99
- const collateralClaimableNow = new Amount(
100
- BigInt(r.for_collateral?.claimable_now || 0),
101
- rewardToken.decimals,
102
- );
103
- const collateralClaimableNowUsd =
104
- collateralClaimableNow.value * (asset.priceUsd || 0);
105
- const collateralClaimableFuture = new Amount(
106
- BigInt(r.for_collateral?.claimable_next || 0),
107
- rewardToken.decimals,
108
- );
109
- const collateralClaimableFutureUsd =
110
- collateralClaimableFuture.value * (asset.priceUsd || 0);
111
-
112
- const borrowClaimableNow = new Amount(
113
- BigInt(r.for_borrow?.claimable_now || 0),
114
- rewardToken.decimals,
115
- );
116
- const borrowClaimableNowUsd =
117
- borrowClaimableNow.value * (asset.priceUsd || 0);
118
- const borrowClaimableFuture = new Amount(
119
- BigInt(r.for_borrow?.claimable_next || 0),
120
- rewardToken.decimals,
121
- );
122
- const borrowClaimableFutureUsd =
123
- borrowClaimableFuture.value * (asset.priceUsd || 0);
124
-
125
- //Rewards reallocated to vaults are reported as vault rewards
126
- if (r.reallocated_from) {
127
- const vaultReward: MorphoUserReward = {
128
- type: "vault-reward",
129
- chainId: r.program.chain_id,
130
- account: r.user,
131
- vaultId: r.reallocated_from,
132
- rewardToken,
133
- claimableNow,
134
- claimableNowUsd,
135
- claimableFuture,
136
- claimableFutureUsd,
137
- };
138
- return vaultReward;
139
- } else {
140
- const marketReward: MorphoUserReward = {
141
- type: "market-reward",
142
- chainId: r.program.chain_id,
143
- account: r.user,
144
- marketId: r.program.market_id || "",
145
- rewardToken,
146
- collateralRewards: {
147
- claimableNow: collateralClaimableNow,
148
- claimableNowUsd: collateralClaimableNowUsd,
149
- claimableFuture: collateralClaimableFuture,
150
- claimableFutureUsd: collateralClaimableFutureUsd,
151
- },
152
- borrowRewards: {
153
- claimableNow: borrowClaimableNow,
154
- claimableNowUsd: borrowClaimableNowUsd,
155
- claimableFuture: borrowClaimableFuture,
156
- claimableFutureUsd: borrowClaimableFutureUsd,
157
- },
158
- };
159
- return marketReward;
160
- }
161
- }
162
- case "vault-reward": {
163
- const claimableNow = new Amount(
164
- BigInt(r.for_supply?.claimable_now || 0),
165
- rewardToken.decimals,
166
- );
167
- const claimableNowUsd = claimableNow.value * (asset.priceUsd || 0);
168
- const claimableFuture = new Amount(
169
- BigInt(r.for_supply?.claimable_next || 0),
170
- rewardToken.decimals,
171
- );
172
- const claimableFutureUsd =
173
- claimableFuture.value * (asset.priceUsd || 0);
174
-
175
- const vaultReward: MorphoUserReward = {
176
- type: "vault-reward",
177
- chainId: r.program.chain_id,
178
- account: r.user,
179
- vaultId: r.program.vault,
180
- rewardToken,
181
- claimableNow,
182
- claimableNowUsd,
183
- claimableFuture,
184
- claimableFutureUsd,
185
- };
186
-
187
- return vaultReward;
188
- }
189
- }
190
- },
191
- );
192
-
193
- // Process Merkl rewards
194
- const vaultCampaignIds = new Set<string>(
195
- (Object.values(publicEnvironments) as Environment[]).flatMap(
196
- (environment) =>
197
- Object.values(environment.config.vaults ?? {})
198
- .map((vault) => vault.campaignId)
199
- .filter((id): id is string => id !== undefined),
200
- ),
31
+ // For full deployments (Base), restrict to Moonwell vault campaigns so the
32
+ // result excludes staking and other Moonwell campaigns; those are returned
33
+ // by their own actions (e.g. getUserStakingInfo). On other chains, surface
34
+ // every Merkl reward we get back.
35
+ const vaultCampaignIds = isFullDeployment
36
+ ? new Set<string>(
37
+ (Object.values(publicEnvironments) as Environment[]).flatMap(
38
+ (environment) =>
39
+ Object.values(environment.config.vaults ?? {})
40
+ .map((vault) => vault.campaignId)
41
+ .filter((id): id is string => id !== undefined),
42
+ ),
43
+ )
44
+ : null;
45
+
46
+ const sumBreakdowns = (
47
+ breakdowns: {
48
+ campaignId: string;
49
+ amount: string;
50
+ claimed: string;
51
+ pending: string;
52
+ }[],
53
+ field: "amount" | "claimed" | "pending",
54
+ ): bigint =>
55
+ breakdowns.reduce(
56
+ (acc, curr) =>
57
+ vaultCampaignIds === null || vaultCampaignIds.has(curr.campaignId)
58
+ ? acc + BigInt(curr[field])
59
+ : acc,
60
+ 0n,
201
61
  );
202
62
 
203
- const getVaultRewardAmount = (
204
- breakdowns: any[],
205
- field: "amount" | "claimed" | "pending",
206
- ) => {
207
- return breakdowns.reduce(
208
- (acc, curr) =>
209
- vaultCampaignIds.has(curr.campaignId)
210
- ? acc + BigInt(curr[field])
211
- : acc,
212
- 0n,
213
- );
214
- };
215
-
216
- const merklResult: MorphoUserReward[] = [];
217
-
218
- for (const chainData of merklRewards) {
219
- for (const reward of chainData.rewards) {
220
- // Try to find token info in morphoAssets first
221
- const morphoAsset = morphoAssets.find(
222
- (a) => a.address.toLowerCase() === reward.token.address.toLowerCase(),
223
- );
224
-
225
- const rewardToken: TokenConfig = {
226
- address: reward.token.address as Address,
227
- decimals: morphoAsset?.decimals ?? reward.token.decimals,
228
- symbol: morphoAsset?.symbol ?? reward.token.symbol,
229
- name: morphoAsset?.name ?? reward.token.symbol,
230
- };
231
-
232
- const amount = getVaultRewardAmount(reward.breakdowns, "amount");
233
- const claimed = getVaultRewardAmount(reward.breakdowns, "claimed");
234
- const pending = getVaultRewardAmount(reward.breakdowns, "pending");
235
-
236
- const claimableNow = new Amount(amount - claimed, rewardToken.decimals);
237
- const claimableNowUsd =
238
- claimableNow.value *
239
- (morphoAsset?.priceUsd ?? reward.token.price ?? 0);
240
- const claimableFuture = new Amount(pending, rewardToken.decimals);
241
- const claimableFutureUsd =
242
- claimableFuture.value *
243
- (morphoAsset?.priceUsd ?? reward.token.price ?? 0);
244
-
245
- const merklReward: MorphoUserReward = {
246
- type: "merkl-reward",
247
- chainId: chainData.chain.id,
248
- account: params.account,
249
- rewardToken,
250
- claimableNow,
251
- claimableNowUsd,
252
- claimableFuture,
253
- claimableFutureUsd,
254
- };
255
-
256
- merklResult.push(merklReward);
257
- }
258
- }
259
-
260
- // Combine both results
261
- const allResults = [
262
- ...(morphoResult.filter((r) => r !== undefined) as MorphoUserReward[]),
263
- ...merklResult,
264
- ];
265
-
266
- return allResults;
267
- }
268
-
269
63
  const merklResult: MorphoUserReward[] = [];
270
64
 
271
65
  for (const chainData of merklRewards) {
@@ -277,19 +71,23 @@ export async function getUserMorphoRewardsData(params: {
277
71
  name: reward.token.symbol,
278
72
  };
279
73
 
280
- const claimableNow = new Amount(
281
- BigInt(reward.amount) - BigInt(reward.claimed),
282
- rewardToken.decimals,
283
- );
74
+ const amount = vaultCampaignIds
75
+ ? sumBreakdowns(reward.breakdowns, "amount")
76
+ : BigInt(reward.amount);
77
+ const claimed = vaultCampaignIds
78
+ ? sumBreakdowns(reward.breakdowns, "claimed")
79
+ : BigInt(reward.claimed);
80
+ const pending = vaultCampaignIds
81
+ ? sumBreakdowns(reward.breakdowns, "pending")
82
+ : BigInt(reward.pending);
83
+
84
+ const claimableNow = new Amount(amount - claimed, rewardToken.decimals);
284
85
  const claimableNowUsd = claimableNow.value * (reward.token.price ?? 0);
285
- const claimableFuture = new Amount(
286
- BigInt(reward.pending),
287
- rewardToken.decimals,
288
- );
86
+ const claimableFuture = new Amount(pending, rewardToken.decimals);
289
87
  const claimableFutureUsd =
290
88
  claimableFuture.value * (reward.token.price ?? 0);
291
89
 
292
- const merklReward: MorphoUserReward = {
90
+ merklResult.push({
293
91
  type: "merkl-reward",
294
92
  chainId: chainData.chain.id,
295
93
  account: params.account,
@@ -298,9 +96,7 @@ export async function getUserMorphoRewardsData(params: {
298
96
  claimableNowUsd,
299
97
  claimableFuture,
300
98
  claimableFutureUsd,
301
- };
302
-
303
- merklResult.push(merklReward);
99
+ });
304
100
  }
305
101
  }
306
102
 
@@ -456,94 +252,6 @@ const getRewardsEarnedData = async (
456
252
  return rewards.filter(Boolean);
457
253
  };
458
254
 
459
- type MorphoRewardsResponse = {
460
- user: Address;
461
- for_borrow: {
462
- claimable_next: string;
463
- claimable_now: string;
464
- claimed: string;
465
- total: string;
466
- };
467
- for_collateral: {
468
- claimable_next: string;
469
- claimable_now: string;
470
- claimed: string;
471
- total: string;
472
- };
473
- for_supply: {
474
- claimable_next: string;
475
- claimable_now: string;
476
- claimed: string;
477
- total: string;
478
- };
479
- program: {
480
- asset: { address: Address };
481
- market_id?: string;
482
- chain_id: number;
483
- vault: Address;
484
- };
485
- asset: { address: Address; chain_id: number };
486
- amount?: { claimable_next: string; claimable_now: string };
487
- type: "vault-reward" | "market-reward" | "uniform-reward";
488
- reallocated_from: Address;
489
- };
490
-
491
- type MorphoAssetResponse = {
492
- address: Address;
493
- symbol: string;
494
- priceUsd: number | undefined;
495
- name: string;
496
- decimals: number;
497
- };
498
-
499
- async function getMorphoRewardsData(
500
- environment: Environment,
501
- account: Address,
502
- ): Promise<MorphoRewardsResponse[]> {
503
- const baseUrl =
504
- environment.custom.morpho?.rewardsApiUrl || "https://rewards.morpho.org";
505
- const rewardsRequest = await fetch(
506
- `${baseUrl}/v1/users/${account}/rewards?chain_id=${environment.chainId}&trusted=true&exclude_merkl_programs=true`,
507
- {
508
- headers: MOONWELL_FETCH_JSON_HEADERS,
509
- },
510
- );
511
- const rewards = await rewardsRequest.json();
512
- return (rewards.data || []) as MorphoRewardsResponse[];
513
- }
514
-
515
- async function getMorphoAssetsData(
516
- environment: Environment,
517
- addresses: Address[],
518
- ): Promise<MorphoAssetResponse[]> {
519
- const rewardsRequest = await getGraphQL<{
520
- assets: {
521
- items: MorphoAssetResponse[];
522
- };
523
- }>(
524
- environment,
525
- `
526
- query {
527
- assets(where: { address_in:[${uniq(addresses)
528
- .map((a: string) => `"${a.toLowerCase()}"`)
529
- .join(",")}]}) {
530
- items {
531
- address
532
- symbol
533
- priceUsd
534
- name
535
- decimals
536
- }
537
- }
538
- }
539
- `,
540
- );
541
- if (rewardsRequest) {
542
- return rewardsRequest.assets.items;
543
- }
544
- return [];
545
- }
546
-
547
255
  type MerklRewardsResponse = {
548
256
  chain: {
549
257
  id: number;
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.12.1'
1
+ export const version = '0.12.2'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moonwell-fi/moonwell-sdk",
3
3
  "description": "TypeScript Interface for Moonwell",
4
- "version": "0.12.1",
4
+ "version": "0.12.2",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",