@moonwell-fi/moonwell-sdk 0.21.0 → 0.23.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 +51 -0
- package/README.md +9 -3
- package/_cjs/actions/beam/common.js +1 -2
- package/_cjs/actions/beam/common.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 +146 -96
- package/_cjs/actions/core/markets/common.js.map +1 -1
- package/_cjs/actions/governance/getDelegates.js +2 -7
- package/_cjs/actions/governance/getDelegates.js.map +1 -1
- package/_cjs/actions/governance/getGovernanceTokenInfo.js +7 -12
- package/_cjs/actions/governance/getGovernanceTokenInfo.js.map +1 -1
- package/_cjs/actions/governance/getUserVoteReceipt.js +5 -4
- package/_cjs/actions/governance/getUserVoteReceipt.js.map +1 -1
- package/_cjs/actions/governance/governor-api-client.js.map +1 -1
- package/_cjs/actions/governance/proposals/common.js +73 -6
- package/_cjs/actions/governance/proposals/common.js.map +1 -1
- package/_cjs/actions/governance/proposals/getProposal.js +6 -73
- package/_cjs/actions/governance/proposals/getProposal.js.map +1 -1
- package/_cjs/actions/governance/proposals/getProposals.js +12 -92
- package/_cjs/actions/governance/proposals/getProposals.js.map +1 -1
- package/_cjs/client/createMoonwellClient.js +17 -15
- package/_cjs/client/createMoonwellClient.js.map +1 -1
- package/_cjs/common/index.js +4 -4
- package/_cjs/common/index.js.map +1 -1
- package/_cjs/environments/definitions/governance.js +1 -8
- package/_cjs/environments/definitions/governance.js.map +1 -1
- package/_cjs/environments/index.js +28 -42
- 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/_esm/actions/beam/common.js +3 -2
- package/_esm/actions/beam/common.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 +192 -104
- package/_esm/actions/core/markets/common.js.map +1 -1
- package/_esm/actions/governance/getDelegates.js +8 -8
- package/_esm/actions/governance/getDelegates.js.map +1 -1
- package/_esm/actions/governance/getGovernanceTokenInfo.js +20 -13
- package/_esm/actions/governance/getGovernanceTokenInfo.js.map +1 -1
- package/_esm/actions/governance/getUserVoteReceipt.js +16 -5
- package/_esm/actions/governance/getUserVoteReceipt.js.map +1 -1
- package/_esm/actions/governance/governor-api-client.js +14 -5
- package/_esm/actions/governance/governor-api-client.js.map +1 -1
- package/_esm/actions/governance/proposals/common.js +122 -10
- package/_esm/actions/governance/proposals/common.js.map +1 -1
- package/_esm/actions/governance/proposals/getProposal.js +16 -93
- package/_esm/actions/governance/proposals/getProposal.js.map +1 -1
- package/_esm/actions/governance/proposals/getProposals.js +37 -132
- package/_esm/actions/governance/proposals/getProposals.js.map +1 -1
- package/_esm/client/createMoonwellClient.js +24 -16
- package/_esm/client/createMoonwellClient.js.map +1 -1
- package/_esm/common/index.js +15 -4
- package/_esm/common/index.js.map +1 -1
- package/_esm/environments/definitions/governance.js +5 -9
- package/_esm/environments/definitions/governance.js.map +1 -1
- package/_esm/environments/index.js +2 -14
- 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/_types/actions/beam/common.d.ts +1 -1
- package/_types/actions/beam/common.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/governance/getGovernanceTokenInfo.d.ts.map +1 -1
- package/_types/actions/governance/getUserVoteReceipt.d.ts.map +1 -1
- package/_types/actions/governance/governor-api-client.d.ts +14 -5
- package/_types/actions/governance/governor-api-client.d.ts.map +1 -1
- package/_types/actions/governance/proposals/common.d.ts +41 -6
- package/_types/actions/governance/proposals/common.d.ts.map +1 -1
- package/_types/actions/governance/proposals/getProposal.d.ts +1 -1
- package/_types/actions/governance/proposals/getProposal.d.ts.map +1 -1
- package/_types/actions/governance/proposals/getProposals.d.ts +1 -1
- package/_types/actions/governance/proposals/getProposals.d.ts.map +1 -1
- package/_types/client/createMoonwellClient.d.ts +10 -843
- package/_types/client/createMoonwellClient.d.ts.map +1 -1
- package/_types/common/index.d.ts +11 -0
- package/_types/common/index.d.ts.map +1 -1
- package/_types/environments/definitions/governance.d.ts +0 -7
- package/_types/environments/definitions/governance.d.ts.map +1 -1
- package/_types/environments/index.d.ts +7 -518
- package/_types/environments/index.d.ts.map +1 -1
- package/_types/environments/types/config.d.ts +22 -8
- package/_types/environments/types/config.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/actions/beam/common.ts +4 -9
- package/actions/core/getUserBalances.ts +25 -31
- package/actions/core/markets/common.ts +307 -166
- package/actions/governance/getDelegates.ts +8 -8
- package/actions/governance/getGovernanceTokenInfo.ts +23 -15
- package/actions/governance/getUserVoteReceipt.ts +19 -5
- package/actions/governance/governor-api-client.ts +14 -5
- package/actions/governance/proposals/common.ts +143 -10
- package/actions/governance/proposals/getProposal.ts +29 -112
- package/actions/governance/proposals/getProposals.ts +52 -153
- package/client/createMoonwellClient.ts +34 -44
- package/common/index.ts +17 -6
- package/environments/definitions/governance.ts +5 -9
- package/environments/index.ts +34 -71
- package/environments/types/config.ts +24 -8
- package/errors/version.ts +1 -1
- package/package.json +1 -1
- package/_cjs/environments/definitions/moonbeam/contracts.js +0 -22
- package/_cjs/environments/definitions/moonbeam/contracts.js.map +0 -1
- package/_cjs/environments/definitions/moonbeam/core-markets.js +0 -65
- package/_cjs/environments/definitions/moonbeam/core-markets.js.map +0 -1
- package/_cjs/environments/definitions/moonbeam/custom.js +0 -24
- package/_cjs/environments/definitions/moonbeam/custom.js.map +0 -1
- package/_cjs/environments/definitions/moonbeam/environment.js +0 -38
- package/_cjs/environments/definitions/moonbeam/environment.js.map +0 -1
- package/_cjs/environments/definitions/moonbeam/tokens.js +0 -176
- package/_cjs/environments/definitions/moonbeam/tokens.js.map +0 -1
- package/_cjs/environments/definitions/moonriver/contracts.js +0 -19
- package/_cjs/environments/definitions/moonriver/contracts.js.map +0 -1
- package/_cjs/environments/definitions/moonriver/core-markets.js +0 -46
- package/_cjs/environments/definitions/moonriver/core-markets.js.map +0 -1
- package/_cjs/environments/definitions/moonriver/custom.js +0 -12
- package/_cjs/environments/definitions/moonriver/custom.js.map +0 -1
- package/_cjs/environments/definitions/moonriver/environment.js +0 -36
- package/_cjs/environments/definitions/moonriver/environment.js.map +0 -1
- package/_cjs/environments/definitions/moonriver/tokens.js +0 -110
- package/_cjs/environments/definitions/moonriver/tokens.js.map +0 -1
- package/_esm/environments/definitions/moonbeam/contracts.js +0 -19
- package/_esm/environments/definitions/moonbeam/contracts.js.map +0 -1
- package/_esm/environments/definitions/moonbeam/core-markets.js +0 -62
- package/_esm/environments/definitions/moonbeam/core-markets.js.map +0 -1
- package/_esm/environments/definitions/moonbeam/custom.js +0 -21
- package/_esm/environments/definitions/moonbeam/custom.js.map +0 -1
- package/_esm/environments/definitions/moonbeam/environment.js +0 -32
- package/_esm/environments/definitions/moonbeam/environment.js.map +0 -1
- package/_esm/environments/definitions/moonbeam/tokens.js +0 -173
- package/_esm/environments/definitions/moonbeam/tokens.js.map +0 -1
- package/_esm/environments/definitions/moonriver/contracts.js +0 -16
- package/_esm/environments/definitions/moonriver/contracts.js.map +0 -1
- package/_esm/environments/definitions/moonriver/core-markets.js +0 -43
- package/_esm/environments/definitions/moonriver/core-markets.js.map +0 -1
- package/_esm/environments/definitions/moonriver/custom.js +0 -9
- package/_esm/environments/definitions/moonriver/custom.js.map +0 -1
- package/_esm/environments/definitions/moonriver/environment.js +0 -31
- package/_esm/environments/definitions/moonriver/environment.js.map +0 -1
- package/_esm/environments/definitions/moonriver/tokens.js +0 -107
- package/_esm/environments/definitions/moonriver/tokens.js.map +0 -1
- package/_types/environments/definitions/moonbeam/contracts.d.ts +0 -14
- package/_types/environments/definitions/moonbeam/contracts.d.ts.map +0 -1
- package/_types/environments/definitions/moonbeam/core-markets.d.ts +0 -57
- package/_types/environments/definitions/moonbeam/core-markets.d.ts.map +0 -1
- package/_types/environments/definitions/moonbeam/custom.d.ts +0 -25
- package/_types/environments/definitions/moonbeam/custom.d.ts.map +0 -1
- package/_types/environments/definitions/moonbeam/environment.d.ts +0 -265
- package/_types/environments/definitions/moonbeam/environment.d.ts.map +0 -1
- package/_types/environments/definitions/moonbeam/tokens.d.ts +0 -171
- package/_types/environments/definitions/moonbeam/tokens.d.ts.map +0 -1
- package/_types/environments/definitions/moonriver/contracts.d.ts +0 -11
- package/_types/environments/definitions/moonriver/contracts.d.ts.map +0 -1
- package/_types/environments/definitions/moonriver/core-markets.d.ts +0 -38
- package/_types/environments/definitions/moonriver/core-markets.d.ts.map +0 -1
- package/_types/environments/definitions/moonriver/custom.d.ts +0 -8
- package/_types/environments/definitions/moonriver/custom.d.ts.map +0 -1
- package/_types/environments/definitions/moonriver/environment.d.ts +0 -159
- package/_types/environments/definitions/moonriver/environment.d.ts.map +0 -1
- package/_types/environments/definitions/moonriver/tokens.d.ts +0 -105
- package/_types/environments/definitions/moonriver/tokens.d.ts.map +0 -1
- package/environments/definitions/moonbeam/contracts.ts +0 -19
- package/environments/definitions/moonbeam/core-markets.ts +0 -62
- package/environments/definitions/moonbeam/custom.ts +0 -21
- package/environments/definitions/moonbeam/environment.ts +0 -39
- package/environments/definitions/moonbeam/tokens.ts +0 -173
- package/environments/definitions/moonriver/contracts.ts +0 -16
- package/environments/definitions/moonriver/core-markets.ts +0 -43
- package/environments/definitions/moonriver/custom.ts +0 -9
- package/environments/definitions/moonriver/environment.ts +0 -38
- package/environments/definitions/moonriver/tokens.ts +0 -107
|
@@ -15,11 +15,20 @@ const getGovernorApiUrl = (environment: Environment): string => {
|
|
|
15
15
|
* active multigov contract lives; Moonbeam follows for the historical archive,
|
|
16
16
|
* then Moonriver (legacy standalone governor).
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
* queried on every chain and non-empty receipts are concatenated
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* The fan-out set for two consumers: `getUserVoteReceipt` (a bare proposalId is
|
|
19
|
+
* queried on every chain and non-empty receipts are concatenated) and
|
|
20
|
+
* `getProposals` (every chain's list is fetched and merged, so the archive stays
|
|
21
|
+
* listable after the sunset).
|
|
22
|
+
*
|
|
23
|
+
* Since MOO-551 no Moonbeam/Moonriver environment exists, so 1284 and 1285 are
|
|
24
|
+
* served through whichever governance environment resolved — reporting
|
|
25
|
+
* `quorum: 0` and no `eta` because nothing on-chain can be read for a halted
|
|
26
|
+
* chain. That degradation is the accepted post-sunset design here, not a bug.
|
|
27
|
+
*
|
|
28
|
+
* Still do NOT use this set as the *single-proposal* fallback in `getProposal`:
|
|
29
|
+
* a bare lookup must not silently resolve to a 1285 proposal it wasn't asked
|
|
30
|
+
* for — that stays an explicit `chainId` route (see
|
|
31
|
+
* `MULTIGOV_PROPOSAL_FALLBACK_CHAIN_IDS`).
|
|
23
32
|
*/
|
|
24
33
|
export const SUPPORTED_GOVERNOR_CHAIN_IDS = [1, 1284, 1285] as const;
|
|
25
34
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
+
import { mainnet } from "viem/chains";
|
|
2
3
|
import { Amount } from "../../../common/index.js";
|
|
3
4
|
import type { Environment } from "../../../environments/index.js";
|
|
4
5
|
import { publicEnvironments } from "../../../environments/index.js";
|
|
5
6
|
import {
|
|
6
7
|
MultichainProposalState,
|
|
7
8
|
MultichainProposalStateMapping,
|
|
9
|
+
type Proposal,
|
|
8
10
|
ProposalState,
|
|
9
11
|
} from "../../../types/proposal.js";
|
|
10
12
|
import type { ApiProposal } from "../governor-api-client.js";
|
|
@@ -176,14 +178,24 @@ export type ApiProposalFormatted = {
|
|
|
176
178
|
/**
|
|
177
179
|
* Derive a ProposalState value from API data alone (no on-chain read).
|
|
178
180
|
*
|
|
179
|
-
* Used for proposals whose chainId
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
181
|
+
* Used for proposals whose chainId has no resolvable environment, or whose
|
|
182
|
+
* on-chain state read failed. Since the sunset (MOO-551) this is the permanent
|
|
183
|
+
* — not transient — route for every Moonbeam (1284) and Moonriver (1285)
|
|
184
|
+
* proposal, because no environment exists for those chains any more.
|
|
183
185
|
*
|
|
184
|
-
* Precedence (highest wins):
|
|
186
|
+
* Precedence (highest wins):
|
|
187
|
+
* Executed → Canceled → Queued → Active → Succeeded/Defeated → Pending.
|
|
185
188
|
* Executed wins over Canceled because the SDK treats EXECUTED state changes as
|
|
186
189
|
* the terminal truth even when an earlier CANCELED event is present.
|
|
190
|
+
*
|
|
191
|
+
* Once voting has closed with no terminal state change, the outcome is decided
|
|
192
|
+
* from the tallies. Without that branch every defeated archive proposal fell
|
|
193
|
+
* through to `Pending` and listed as perpetually pending — the pre-sunset
|
|
194
|
+
* Moonbeam governor read used to report the real `Defeated`. The tallies are
|
|
195
|
+
* the only signal available here: quorum needs an RPC and reports 0 for the
|
|
196
|
+
* archive, so a for-majority that missed quorum reads as Succeeded rather than
|
|
197
|
+
* Defeated. That is the same degradation quorum already carries, and it is
|
|
198
|
+
* strictly closer to the truth than Pending.
|
|
187
199
|
*/
|
|
188
200
|
export const deriveProposalStateFromApi = (
|
|
189
201
|
formatted: ApiProposalFormatted,
|
|
@@ -199,6 +211,11 @@ export const deriveProposalStateFromApi = (
|
|
|
199
211
|
if (now >= apiProposal.votingStartTime && now <= apiProposal.votingEndTime) {
|
|
200
212
|
return ProposalState.Active;
|
|
201
213
|
}
|
|
214
|
+
if (now > apiProposal.votingEndTime) {
|
|
215
|
+
return formatted.forVotes.exponential > formatted.againstVotes.exponential
|
|
216
|
+
? ProposalState.Succeeded
|
|
217
|
+
: ProposalState.Defeated;
|
|
218
|
+
}
|
|
202
219
|
return ProposalState.Pending;
|
|
203
220
|
};
|
|
204
221
|
|
|
@@ -332,6 +349,26 @@ export const getEnvironmentByChainId = (
|
|
|
332
349
|
(e) => e.chainId === chainId,
|
|
333
350
|
);
|
|
334
351
|
|
|
352
|
+
/**
|
|
353
|
+
* The environment that drives Governor-API reads for the whole proposal surface.
|
|
354
|
+
*
|
|
355
|
+
* Before the sunset this was the caller's Moonbeam/Moonriver environment, and
|
|
356
|
+
* both `getProposals` and `getProposal` gated on one of those being registered.
|
|
357
|
+
* Those chains are gone (MOO-551), so governance is now homed on the Ethereum
|
|
358
|
+
* multigov hub. Every environment resolves the same `governanceIndexerUrl`, so
|
|
359
|
+
* any registered chain can serve the indexer — Ethereum is merely preferred
|
|
360
|
+
* because its `multichainGovernor` also answers the live quorum/state reads.
|
|
361
|
+
*
|
|
362
|
+
* Consequence for the historical archive: Moonbeam (1284) and Moonriver (1285)
|
|
363
|
+
* proposals still list, because the indexer keeps serving them, but nothing
|
|
364
|
+
* on-chain can be read for them any more. They surface with indexer-derived
|
|
365
|
+
* state and no quorum or eta — see `getProposalsOnChainData`'s `!homeEnv` path.
|
|
366
|
+
*/
|
|
367
|
+
export const resolveGovernanceEnvironment = (
|
|
368
|
+
environments: Environment[],
|
|
369
|
+
): Environment | undefined =>
|
|
370
|
+
environments.find((e) => e.chainId === mainnet.id) ?? environments[0];
|
|
371
|
+
|
|
335
372
|
export const readCrossChainQuorums = async (
|
|
336
373
|
apiProposals: ApiProposal[],
|
|
337
374
|
governanceEnvironment: Environment,
|
|
@@ -376,12 +413,18 @@ export const getProposalsOnChainData = async (
|
|
|
376
413
|
): Promise<ProposalOnChainData[]> => {
|
|
377
414
|
let quorum = 0n;
|
|
378
415
|
|
|
379
|
-
|
|
416
|
+
// The governance environment is now the Ethereum multigov hub (MOO-551), which
|
|
417
|
+
// has a `multichainGovernor` and no legacy `governor`. Read whichever the env
|
|
418
|
+
// actually wires, otherwise a hub-local proposal would report quorum 0.
|
|
419
|
+
const localQuorumRead = governanceEnvironment.contracts.governor
|
|
420
|
+
? () => governanceEnvironment.contracts.governor?.read.getQuorum()
|
|
421
|
+
: governanceEnvironment.contracts.multichainGovernor
|
|
422
|
+
? () => governanceEnvironment.contracts.multichainGovernor?.read.quorum()
|
|
423
|
+
: undefined;
|
|
424
|
+
|
|
425
|
+
if (localQuorumRead) {
|
|
380
426
|
try {
|
|
381
|
-
quorum =
|
|
382
|
-
governanceEnvironment.chainId === 1284
|
|
383
|
-
? await governanceEnvironment.contracts.governor.read.quorumVotes()
|
|
384
|
-
: await governanceEnvironment.contracts.governor.read.getQuorum();
|
|
427
|
+
quorum = (await localQuorumRead()) ?? 0n;
|
|
385
428
|
} catch (error) {
|
|
386
429
|
console.warn("Failed to fetch quorum:", error);
|
|
387
430
|
// Report so a swallowed quorum failure (which leaves quorum at 0n) is
|
|
@@ -571,3 +614,93 @@ export const getProposalsOnChainData = async (
|
|
|
571
614
|
|
|
572
615
|
return onChainDataList;
|
|
573
616
|
};
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Map an indexer proposal plus its on-chain data onto the public `Proposal`.
|
|
620
|
+
*
|
|
621
|
+
* `getProposal` and `getProposals` both used to carry their own copy of this
|
|
622
|
+
* block, and both carried a comment warning the other not to drift from it —
|
|
623
|
+
* jscpd measured 69 duplicated lines between them. One implementation removes
|
|
624
|
+
* the drift risk those comments were describing.
|
|
625
|
+
*/
|
|
626
|
+
export const mapApiProposalToProposal = (
|
|
627
|
+
apiProposal: ApiProposal,
|
|
628
|
+
onChainData: ProposalOnChainData,
|
|
629
|
+
governanceEnvironment: Environment,
|
|
630
|
+
): Proposal => {
|
|
631
|
+
const formattedData = formatApiProposalData(apiProposal);
|
|
632
|
+
// getProposalsOnChainData already classified this proposal and routed its
|
|
633
|
+
// reads accordingly; re-classifying here would drift and drop `multichain`.
|
|
634
|
+
const isMultichain = onChainData.isMultichain;
|
|
635
|
+
|
|
636
|
+
const now = Math.floor(Date.now() / 1000);
|
|
637
|
+
let proposalState = onChainData.state;
|
|
638
|
+
|
|
639
|
+
if (
|
|
640
|
+
proposalState === ProposalState.Pending &&
|
|
641
|
+
now >= apiProposal.votingStartTime &&
|
|
642
|
+
now <= apiProposal.votingEndTime
|
|
643
|
+
) {
|
|
644
|
+
proposalState = ProposalState.Active;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (formattedData.executed) {
|
|
648
|
+
proposalState = ProposalState.Executed;
|
|
649
|
+
} else if (
|
|
650
|
+
isMultichain &&
|
|
651
|
+
onChainData.votesCollected &&
|
|
652
|
+
now > apiProposal.votingEndTime &&
|
|
653
|
+
proposalState === ProposalState.Succeeded
|
|
654
|
+
) {
|
|
655
|
+
// Succeeded with collection done means "awaiting execution" — surface as
|
|
656
|
+
// Queued so the frontend renders the "Ready to Execute" timeline step.
|
|
657
|
+
// Defeated/Canceled/Executed must NOT be promoted: under the
|
|
658
|
+
// state-machine-based votesCollected those terminal states also satisfy
|
|
659
|
+
// `votesCollected: true`, so a `< Queued` gate would mislabel them.
|
|
660
|
+
proposalState = ProposalState.Queued;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const proposal: Proposal = {
|
|
664
|
+
id: apiProposal.proposalId,
|
|
665
|
+
chainId: apiProposal.chainId,
|
|
666
|
+
proposalId: apiProposal.proposalId,
|
|
667
|
+
proposer: apiProposal.proposer as `0x${string}`,
|
|
668
|
+
eta: onChainData.eta,
|
|
669
|
+
startTimestamp: apiProposal.votingStartTime,
|
|
670
|
+
endTimestamp: apiProposal.votingEndTime,
|
|
671
|
+
startBlock: Number(apiProposal.blockNumber),
|
|
672
|
+
forVotes: formattedData.forVotes,
|
|
673
|
+
againstVotes: formattedData.againstVotes,
|
|
674
|
+
abstainVotes: formattedData.abstainVotes,
|
|
675
|
+
totalVotes: formattedData.totalVotes,
|
|
676
|
+
canceled: formattedData.canceled,
|
|
677
|
+
executed: formattedData.executed,
|
|
678
|
+
quorum: new Amount(onChainData.quorum, 18),
|
|
679
|
+
state: proposalState,
|
|
680
|
+
// Extended data
|
|
681
|
+
title: formattedData.title,
|
|
682
|
+
subtitle: formattedData.subtitle,
|
|
683
|
+
description: apiProposal.description,
|
|
684
|
+
targets: apiProposal.targets,
|
|
685
|
+
calldatas: apiProposal.calldatas,
|
|
686
|
+
// Legacy-governor proposals (Moonriver, early Moonbeam) carry the function
|
|
687
|
+
// signature separately from the selector-less calldata; pass it through so
|
|
688
|
+
// consumers can decode the call. Empty for multichain-governor proposals.
|
|
689
|
+
signatures: apiProposal.signatures ?? [],
|
|
690
|
+
stateChanges: formattedData.stateChanges,
|
|
691
|
+
environment: governanceEnvironment,
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
if (apiProposal.snapshotBlocks) {
|
|
695
|
+
proposal.snapshotBlocks = apiProposal.snapshotBlocks;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (isMultichain) {
|
|
699
|
+
proposal.multichain = {
|
|
700
|
+
id: apiProposal.proposalId,
|
|
701
|
+
votesCollected: onChainData.votesCollected,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return proposal;
|
|
706
|
+
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { mainnet, moonbeam, moonriver } from "viem/chains";
|
|
2
1
|
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
|
|
3
|
-
import { Amount, getEnvironmentFromArgs } from "../../../common/index.js";
|
|
4
2
|
import type { NetworkParameterType } from "../../../common/types.js";
|
|
5
3
|
import type { Chain, Environment } from "../../../environments/index.js";
|
|
6
|
-
import {
|
|
4
|
+
import type { Proposal } from "../../../types/proposal.js";
|
|
7
5
|
import {
|
|
8
6
|
type ApiProposal,
|
|
9
7
|
MULTIGOV_PROPOSAL_FALLBACK_CHAIN_IDS,
|
|
@@ -12,9 +10,10 @@ import {
|
|
|
12
10
|
} from "../governor-api-client.js";
|
|
13
11
|
import { resolveIpfsDescriptions } from "../ipfs.js";
|
|
14
12
|
import {
|
|
15
|
-
formatApiProposalData,
|
|
16
13
|
getProposalsOnChainData,
|
|
14
|
+
mapApiProposalToProposal,
|
|
17
15
|
readCrossChainQuorums,
|
|
16
|
+
resolveGovernanceEnvironment,
|
|
18
17
|
} from "./common.js";
|
|
19
18
|
|
|
20
19
|
export type GetProposalParameters<
|
|
@@ -41,42 +40,24 @@ export async function getProposal<
|
|
|
41
40
|
): GetProposalReturnType {
|
|
42
41
|
const { proposalId } = args;
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// reload path returns undefined.
|
|
55
|
-
if (environment.chainId === mainnet.id) {
|
|
56
|
-
const moonbeamEnv = Object.values(
|
|
57
|
-
client.environments as Record<string, Environment>,
|
|
58
|
-
).find((e) => e.chainId === moonbeam.id);
|
|
59
|
-
if (!moonbeamEnv) {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
return getGovernorApiProposal(
|
|
63
|
-
moonbeamEnv,
|
|
64
|
-
proposalId,
|
|
65
|
-
args.chainId ?? mainnet.id,
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
environment.chainId !== moonbeam.id &&
|
|
71
|
-
environment.chainId !== moonriver.id
|
|
72
|
-
) {
|
|
43
|
+
// `args.chainId` identifies the chain the PROPOSAL lives on, which is no longer
|
|
44
|
+
// the same thing as the environment we read through. Before the sunset it was
|
|
45
|
+
// both, and the action resolved a Moonbeam/Moonriver environment from it —
|
|
46
|
+
// which now means a `chainId: 1284` lookup would resolve nothing and the whole
|
|
47
|
+
// historical archive would 404 (MOO-551). Resolve the indexer source
|
|
48
|
+
// independently and pass `chainId` through untouched.
|
|
49
|
+
const governanceEnvironment = resolveGovernanceEnvironment(
|
|
50
|
+
Object.values(client.environments as Record<string, Environment>),
|
|
51
|
+
);
|
|
52
|
+
if (!governanceEnvironment) {
|
|
73
53
|
return undefined;
|
|
74
54
|
}
|
|
75
55
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
56
|
+
return getGovernorApiProposal(
|
|
57
|
+
governanceEnvironment,
|
|
58
|
+
proposalId,
|
|
59
|
+
args.chainId,
|
|
60
|
+
);
|
|
80
61
|
}
|
|
81
62
|
|
|
82
63
|
/**
|
|
@@ -118,87 +99,23 @@ async function getGovernorApiProposal(
|
|
|
118
99
|
readCrossChainQuorums([apiProposal], governanceEnvironment),
|
|
119
100
|
]);
|
|
120
101
|
|
|
121
|
-
const formattedData = formatApiProposalData(apiProposal);
|
|
122
102
|
const onChainDataList = await getProposalsOnChainData(
|
|
123
103
|
[apiProposal],
|
|
124
104
|
governanceEnvironment,
|
|
125
105
|
{ crossChainQuorums },
|
|
126
106
|
);
|
|
127
|
-
const onChainData = onChainDataList[0]!;
|
|
128
|
-
// Single source of truth: getProposalsOnChainData already classified this
|
|
129
|
-
// proposal with the caller env's Artemis cutoff and used it to route the
|
|
130
|
-
// on-chain reads. Reusing it avoids the drift that left Moonbeam-homed
|
|
131
|
-
// local-target proposals (and hub-local Ethereum ones) without `multichain`.
|
|
132
|
-
const isMultichain = onChainData.isMultichain;
|
|
133
|
-
|
|
134
|
-
const now = Math.floor(Date.now() / 1000);
|
|
135
|
-
let proposalState = onChainData.state;
|
|
136
107
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
) {
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (formattedData.executed) {
|
|
146
|
-
proposalState = ProposalState.Executed;
|
|
147
|
-
} else if (
|
|
148
|
-
isMultichain &&
|
|
149
|
-
onChainData.votesCollected &&
|
|
150
|
-
now > apiProposal.votingEndTime &&
|
|
151
|
-
proposalState === ProposalState.Succeeded
|
|
152
|
-
) {
|
|
153
|
-
// Succeeded with collection done means "awaiting execution" — surface as
|
|
154
|
-
// Queued so the frontend renders the "Ready to Execute" timeline step.
|
|
155
|
-
// Defeated/Canceled/Executed must NOT be promoted: under the new
|
|
156
|
-
// state-machine-based votesCollected, those terminal states also satisfy
|
|
157
|
-
// `votesCollected: true`, so a `< Queued` gate would mislabel them.
|
|
158
|
-
proposalState = ProposalState.Queued;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const proposal: Proposal = {
|
|
162
|
-
id: apiProposal.proposalId,
|
|
163
|
-
chainId: apiProposal.chainId,
|
|
164
|
-
proposalId: apiProposal.proposalId,
|
|
165
|
-
proposer: apiProposal.proposer as `0x${string}`,
|
|
166
|
-
eta: onChainData.eta,
|
|
167
|
-
startTimestamp: apiProposal.votingStartTime,
|
|
168
|
-
endTimestamp: apiProposal.votingEndTime,
|
|
169
|
-
startBlock: Number(apiProposal.blockNumber),
|
|
170
|
-
forVotes: formattedData.forVotes,
|
|
171
|
-
againstVotes: formattedData.againstVotes,
|
|
172
|
-
abstainVotes: formattedData.abstainVotes,
|
|
173
|
-
totalVotes: formattedData.totalVotes,
|
|
174
|
-
canceled: formattedData.canceled,
|
|
175
|
-
executed: formattedData.executed,
|
|
176
|
-
quorum: new Amount(onChainData.quorum, 18),
|
|
177
|
-
state: proposalState,
|
|
178
|
-
// Extended data
|
|
179
|
-
title: formattedData.title,
|
|
180
|
-
subtitle: formattedData.subtitle,
|
|
181
|
-
description: apiProposal.description,
|
|
182
|
-
targets: apiProposal.targets,
|
|
183
|
-
calldatas: apiProposal.calldatas,
|
|
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 ?? [],
|
|
188
|
-
stateChanges: formattedData.stateChanges,
|
|
189
|
-
environment: governanceEnvironment,
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
if (apiProposal.snapshotBlocks) {
|
|
193
|
-
proposal.snapshotBlocks = apiProposal.snapshotBlocks;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (isMultichain) {
|
|
197
|
-
proposal.multichain = {
|
|
198
|
-
id: apiProposal.proposalId,
|
|
199
|
-
votesCollected: onChainData.votesCollected,
|
|
200
|
-
};
|
|
108
|
+
// `getProposalsOnChainData` maps 1:1 over its input, so the single entry is
|
|
109
|
+
// always present — guard rather than assert, so the invariant is enforced
|
|
110
|
+
// instead of asserted away if that ever stops holding.
|
|
111
|
+
const onChainData = onChainDataList[0];
|
|
112
|
+
if (!onChainData) {
|
|
113
|
+
return undefined;
|
|
201
114
|
}
|
|
202
115
|
|
|
203
|
-
return
|
|
116
|
+
return mapApiProposalToProposal(
|
|
117
|
+
apiProposal,
|
|
118
|
+
onChainData,
|
|
119
|
+
governanceEnvironment,
|
|
120
|
+
);
|
|
204
121
|
}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { moonbeam, moonriver } from "viem/chains";
|
|
2
1
|
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
|
|
3
|
-
import {
|
|
2
|
+
import { getEnvironmentsFromArgs } from "../../../common/index.js";
|
|
4
3
|
import type { OptionalNetworkParameterType } from "../../../common/types.js";
|
|
5
4
|
import type { Chain, Environment } from "../../../environments/index.js";
|
|
6
5
|
import * as logger from "../../../logger/console.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import type { Proposal } from "../../../types/proposal.js";
|
|
7
|
+
import {
|
|
8
|
+
type ApiProposal,
|
|
9
|
+
SUPPORTED_GOVERNOR_CHAIN_IDS,
|
|
10
|
+
fetchAllProposals,
|
|
11
|
+
} from "../governor-api-client.js";
|
|
9
12
|
import { resolveIpfsDescriptions } from "../ipfs.js";
|
|
10
13
|
import {
|
|
11
|
-
formatApiProposalData,
|
|
12
14
|
getProposalsOnChainData,
|
|
15
|
+
mapApiProposalToProposal,
|
|
13
16
|
readCrossChainQuorums,
|
|
17
|
+
resolveGovernanceEnvironment,
|
|
14
18
|
} from "./common.js";
|
|
15
19
|
|
|
16
20
|
export type GetProposalsParameters<
|
|
@@ -31,30 +35,14 @@ export async function getProposals<
|
|
|
31
35
|
|
|
32
36
|
const environments = getEnvironmentsFromArgs(client, args);
|
|
33
37
|
|
|
34
|
-
const
|
|
35
|
-
(environment) =>
|
|
36
|
-
environment.chainId === moonbeam.id ||
|
|
37
|
-
environment.chainId === moonriver.id,
|
|
38
|
-
);
|
|
38
|
+
const governanceEnvironment = resolveGovernanceEnvironment(environments);
|
|
39
39
|
|
|
40
|
-
if (
|
|
40
|
+
if (!governanceEnvironment) {
|
|
41
41
|
logger.end(logId);
|
|
42
42
|
return [];
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const
|
|
46
|
-
governanceEnvironments.map(async (governanceEnvironment) => {
|
|
47
|
-
if (governanceEnvironment.chainId === moonbeam.id) {
|
|
48
|
-
// Moonbeam + Ethereum: Governor API (chainIds 1 and 1284)
|
|
49
|
-
return getMoonbeamProposals(governanceEnvironment);
|
|
50
|
-
} else {
|
|
51
|
-
// Moonriver: Governor API, single legacy governor (chainId 1285)
|
|
52
|
-
return getMoonriverProposals(governanceEnvironment);
|
|
53
|
-
}
|
|
54
|
-
}),
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const proposals = allProposals.flat();
|
|
45
|
+
const proposals = await fetchGovernorProposals(governanceEnvironment);
|
|
58
46
|
// Newer multigov-ethereum proposals (chainId=1) and historical Moonbeam
|
|
59
47
|
// proposals (chainId=1284) restart their proposalId counters from 1, so IDs
|
|
60
48
|
// may collide across chains. Sort by proposalId desc with chainId as a
|
|
@@ -70,27 +58,34 @@ export async function getProposals<
|
|
|
70
58
|
}
|
|
71
59
|
|
|
72
60
|
/**
|
|
73
|
-
* Fetch
|
|
61
|
+
* Fetch every proposal the Governor API serves, across all governance chains.
|
|
74
62
|
*
|
|
75
|
-
*
|
|
76
|
-
* - chainId=1
|
|
77
|
-
* - chainId=1284 (Moonbeam)
|
|
63
|
+
* One indexer DO serves them all:
|
|
64
|
+
* - chainId=1 (Ethereum) — the active multigov contract
|
|
65
|
+
* - chainId=1284 (Moonbeam) — historical archive, chain halted
|
|
66
|
+
* - chainId=1285 (Moonriver) — legacy Apollo governor, chain halted
|
|
78
67
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
68
|
+
* The sunset chains keep appearing here on purpose: the indexer still has their
|
|
69
|
+
* proposals even though no RPC exists for them any more, so the governance
|
|
70
|
+
* record stays readable (MOO-551). What they lose is the on-chain half —
|
|
71
|
+
* `getProposalsOnChainData` finds no environment for 1284/1285 and derives their
|
|
72
|
+
* state from indexer events, leaving quorum and eta unset.
|
|
73
|
+
*
|
|
74
|
+
* `Promise.allSettled` so one chain's outage degrades to a missing slice rather
|
|
75
|
+
* than an empty list.
|
|
81
76
|
*/
|
|
82
|
-
async function
|
|
77
|
+
async function fetchGovernorProposals(
|
|
83
78
|
governanceEnvironment: Environment,
|
|
84
79
|
): Promise<Proposal[]> {
|
|
85
|
-
const results = await Promise.allSettled(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
const results = await Promise.allSettled(
|
|
81
|
+
SUPPORTED_GOVERNOR_CHAIN_IDS.map((chainId) =>
|
|
82
|
+
fetchAllProposals(governanceEnvironment, { chainId }),
|
|
83
|
+
),
|
|
84
|
+
);
|
|
89
85
|
|
|
90
|
-
const chainsAttempted: ReadonlyArray<1 | 1284> = [1, 1284];
|
|
91
86
|
const apiProposals: ApiProposal[] = [];
|
|
92
87
|
results.forEach((result, index) => {
|
|
93
|
-
const chainId =
|
|
88
|
+
const chainId = SUPPORTED_GOVERNOR_CHAIN_IDS[index];
|
|
94
89
|
if (result.status === "fulfilled") {
|
|
95
90
|
apiProposals.push(...result.value);
|
|
96
91
|
} else if (chainId !== undefined) {
|
|
@@ -109,44 +104,13 @@ async function getMoonbeamProposals(
|
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
/**
|
|
112
|
-
*
|
|
107
|
+
* Governor-API pipeline: resolve IPFS descriptions + cross-chain quorums in
|
|
108
|
+
* parallel, read on-chain data, then map each ApiProposal to a Proposal.
|
|
113
109
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
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.
|
|
110
|
+
* Single caller (`fetchGovernorProposals`) since the sunset removed the separate
|
|
111
|
+
* `getMoonbeamProposals` / `getMoonriverProposals` entry points (MOO-551) —
|
|
112
|
+
* kept as its own function because it is the list-shaped counterpart to
|
|
113
|
+
* `getProposal`'s single-proposal pipeline, and the two are read together.
|
|
150
114
|
*/
|
|
151
115
|
async function buildProposals(
|
|
152
116
|
apiProposals: ApiProposal[],
|
|
@@ -164,85 +128,20 @@ async function buildProposals(
|
|
|
164
128
|
{ crossChainQuorums },
|
|
165
129
|
);
|
|
166
130
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
now <= apiProposal.votingEndTime
|
|
182
|
-
) {
|
|
183
|
-
proposalState = ProposalState.Active;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (formattedData.executed) {
|
|
187
|
-
proposalState = ProposalState.Executed;
|
|
188
|
-
} else if (
|
|
189
|
-
isMultichain &&
|
|
190
|
-
onChainData.votesCollected &&
|
|
191
|
-
now > apiProposal.votingEndTime &&
|
|
192
|
-
proposalState === ProposalState.Succeeded
|
|
193
|
-
) {
|
|
194
|
-
// Succeeded with collection done means "awaiting execution" — surface
|
|
195
|
-
// as Queued so the frontend renders the "Ready to Execute" timeline
|
|
196
|
-
// step. Defeated/Canceled/Executed must NOT be promoted: under the new
|
|
197
|
-
// state-machine-based votesCollected, those terminal states also
|
|
198
|
-
// satisfy `votesCollected: true`, so a `< Queued` gate would mislabel
|
|
199
|
-
// them.
|
|
200
|
-
proposalState = ProposalState.Queued;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const proposal: Proposal = {
|
|
204
|
-
id: apiProposal.proposalId,
|
|
205
|
-
chainId: apiProposal.chainId,
|
|
206
|
-
proposalId: apiProposal.proposalId,
|
|
207
|
-
proposer: apiProposal.proposer as `0x${string}`,
|
|
208
|
-
eta: onChainData.eta,
|
|
209
|
-
startTimestamp: apiProposal.votingStartTime,
|
|
210
|
-
endTimestamp: apiProposal.votingEndTime,
|
|
211
|
-
startBlock: Number(apiProposal.blockNumber),
|
|
212
|
-
forVotes: formattedData.forVotes,
|
|
213
|
-
againstVotes: formattedData.againstVotes,
|
|
214
|
-
abstainVotes: formattedData.abstainVotes,
|
|
215
|
-
totalVotes: formattedData.totalVotes,
|
|
216
|
-
canceled: formattedData.canceled,
|
|
217
|
-
executed: formattedData.executed,
|
|
218
|
-
quorum: new Amount(onChainData.quorum, 18),
|
|
219
|
-
state: proposalState,
|
|
220
|
-
// Extended data
|
|
221
|
-
title: formattedData.title,
|
|
222
|
-
subtitle: formattedData.subtitle,
|
|
223
|
-
description: apiProposal.description,
|
|
224
|
-
targets: apiProposal.targets,
|
|
225
|
-
calldatas: apiProposal.calldatas,
|
|
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 ?? [],
|
|
230
|
-
stateChanges: formattedData.stateChanges,
|
|
231
|
-
environment: governanceEnvironment,
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
if (apiProposal.snapshotBlocks) {
|
|
235
|
-
proposal.snapshotBlocks = apiProposal.snapshotBlocks;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (isMultichain) {
|
|
239
|
-
proposal.multichain = {
|
|
240
|
-
id: apiProposal.proposalId,
|
|
241
|
-
votesCollected: onChainData.votesCollected,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return proposal;
|
|
131
|
+
// `getProposalsOnChainData` maps 1:1 over its input, so every index resolves —
|
|
132
|
+
// guard rather than assert, so the invariant is enforced instead of asserted
|
|
133
|
+
// away if that ever stops holding.
|
|
134
|
+
const proposals: Proposal[] = apiProposals.flatMap((apiProposal, index) => {
|
|
135
|
+
const onChainData = onChainDataList[index];
|
|
136
|
+
return onChainData
|
|
137
|
+
? [
|
|
138
|
+
mapApiProposalToProposal(
|
|
139
|
+
apiProposal,
|
|
140
|
+
onChainData,
|
|
141
|
+
governanceEnvironment,
|
|
142
|
+
),
|
|
143
|
+
]
|
|
144
|
+
: [];
|
|
246
145
|
});
|
|
247
146
|
|
|
248
147
|
return proposals;
|