@moonwell-fi/moonwell-sdk 0.19.1 → 0.20.1
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 +24 -0
- package/_cjs/actions/governance/getUserVotingPowers.js +15 -6
- package/_cjs/actions/governance/getUserVotingPowers.js.map +1 -1
- package/_cjs/actions/governance/proposals/common.js +15 -5
- package/_cjs/actions/governance/proposals/common.js.map +1 -1
- package/_cjs/actions/governance/proposals/getProposal.js +1 -1
- package/_cjs/actions/governance/proposals/getProposal.js.map +1 -1
- package/_cjs/actions/governance/proposals/getProposals.js +1 -1
- package/_cjs/actions/governance/proposals/getProposals.js.map +1 -1
- package/_cjs/actions/morpho/markets/common.js +25 -16
- package/_cjs/actions/morpho/markets/common.js.map +1 -1
- package/_cjs/actions/morpho/utils/graphql.js +4 -12
- package/_cjs/actions/morpho/utils/graphql.js.map +1 -1
- package/_cjs/actions/morpho/vaults/common.js +6 -24
- package/_cjs/actions/morpho/vaults/common.js.map +1 -1
- package/_cjs/common/getBlockNumberAtTimestamp.js +12 -2
- package/_cjs/common/getBlockNumberAtTimestamp.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_esm/actions/governance/getUserVotingPowers.js +22 -6
- package/_esm/actions/governance/getUserVotingPowers.js.map +1 -1
- package/_esm/actions/governance/proposals/common.js +59 -19
- package/_esm/actions/governance/proposals/common.js.map +1 -1
- package/_esm/actions/governance/proposals/getProposal.js +6 -2
- package/_esm/actions/governance/proposals/getProposal.js.map +1 -1
- package/_esm/actions/governance/proposals/getProposals.js +5 -2
- package/_esm/actions/governance/proposals/getProposals.js.map +1 -1
- package/_esm/actions/morpho/markets/common.js +31 -19
- package/_esm/actions/morpho/markets/common.js.map +1 -1
- package/_esm/actions/morpho/utils/graphql.js +4 -12
- package/_esm/actions/morpho/utils/graphql.js.map +1 -1
- package/_esm/actions/morpho/vaults/common.js +9 -24
- package/_esm/actions/morpho/vaults/common.js.map +1 -1
- package/_esm/common/getBlockNumberAtTimestamp.js +36 -8
- package/_esm/common/getBlockNumberAtTimestamp.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_types/actions/governance/getUserVotingPowers.d.ts.map +1 -1
- package/_types/actions/governance/proposals/common.d.ts +48 -12
- package/_types/actions/governance/proposals/common.d.ts.map +1 -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/actions/morpho/utils/graphql.d.ts.map +1 -1
- package/_types/actions/morpho/vaults/common.d.ts.map +1 -1
- package/_types/common/getBlockNumberAtTimestamp.d.ts +18 -3
- package/_types/common/getBlockNumberAtTimestamp.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/actions/governance/getUserVotingPowers.ts +30 -15
- package/actions/governance/proposals/common.ts +77 -21
- package/actions/governance/proposals/getProposal.ts +5 -2
- package/actions/governance/proposals/getProposals.ts +4 -2
- package/actions/morpho/markets/common.ts +36 -28
- package/actions/morpho/utils/graphql.ts +10 -18
- package/actions/morpho/vaults/common.ts +11 -38
- package/common/getBlockNumberAtTimestamp.ts +36 -8
- package/errors/version.ts +1 -1
- package/package.json +1 -1
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* converges in ~3–5 reads
|
|
4
|
-
*
|
|
2
|
+
* Cap on interpolation iterations before falling back to binary search. With
|
|
3
|
+
* near-linear `ts(block)` the interpolation phase converges in ~3–5 reads.
|
|
4
|
+
* On chains whose block time changed over their history the projection can
|
|
5
|
+
* fail to converge at all — see the binary completion phase below.
|
|
5
6
|
*/
|
|
6
|
-
const
|
|
7
|
+
const MAX_INTERPOLATION_ITERATIONS = 8;
|
|
7
8
|
/**
|
|
8
9
|
* Find the block number on a chain whose timestamp is the latest one ≤ the target unix timestamp.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Two phases:
|
|
12
|
+
*
|
|
13
|
+
* 1. Interpolation search anchored on the latest block and block 1: each iteration
|
|
14
|
+
* narrows the range by reading one block and projecting the target via the slope
|
|
15
|
+
* of the remaining range. Converges in ~3–5 RPC calls on chains with a roughly
|
|
16
|
+
* constant block time.
|
|
17
|
+
* 2. Binary-search completion: if interpolation hasn't converged after
|
|
18
|
+
* `MAX_INTERPOLATION_ITERATIONS`, finish with plain bisection — guaranteed
|
|
19
|
+
* convergence in ~log2(remaining range) reads.
|
|
20
|
+
*
|
|
21
|
+
* The completion phase is load-bearing, not defensive: on Moonbeam the block time
|
|
22
|
+
* changed ~12s → ~6s (async backing), so the interpolated projection — whose slope
|
|
23
|
+
* is the all-history average — lands after the target on every probe. Only the
|
|
24
|
+
* upper bound moves, the lower bound never leaves block 1, and the previous
|
|
25
|
+
* implementation silently returned that unconverged bound. Voting-power reads then
|
|
26
|
+
* executed against a block years before the views contract existed and reverted
|
|
27
|
+
* (governance proposal 171 incident, June 2026). An unconverged bound must never
|
|
28
|
+
* be returned.
|
|
13
29
|
*
|
|
14
30
|
* Assumes block 1 exists on the chain — true for every EVM chain Moonwell supports.
|
|
15
31
|
*
|
|
@@ -25,11 +41,12 @@ export async function getBlockNumberAtTimestamp(publicClient, targetTimestamp) {
|
|
|
25
41
|
const first = await publicClient.getBlock({ blockNumber: 1n });
|
|
26
42
|
if (targetTimestamp <= first.timestamp)
|
|
27
43
|
return first.number;
|
|
44
|
+
// Invariant throughout: ts(lo) <= targetTimestamp < ts(hi).
|
|
28
45
|
let lo = first.number;
|
|
29
46
|
let loTs = first.timestamp;
|
|
30
47
|
let hi = latest.number;
|
|
31
48
|
let hiTs = latest.timestamp;
|
|
32
|
-
for (let i = 0; i <
|
|
49
|
+
for (let i = 0; i < MAX_INTERPOLATION_ITERATIONS; i += 1) {
|
|
33
50
|
if (hi - lo <= 1n)
|
|
34
51
|
break;
|
|
35
52
|
const tsRange = hiTs - loTs;
|
|
@@ -51,6 +68,17 @@ export async function getBlockNumberAtTimestamp(publicClient, targetTimestamp) {
|
|
|
51
68
|
hiTs = block.timestamp;
|
|
52
69
|
}
|
|
53
70
|
}
|
|
71
|
+
// Binary completion: never return an unconverged bound.
|
|
72
|
+
while (hi - lo > 1n) {
|
|
73
|
+
const mid = (lo + hi) / 2n;
|
|
74
|
+
const block = await publicClient.getBlock({ blockNumber: mid });
|
|
75
|
+
if (block.timestamp <= targetTimestamp) {
|
|
76
|
+
lo = mid;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
hi = mid;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
54
82
|
return lo;
|
|
55
83
|
}
|
|
56
84
|
//# sourceMappingURL=getBlockNumberAtTimestamp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getBlockNumberAtTimestamp.js","sourceRoot":"","sources":["../../common/getBlockNumberAtTimestamp.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"getBlockNumberAtTimestamp.js","sourceRoot":"","sources":["../../common/getBlockNumberAtTimestamp.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,YAA0B,EAC1B,eAAuB;IAEvB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnE,IAAI,eAAe,IAAI,MAAM,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC;IAC9D,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC;IAE/C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/D,IAAI,eAAe,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAE5D,4DAA4D;IAC5D,IAAI,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC;IACtB,IAAI,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC;IACvB,IAAI,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;IAE5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,4BAA4B,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzD,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;YAAE,MAAM;QACzB,MAAM,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;QAC5B,IAAI,OAAO,IAAI,EAAE;YAAE,MAAM;QAEzB,MAAM,MAAM,GAAG,CAAC,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC;QAChE,IAAI,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC;QACtB,IAAI,GAAG,IAAI,EAAE;YAAE,GAAG,GAAG,EAAE,GAAG,EAAE,CAAC;QAC7B,IAAI,GAAG,IAAI,EAAE;YAAE,GAAG,GAAG,EAAE,GAAG,EAAE,CAAC;QAE7B,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;QAChE,IAAI,KAAK,CAAC,SAAS,IAAI,eAAe,EAAE,CAAC;YACvC,EAAE,GAAG,GAAG,CAAC;YACT,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,GAAG,CAAC;YACT,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC;QACzB,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;QAChE,IAAI,KAAK,CAAC,SAAS,IAAI,eAAe,EAAE,CAAC;YACvC,EAAE,GAAG,GAAG,CAAC;QACX,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,GAAG,CAAC;QACX,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/_esm/errors/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '0.
|
|
1
|
+
export const version = '0.20.1';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getUserVotingPowers.d.ts","sourceRoot":"","sources":["../../../actions/governance/getUserVotingPowers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAe,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAM3E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAIxE,MAAM,MAAM,6BAA6B,CACvC,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,IAC/B,4BAA4B,CAAC,YAAY,EAAE,OAAO,CAAC,GAAG;IACxD,uBAAuB;IACvB,eAAe,EAAE,eAAe,CAAC;IAEjC,kBAAkB;IAClB,WAAW,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAExE,wBAAsB,mBAAmB,CACvC,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,EAEjC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,6BAA6B,CAAC,YAAY,EAAE,OAAO,CAAC,GACzD,6BAA6B,
|
|
1
|
+
{"version":3,"file":"getUserVotingPowers.d.ts","sourceRoot":"","sources":["../../../actions/governance/getUserVotingPowers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAe,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAM3E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAIxE,MAAM,MAAM,6BAA6B,CACvC,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,IAC/B,4BAA4B,CAAC,YAAY,EAAE,OAAO,CAAC,GAAG;IACxD,uBAAuB;IACvB,eAAe,EAAE,eAAe,CAAC;IAEjC,kBAAkB;IAClB,WAAW,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAExE,wBAAsB,mBAAmB,CACvC,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,EAEjC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,6BAA6B,CAAC,YAAY,EAAE,OAAO,CAAC,GACzD,6BAA6B,CAuJ/B"}
|
|
@@ -22,22 +22,51 @@ type PonderExtendedProposalData = {
|
|
|
22
22
|
*/
|
|
23
23
|
export declare const extractProposalSubtitle: (input: string) => string;
|
|
24
24
|
/**
|
|
25
|
-
* Detects
|
|
26
|
-
* its targets is a Wormhole Core Bridge on a known multichain-governance
|
|
27
|
-
* (Moonbeam or Ethereum).
|
|
28
|
-
*
|
|
25
|
+
* Detects whether a proposal SENDS a cross-chain message, by checking whether
|
|
26
|
+
* any of its targets is a Wormhole Core Bridge on a known multichain-governance
|
|
27
|
+
* hub (Moonbeam or Ethereum).
|
|
28
|
+
*
|
|
29
|
+
* WARNING: this is NOT sufficient to classify a proposal as belonging to the
|
|
30
|
+
* multichain governor. Hub-local proposals (created on the Ethereum
|
|
31
|
+
* MultichainGovernor with only same-chain targets) never message a bridge and
|
|
32
|
+
* return `false` here — misclassifying them broke voting on proposal 171
|
|
33
|
+
* (June 2026). Use `classifyProposalMultichain` for governor classification.
|
|
29
34
|
*/
|
|
30
35
|
export declare const isMultichainProposal: (targets?: string[]) => boolean;
|
|
31
36
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
* True when `chainId`'s environment is a multichain-governance hub with no
|
|
38
|
+
* legacy governor lineage (Ethereum: `multichainGovernor` only, no Artemis
|
|
39
|
+
* predecessor). On such a chain every proposal belongs to the multichain
|
|
40
|
+
* governor by construction — what the proposal targets is irrelevant.
|
|
41
|
+
*/
|
|
42
|
+
export declare const isMultichainHomeChain: (chainId: number) => boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Canonical multichain classification for a proposal — the single entry point
|
|
45
|
+
* used by `getProposal`, `getProposals`, and on-chain-data routing so the
|
|
46
|
+
* paths cannot drift. A proposal belongs to the multichain governor when:
|
|
47
|
+
* 1. it is homed on a hub chain with no legacy governor (every Ethereum-hub
|
|
48
|
+
* proposal, including hub-local ones with no bridge target), OR
|
|
49
|
+
* 2. its targets include a Wormhole Core Bridge (legacy detection), OR
|
|
50
|
+
* 3. its proposalId is past the legacy Artemis governor's `proposalCount`
|
|
51
|
+
* (Moonbeam-hub-era proposals with local-only targets), OR
|
|
52
|
+
* 4. the Artemis cutoff is unknown because the read failed (`undefined`) — we
|
|
53
|
+
* bias to multichain rather than risk routing a live proposal to the dead
|
|
54
|
+
* Artemis governor (the proposal-171 root cause, on Moonbeam).
|
|
38
55
|
*
|
|
39
|
-
* `legacyArtemisMaxId
|
|
40
|
-
*
|
|
56
|
+
* `legacyArtemisMaxId` is only meaningful for Moonbeam-homed proposals. Pass 0
|
|
57
|
+
* for chains with no legacy governor — the ID check is N/A and classification
|
|
58
|
+
* falls back to the home/bridge checks. Omit it or pass `undefined` when the
|
|
59
|
+
* count read failed so the unknown-cutoff bias above applies. (No `= 0` default:
|
|
60
|
+
* that would coerce an explicit `undefined` back to 0 and defeat the bias.)
|
|
61
|
+
*/
|
|
62
|
+
export declare const classifyProposalMultichain: (proposal: {
|
|
63
|
+
targets?: string[];
|
|
64
|
+
proposalId: number;
|
|
65
|
+
chainId: number;
|
|
66
|
+
}, legacyArtemisMaxId?: number) => boolean;
|
|
67
|
+
/**
|
|
68
|
+
* @deprecated Use `classifyProposalMultichain` — this variant misses hub-homed
|
|
69
|
+
* proposals when the Artemis count is unavailable. Kept for compatibility.
|
|
41
70
|
*/
|
|
42
71
|
export declare const isMultichainAware: (proposal: {
|
|
43
72
|
targets?: string[];
|
|
@@ -82,6 +111,13 @@ export type ProposalOnChainData = {
|
|
|
82
111
|
eta: number;
|
|
83
112
|
votesCollected: boolean;
|
|
84
113
|
quorum: bigint;
|
|
114
|
+
/**
|
|
115
|
+
* Canonical multichain classification, computed here with the caller env's
|
|
116
|
+
* Artemis cutoff. Returned so `getProposal`/`getProposals` consume it instead
|
|
117
|
+
* of re-running `classifyProposalMultichain` (which, without the cutoff,
|
|
118
|
+
* misses Moonbeam-homed local-target proposals and drifts from this routing).
|
|
119
|
+
*/
|
|
120
|
+
isMultichain: boolean;
|
|
85
121
|
};
|
|
86
122
|
/**
|
|
87
123
|
* Reads the multichain governor's quorum on every chain that holds an active
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../actions/governance/proposals/common.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAElE,OAAO,EAGL,KAAK,QAAQ,EACb,aAAa,EACd,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,eAAO,MAAM,iBAAiB,+CAA+C,CAAC;AAY9E,KAAK,0BAA0B,GAAG;IAChC,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;KACf,EAAE,CAAC;CACL,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,KAAG,MAoDvD,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../actions/governance/proposals/common.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getProposal.d.ts","sourceRoot":"","sources":["../../../../actions/governance/proposals/getProposal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"getProposal.d.ts","sourceRoot":"","sources":["../../../../actions/governance/proposals/getProposal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,4BAA4B,CAAC;AAkB1E,MAAM,MAAM,qBAAqB,CAC/B,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,IAC/B,oBAAoB,CAAC,YAAY,EAAE,OAAO,CAAC,GAAG;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;AAElE,wBAAsB,WAAW,CAC/B,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,EAEjC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,qBAAqB,CAAC,YAAY,EAAE,OAAO,CAAC,GACjD,qBAAqB,CAuCvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getProposals.d.ts","sourceRoot":"","sources":["../../../../actions/governance/proposals/getProposals.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"getProposals.d.ts","sourceRoot":"","sources":["../../../../actions/governance/proposals/getProposals.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,4BAA4B,CAAC;AAa1E,MAAM,MAAM,sBAAsB,CAChC,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,IAC/B,4BAA4B,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAExD,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;AAEzD,wBAAsB,YAAY,CAChC,YAAY,EACZ,OAAO,SAAS,KAAK,GAAG,SAAS,EAEjC,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,sBAAsB,CAAC,YAAY,EAAE,OAAO,CAAC,GACnD,sBAAsB,CAyCxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../../../actions/morpho/utils/graphql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAE9E,wBAAsB,UAAU,CAAC,CAAC,EAChC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,MAAM,EACtB,SAAS,CAAC,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../../../actions/morpho/utils/graphql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAE9E,wBAAsB,UAAU,CAAC,CAAC,EAChC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,MAAM,EACtB,SAAS,CAAC,EAAE,GAAG,0BA0BhB;AAED,wBAAsB,aAAa,CACjC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC,CAgE/C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../actions/morpho/vaults/common.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,gCAAgC,CAAC;AAKxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EACV,WAAW,EAEZ,MAAM,+BAA+B,CAAC;AAsavC,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAazB;AA43BD,KAAK,4BAA4B,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,WAAW,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,WAAW,EAAE,EACrB,uBAAuB,CAAC,EAAE,OAAO,GAChC,OAAO,CAAC,4BAA4B,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../actions/morpho/vaults/common.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,gCAAgC,CAAC;AAKxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EACV,WAAW,EAEZ,MAAM,+BAA+B,CAAC;AAsavC,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAazB;AA43BD,KAAK,4BAA4B,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,WAAW,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,WAAW,EAAE,EACrB,uBAAuB,CAAC,EAAE,OAAO,GAChC,OAAO,CAAC,4BAA4B,EAAE,CAAC,CAmMzC"}
|
|
@@ -2,9 +2,24 @@ import type { PublicClient } from "viem";
|
|
|
2
2
|
/**
|
|
3
3
|
* Find the block number on a chain whose timestamp is the latest one ≤ the target unix timestamp.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Two phases:
|
|
6
|
+
*
|
|
7
|
+
* 1. Interpolation search anchored on the latest block and block 1: each iteration
|
|
8
|
+
* narrows the range by reading one block and projecting the target via the slope
|
|
9
|
+
* of the remaining range. Converges in ~3–5 RPC calls on chains with a roughly
|
|
10
|
+
* constant block time.
|
|
11
|
+
* 2. Binary-search completion: if interpolation hasn't converged after
|
|
12
|
+
* `MAX_INTERPOLATION_ITERATIONS`, finish with plain bisection — guaranteed
|
|
13
|
+
* convergence in ~log2(remaining range) reads.
|
|
14
|
+
*
|
|
15
|
+
* The completion phase is load-bearing, not defensive: on Moonbeam the block time
|
|
16
|
+
* changed ~12s → ~6s (async backing), so the interpolated projection — whose slope
|
|
17
|
+
* is the all-history average — lands after the target on every probe. Only the
|
|
18
|
+
* upper bound moves, the lower bound never leaves block 1, and the previous
|
|
19
|
+
* implementation silently returned that unconverged bound. Voting-power reads then
|
|
20
|
+
* executed against a block years before the views contract existed and reverted
|
|
21
|
+
* (governance proposal 171 incident, June 2026). An unconverged bound must never
|
|
22
|
+
* be returned.
|
|
8
23
|
*
|
|
9
24
|
* Assumes block 1 exists on the chain — true for every EVM chain Moonwell supports.
|
|
10
25
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getBlockNumberAtTimestamp.d.ts","sourceRoot":"","sources":["../../common/getBlockNumberAtTimestamp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"getBlockNumberAtTimestamp.d.ts","sourceRoot":"","sources":["../../common/getBlockNumberAtTimestamp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAUzC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,YAAY,EAC1B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC,CA8CjB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "0.
|
|
1
|
+
export declare const version = "0.20.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -72,23 +72,22 @@ export async function getUserVotingPowers<
|
|
|
72
72
|
return [{ env, views }];
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
// Per-chain isolation: one chain's failing block resolution or views read
|
|
76
|
+
// must degrade ONLY that chain, never the whole call. The previous
|
|
77
|
+
// implementation wrapped everything in a single Promise.all, so a Moonbeam
|
|
78
|
+
// revert erased voting power on every chain at once — which hid the vote
|
|
79
|
+
// buttons for all users during the proposal-171 incident (June 2026).
|
|
80
|
+
// Failed chains are reported through `env.onError` and omitted from the
|
|
81
|
+
// result; consumers see partial data instead of a rejection.
|
|
82
|
+
const settled = await Promise.allSettled(
|
|
83
|
+
tokenEnvironments.map(async ({ env, views }) => {
|
|
84
|
+
const blockForChain =
|
|
85
|
+
snapshotTimestamp !== undefined
|
|
86
|
+
? await getBlockNumberAtTimestamp(
|
|
80
87
|
env.publicClient,
|
|
81
88
|
BigInt(snapshotTimestamp),
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
)
|
|
85
|
-
: undefined;
|
|
86
|
-
|
|
87
|
-
const resolvedVotingPowers = await Promise.all(
|
|
88
|
-
tokenEnvironments.map(async ({ env, views }, index) => {
|
|
89
|
-
const blockForChain = perChainBlockNumbers
|
|
90
|
-
? perChainBlockNumbers[index]
|
|
91
|
-
: blockNumber;
|
|
89
|
+
)
|
|
90
|
+
: blockNumber;
|
|
92
91
|
const votingPowers = await views.read.getUserVotingPower([userAddress], {
|
|
93
92
|
blockNumber: blockForChain,
|
|
94
93
|
});
|
|
@@ -96,6 +95,22 @@ export async function getUserVotingPowers<
|
|
|
96
95
|
}),
|
|
97
96
|
);
|
|
98
97
|
|
|
98
|
+
const resolvedVotingPowers = settled.flatMap((result, index) => {
|
|
99
|
+
if (result.status === "fulfilled") {
|
|
100
|
+
return [result.value];
|
|
101
|
+
}
|
|
102
|
+
const env = tokenEnvironments[index]?.env;
|
|
103
|
+
console.warn(
|
|
104
|
+
`[moonwell-sdk] getUserVotingPowers: skipping chainId=${env?.chainId} — voting-power read failed; returning remaining chains.`,
|
|
105
|
+
result.reason,
|
|
106
|
+
);
|
|
107
|
+
env?.onError?.(result.reason, {
|
|
108
|
+
source: "getUserVotingPowers",
|
|
109
|
+
chainId: env.chainId,
|
|
110
|
+
});
|
|
111
|
+
return [];
|
|
112
|
+
});
|
|
113
|
+
|
|
99
114
|
return resolvedVotingPowers.map(({ env: environment, votingPowers }) => {
|
|
100
115
|
const { tokenVotes } = votingPowers;
|
|
101
116
|
|
|
@@ -100,25 +100,62 @@ export const extractProposalSubtitle = (input: string): string => {
|
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
* Detects
|
|
104
|
-
* its targets is a Wormhole Core Bridge on a known multichain-governance
|
|
105
|
-
* (Moonbeam or Ethereum).
|
|
106
|
-
*
|
|
103
|
+
* Detects whether a proposal SENDS a cross-chain message, by checking whether
|
|
104
|
+
* any of its targets is a Wormhole Core Bridge on a known multichain-governance
|
|
105
|
+
* hub (Moonbeam or Ethereum).
|
|
106
|
+
*
|
|
107
|
+
* WARNING: this is NOT sufficient to classify a proposal as belonging to the
|
|
108
|
+
* multichain governor. Hub-local proposals (created on the Ethereum
|
|
109
|
+
* MultichainGovernor with only same-chain targets) never message a bridge and
|
|
110
|
+
* return `false` here — misclassifying them broke voting on proposal 171
|
|
111
|
+
* (June 2026). Use `classifyProposalMultichain` for governor classification.
|
|
107
112
|
*/
|
|
108
113
|
export const isMultichainProposal = (targets?: string[]): boolean =>
|
|
109
114
|
targets?.some((t) => MULTICHAIN_WORMHOLE_BRIDGES.has(t.toLowerCase())) ??
|
|
110
115
|
false;
|
|
111
116
|
|
|
112
117
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
* True when `chainId`'s environment is a multichain-governance hub with no
|
|
119
|
+
* legacy governor lineage (Ethereum: `multichainGovernor` only, no Artemis
|
|
120
|
+
* predecessor). On such a chain every proposal belongs to the multichain
|
|
121
|
+
* governor by construction — what the proposal targets is irrelevant.
|
|
122
|
+
*/
|
|
123
|
+
export const isMultichainHomeChain = (chainId: number): boolean => {
|
|
124
|
+
const env = getEnvironmentByChainId(chainId);
|
|
125
|
+
return Boolean(env?.contracts.multichainGovernor && !env.contracts.governor);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Canonical multichain classification for a proposal — the single entry point
|
|
130
|
+
* used by `getProposal`, `getProposals`, and on-chain-data routing so the
|
|
131
|
+
* paths cannot drift. A proposal belongs to the multichain governor when:
|
|
132
|
+
* 1. it is homed on a hub chain with no legacy governor (every Ethereum-hub
|
|
133
|
+
* proposal, including hub-local ones with no bridge target), OR
|
|
134
|
+
* 2. its targets include a Wormhole Core Bridge (legacy detection), OR
|
|
135
|
+
* 3. its proposalId is past the legacy Artemis governor's `proposalCount`
|
|
136
|
+
* (Moonbeam-hub-era proposals with local-only targets), OR
|
|
137
|
+
* 4. the Artemis cutoff is unknown because the read failed (`undefined`) — we
|
|
138
|
+
* bias to multichain rather than risk routing a live proposal to the dead
|
|
139
|
+
* Artemis governor (the proposal-171 root cause, on Moonbeam).
|
|
119
140
|
*
|
|
120
|
-
* `legacyArtemisMaxId
|
|
121
|
-
*
|
|
141
|
+
* `legacyArtemisMaxId` is only meaningful for Moonbeam-homed proposals. Pass 0
|
|
142
|
+
* for chains with no legacy governor — the ID check is N/A and classification
|
|
143
|
+
* falls back to the home/bridge checks. Omit it or pass `undefined` when the
|
|
144
|
+
* count read failed so the unknown-cutoff bias above applies. (No `= 0` default:
|
|
145
|
+
* that would coerce an explicit `undefined` back to 0 and defeat the bias.)
|
|
146
|
+
*/
|
|
147
|
+
export const classifyProposalMultichain = (
|
|
148
|
+
proposal: { targets?: string[]; proposalId: number; chainId: number },
|
|
149
|
+
legacyArtemisMaxId?: number,
|
|
150
|
+
): boolean =>
|
|
151
|
+
isMultichainHomeChain(proposal.chainId) ||
|
|
152
|
+
isMultichainProposal(proposal.targets) ||
|
|
153
|
+
legacyArtemisMaxId === undefined ||
|
|
154
|
+
(legacyArtemisMaxId > 0 && proposal.proposalId > legacyArtemisMaxId);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @deprecated Use `classifyProposalMultichain` — this variant misses hub-homed
|
|
158
|
+
* proposals when the Artemis count is unavailable. Kept for compatibility.
|
|
122
159
|
*/
|
|
123
160
|
export const isMultichainAware = (
|
|
124
161
|
proposal: { targets?: string[]; proposalId: number },
|
|
@@ -236,6 +273,13 @@ export type ProposalOnChainData = {
|
|
|
236
273
|
eta: number;
|
|
237
274
|
votesCollected: boolean;
|
|
238
275
|
quorum: bigint;
|
|
276
|
+
/**
|
|
277
|
+
* Canonical multichain classification, computed here with the caller env's
|
|
278
|
+
* Artemis cutoff. Returned so `getProposal`/`getProposals` consume it instead
|
|
279
|
+
* of re-running `classifyProposalMultichain` (which, without the cutoff,
|
|
280
|
+
* misses Moonbeam-homed local-target proposals and drifts from this routing).
|
|
281
|
+
*/
|
|
282
|
+
isMultichain: boolean;
|
|
239
283
|
};
|
|
240
284
|
|
|
241
285
|
// Cached per chain: highest proposalId held by the legacy Artemis governor.
|
|
@@ -250,7 +294,7 @@ const legacyArtemisMaxIdCache = new Map<
|
|
|
250
294
|
|
|
251
295
|
const getLegacyArtemisMaxId = async (
|
|
252
296
|
governanceEnvironment: Environment,
|
|
253
|
-
): Promise<number> => {
|
|
297
|
+
): Promise<number | undefined> => {
|
|
254
298
|
const governor = governanceEnvironment.contracts.governor;
|
|
255
299
|
if (!governor) return 0;
|
|
256
300
|
|
|
@@ -268,7 +312,13 @@ const getLegacyArtemisMaxId = async (
|
|
|
268
312
|
return value;
|
|
269
313
|
} catch (error) {
|
|
270
314
|
console.warn("Failed to fetch legacy governor proposalCount:", error);
|
|
271
|
-
|
|
315
|
+
// A cold-cache read failure must NOT collapse to 0. On a dual-governor
|
|
316
|
+
// chain (Moonbeam) that would make a live multichain proposal with
|
|
317
|
+
// local-only targets look like a pre-cutoff legacy proposal and route its
|
|
318
|
+
// votes to the dead Artemis governor — the proposal-171 root cause. Return
|
|
319
|
+
// a stale cached cutoff if we have one, otherwise `undefined` (unknown) so
|
|
320
|
+
// `classifyProposalMultichain` biases to the multichain governor instead.
|
|
321
|
+
return cached?.value;
|
|
272
322
|
}
|
|
273
323
|
};
|
|
274
324
|
|
|
@@ -365,6 +415,17 @@ export const getProposalsOnChainData = async (
|
|
|
365
415
|
: (options?.crossChainQuorums?.get(p.chainId) ?? 0n);
|
|
366
416
|
const homeEnv = resolveHomeEnv(p.chainId);
|
|
367
417
|
|
|
418
|
+
// legacyArtemisMaxId is meaningful only for the caller's env — it's the
|
|
419
|
+
// Moonbeam Artemis cap; pass 0 for foreign envs. Hub-homed proposals
|
|
420
|
+
// (Ethereum multigov, no Artemis predecessor) classify as multichain
|
|
421
|
+
// regardless of targets via classifyProposalMultichain. Computed once
|
|
422
|
+
// here and returned on ProposalOnChainData so callers don't re-classify
|
|
423
|
+
// and drift (see getProposal/getProposals).
|
|
424
|
+
const isMultichain = classifyProposalMultichain(
|
|
425
|
+
p,
|
|
426
|
+
isLocal ? legacyArtemisMaxId : 0,
|
|
427
|
+
);
|
|
428
|
+
|
|
368
429
|
if (!homeEnv) {
|
|
369
430
|
const formatted = formatApiProposalData(p);
|
|
370
431
|
return {
|
|
@@ -373,16 +434,10 @@ export const getProposalsOnChainData = async (
|
|
|
373
434
|
eta: 0,
|
|
374
435
|
votesCollected: false,
|
|
375
436
|
quorum: proposalQuorum,
|
|
437
|
+
isMultichain,
|
|
376
438
|
};
|
|
377
439
|
}
|
|
378
440
|
|
|
379
|
-
// legacyArtemisMaxId is meaningful only for the caller's env — it's the
|
|
380
|
-
// Moonbeam Artemis cap. For foreign envs the targets-only heuristic is
|
|
381
|
-
// the right check (Ethereum-hub multigov has no Artemis predecessor).
|
|
382
|
-
const isMultichain = isLocal
|
|
383
|
-
? isMultichainAware(p, legacyArtemisMaxId)
|
|
384
|
-
: isMultichainProposal(p.targets);
|
|
385
|
-
|
|
386
441
|
const governorContract = isMultichain
|
|
387
442
|
? homeEnv.contracts.multichainGovernor
|
|
388
443
|
: homeEnv.contracts.governor;
|
|
@@ -495,6 +550,7 @@ export const getProposalsOnChainData = async (
|
|
|
495
550
|
eta,
|
|
496
551
|
votesCollected,
|
|
497
552
|
quorum: proposalQuorum,
|
|
553
|
+
isMultichain,
|
|
498
554
|
};
|
|
499
555
|
}),
|
|
500
556
|
);
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
getExtendedProposalData,
|
|
19
19
|
getProposalData,
|
|
20
20
|
getProposalsOnChainData,
|
|
21
|
-
isMultichainProposal,
|
|
22
21
|
readCrossChainQuorums,
|
|
23
22
|
} from "./common.js";
|
|
24
23
|
|
|
@@ -125,7 +124,11 @@ async function getMoonbeamProposal(
|
|
|
125
124
|
{ crossChainQuorums },
|
|
126
125
|
);
|
|
127
126
|
const onChainData = onChainDataList[0]!;
|
|
128
|
-
|
|
127
|
+
// Single source of truth: getProposalsOnChainData already classified this
|
|
128
|
+
// proposal with the caller env's Artemis cutoff and used it to route the
|
|
129
|
+
// on-chain reads. Reusing it avoids the drift that left Moonbeam-homed
|
|
130
|
+
// local-target proposals (and hub-local Ethereum ones) without `multichain`.
|
|
131
|
+
const isMultichain = onChainData.isMultichain;
|
|
129
132
|
|
|
130
133
|
const now = Math.floor(Date.now() / 1000);
|
|
131
134
|
let proposalState = onChainData.state;
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
getExtendedProposalData,
|
|
15
15
|
getProposalData,
|
|
16
16
|
getProposalsOnChainData,
|
|
17
|
-
isMultichainProposal,
|
|
18
17
|
readCrossChainQuorums,
|
|
19
18
|
} from "./common.js";
|
|
20
19
|
|
|
@@ -125,7 +124,10 @@ async function getMoonbeamProposals(
|
|
|
125
124
|
const proposals: Proposal[] = apiProposals.map((apiProposal, index) => {
|
|
126
125
|
const onChainData = onChainDataList[index]!;
|
|
127
126
|
const formattedData = formatApiProposalData(apiProposal);
|
|
128
|
-
|
|
127
|
+
// Single source of truth — see getProposal.ts. getProposalsOnChainData
|
|
128
|
+
// classified with the Artemis cutoff and routed the reads accordingly;
|
|
129
|
+
// re-classifying here without the cutoff would drift and drop `multichain`.
|
|
130
|
+
const isMultichain = onChainData.isMultichain;
|
|
129
131
|
|
|
130
132
|
const now = Math.floor(Date.now() / 1000);
|
|
131
133
|
let proposalState = onChainData.state;
|