@moonwell-fi/moonwell-sdk 0.20.3 → 0.20.4

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/_cjs/actions/governance/governor-api-client.js +3 -2
  3. package/_cjs/actions/governance/governor-api-client.js.map +1 -1
  4. package/_cjs/actions/governance/proposals/common.js +11 -296
  5. package/_cjs/actions/governance/proposals/common.js.map +1 -1
  6. package/_cjs/actions/governance/proposals/getProposal.js +9 -25
  7. package/_cjs/actions/governance/proposals/getProposal.js.map +1 -1
  8. package/_cjs/actions/governance/proposals/getProposals.js +23 -14
  9. package/_cjs/actions/governance/proposals/getProposals.js.map +1 -1
  10. package/_cjs/errors/version.js +1 -1
  11. package/_cjs/types/proposal.js.map +1 -1
  12. package/_esm/actions/governance/governor-api-client.js +21 -4
  13. package/_esm/actions/governance/governor-api-client.js.map +1 -1
  14. package/_esm/actions/governance/ipfs.js +2 -2
  15. package/_esm/actions/governance/proposals/common.js +28 -302
  16. package/_esm/actions/governance/proposals/common.js.map +1 -1
  17. package/_esm/actions/governance/proposals/getProposal.js +23 -35
  18. package/_esm/actions/governance/proposals/getProposal.js.map +1 -1
  19. package/_esm/actions/governance/proposals/getProposals.js +48 -20
  20. package/_esm/actions/governance/proposals/getProposals.js.map +1 -1
  21. package/_esm/errors/version.js +1 -1
  22. package/_esm/types/proposal.js.map +1 -1
  23. package/_types/actions/governance/getUserVoteReceipt.d.ts +4 -4
  24. package/_types/actions/governance/governor-api-client.d.ts +34 -4
  25. package/_types/actions/governance/governor-api-client.d.ts.map +1 -1
  26. package/_types/actions/governance/ipfs.d.ts +2 -2
  27. package/_types/actions/governance/proposals/common.d.ts +12 -39
  28. package/_types/actions/governance/proposals/common.d.ts.map +1 -1
  29. package/_types/actions/governance/proposals/getProposal.d.ts +2 -1
  30. package/_types/actions/governance/proposals/getProposal.d.ts.map +1 -1
  31. package/_types/actions/governance/proposals/getProposals.d.ts.map +1 -1
  32. package/_types/errors/version.d.ts +1 -1
  33. package/_types/types/proposal.d.ts +11 -0
  34. package/_types/types/proposal.d.ts.map +1 -1
  35. package/actions/governance/getUserVoteReceipt.ts +4 -4
  36. package/actions/governance/governor-api-client.ts +35 -4
  37. package/actions/governance/ipfs.ts +2 -2
  38. package/actions/governance/proposals/common.ts +28 -466
  39. package/actions/governance/proposals/getProposal.ts +25 -49
  40. package/actions/governance/proposals/getProposals.ts +57 -30
  41. package/errors/version.ts +1 -1
  42. package/package.json +1 -1
  43. package/types/proposal.ts +11 -0
@@ -6,17 +6,13 @@ import type { Chain, Environment } from "../../../environments/index.js";
6
6
  import { type Proposal, ProposalState } from "../../../types/proposal.js";
7
7
  import {
8
8
  type ApiProposal,
9
- SUPPORTED_GOVERNOR_CHAIN_IDS,
9
+ MULTIGOV_PROPOSAL_FALLBACK_CHAIN_IDS,
10
10
  fetchProposal,
11
11
  isNotFoundError,
12
12
  } from "../governor-api-client.js";
13
13
  import { resolveIpfsDescriptions } from "../ipfs.js";
14
14
  import {
15
- appendProposalExtendedData,
16
15
  formatApiProposalData,
17
- getCrossChainProposalData,
18
- getExtendedProposalData,
19
- getProposalData,
20
16
  getProposalsOnChainData,
21
17
  readCrossChainQuorums,
22
18
  } from "./common.js";
@@ -28,7 +24,8 @@ export type GetProposalParameters<
28
24
  proposalId: number;
