@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.
- package/CHANGELOG.md +10 -0
- package/_cjs/actions/governance/governor-api-client.js +3 -2
- package/_cjs/actions/governance/governor-api-client.js.map +1 -1
- package/_cjs/actions/governance/proposals/common.js +11 -296
- package/_cjs/actions/governance/proposals/common.js.map +1 -1
- package/_cjs/actions/governance/proposals/getProposal.js +9 -25
- package/_cjs/actions/governance/proposals/getProposal.js.map +1 -1
- package/_cjs/actions/governance/proposals/getProposals.js +23 -14
- package/_cjs/actions/governance/proposals/getProposals.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_cjs/types/proposal.js.map +1 -1
- package/_esm/actions/governance/governor-api-client.js +21 -4
- package/_esm/actions/governance/governor-api-client.js.map +1 -1
- package/_esm/actions/governance/ipfs.js +2 -2
- package/_esm/actions/governance/proposals/common.js +28 -302
- package/_esm/actions/governance/proposals/common.js.map +1 -1
- package/_esm/actions/governance/proposals/getProposal.js +23 -35
- package/_esm/actions/governance/proposals/getProposal.js.map +1 -1
- package/_esm/actions/governance/proposals/getProposals.js +48 -20
- package/_esm/actions/governance/proposals/getProposals.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/types/proposal.js.map +1 -1
- package/_types/actions/governance/getUserVoteReceipt.d.ts +4 -4
- package/_types/actions/governance/governor-api-client.d.ts +34 -4
- package/_types/actions/governance/governor-api-client.d.ts.map +1 -1
- package/_types/actions/governance/ipfs.d.ts +2 -2
- package/_types/actions/governance/proposals/common.d.ts +12 -39
- package/_types/actions/governance/proposals/common.d.ts.map +1 -1
- package/_types/actions/governance/proposals/getProposal.d.ts +2 -1
- package/_types/actions/governance/proposals/getProposal.d.ts.map +1 -1
- package/_types/actions/governance/proposals/getProposals.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/types/proposal.d.ts +11 -0
- package/_types/types/proposal.d.ts.map +1 -1
- package/actions/governance/getUserVoteReceipt.ts +4 -4
- package/actions/governance/governor-api-client.ts +35 -4
- package/actions/governance/ipfs.ts +2 -2
- package/actions/governance/proposals/common.ts +28 -466
- package/actions/governance/proposals/getProposal.ts +25 -49
- package/actions/governance/proposals/getProposals.ts +57 -30
- package/errors/version.ts +1 -1
- package/package.json +1 -1
- package/types/proposal.ts +11 -0
|
@@ -22,6 +22,17 @@ export type Proposal = {
|
|
|
22
22
|
id: number;
|
|
23
23
|
votesCollected: boolean;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Authoritative per-chain voting-power snapshot blocks resolved by the
|
|
27
|
+
* lunar-indexer, keyed by chain name (`mainnet` → 1, `base` → 8453,
|
|
28
|
+
* `optimism` → 10, `moonbeam` → 1284). Block numbers are decimal strings.
|
|
29
|
+
*
|
|
30
|
+
* Present on indexer-sourced multichain proposals; absent for older
|
|
31
|
+
* proposals indexed before the field existed and for on-chain (Moonriver)
|
|
32
|
+
* proposals. Consumers reading voting power should prefer these blocks over
|
|
33
|
+
* resolving the snapshot timestamp to a block client-side.
|
|
34
|
+
*/
|
|
35
|
+
snapshotBlocks?: Record<string, string>;
|
|
25
36
|
title?: string;
|
|
26
37
|
subtitle?: string;
|
|
27
38
|
description?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../types/proposal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,aAAa,CAAC;IACrB,UAAU,CAAC,EAAE;QACX,EAAE,EAAE,MAAM,CAAC;QACX,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;
|
|
1
|
+
{"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../types/proposal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,aAAa,CAAC;IACrB,UAAU,CAAC,EAAE;QACX,EAAE,EAAE,MAAM,CAAC;QACX,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,EAAE,CAAC;IACJ,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF,oBAAY,aAAa;IACvB,OAAO,IAAI;IACX,MAAM,IAAI;IACV,QAAQ,IAAI;IACZ,QAAQ,IAAI;IACZ,SAAS,IAAI;IACb,MAAM,IAAI;IACV,OAAO,IAAI;IACX,QAAQ,IAAI;IACZ,gBAAgB,IAAI;IACpB,kBAAkB,IAAI;CACvB;AAGD,oBAAY,uBAAuB;IACjC,MAAM,IAAI;IACV,wBAAwB,IAAI;IAC5B,QAAQ,IAAI;IACZ,QAAQ,IAAI;IACZ,SAAS,IAAI;IACb,QAAQ,IAAI;CACb;AAGD,eAAO,MAAM,8BAA8B;;;;;;;CAO1C,CAAC;AAGF,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE;QACZ,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,EAAE,CAAC;CACL,CAAC"}
|
|
@@ -23,10 +23,10 @@ export type GetUserVoteReceiptParameters<
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* The chain the proposal lives on (1 = Ethereum multigov,
|
|
26
|
-
* 1284 = Moonbeam historical). When omitted, every
|
|
27
|
-
* and non-empty receipts are concatenated —
|
|
28
|
-
* chains (they represent different proposals),
|
|
29
|
-
* can have votes on
|
|
26
|
+
* 1284 = Moonbeam historical, 1285 = Moonriver legacy). When omitted, every
|
|
27
|
+
* supported chain is queried and non-empty receipts are concatenated —
|
|
28
|
+
* proposalIds may collide across chains (they represent different proposals),
|
|
29
|
+
* so a single bare proposalId can have votes on more than one chain.
|
|
30
30
|
*/
|
|
31
31
|
chainId?: number;
|
|
32
32
|
};
|
|
@@ -12,9 +12,26 @@ const getGovernorApiUrl = (environment: Environment): string => {
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Chains the governor indexer serves. Ethereum first because that's where the
|
|
15
|
-
* active multigov contract lives; Moonbeam follows for the historical archive
|
|
15
|
+
* active multigov contract lives; Moonbeam follows for the historical archive,
|
|
16
|
+
* then Moonriver (legacy standalone governor).
|
|
17
|
+
*
|
|
18
|
+
* This is the fan-out set for `getUserVoteReceipt` — a bare proposalId is
|
|
19
|
+
* queried on every chain and non-empty receipts are concatenated. Do NOT use it
|
|
20
|
+
* as the single-proposal fallback in `getProposal`: Moonriver has its own
|
|
21
|
+
* explicit route there, and reaching a 1285 proposal through a Moonbeam env
|
|
22
|
+
* would surface a degraded result (see `MULTIGOV_PROPOSAL_FALLBACK_CHAIN_IDS`).
|
|
23
|
+
*/
|
|
24
|
+
export const SUPPORTED_GOVERNOR_CHAIN_IDS = [1, 1284, 1285] as const;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Fallback chains for a single multigov proposal lookup (`getProposal` with no
|
|
28
|
+
* explicit `chainId`), tried first-hit-wins. Deliberately excludes Moonriver
|
|
29
|
+
* (1285): Moonriver proposals are fetched through their own env via an explicit
|
|
30
|
+
* chainId, so a bare Moonbeam/Ethereum lookup must never resolve to a 1285
|
|
31
|
+
* proposal — that would return `quorum: 0n` (cross-chain quorum skips
|
|
32
|
+
* governor-less chains) and the wrong `environment`.
|
|
16
33
|
*/
|
|
17
|
-
export const
|
|
34
|
+
export const MULTIGOV_PROPOSAL_FALLBACK_CHAIN_IDS = [1, 1284] as const;
|
|
18
35
|
|
|
19
36
|
/**
|
|
20
37
|
* Build the chain-prefixed proposal key the indexer requires
|
|
@@ -103,6 +120,13 @@ export type ApiProposal = {
|
|
|
103
120
|
targets: string[];
|
|
104
121
|
values: string[];
|
|
105
122
|
calldatas: string[];
|
|
123
|
+
/**
|
|
124
|
+
* Legacy/Artemis-governor function signatures (e.g. "setDirectPrice(address,uint256)"),
|
|
125
|
+
* parallel to `targets`/`calldatas`. Present for legacy-governor proposals
|
|
126
|
+
* (Moonriver, early Moonbeam) whose calldata carries no 4-byte selector;
|
|
127
|
+
* empty/absent for multichain-governor proposals (selector is in the calldata).
|
|
128
|
+
*/
|
|
129
|
+
signatures?: string[];
|
|
106
130
|
votingStartTime: number;
|
|
107
131
|
votingEndTime: number;
|
|
108
132
|
description: string;
|
|
@@ -113,6 +137,12 @@ export type ApiProposal = {
|
|
|
113
137
|
timestamp: number;
|
|
114
138
|
transactionHash: string;
|
|
115
139
|
stateChanges?: ApiProposalStateChange[];
|
|
140
|
+
/**
|
|
141
|
+
* Per-chain voting-power snapshot blocks resolved by the indexer, keyed by
|
|
142
|
+
* chain name (`mainnet`, `base`, `optimism`, `moonbeam`). Decimal strings.
|
|
143
|
+
* Omitted for proposals indexed before this field existed.
|
|
144
|
+
*/
|
|
145
|
+
snapshotBlocks?: Record<string, string>;
|
|
116
146
|
};
|
|
117
147
|
|
|
118
148
|
export type ApiVote = {
|
|
@@ -168,8 +198,9 @@ export type FetchProposalsOptions = PaginationOptions & {
|
|
|
168
198
|
/**
|
|
169
199
|
* Fetch proposals from Governor API
|
|
170
200
|
*
|
|
171
|
-
* `chainId` is required by the indexer — 1 (Ethereum multigov)
|
|
172
|
-
* 1284 (Moonbeam historical). Missing/unsupported
|
|
201
|
+
* `chainId` is required by the indexer — 1 (Ethereum multigov),
|
|
202
|
+
* 1284 (Moonbeam historical), or 1285 (Moonriver legacy). Missing/unsupported
|
|
203
|
+
* chainId returns 400.
|
|
173
204
|
*/
|
|
174
205
|
export async function fetchProposals(
|
|
175
206
|
environment: Environment,
|
|
@@ -50,8 +50,8 @@ export const fetchIpfsContent = async (hash: string): Promise<string> => {
|
|
|
50
50
|
* fetches run in parallel.
|
|
51
51
|
*
|
|
52
52
|
* Failures are logged via console.warn AND surfaced through
|
|
53
|
-
* `env.onError` (matching the convention used by `
|
|
54
|
-
* `
|
|
53
|
+
* `env.onError` (matching the convention used by `getProposalsOnChainData` /
|
|
54
|
+
* `readCrossChainQuorums`), then swallowed per-proposal: the `ipfs://` URI
|
|
55
55
|
* is left untouched so consumers can detect (e.g. via
|
|
56
56
|
* `description.startsWith("ipfs://")`) and render a fallback. The bulk call
|
|
57
57
|
* never rejects, so a single bad pin doesn't kill `getProposals()` for
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import last from "lodash/last.js";
|
|
3
|
-
import { moonriver } from "viem/chains";
|
|
4
2
|
import { Amount } from "../../../common/index.js";
|
|
5
3
|
import type { Environment } from "../../../environments/index.js";
|
|
6
4
|
import { publicEnvironments } from "../../../environments/index.js";
|
|
7
5
|
import {
|
|
8
6
|
MultichainProposalState,
|
|
9
7
|
MultichainProposalStateMapping,
|
|
10
|
-
type Proposal,
|
|
11
8
|
ProposalState,
|
|
12
9
|
} from "../../../types/proposal.js";
|
|
13
|
-
import { postWithRetry } from "../../axiosWithRetry.js";
|
|
14
10
|
import type { ApiProposal } from "../governor-api-client.js";
|
|
15
11
|
|
|
16
12
|
export const WORMHOLE_CONTRACT = "0xc8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3"; // Moonbeam
|
|
@@ -25,21 +21,6 @@ const MULTICHAIN_WORMHOLE_BRIDGES: ReadonlySet<string> = new Set([
|
|
|
25
21
|
"0x98f3c9e6e3face36baad05fe09d375ef1464288b", // Ethereum
|
|
26
22
|
]);
|
|
27
23
|
|
|
28
|
-
type PonderExtendedProposalData = {
|
|
29
|
-
id: number;
|
|
30
|
-
title: string;
|
|
31
|
-
subtitle: string;
|
|
32
|
-
description: string;
|
|
33
|
-
targets: string[];
|
|
34
|
-
calldatas: string[];
|
|
35
|
-
signatures: string[];
|
|
36
|
-
stateChanges: {
|
|
37
|
-
blockNumber: number;
|
|
38
|
-
transactionHash: string;
|
|
39
|
-
state: string;
|
|
40
|
-
}[];
|
|
41
|
-
};
|
|
42
|
-
|
|
43
24
|
axios.defaults.timeout = 5_000;
|
|
44
25
|
|
|
45
26
|
/**
|
|
@@ -143,15 +124,26 @@ export const isMultichainHomeChain = (chainId: number): boolean => {
|
|
|
143
124
|
* falls back to the home/bridge checks. Omit it or pass `undefined` when the
|
|
144
125
|
* count read failed so the unknown-cutoff bias above applies. (No `= 0` default:
|
|
145
126
|
* that would coerce an explicit `undefined` back to 0 and defeat the bias.)
|
|
127
|
+
*
|
|
128
|
+
* `hasMultichainGovernor` gates rule 4: the unknown-cutoff bias only makes sense
|
|
129
|
+
* on a chain that actually has a multichain governor to route to. On a
|
|
130
|
+
* legacy-only chain (Moonriver — legacy `governor`, no `multichainGovernor`) a
|
|
131
|
+
* failed Artemis count must NOT flip the proposal to multichain, which would
|
|
132
|
+
* route reads to a null contract and surface a spurious `multichain` field.
|
|
133
|
+
* Defaults to `true` so callers that don't supply it keep the dual-governor
|
|
134
|
+
* (Moonbeam) bias behavior.
|
|
146
135
|
*/
|
|
147
136
|
export const classifyProposalMultichain = (
|
|
148
137
|
proposal: { targets?: string[]; proposalId: number; chainId: number },
|
|
149
138
|
legacyArtemisMaxId?: number,
|
|
139
|
+
hasMultichainGovernor = true,
|
|
150
140
|
): boolean =>
|
|
151
141
|
isMultichainHomeChain(proposal.chainId) ||
|
|
152
142
|
isMultichainProposal(proposal.targets) ||
|
|
153
|
-
legacyArtemisMaxId === undefined ||
|
|
154
|
-
(legacyArtemisMaxId
|
|
143
|
+
(legacyArtemisMaxId === undefined && hasMultichainGovernor) ||
|
|
144
|
+
(legacyArtemisMaxId !== undefined &&
|
|
145
|
+
legacyArtemisMaxId > 0 &&
|
|
146
|
+
proposal.proposalId > legacyArtemisMaxId);
|
|
155
147
|
|
|
156
148
|
/**
|
|
157
149
|
* @deprecated Use `classifyProposalMultichain` — this variant misses hub-homed
|
|
@@ -330,9 +322,8 @@ const getLegacyArtemisMaxId = async (
|
|
|
330
322
|
* the `options.crossChainQuorums?.get(...) ?? 0n` pattern.
|
|
331
323
|
*
|
|
332
324
|
* Read failures are routed through `onError` so Sentry-wired consumers see
|
|
333
|
-
* them — matching `
|
|
334
|
-
* `
|
|
335
|
-
* `onError` since we don't have one per foreign chain in scope.
|
|
325
|
+
* them — matching `getProposalsOnChainData`. The caller's `governanceEnvironment`
|
|
326
|
+
* carries `onError` since we don't have one per foreign chain in scope.
|
|
336
327
|
*/
|
|
337
328
|
export const getEnvironmentByChainId = (
|
|
338
329
|
chainId: number,
|
|
@@ -393,6 +384,15 @@ export const getProposalsOnChainData = async (
|
|
|
393
384
|
: await governanceEnvironment.contracts.governor.read.getQuorum();
|
|
394
385
|
} catch (error) {
|
|
395
386
|
console.warn("Failed to fetch quorum:", error);
|
|
387
|
+
// Report so a swallowed quorum failure (which leaves quorum at 0n) is
|
|
388
|
+
// still observable to onError-wired consumers — the legacy Moonriver path
|
|
389
|
+
// surfaced this too. Uses a distinct `source` from the state/proposals
|
|
390
|
+
// reads (and from readCrossChainQuorums' "governance-cross-chain-quorum")
|
|
391
|
+
// so a local quorum-read failure is attributable on its own.
|
|
392
|
+
governanceEnvironment.onError?.(error, {
|
|
393
|
+
source: "governance-quorum",
|
|
394
|
+
chainId: governanceEnvironment.chainId,
|
|
395
|
+
});
|
|
396
396
|
}
|
|
397
397
|
}
|
|
398
398
|
|
|
@@ -420,10 +420,13 @@ export const getProposalsOnChainData = async (
|
|
|
420
420
|
// (Ethereum multigov, no Artemis predecessor) classify as multichain
|
|
421
421
|
// regardless of targets via classifyProposalMultichain. Computed once
|
|
422
422
|
// here and returned on ProposalOnChainData so callers don't re-classify
|
|
423
|
-
// and drift (see getProposal/getProposals).
|
|
423
|
+
// and drift (see getProposal/getProposals). `hasMultichainGovernor` keeps
|
|
424
|
+
// the unknown-cutoff bias from misrouting legacy-only chains (Moonriver)
|
|
425
|
+
// to a nonexistent multichain governor when the Artemis count read fails.
|
|
424
426
|
const isMultichain = classifyProposalMultichain(
|
|
425
427
|
p,
|
|
426
428
|
isLocal ? legacyArtemisMaxId : 0,
|
|
429
|
+
Boolean(homeEnv?.contracts.multichainGovernor),
|
|
427
430
|
);
|
|
428
431
|
|
|
429
432
|
if (!homeEnv) {
|
|
@@ -557,444 +560,3 @@ export const getProposalsOnChainData = async (
|
|
|
557
560
|
|
|
558
561
|
return onChainDataList;
|
|
559
562
|
};
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* Get proposal data from on-chain (Ponder-based, for Moonriver)
|
|
563
|
-
*/
|
|
564
|
-
export const getProposalData = async (params: {
|
|
565
|
-
environment: Environment;
|
|
566
|
-
id?: number;
|
|
567
|
-
}) => {
|
|
568
|
-
try {
|
|
569
|
-
if (params.environment.contracts.governor) {
|
|
570
|
-
let count = 0n;
|
|
571
|
-
let quorum = 0n;
|
|
572
|
-
|
|
573
|
-
if (params.environment.chainId === moonriver.id) {
|
|
574
|
-
[count, quorum] = await Promise.all([
|
|
575
|
-
params.environment.contracts.governor.read.proposalCount(),
|
|
576
|
-
params.environment.contracts.governor.read.getQuorum(),
|
|
577
|
-
]);
|
|
578
|
-
} else {
|
|
579
|
-
[count, quorum] = await Promise.all([
|
|
580
|
-
params.environment.contracts.governor.read.proposalCount(),
|
|
581
|
-
params.environment.contracts.governor.read.quorumVotes(),
|
|
582
|
-
]);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
if (params.id) {
|
|
586
|
-
if (BigInt(params.id) > count) {
|
|
587
|
-
return [];
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
const ids = params.id
|
|
592
|
-
? [BigInt(params.id)]
|
|
593
|
-
: Array.from({ length: Number(count) }, (_, i) => count - BigInt(i));
|
|
594
|
-
|
|
595
|
-
const proposalDataCall = Promise.all(
|
|
596
|
-
ids.map((id) =>
|
|
597
|
-
params.environment.contracts.governor?.read.proposals([id]),
|
|
598
|
-
),
|
|
599
|
-
);
|
|
600
|
-
|
|
601
|
-
const proposalStateCall = Promise.all(
|
|
602
|
-
ids.map((id) =>
|
|
603
|
-
params.environment.contracts.governor?.read.state([id]),
|
|
604
|
-
),
|
|
605
|
-
);
|
|
606
|
-
|
|
607
|
-
const [proposalsData, proposalsState] = await Promise.all([
|
|
608
|
-
proposalDataCall,
|
|
609
|
-
proposalStateCall,
|
|
610
|
-
]);
|
|
611
|
-
|
|
612
|
-
const proposals = proposalsData?.map((item, index: number) => {
|
|
613
|
-
const state = proposalsState?.[index]!;
|
|
614
|
-
|
|
615
|
-
const [
|
|
616
|
-
id,
|
|
617
|
-
proposer,
|
|
618
|
-
eta,
|
|
619
|
-
startTimestamp,
|
|
620
|
-
endTimestamp,
|
|
621
|
-
startBlock,
|
|
622
|
-
forVotes,
|
|
623
|
-
againstVotes,
|
|
624
|
-
abstainVotes,
|
|
625
|
-
totalVotes,
|
|
626
|
-
canceled,
|
|
627
|
-
executed,
|
|
628
|
-
] = item!;
|
|
629
|
-
|
|
630
|
-
const proposal: Proposal = {
|
|
631
|
-
chainId: params.environment.chainId,
|
|
632
|
-
id: Number(id),
|
|
633
|
-
proposalId: Number(id),
|
|
634
|
-
proposer,
|
|
635
|
-
eta: Number(eta),
|
|
636
|
-
startTimestamp: Number(startTimestamp),
|
|
637
|
-
endTimestamp: Number(endTimestamp),
|
|
638
|
-
startBlock: Number(startBlock),
|
|
639
|
-
forVotes: new Amount(forVotes, 18),
|
|
640
|
-
againstVotes: new Amount(againstVotes, 18),
|
|
641
|
-
abstainVotes: new Amount(abstainVotes, 18),
|
|
642
|
-
totalVotes: new Amount(totalVotes, 18),
|
|
643
|
-
canceled,
|
|
644
|
-
executed,
|
|
645
|
-
quorum: new Amount(quorum, 18),
|
|
646
|
-
state,
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
return proposal;
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
return proposals;
|
|
653
|
-
} else {
|
|
654
|
-
return [];
|
|
655
|
-
}
|
|
656
|
-
} catch (error) {
|
|
657
|
-
console.warn(
|
|
658
|
-
`[getProposalData] RPC failed for chain ${params.environment.chainId}:`,
|
|
659
|
-
error,
|
|
660
|
-
);
|
|
661
|
-
params.environment.onError?.(error, {
|
|
662
|
-
source: "governance-proposals",
|
|
663
|
-
chainId: params.environment.chainId,
|
|
664
|
-
});
|
|
665
|
-
return [];
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* Get cross-chain proposal data (Ponder-based, for Moonriver)
|
|
671
|
-
*/
|
|
672
|
-
export const getCrossChainProposalData = async (params: {
|
|
673
|
-
environment: Environment;
|
|
674
|
-
id?: number;
|
|
675
|
-
}) => {
|
|
676
|
-
try {
|
|
677
|
-
if (params.environment.contracts.governor) {
|
|
678
|
-
const xcGovernanceSettings = params.environment.custom.governance;
|
|
679
|
-
if (
|
|
680
|
-
params.environment.contracts.multichainGovernor &&
|
|
681
|
-
xcGovernanceSettings &&
|
|
682
|
-
xcGovernanceSettings.chainIds.length > 0
|
|
683
|
-
) {
|
|
684
|
-
const xcEnvironments = xcGovernanceSettings.chainIds
|
|
685
|
-
.map((chainId) =>
|
|
686
|
-
(Object.values(publicEnvironments) as Environment[]).find(
|
|
687
|
-
(env) => env.chainId === chainId,
|
|
688
|
-
),
|
|
689
|
-
)
|
|
690
|
-
.filter((xcEnvironment) => !!xcEnvironment)
|
|
691
|
-
.filter(
|
|
692
|
-
(xcEnvironment) =>
|
|
693
|
-
xcEnvironment!.custom?.wormhole?.chainId &&
|
|
694
|
-
xcEnvironment!.contracts.voteCollector,
|
|
695
|
-
);
|
|
696
|
-
|
|
697
|
-
const [xcCount, xcQuorum] = await Promise.all([
|
|
698
|
-
params.environment.contracts.multichainGovernor.read.proposalCount(),
|
|
699
|
-
params.environment.contracts.multichainGovernor.read.quorum(),
|
|
700
|
-
]);
|
|
701
|
-
|
|
702
|
-
if (params.id) {
|
|
703
|
-
params.id =
|
|
704
|
-
Number(params.id) -
|
|
705
|
-
(params.environment.custom?.governance?.proposalIdOffset || 0);
|
|
706
|
-
|
|
707
|
-
if (params.id < 0) return [];
|
|
708
|
-
if (BigInt(params.id) > xcCount) return [];
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
const ids = params.id
|
|
712
|
-
? [BigInt(params.id)]
|
|
713
|
-
: Array.from(
|
|
714
|
-
{ length: Number(xcCount) },
|
|
715
|
-
(_, i) => xcCount - BigInt(i),
|
|
716
|
-
);
|
|
717
|
-
|
|
718
|
-
const xcProposalsDataCall = Promise.all(
|
|
719
|
-
ids.map((id) =>
|
|
720
|
-
params.environment.contracts.multichainGovernor?.read.proposals([
|
|
721
|
-
id,
|
|
722
|
-
]),
|
|
723
|
-
),
|
|
724
|
-
);
|
|
725
|
-
|
|
726
|
-
const xcProposalsStateCall = Promise.all(
|
|
727
|
-
ids.map((id) =>
|
|
728
|
-
params.environment.contracts.multichainGovernor?.read.state([id]),
|
|
729
|
-
),
|
|
730
|
-
);
|
|
731
|
-
|
|
732
|
-
const xcVotesCall = Promise.all(
|
|
733
|
-
xcEnvironments.map((xcEnvironment) =>
|
|
734
|
-
Promise.all(
|
|
735
|
-
ids.map((id) =>
|
|
736
|
-
params.environment.contracts.multichainGovernor?.read.chainVoteCollectorVotes(
|
|
737
|
-
[(xcEnvironment!.custom as any).wormhole.chainId, id],
|
|
738
|
-
),
|
|
739
|
-
),
|
|
740
|
-
),
|
|
741
|
-
),
|
|
742
|
-
);
|
|
743
|
-
|
|
744
|
-
const [xcProposalsData, xcProposalsState, xcVotes] = await Promise.all([
|
|
745
|
-
xcProposalsDataCall,
|
|
746
|
-
xcProposalsStateCall,
|
|
747
|
-
xcVotesCall,
|
|
748
|
-
]);
|
|
749
|
-
|
|
750
|
-
const proposals = ids.map((xcId, proposalIndex: number) => {
|
|
751
|
-
const state = xcProposalsState?.[proposalIndex]!;
|
|
752
|
-
const id =
|
|
753
|
-
Number(xcId) +
|
|
754
|
-
(params.environment.custom?.governance?.proposalIdOffset || 0);
|
|
755
|
-
|
|
756
|
-
const votesCollected = false;
|
|
757
|
-
|
|
758
|
-
const votes = xcVotes.reduce(
|
|
759
|
-
(prevVotes, currVotes) => {
|
|
760
|
-
const voteData = currVotes[proposalIndex];
|
|
761
|
-
if (!voteData) {
|
|
762
|
-
return prevVotes;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// chainVoteCollectorVotes returns [forVotes, againstVotes, abstainVotes]
|
|
766
|
-
const forVotes = voteData[0] || 0n;
|
|
767
|
-
const againstVotes = voteData[1] || 0n;
|
|
768
|
-
const abstainVotes = voteData[2] || 0n;
|
|
769
|
-
const totalVotes = forVotes + againstVotes + abstainVotes;
|
|
770
|
-
|
|
771
|
-
return {
|
|
772
|
-
totalVotes: prevVotes.totalVotes + totalVotes,
|
|
773
|
-
forVotes: prevVotes.forVotes + forVotes,
|
|
774
|
-
againstVotes: prevVotes.againstVotes + againstVotes,
|
|
775
|
-
abstainVotes: prevVotes.abstainVotes + abstainVotes,
|
|
776
|
-
};
|
|
777
|
-
},
|
|
778
|
-
{
|
|
779
|
-
totalVotes: 0n,
|
|
780
|
-
forVotes: 0n,
|
|
781
|
-
againstVotes: 0n,
|
|
782
|
-
abstainVotes: 0n,
|
|
783
|
-
},
|
|
784
|
-
);
|
|
785
|
-
|
|
786
|
-
const proposalData = xcProposalsData?.[proposalIndex];
|
|
787
|
-
if (!proposalData) {
|
|
788
|
-
throw new Error(
|
|
789
|
-
`Proposal data not found for index ${proposalIndex}`,
|
|
790
|
-
);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
const [
|
|
794
|
-
proposer,
|
|
795
|
-
_voteSnapshotTimestamp,
|
|
796
|
-
votingStartTime,
|
|
797
|
-
votingEndTime,
|
|
798
|
-
crossChainVoteCollectionEndTimestamp,
|
|
799
|
-
voteSnapshotBlock,
|
|
800
|
-
proposalForVotes,
|
|
801
|
-
proposalAgainstVotes,
|
|
802
|
-
proposalAbstainVotes,
|
|
803
|
-
proposalTotalVotes,
|
|
804
|
-
canceled,
|
|
805
|
-
executed,
|
|
806
|
-
] = proposalData;
|
|
807
|
-
|
|
808
|
-
const multichainState = (
|
|
809
|
-
MultichainProposalStateMapping as { [key: number]: ProposalState }
|
|
810
|
-
)[state]!;
|
|
811
|
-
|
|
812
|
-
const proposal: Proposal = {
|
|
813
|
-
chainId: params.environment.chainId,
|
|
814
|
-
id,
|
|
815
|
-
proposalId: Number(xcId),
|
|
816
|
-
proposer,
|
|
817
|
-
eta: Number(crossChainVoteCollectionEndTimestamp),
|
|
818
|
-
startTimestamp: Number(votingStartTime),
|
|
819
|
-
endTimestamp: Number(votingEndTime),
|
|
820
|
-
startBlock: Number(voteSnapshotBlock),
|
|
821
|
-
forVotes: new Amount(proposalForVotes + votes.forVotes, 18),
|
|
822
|
-
againstVotes: new Amount(
|
|
823
|
-
proposalAgainstVotes + votes.againstVotes,
|
|
824
|
-
18,
|
|
825
|
-
),
|
|
826
|
-
abstainVotes: new Amount(
|
|
827
|
-
proposalAbstainVotes + votes.abstainVotes,
|
|
828
|
-
18,
|
|
829
|
-
),
|
|
830
|
-
totalVotes: new Amount(proposalTotalVotes + votes.totalVotes, 18),
|
|
831
|
-
canceled,
|
|
832
|
-
executed,
|
|
833
|
-
quorum: new Amount(xcQuorum, 18),
|
|
834
|
-
state: multichainState,
|
|
835
|
-
multichain: {
|
|
836
|
-
id: Number(xcId),
|
|
837
|
-
votesCollected,
|
|
838
|
-
},
|
|
839
|
-
};
|
|
840
|
-
|
|
841
|
-
return proposal;
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
return proposals;
|
|
845
|
-
} else {
|
|
846
|
-
return [];
|
|
847
|
-
}
|
|
848
|
-
} else {
|
|
849
|
-
return [];
|
|
850
|
-
}
|
|
851
|
-
} catch (error) {
|
|
852
|
-
console.warn(
|
|
853
|
-
`[getCrossChainProposalData] RPC failed for chain ${params.environment.chainId}:`,
|
|
854
|
-
error,
|
|
855
|
-
);
|
|
856
|
-
params.environment.onError?.(error, {
|
|
857
|
-
source: "governance-proposals",
|
|
858
|
-
chainId: params.environment.chainId,
|
|
859
|
-
});
|
|
860
|
-
return [];
|
|
861
|
-
}
|
|
862
|
-
};
|
|
863
|
-
|
|
864
|
-
export const appendProposalExtendedData = (
|
|
865
|
-
proposals: Proposal[],
|
|
866
|
-
extendedDatas: PonderExtendedProposalData[],
|
|
867
|
-
) => {
|
|
868
|
-
proposals.forEach((proposal) => {
|
|
869
|
-
const extendedData = extendedDatas.find(
|
|
870
|
-
(item) => item.id === proposal.proposalId,
|
|
871
|
-
);
|
|
872
|
-
|
|
873
|
-
if (extendedData) {
|
|
874
|
-
proposal.title = extendedData.title;
|
|
875
|
-
proposal.calldatas = extendedData.calldatas;
|
|
876
|
-
proposal.description = extendedData.description;
|
|
877
|
-
proposal.signatures = extendedData.signatures;
|
|
878
|
-
proposal.stateChanges = extendedData.stateChanges.map((change) => ({
|
|
879
|
-
blockNumber: change.blockNumber,
|
|
880
|
-
transactionHash: change.transactionHash,
|
|
881
|
-
state: change.state,
|
|
882
|
-
chainId: proposal.chainId,
|
|
883
|
-
}));
|
|
884
|
-
proposal.subtitle = extendedData.subtitle;
|
|
885
|
-
proposal.targets = extendedData.targets;
|
|
886
|
-
}
|
|
887
|
-
});
|
|
888
|
-
};
|
|
889
|
-
|
|
890
|
-
export const getExtendedProposalData = async (params: {
|
|
891
|
-
environment: Environment;
|
|
892
|
-
id?: number;
|
|
893
|
-
}): Promise<PonderExtendedProposalData[]> => {
|
|
894
|
-
const result: PonderExtendedProposalData[] = [];
|
|
895
|
-
let lastId = -1;
|
|
896
|
-
let shouldContinue = true;
|
|
897
|
-
const MAX_PAGES = 100;
|
|
898
|
-
let page = 0;
|
|
899
|
-
|
|
900
|
-
try {
|
|
901
|
-
while (shouldContinue && page < MAX_PAGES) {
|
|
902
|
-
page++;
|
|
903
|
-
const response = await postWithRetry<{
|
|
904
|
-
data: {
|
|
905
|
-
proposals: {
|
|
906
|
-
items: {
|
|
907
|
-
proposalId: number;
|
|
908
|
-
description: string;
|
|
909
|
-
targets: string[];
|
|
910
|
-
calldatas: string[];
|
|
911
|
-
signatures: string[];
|
|
912
|
-
stateChanges: {
|
|
913
|
-
items: {
|
|
914
|
-
txnHash: string;
|
|
915
|
-
blockNumber: number;
|
|
916
|
-
newState: string;
|
|
917
|
-
}[];
|
|
918
|
-
};
|
|
919
|
-
}[];
|
|
920
|
-
};
|
|
921
|
-
};
|
|
922
|
-
}>("https://ponder-eu2.moonwell.fi", {
|
|
923
|
-
query: `
|
|
924
|
-
query {
|
|
925
|
-
proposals(
|
|
926
|
-
limit: 1000,
|
|
927
|
-
orderDirection: "desc",
|
|
928
|
-
orderBy: "proposalId",
|
|
929
|
-
where: {
|
|
930
|
-
chainId: ${params.environment.chainId}
|
|
931
|
-
${params.id ? `, proposalId: ${params.id}` : lastId >= 0 ? `, proposalId_lt: ${lastId}` : ""}
|
|
932
|
-
}
|
|
933
|
-
) {
|
|
934
|
-
items {
|
|
935
|
-
id
|
|
936
|
-
proposalId
|
|
937
|
-
description
|
|
938
|
-
targets
|
|
939
|
-
calldatas
|
|
940
|
-
signatures
|
|
941
|
-
stateChanges(orderBy: "blockNumber") {
|
|
942
|
-
items {
|
|
943
|
-
txnHash
|
|
944
|
-
blockNumber
|
|
945
|
-
newState
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
`,
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
if (response.status === 200 && response.data?.data?.proposals) {
|
|
955
|
-
const proposals = response.data.data.proposals.items.map((item) => {
|
|
956
|
-
const extendedProposalData: PonderExtendedProposalData = {
|
|
957
|
-
id: item.proposalId,
|
|
958
|
-
title: `Proposal #${item.proposalId}`,
|
|
959
|
-
subtitle: extractProposalSubtitle(item.description),
|
|
960
|
-
description: item.description,
|
|
961
|
-
calldatas: item.calldatas,
|
|
962
|
-
signatures: item.signatures,
|
|
963
|
-
stateChanges: item.stateChanges.items.map((change) => {
|
|
964
|
-
return {
|
|
965
|
-
blockNumber: change.blockNumber,
|
|
966
|
-
state: change.newState,
|
|
967
|
-
transactionHash: change.txnHash,
|
|
968
|
-
};
|
|
969
|
-
}),
|
|
970
|
-
targets: item.targets,
|
|
971
|
-
};
|
|
972
|
-
|
|
973
|
-
return extendedProposalData;
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
if (proposals.length < 1000 || proposals.length === 0) {
|
|
977
|
-
shouldContinue = false;
|
|
978
|
-
} else {
|
|
979
|
-
lastId = last(proposals)!.id;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
result.push(...proposals);
|
|
983
|
-
} else {
|
|
984
|
-
shouldContinue = false;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
} catch (error) {
|
|
988
|
-
console.warn(
|
|
989
|
-
`[getExtendedProposalData] Ponder failed for chain ${params.environment.chainId}:`,
|
|
990
|
-
error,
|
|
991
|
-
);
|
|
992
|
-
params.environment.onError?.(error, {
|
|
993
|
-
source: "governance-proposals",
|
|
994
|
-
chainId: params.environment.chainId,
|
|
995
|
-
});
|
|
996
|
-
return result;
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
return result;
|
|
1000
|
-
};
|