29
25
  /**
30
26
  * The chain the proposal lives on (1 = Ethereum multigov,
31
- * 1284 = Moonbeam historical). When omitted, both are tried in turn.
27
+ * 1284 = Moonbeam historical, 1285 = Moonriver legacy). When omitted, the
28
+ * supported chains are tried in turn.
32
29
  */
33
30
  chainId?: number;
34
31
  };
@@ -51,7 +48,7 @@ export async function getProposal<
51
48
 
52
49
  // Ethereum-home multigov proposals are served by the same Governor API as
53
50
  // historical Moonbeam ones (the lunar indexer fans out both chainIds), so
54
- // route them through `getMoonbeamProposal` using the Moonbeam env as the
51
+ // route them through `getGovernorApiProposal` using the Moonbeam env as the
55
52
  // indexer source. Without this, a caller resolving the env by `chainId: 1`
56
53
  // would bail out on the `!moonbeam && !moonriver` check below and the page
57
54
  // reload path returns undefined.
@@ -62,7 +59,7 @@ export async function getProposal<
62
59
  if (!moonbeamEnv) {
63
60
  return undefined;
64
61
  }
65
- return getMoonbeamProposal(
62
+ return getGovernorApiProposal(
66
63
  moonbeamEnv,
67
64
  proposalId,
68
65
  args.chainId ?? mainnet.id,
@@ -77,25 +74,29 @@ export async function getProposal<
77
74
  }
78
75
 
79
76
  if (environment.chainId === moonbeam.id) {
80
- return getMoonbeamProposal(environment, proposalId, args.chainId);
77
+ return getGovernorApiProposal(environment, proposalId, args.chainId);
81
78
  }
82
- return getMoonriverProposal(environment, proposalId);
79
+ return getGovernorApiProposal(environment, proposalId, moonriver.id);
83
80
  }
84
81
 
85
82
  /**
86
- * Fetch a single proposal for Moonbeam using the Governor API.
83
+ * Fetch a single proposal from the Governor API (Moonbeam/Ethereum multigov or
84
+ * Moonriver legacy governor).
87
85
  *
88
86
  * When `chainId` is provided we hit only that chain. When omitted we try the
89
- * supported chains in order (Ethereum first since that's where active multigov
90
- * proposals live) and fall back on `NotFoundError`. Real outages (5xx, network
91
- * errors) propagate so callers can distinguish "missing" from "broken".
87
+ * multigov chains in order (Ethereum first since that's where active multigov
88
+ * proposals live) and fall back on `NotFoundError`. Moonriver (1285) is
89
+ * deliberately absent from the fallback it has its own explicit route, and a
90
+ * bare lookup resolving to it through a non-Moonriver env would be degraded.
91
+ * Real outages (5xx, network errors) propagate so callers can distinguish
92
+ * "missing" from "broken".
92
93
  */
93
- async function getMoonbeamProposal(
94
+ async function getGovernorApiProposal(
94
95
  governanceEnvironment: Environment,
95
96
  proposalId: number,
96
97
  chainId?: number,
97
98
  ): Promise<Proposal | undefined> {
98
- const tryChains = chainId ? [chainId] : SUPPORTED_GOVERNOR_CHAIN_IDS;
99
+ const tryChains = chainId ? [chainId] : MULTIGOV_PROPOSAL_FALLBACK_CHAIN_IDS;
99
100
 
100
101
  let apiProposal: ApiProposal | undefined;
101
102
  for (const cid of tryChains) {
@@ -180,11 +181,18 @@ async function getMoonbeamProposal(
180
181
  description: apiProposal.description,
181
182
  targets: apiProposal.targets,
182
183
  calldatas: apiProposal.calldatas,
183
- signatures: [],
184
+ // Legacy-governor proposals (Moonriver, early Moonbeam) carry the function
185
+ // signature separately from the selector-less calldata; pass it through so
186
+ // consumers can decode the call. Empty for multichain-governor proposals.
187
+ signatures: apiProposal.signatures ?? [],
184
188
  stateChanges: formattedData.stateChanges,
185
189
  environment: governanceEnvironment,
186
190
  };
187
191
 
192
+ if (apiProposal.snapshotBlocks) {
193
+ proposal.snapshotBlocks = apiProposal.snapshotBlocks;
194
+ }
195
+
188
196
  if (isMultichain) {
189
197
  proposal.multichain = {
190
198
  id: apiProposal.proposalId,
@@ -194,35 +202,3 @@ async function getMoonbeamProposal(
194
202
 
195
203
  return proposal;
196
204
  }
197
-
198
- /**
199
- * Fetch a single proposal for Moonriver using the old Ponder-based approach
200
- */
201
- async function getMoonriverProposal(
202
- governanceEnvironment: Environment,
203
- proposalId: number,
204
- ): Promise<Proposal | undefined> {
205
- const [_proposals, _xcProposals, _extendedDatas] = await Promise.all([
206
- getProposalData({ environment: governanceEnvironment, id: proposalId }),
207
- getCrossChainProposalData({
208
- environment: governanceEnvironment,
209
- id: proposalId,
210
- }),
211
- getExtendedProposalData({
212
- environment: governanceEnvironment,
213
- id: proposalId,
214
- }),
215
- ]);
216
-
217
- const proposals = [..._proposals, ..._xcProposals];
218
-
219
- proposals.forEach((proposal) => {
220
- proposal.environment = governanceEnvironment;
221
- });
222
-
223
- appendProposalExtendedData(proposals, _extendedDatas);
224
-
225
- return proposals.find(
226
- (p) => p.proposalId === proposalId || p.id === proposalId,
227
- );
228
- }
@@ -8,11 +8,7 @@ import { type Proposal, ProposalState } from "../../../types/proposal.js";
8
8
  import { type ApiProposal, fetchAllProposals } from "../governor-api-client.js";
9
9
  import { resolveIpfsDescriptions } from "../ipfs.js";
10
10
  import {
11
- appendProposalExtendedData,
12
11
  formatApiProposalData,
13
- getCrossChainProposalData,
14
- getExtendedProposalData,
15
- getProposalData,
16
12
  getProposalsOnChainData,
17
13
  readCrossChainQuorums,
18
14
  } from "./common.js";
@@ -49,10 +45,10 @@ export async function getProposals<
49
45
  const allProposals = await Promise.all(
50
46
  governanceEnvironments.map(async (governanceEnvironment) => {
51
47
  if (governanceEnvironment.chainId === moonbeam.id) {
52
- // Moonbeam: Use new Governor API
48
+ // Moonbeam + Ethereum: Governor API (chainIds 1 and 1284)
53
49
  return getMoonbeamProposals(governanceEnvironment);
54
50
  } else {
55
- // Moonriver: Use old Ponder approach
51
+ // Moonriver: Governor API, single legacy governor (chainId 1285)
56
52
  return getMoonriverProposals(governanceEnvironment);
57
53
  }
58
54
  }),
@@ -109,6 +105,53 @@ async function getMoonbeamProposals(
109
105
  }
110
106
  });
111
107
 
108
+ return buildProposals(apiProposals, governanceEnvironment);
109
+ }
110
+
111
+ /**
112
+ * Fetch proposals for Moonriver using the Governor API.
113
+ *
114
+ * Moonriver runs a single legacy standalone governor (no multichain governor),
115
+ * so we fetch only its chainId from the same lunar indexer and reuse the shared
116
+ * pipeline. `getProposalsOnChainData` classifies these as non-multichain and
117
+ * reads state/quorum from the legacy governor (via `getQuorum`).
118
+ */
119
+ async function getMoonriverProposals(
120
+ governanceEnvironment: Environment,
121
+ ): Promise<Proposal[]> {
122
+ // Moonriver is a single chain, so there's no other chain to "continue with"
123
+ // like the Moonbeam fan-out — but a bare throw would reject the whole
124
+ // `Promise.all` in getProposals and drop Moonbeam/Ethereum results too. Mirror
125
+ // the per-chain handling getMoonbeamProposals uses: report the outage via
126
+ // onError and degrade to an empty Moonriver list instead of rejecting.
127
+ let apiProposals: ApiProposal[] = [];
128
+ try {
129
+ apiProposals = await fetchAllProposals(governanceEnvironment, {
130
+ chainId: moonriver.id,
131
+ });
132
+ } catch (reason) {
133
+ console.warn(
134
+ `[getProposals] Failed to fetch proposals for chainId=${moonriver.id}; continuing with an empty Moonriver list.`,
135
+ reason,
136
+ );
137
+ governanceEnvironment.onError?.(reason, {
138
+ source: "governance-proposals",
139
+ chainId: moonriver.id,
140
+ });
141
+ }
142
+ return buildProposals(apiProposals, governanceEnvironment);
143
+ }
144
+
145
+ /**
146
+ * Shared Governor-API pipeline: resolve IPFS descriptions + cross-chain quorums
147
+ * in parallel, read on-chain data, then map each ApiProposal to a Proposal.
148
+ * Used by both the Moonbeam/Ethereum and Moonriver paths so the mapping stays
149
+ * in one place.
150
+ */
151
+ async function buildProposals(
152
+ apiProposals: ApiProposal[],
153
+ governanceEnvironment: Environment,
154
+ ): Promise<Proposal[]> {
112
155
  // IPFS resolution and cross-chain quorum reads are independent — run them in
113
156
  // parallel to save one network round-trip on the proposal list path.
114
157
  const [, crossChainQuorums] = await Promise.all([
@@ -180,11 +223,18 @@ async function getMoonbeamProposals(
180
223
  description: apiProposal.description,
181
224
  targets: apiProposal.targets,
182
225
  calldatas: apiProposal.calldatas,
183
- signatures: [],
226
+ // Legacy-governor proposals (Moonriver, early Moonbeam) carry the function
227
+ // signature separately from the selector-less calldata; pass it through so
228
+ // consumers can decode the call. Empty for multichain-governor proposals.
229
+ signatures: apiProposal.signatures ?? [],
184
230
  stateChanges: formattedData.stateChanges,
185
231
  environment: governanceEnvironment,
186
232
  };
187
233
 
234
+ if (apiProposal.snapshotBlocks) {
235
+ proposal.snapshotBlocks = apiProposal.snapshotBlocks;
236
+ }
237
+
188
238
  if (isMultichain) {
189
239
  proposal.multichain = {
190
240
  id: apiProposal.proposalId,
@@ -197,26 +247,3 @@ async function getMoonbeamProposals(
197
247
 
198
248
  return proposals;
199
249
  }
200
-
201
- /**
202
- * Fetch proposals for Moonriver using the old Ponder-based approach
203
- */
204
- async function getMoonriverProposals(
205
- governanceEnvironment: Environment,
206
- ): Promise<Proposal[]> {
207
- const [_proposals, _xcProposals, _extendedDatas] = await Promise.all([
208
- getProposalData({ environment: governanceEnvironment }),
209
- getCrossChainProposalData({ environment: governanceEnvironment }),
210
- getExtendedProposalData({ environment: governanceEnvironment }),
211
- ]);
212
-
213
- const proposals = [..._proposals, ..._xcProposals];
214
-
215
- proposals.forEach((proposal) => {
216
- proposal.environment = governanceEnvironment;
217
- });
218
-
219
- appendProposalExtendedData(proposals, _extendedDatas);
220
-
221
- return proposals;
222
- }
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.20.3'
1
+ export const version = '0.20.4'
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.20.3",
4
+ "version": "0.20.4",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
package/types/proposal.ts CHANGED
@@ -23,6 +23,17 @@ export type Proposal = {
23
23
  id: number;
24
24
  votesCollected: boolean;
25
25
  };
26
+ /**
27
+ * Authoritative per-chain voting-power snapshot blocks resolved by the
28
+ * lunar-indexer, keyed by chain name (`mainnet` → 1, `base` → 8453,
29
+ * `optimism` → 10, `moonbeam` → 1284). Block numbers are decimal strings.
30
+ *
31
+ * Present on indexer-sourced multichain proposals; absent for older
32
+ * proposals indexed before the field existed and for on-chain (Moonriver)
33
+ * proposals. Consumers reading voting power should prefer these blocks over
34
+ * resolving the snapshot timestamp to a block client-side.
35
+ */
36
+ snapshotBlocks?: Record<string, string>;
26
37
  //proposal extended data
27
38
  title?: string;
28
39
  subtitle?: string;