@gzeoneth/gov-tracker 0.2.1-beta.b2eeb41 → 0.2.1-beta.d575294

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +81 -2
  2. package/dist/abis.d.ts +3 -0
  3. package/dist/abis.d.ts.map +1 -1
  4. package/dist/abis.js +30 -2
  5. package/dist/abis.js.map +1 -1
  6. package/dist/cli/cli.js +265 -27
  7. package/dist/cli/cli.js.map +1 -1
  8. package/dist/cli/lib/cli.d.ts +48 -2
  9. package/dist/cli/lib/cli.d.ts.map +1 -1
  10. package/dist/cli/lib/cli.js +356 -63
  11. package/dist/cli/lib/cli.js.map +1 -1
  12. package/dist/cli/lib/json-state.d.ts +23 -0
  13. package/dist/cli/lib/json-state.d.ts.map +1 -1
  14. package/dist/cli/lib/json-state.js +51 -4
  15. package/dist/cli/lib/json-state.js.map +1 -1
  16. package/dist/constants.d.ts +39 -1
  17. package/dist/constants.d.ts.map +1 -1
  18. package/dist/constants.js +47 -2
  19. package/dist/constants.js.map +1 -1
  20. package/dist/data/bundled-cache.json +11061 -1460
  21. package/dist/deduplication.d.ts +132 -0
  22. package/dist/deduplication.d.ts.map +1 -0
  23. package/dist/deduplication.js +270 -0
  24. package/dist/deduplication.js.map +1 -0
  25. package/dist/discovery/governor-discovery.d.ts.map +1 -1
  26. package/dist/discovery/governor-discovery.js +50 -35
  27. package/dist/discovery/governor-discovery.js.map +1 -1
  28. package/dist/discovery/timelock-discovery.d.ts +15 -6
  29. package/dist/discovery/timelock-discovery.d.ts.map +1 -1
  30. package/dist/discovery/timelock-discovery.js +27 -11
  31. package/dist/discovery/timelock-discovery.js.map +1 -1
  32. package/dist/election/contracts.d.ts +8 -0
  33. package/dist/election/contracts.d.ts.map +1 -0
  34. package/dist/election/contracts.js +28 -0
  35. package/dist/election/contracts.js.map +1 -0
  36. package/dist/election/details.d.ts +15 -0
  37. package/dist/election/details.d.ts.map +1 -0
  38. package/dist/election/details.js +157 -0
  39. package/dist/election/details.js.map +1 -0
  40. package/dist/election/index.d.ts +11 -0
  41. package/dist/election/index.d.ts.map +1 -0
  42. package/dist/election/index.js +48 -0
  43. package/dist/election/index.js.map +1 -0
  44. package/dist/election/params.d.ts +13 -0
  45. package/dist/election/params.d.ts.map +1 -0
  46. package/dist/election/params.js +93 -0
  47. package/dist/election/params.js.map +1 -0
  48. package/dist/election/participants.d.ts +6 -0
  49. package/dist/election/participants.d.ts.map +1 -0
  50. package/dist/election/participants.js +104 -0
  51. package/dist/election/participants.js.map +1 -0
  52. package/dist/election/prepare.d.ts +10 -0
  53. package/dist/election/prepare.d.ts.map +1 -0
  54. package/dist/election/prepare.js +52 -0
  55. package/dist/election/prepare.js.map +1 -0
  56. package/dist/election/proposal-ids.d.ts +18 -0
  57. package/dist/election/proposal-ids.d.ts.map +1 -0
  58. package/dist/election/proposal-ids.js +77 -0
  59. package/dist/election/proposal-ids.js.map +1 -0
  60. package/dist/election/status.d.ts +15 -0
  61. package/dist/election/status.d.ts.map +1 -0
  62. package/dist/election/status.js +102 -0
  63. package/dist/election/status.js.map +1 -0
  64. package/dist/election/tracking.d.ts +28 -0
  65. package/dist/election/tracking.d.ts.map +1 -0
  66. package/dist/election/tracking.js +412 -0
  67. package/dist/election/tracking.js.map +1 -0
  68. package/dist/index.d.ts +9 -5
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +72 -8
  71. package/dist/index.js.map +1 -1
  72. package/dist/stages/builder.d.ts +4 -0
  73. package/dist/stages/builder.d.ts.map +1 -1
  74. package/dist/stages/builder.js +7 -0
  75. package/dist/stages/builder.js.map +1 -1
  76. package/dist/stages/l2-to-l1-message.d.ts +3 -1
  77. package/dist/stages/l2-to-l1-message.d.ts.map +1 -1
  78. package/dist/stages/l2-to-l1-message.js +6 -6
  79. package/dist/stages/l2-to-l1-message.js.map +1 -1
  80. package/dist/stages/proposal-created.d.ts +1 -0
  81. package/dist/stages/proposal-created.d.ts.map +1 -1
  82. package/dist/stages/proposal-created.js +1 -0
  83. package/dist/stages/proposal-created.js.map +1 -1
  84. package/dist/stages/proposal-queued.d.ts +1 -0
  85. package/dist/stages/proposal-queued.d.ts.map +1 -1
  86. package/dist/stages/proposal-queued.js +3 -1
  87. package/dist/stages/proposal-queued.js.map +1 -1
  88. package/dist/stages/retryables.js +2 -2
  89. package/dist/stages/retryables.js.map +1 -1
  90. package/dist/stages/timelock.d.ts +3 -1
  91. package/dist/stages/timelock.d.ts.map +1 -1
  92. package/dist/stages/timelock.js +12 -12
  93. package/dist/stages/timelock.js.map +1 -1
  94. package/dist/stages/utils.d.ts +5 -9
  95. package/dist/stages/utils.d.ts.map +1 -1
  96. package/dist/stages/utils.js +34 -29
  97. package/dist/stages/utils.js.map +1 -1
  98. package/dist/stages/voting.d.ts.map +1 -1
  99. package/dist/stages/voting.js +5 -4
  100. package/dist/stages/voting.js.map +1 -1
  101. package/dist/tracker/cache.d.ts +10 -6
  102. package/dist/tracker/cache.d.ts.map +1 -1
  103. package/dist/tracker/cache.js +36 -13
  104. package/dist/tracker/cache.js.map +1 -1
  105. package/dist/tracker/checkpoint-helpers.d.ts +81 -0
  106. package/dist/tracker/checkpoint-helpers.d.ts.map +1 -0
  107. package/dist/tracker/checkpoint-helpers.js +201 -0
  108. package/dist/tracker/checkpoint-helpers.js.map +1 -0
  109. package/dist/tracker/discovery.d.ts +40 -9
  110. package/dist/tracker/discovery.d.ts.map +1 -1
  111. package/dist/tracker/discovery.js +152 -15
  112. package/dist/tracker/discovery.js.map +1 -1
  113. package/dist/tracker/execute.d.ts.map +1 -1
  114. package/dist/tracker/execute.js +1 -25
  115. package/dist/tracker/execute.js.map +1 -1
  116. package/dist/tracker/pipeline.d.ts.map +1 -1
  117. package/dist/tracker/pipeline.js +26 -11
  118. package/dist/tracker/pipeline.js.map +1 -1
  119. package/dist/tracker/query.d.ts +1 -0
  120. package/dist/tracker/query.d.ts.map +1 -1
  121. package/dist/tracker/query.js +14 -61
  122. package/dist/tracker/query.js.map +1 -1
  123. package/dist/tracker/state.d.ts +0 -10
  124. package/dist/tracker/state.d.ts.map +1 -1
  125. package/dist/tracker/state.js +1 -28
  126. package/dist/tracker/state.js.map +1 -1
  127. package/dist/tracker.d.ts +100 -8
  128. package/dist/tracker.d.ts.map +1 -1
  129. package/dist/tracker.js +405 -42
  130. package/dist/tracker.js.map +1 -1
  131. package/dist/types/config.d.ts +49 -0
  132. package/dist/types/config.d.ts.map +1 -1
  133. package/dist/types/core.d.ts +4 -2
  134. package/dist/types/core.d.ts.map +1 -1
  135. package/dist/types/election.d.ts +143 -0
  136. package/dist/types/election.d.ts.map +1 -1
  137. package/dist/types/index.d.ts +5 -7
  138. package/dist/types/index.d.ts.map +1 -1
  139. package/dist/types/index.js +2 -3
  140. package/dist/types/index.js.map +1 -1
  141. package/dist/types/stages.d.ts +72 -7
  142. package/dist/types/stages.d.ts.map +1 -1
  143. package/dist/types/stages.js.map +1 -1
  144. package/dist/types/tracking.d.ts +34 -8
  145. package/dist/types/tracking.d.ts.map +1 -1
  146. package/dist/utils/block-cache.d.ts +50 -0
  147. package/dist/utils/block-cache.d.ts.map +1 -0
  148. package/dist/utils/block-cache.js +80 -0
  149. package/dist/utils/block-cache.js.map +1 -0
  150. package/dist/utils/formatters.d.ts +91 -0
  151. package/dist/utils/formatters.d.ts.map +1 -0
  152. package/dist/utils/formatters.js +327 -0
  153. package/dist/utils/formatters.js.map +1 -0
  154. package/dist/utils/multicall.d.ts +52 -0
  155. package/dist/utils/multicall.d.ts.map +1 -0
  156. package/dist/utils/multicall.js +75 -0
  157. package/dist/utils/multicall.js.map +1 -0
  158. package/dist/utils/rpc-utils.d.ts +7 -1
  159. package/dist/utils/rpc-utils.d.ts.map +1 -1
  160. package/dist/utils/rpc-utils.js +42 -29
  161. package/dist/utils/rpc-utils.js.map +1 -1
  162. package/dist/utils/salt-computation.d.ts.map +1 -1
  163. package/dist/utils/salt-computation.js +33 -7
  164. package/dist/utils/salt-computation.js.map +1 -1
  165. package/dist/utils/sanitize.d.ts.map +1 -1
  166. package/dist/utils/sanitize.js +3 -0
  167. package/dist/utils/sanitize.js.map +1 -1
  168. package/dist/utils/stage-metadata.d.ts +0 -20
  169. package/dist/utils/stage-metadata.d.ts.map +1 -1
  170. package/dist/utils/stage-metadata.js +29 -44
  171. package/dist/utils/stage-metadata.js.map +1 -1
  172. package/dist/utils/timing.d.ts +13 -0
  173. package/dist/utils/timing.d.ts.map +1 -1
  174. package/dist/utils/timing.js +37 -1
  175. package/dist/utils/timing.js.map +1 -1
  176. package/package.json +3 -2
  177. package/dist/election.d.ts +0 -172
  178. package/dist/election.d.ts.map +0 -1
  179. package/dist/election.js +0 -467
  180. package/dist/election.js.map +0 -1
  181. package/dist/types/cross-chain.d.ts +0 -24
  182. package/dist/types/cross-chain.d.ts.map +0 -1
  183. package/dist/types/cross-chain.js +0 -6
  184. package/dist/types/cross-chain.js.map +0 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![codecov](https://codecov.io/gh/gzeoneth/gov-tracker/graph/badge.svg?token=2WVH8Z82TE)](https://codecov.io/gh/gzeoneth/gov-tracker)
5
5
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6
6
 
7
- Track and execute Arbitrum DAO governance proposal lifecycle stages.
7
+ Track and execute Arbitrum DAO governance proposal lifecycle stages and Security Council elections.
8
8
 
9
9
  ## Installation
10
10
 
@@ -80,12 +80,90 @@ npx @gzeoneth/gov-tracker track 0x... -w --private-key $PRIVATE_KEY
80
80
  # Discover and track all proposals
81
81
  npx @gzeoneth/gov-tracker run
82
82
 
83
+ # Selective tracking (only track specific types)
84
+ npx @gzeoneth/gov-tracker run --track-core # Constitutional proposals only
85
+ npx @gzeoneth/gov-tracker run --track-treasury # Non-constitutional proposals only
86
+ npx @gzeoneth/gov-tracker run --track-elections # Election governors only
87
+ npx @gzeoneth/gov-tracker run --track-timelocks # Direct timelock operations only
88
+
89
+ # Track with elections enabled in run loop
90
+ npx @gzeoneth/gov-tracker run --loop --election
91
+
83
92
  # Disable caching
84
93
  npx @gzeoneth/gov-tracker track 0x... --no-cache
94
+
95
+ # Interactive TUI (requires ink)
96
+ npx @gzeoneth/gov-tracker ui
97
+
98
+ # Track election creation tx (auto-switches to election view)
99
+ npx @gzeoneth/gov-tracker track 0x82a0baf3...
85
100
  ```
86
101
 
87
102
  **Shorthands:** `-v` (verbose), `-p` (prepare), `-w` (write), `-i` (inspect)
88
103
 
104
+ ### Interactive TUI
105
+
106
+ The `ui` command launches an interactive terminal interface for browsing proposals:
107
+
108
+ ```bash
109
+ # Browse bundled cache (no RPC required)
110
+ npx @gzeoneth/gov-tracker ui
111
+
112
+ # Enable live tracking with RPC
113
+ npx @gzeoneth/gov-tracker ui --l2-rpc $ARB1_RPC --l1-rpc $ETH_RPC
114
+ ```
115
+
116
+ **Features:**
117
+ - Browse bundled proposals with filter tabs (All, Active, Complete, Timelocks, Elections)
118
+ - View proposal details, voting statistics, and stage progress
119
+ - Inspect decoded calldata with nested parameter display
120
+ - View simulation data for Tenderly/Foundry fork testing
121
+ - Monitor Security Council election status with detailed nominee/member info (requires RPC)
122
+ - Live tracking and discovery when RPC providers are configured
123
+
124
+ **Navigation:**
125
+ | Key | Action |
126
+ |-----|--------|
127
+ | `↑↓` | Navigate |
128
+ | `PgUp/Dn` | Page navigation |
129
+ | `Enter` | View details |
130
+ | `Tab` | Cycle filter |
131
+ | `c` | View calldata |
132
+ | `s` | View simulation |
133
+ | `d` | Discover proposals (with RPC) |
134
+ | `e` | Election status (with RPC) |
135
+ | `r` | Re-track (with RPC) |
136
+ | `b`/`Esc` | Go back |
137
+ | `q` | Quit |
138
+
139
+ **Note:** The TUI requires `ink` and `react` packages (installed as optional dependencies).
140
+
141
+ **Auto-Switch:** When tracking a `createElection` transaction, the CLI automatically displays election-specific status (phase, cohort, nominees) instead of proposal stages.
142
+
143
+ ### Elections
144
+
145
+ ```bash
146
+ # Check election status (next election, can create)
147
+ npx @gzeoneth/gov-tracker election
148
+
149
+ # List all elections with statuses
150
+ npx @gzeoneth/gov-tracker election --list
151
+
152
+ # Track specific election by index
153
+ npx @gzeoneth/gov-tracker election --track 0
154
+
155
+ # Track with detailed nominee/member info
156
+ npx @gzeoneth/gov-tracker election --track 0 --details -v
157
+
158
+ # Create election or execute phase transition
159
+ npx @gzeoneth/gov-tracker election --track 0 -w --private-key $PRIVATE_KEY
160
+
161
+ # Monitor elections in loop mode
162
+ npx @gzeoneth/gov-tracker election --loop --interval 300
163
+ ```
164
+
165
+ **Election Phases:** `NOT_STARTED` → `NOMINEE_SELECTION` → `VETTING_PERIOD` → `MEMBER_ELECTION` → `PENDING_EXECUTION` → `COMPLETED`
166
+
89
167
  **Bundled Cache**: The CLI includes a pre-built cache of completed proposals. On first run, this eliminates initial discovery RPC calls. SDK users can access via `getBundledCachePath()` or direct JSON import - see [Examples](./docs/EXAMPLES.md#bundled-cache-bootstrap).
90
168
 
91
169
  ## Environment
@@ -135,7 +213,8 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for development workflow.
135
213
  |----------|-------------|
136
214
  | Constitutional | Core proposals requiring L1 round-trip (8-day L2 timelock) |
137
215
  | Non-Constitutional | Treasury proposals, L2-only execution (3-day L2 timelock) |
138
- | Election | Security Council election proposals |
216
+ | Election | Security Council election with 6-phase lifecycle |
217
+ | Cohort | Security Council has two cohorts (0 and 1) that alternate elections |
139
218
 
140
219
  See [Arbitrum governance docs](https://docs.arbitrum.foundation/concepts/lifecycle-anatomy-aip-proposal) for more details.
141
220
 
package/dist/abis.d.ts CHANGED
@@ -44,4 +44,7 @@ export declare const upgradeExecutorInterface: ethers.utils.Interface;
44
44
  export declare const memberSyncActionInterface: ethers.utils.Interface;
45
45
  export declare const proposalCreatedInterface: ethers.utils.Interface;
46
46
  export declare const proposalQueuedInterface: ethers.utils.Interface;
47
+ export declare const proposalExecutedInterface: ethers.utils.Interface;
48
+ export declare const nomineeElectionGovernorInterface: ethers.utils.Interface;
49
+ export declare const memberElectionGovernorInterface: ethers.utils.Interface;
47
50
  //# sourceMappingURL=abis.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"abis.d.ts","sourceRoot":"","sources":["../src/abis.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;GAEG;AACH,eAAO,MAAM,YAAY,UAmBxB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,UAIpC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,UAiBxB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,UAOxC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,UAErB,CAAC;AA0CF;;GAEG;AACH,eAAO,MAAM,6BAA6B,UAazC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,UAIxC,CAAC;AAeF,eAAO,MAAM,iBAAiB,wBAA2C,CAAC;AAC1E,eAAO,MAAM,iBAAiB,wBAA2C,CAAC;AAC1E,eAAO,MAAM,eAAe,wBAA0C,CAAC;AACvE,eAAO,MAAM,eAAe,wBAAyC,CAAC;AACtE,eAAO,MAAM,sBAAsB,wBAAiD,CAAC;AACrF,eAAO,MAAM,qBAAqB,wBAAmD,CAAC;AACtF,eAAO,MAAM,wBAAwB,wBAAmD,CAAC;AACzF,eAAO,MAAM,yBAAyB,wBAAqD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,wBAAuD,CAAC;AAC7F,eAAO,MAAM,uBAAuB,wBAAsD,CAAC"}
1
+ {"version":3,"file":"abis.d.ts","sourceRoot":"","sources":["../src/abis.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;GAEG;AACH,eAAO,MAAM,YAAY,UAmBxB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,UAIpC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,UAiBxB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,UAOxC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,UAErB,CAAC;AA0CF;;GAEG;AACH,eAAO,MAAM,6BAA6B,UAwBzC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,UAcxC,CAAC;AAoBF,eAAO,MAAM,iBAAiB,wBAA2C,CAAC;AAC1E,eAAO,MAAM,iBAAiB,wBAA2C,CAAC;AAC1E,eAAO,MAAM,eAAe,wBAA0C,CAAC;AACvE,eAAO,MAAM,eAAe,wBAAyC,CAAC;AACtE,eAAO,MAAM,sBAAsB,wBAAiD,CAAC;AACrF,eAAO,MAAM,qBAAqB,wBAAmD,CAAC;AACtF,eAAO,MAAM,wBAAwB,wBAAmD,CAAC;AACzF,eAAO,MAAM,yBAAyB,wBAAqD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,wBAAuD,CAAC;AAC7F,eAAO,MAAM,uBAAuB,wBAAsD,CAAC;AAC3F,eAAO,MAAM,yBAAyB,wBAAwD,CAAC;AAC/F,eAAO,MAAM,gCAAgC,wBAE5C,CAAC;AACF,eAAO,MAAM,+BAA+B,wBAE3C,CAAC"}
package/dist/abis.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * repeated interface creation throughout the codebase.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.proposalQueuedInterface = exports.proposalCreatedInterface = exports.memberSyncActionInterface = exports.upgradeExecutorInterface = exports.arbRetryableInterface = exports.outboxExecuteInterface = exports.outboxInterface = exports.arbSysInterface = exports.timelockInterface = exports.governorInterface = exports.MEMBER_ELECTION_GOVERNOR_ABI = exports.NOMINEE_ELECTION_GOVERNOR_ABI = exports.INBOX_ABI = exports.SECURITY_COUNCIL_MANAGER_ABI = exports.TIMELOCK_ABI = exports.GOVERNOR_WITH_VETTER_ABI = exports.GOVERNOR_ABI = void 0;
10
+ exports.memberElectionGovernorInterface = exports.nomineeElectionGovernorInterface = exports.proposalExecutedInterface = exports.proposalQueuedInterface = exports.proposalCreatedInterface = exports.memberSyncActionInterface = exports.upgradeExecutorInterface = exports.arbRetryableInterface = exports.outboxExecuteInterface = exports.outboxInterface = exports.arbSysInterface = exports.timelockInterface = exports.governorInterface = exports.MEMBER_ELECTION_GOVERNOR_ABI = exports.NOMINEE_ELECTION_GOVERNOR_ABI = exports.INBOX_ABI = exports.SECURITY_COUNCIL_MANAGER_ABI = exports.TIMELOCK_ABI = exports.GOVERNOR_WITH_VETTER_ABI = exports.GOVERNOR_ABI = void 0;
11
11
  const ethers_1 = require("ethers");
12
12
  /**
13
13
  * Governor contract ABI (minimal required interface)
@@ -128,6 +128,17 @@ exports.NOMINEE_ELECTION_GOVERNOR_ABI = [
128
128
  "function state(uint256 proposalId) view returns (uint8)",
129
129
  "function getProposeArgs(uint256 electionIndex) view returns (address[], uint256[], bytes[], string)",
130
130
  "function hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) view returns (bytes32)",
131
+ // Detailed nominee tracking
132
+ "function nominees(uint256 proposalId) view returns (address[])",
133
+ "function compliantNominees(uint256 proposalId) view returns (address[])",
134
+ "function votesReceived(uint256 proposalId, address contender) view returns (uint256)",
135
+ "function isExcluded(uint256 proposalId, address nominee) view returns (bool)",
136
+ "function quorum(uint256 blockNumber) view returns (uint256)",
137
+ // Events
138
+ "event ContenderAdded(uint256 indexed proposalId, address indexed contender)",
139
+ "event NewNominee(uint256 indexed proposalId, address indexed nominee)",
140
+ "event NomineeExcluded(uint256 indexed proposalId, address indexed nominee)",
141
+ "event VoteCastForContender(uint256 indexed proposalId, address indexed voter, address indexed contender, uint256 votes, uint256 totalUsedVotes, uint256 usableVotes)",
131
142
  ];
132
143
  /**
133
144
  * SecurityCouncilMemberElectionGovernor ABI
@@ -135,7 +146,17 @@ exports.NOMINEE_ELECTION_GOVERNOR_ABI = [
135
146
  exports.MEMBER_ELECTION_GOVERNOR_ABI = [
136
147
  "function state(uint256 proposalId) view returns (uint8)",
137
148
  "function proposalDeadline(uint256 proposalId) view returns (uint256)",
138
- "function electionIndexToProposalId(uint256 electionIndex) view returns (uint256)",
149
+ "function proposalSnapshot(uint256 proposalId) view returns (uint256)",
150
+ // From ElectionGovernor base - used to compute proposal ID
151
+ "function getProposeArgs(uint256 electionIndex) view returns (address[], uint256[], bytes[], string)",
152
+ "function hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) view returns (bytes32)",
153
+ // Detailed member election tracking
154
+ "function weightReceived(uint256 proposalId, address nominee) view returns (uint256)",
155
+ "function topNominees(uint256 proposalId) view returns (address[])",
156
+ "function fullWeightVotingDeadline(uint256 proposalId) view returns (uint256)",
157
+ "function fullWeightDuration() view returns (uint256)",
158
+ // Events
159
+ "event VoteCastForNominee(address indexed voter, uint256 indexed proposalId, address indexed nominee, uint256 votes, uint256 weight, uint256 totalUsedVotes, uint256 usableVotes, uint256 weightReceived)",
139
160
  ];
140
161
  /**
141
162
  * ProposalCreated event signature for parsing
@@ -145,6 +166,10 @@ const PROPOSAL_CREATED_EVENT = "event ProposalCreated(uint256 proposalId, addres
145
166
  * ProposalQueued event signature for parsing
146
167
  */
147
168
  const PROPOSAL_QUEUED_EVENT = "event ProposalQueued(uint256 proposalId, uint256 eta)";
169
+ /**
170
+ * ProposalExecuted event signature for parsing
171
+ */
172
+ const PROPOSAL_EXECUTED_EVENT = "event ProposalExecuted(uint256 proposalId)";
148
173
  // Pre-created Interface instances
149
174
  exports.governorInterface = new ethers_1.ethers.utils.Interface(exports.GOVERNOR_ABI);
150
175
  exports.timelockInterface = new ethers_1.ethers.utils.Interface(exports.TIMELOCK_ABI);
@@ -156,4 +181,7 @@ exports.upgradeExecutorInterface = new ethers_1.ethers.utils.Interface(UPGRADE_E
156
181
  exports.memberSyncActionInterface = new ethers_1.ethers.utils.Interface(MEMBER_SYNC_ACTION_ABI);
157
182
  exports.proposalCreatedInterface = new ethers_1.ethers.utils.Interface([PROPOSAL_CREATED_EVENT]);
158
183
  exports.proposalQueuedInterface = new ethers_1.ethers.utils.Interface([PROPOSAL_QUEUED_EVENT]);
184
+ exports.proposalExecutedInterface = new ethers_1.ethers.utils.Interface([PROPOSAL_EXECUTED_EVENT]);
185
+ exports.nomineeElectionGovernorInterface = new ethers_1.ethers.utils.Interface(exports.NOMINEE_ELECTION_GOVERNOR_ABI);
186
+ exports.memberElectionGovernorInterface = new ethers_1.ethers.utils.Interface(exports.MEMBER_ELECTION_GOVERNOR_ABI);
159
187
  //# sourceMappingURL=abis.js.map
package/dist/abis.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"abis.js","sourceRoot":"","sources":["../src/abis.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"abis.js","sourceRoot":"","sources":["../src/abis.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAEH,mCAAgC;AAEhC;;GAEG;AACU,QAAA,YAAY,GAAG;IAC1B,yDAAyD;IACzD,sEAAsE;IACtE,sEAAsE;IACtE,wHAAwH;IACxH,6DAA6D;IAC7D,iEAAiE;IACjE,gDAAgD;IAChD,+CAA+C;IAC/C,gDAAgD;IAChD,4CAA4C;IAC5C,qEAAqE;IACrE,iDAAiD;IACjD,6EAA6E;IAC7E,oCAAoC;IACpC,6EAA6E;IAC7E,8BAA8B;IAC9B,mHAAmH;IACnH,6HAA6H;CAC9H,CAAC;AAEF;;GAEG;AACU,QAAA,wBAAwB,GAAG;IACtC,6EAA6E;IAC7E,0CAA0C;IAC1C,2EAA2E;CAC5E,CAAC;AAEF;;GAEG;AACU,QAAA,YAAY,GAAG;IAC1B,sDAAsD;IACtD,6DAA6D;IAC7D,2DAA2D;IAC3D,0DAA0D;IAC1D,0DAA0D;IAC1D,+CAA+C;IAC/C,sIAAsI;IACtI,yKAAyK;IACzK,oHAAoH;IACpH,oJAAoJ;IACpJ,gHAAgH;IAChH,iIAAiI;IACjI,0DAA0D;IAC1D,yBAAyB;IACzB,+IAA+I;IAC/I,0GAA0G;CAC3G,CAAC;AAEF;;GAEG;AACU,QAAA,4BAA4B,GAAG;IAC1C,8CAA8C;IAC9C,6DAA6D;IAC7D,8DAA8D;IAC9D,+CAA+C;IAC/C,oFAAoF;IACpF,mFAAmF;CACpF,CAAC;AAEF;;GAEG;AACU,QAAA,SAAS,GAAG;IACvB,sGAAsG;CACvG,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,kDAAkD;IAClD,gFAAgF;IAChF,yMAAyM;CAC1M,CAAC;AAEF;;GAEG;AACH,MAAM,oBAAoB,GAAG,CAAC,+CAA+C,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,sBAAsB,GAAG;IAC7B,4FAA4F;CAC7F,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,GAAG;IACjB,+HAA+H;CAChI,CAAC;AAEF;;GAEG;AACH,MAAM,oBAAoB,GAAG,CAAC,4CAA4C,CAAC,CAAC;AAE5E;;GAEG;AACH,MAAM,kBAAkB,GAAG;IACzB,wMAAwM;CACzM,CAAC;AAEF;;GAEG;AACU,QAAA,6BAA6B,GAAG;IAC3C,iDAAiD;IACjD,4EAA4E;IAC5E,4EAA4E;IAC5E,sDAAsD;IACtD,iDAAiD;IACjD,6EAA6E;IAC7E,2EAA2E;IAC3E,sEAAsE;IACtE,sEAAsE;IACtE,yDAAyD;IACzD,qGAAqG;IACrG,+HAA+H;IAC/H,4BAA4B;IAC5B,gEAAgE;IAChE,yEAAyE;IACzE,sFAAsF;IACtF,8EAA8E;IAC9E,6DAA6D;IAC7D,SAAS;IACT,6EAA6E;IAC7E,uEAAuE;IACvE,4EAA4E;IAC5E,sKAAsK;CACvK,CAAC;AAEF;;GAEG;AACU,QAAA,4BAA4B,GAAG;IAC1C,yDAAyD;IACzD,sEAAsE;IACtE,sEAAsE;IACtE,2DAA2D;IAC3D,qGAAqG;IACrG,+HAA+H;IAC/H,oCAAoC;IACpC,qFAAqF;IACrF,mEAAmE;IACnE,8EAA8E;IAC9E,sDAAsD;IACtD,SAAS;IACT,0MAA0M;CAC3M,CAAC;AAEF;;GAEG;AACH,MAAM,sBAAsB,GAC1B,oMAAoM,CAAC;AAEvM;;GAEG;AACH,MAAM,qBAAqB,GAAG,uDAAuD,CAAC;AAEtF;;GAEG;AACH,MAAM,uBAAuB,GAAG,4CAA4C,CAAC;AAE7E,kCAAkC;AAErB,QAAA,iBAAiB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,oBAAY,CAAC,CAAC;AAC7D,QAAA,iBAAiB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,oBAAY,CAAC,CAAC;AAC7D,QAAA,eAAe,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;AAC1D,QAAA,eAAe,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;AACzD,QAAA,sBAAsB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;AACxE,QAAA,qBAAqB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;AACzE,QAAA,wBAAwB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;AAC5E,QAAA,yBAAyB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;AAC/E,QAAA,wBAAwB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC;AAChF,QAAA,uBAAuB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC;AAC9E,QAAA,yBAAyB,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAClF,QAAA,gCAAgC,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CACxE,qCAA6B,CAC9B,CAAC;AACW,QAAA,+BAA+B,GAAG,IAAI,eAAM,CAAC,KAAK,CAAC,SAAS,CACvE,oCAA4B,CAC7B,CAAC"}
package/dist/cli/cli.js CHANGED
@@ -76,12 +76,10 @@ const fs = __importStar(require("fs"));
76
76
  const debug_1 = __importDefault(require("debug"));
77
77
  // Check for required CLI dependencies (optional in package.json)
78
78
  let Command;
79
- let Option;
80
79
  try {
81
80
  // eslint-disable-next-line @typescript-eslint/no-require-imports
82
81
  const commander = require("commander");
83
82
  Command = commander.Command;
84
- Option = commander.Option;
85
83
  }
86
84
  catch {
87
85
  console.error("Error: CLI requires 'commander' package.");
@@ -273,17 +271,23 @@ runCmd
273
271
  .option("--json-output <path>", "Write JSON state for dashboard integration")
274
272
  .option("--election", "Also check for Security Council elections each cycle")
275
273
  .option("--concurrency <n>", "Number of concurrent tracking operations", "1")
274
+ .option("--track-core", "Track constitutional (core) governor proposals")
275
+ .option("--track-treasury", "Track non-constitutional (treasury) governor proposals")
276
+ .option("--track-timelocks", "Track L2 timelock operations (direct schedules)")
277
+ .option("--track-elections", "Track election governor proposals")
276
278
  .action(async (opts) => {
277
279
  if (opts.verbose)
278
280
  debug_1.default.enable("gov-tracker:*");
281
+ (0, cli_1.validateCliOptions)(opts, "run");
279
282
  (0, cli_1.requirePrivateKeyForWrite)(opts);
280
283
  const providers = (0, cli_1.createProvidersFromOptions)(opts);
281
284
  const chunkingConfig = (0, cli_1.parseChunkingConfig)(opts, index_1.CHUNK_SIZES.DELAY_MS);
282
- // --no-cache disables cache entirely
285
+ // --no-cache disables cache entirely; use autoFlush: false for batch writes
283
286
  const cachePath = opts.noCache ? undefined : opts.cache;
287
+ const cache = cachePath ? new index_1.FileCache(cachePath, { autoFlush: false }) : undefined;
284
288
  const tracker = (0, index_1.createTracker)({
285
289
  ...providers,
286
- cachePath,
290
+ cache,
287
291
  chunkingConfig,
288
292
  onProgress: createProgressCallback(),
289
293
  });
@@ -299,6 +303,25 @@ runCmd
299
303
  const intervalMs = parseInt(opts.interval, 10) * 1000;
300
304
  const concurrency = parseInt(opts.concurrency, 10);
301
305
  const gasSettings = (0, cli_1.parseGasSettings)(opts);
306
+ // Build discovery targets from --track-* flags
307
+ // If any --track-* flag is specified, only track those; otherwise track all
308
+ const hasTrackFlags = opts.trackCore || opts.trackTreasury || opts.trackTimelocks || opts.trackElections;
309
+ // Detect what types are being tracked
310
+ const trackingProposals = !hasTrackFlags || opts.trackCore || opts.trackTreasury;
311
+ const trackingTimelocks = !hasTrackFlags || opts.trackTimelocks;
312
+ const trackingElections = !hasTrackFlags || opts.trackElections;
313
+ // Detect elections-only mode (only --track-elections, no other --track-* flags)
314
+ const electionsOnly = opts.trackElections && !opts.trackCore && !opts.trackTreasury && !opts.trackTimelocks;
315
+ const discoveryTargets = hasTrackFlags
316
+ ? {
317
+ constitutionalGovernor: opts.trackCore,
318
+ nonConstitutionalGovernor: opts.trackTreasury,
319
+ l2ConstitutionalTimelock: opts.trackTimelocks,
320
+ l2NonConstitutionalTimelock: opts.trackTimelocks,
321
+ electionNomineeGovernor: opts.trackElections,
322
+ electionMemberGovernor: opts.trackElections,
323
+ }
324
+ : (0, index_1.buildDefaultTargets)();
302
325
  if (opts.verbose) {
303
326
  if (startBlock !== undefined)
304
327
  console.log(`Starting discovery from block ${startBlock}`);
@@ -307,6 +330,12 @@ runCmd
307
330
  console.log(`Max consecutive errors before skip: ${cli_1.MAX_CONSECUTIVE_ERRORS}`);
308
331
  if (concurrency > 1)
309
332
  console.log(`Concurrency: ${concurrency}`);
333
+ if (hasTrackFlags) {
334
+ const enabled = Object.entries(discoveryTargets)
335
+ .filter(([, v]) => v)
336
+ .map(([k]) => k);
337
+ console.log(`Tracking targets: ${enabled.join(", ")}`);
338
+ }
310
339
  console.log(`L2 gas: ${gasSettings.maxFeePerGas} gwei maxFee, ${gasSettings.maxPriorityFeePerGas} gwei priority`);
311
340
  }
312
341
  if (signer)
@@ -314,12 +343,17 @@ runCmd
314
343
  // Track whether this is the first cycle (for startBlock override)
315
344
  let isFirstCycle = true;
316
345
  async function runCycle() {
317
- console.log("Discovering proposals and operations...\n");
346
+ if (electionsOnly) {
347
+ console.log("Tracking elections...\n");
348
+ }
349
+ else {
350
+ console.log("Discovering proposals and operations...\n");
351
+ }
318
352
  let electionsSkipped = 0;
319
353
  // Only use startBlock on the first cycle; subsequent cycles resume from watermarks
320
354
  const cycleStartBlock = isFirstCycle ? startBlock : undefined;
321
355
  isFirstCycle = false;
322
- const { result, proposals, timelockOps } = await (0, cli_1.runMonitorCycle)(tracker, providers, {
356
+ const { result, proposals, timelockOps, elections } = await (0, cli_1.runMonitorCycle)(tracker, providers, {
323
357
  prepare: opts.prepare || opts.write || opts.prepareCompleted || opts.preparePending,
324
358
  prepareCompleted: opts.prepareCompleted,
325
359
  preparePending: opts.preparePending,
@@ -327,6 +361,9 @@ runCmd
327
361
  blockLag,
328
362
  maxAgeDays,
329
363
  concurrency,
364
+ targets: discoveryTargets,
365
+ electionsOnly,
366
+ forceElections: opts.force,
330
367
  onTrack: async (r) => {
331
368
  // Skip showing complete elections
332
369
  if (r.result?.isElection && r.result?.isComplete) {
@@ -335,7 +372,7 @@ runCmd
335
372
  }
336
373
  if (r.result) {
337
374
  console.log(`\n[${r.key}]`);
338
- console.log((0, cli_1.formatTrackingResult)(r.result));
375
+ (0, cli_1.displayTrackingResult)(r.result);
339
376
  }
340
377
  else if (r.error) {
341
378
  console.log(`\n[${r.key}] ERROR: ${r.error}`);
@@ -367,14 +404,36 @@ runCmd
367
404
  // Skip output if shutting down
368
405
  if ((0, cli_1.isShuttingDown)())
369
406
  return;
370
- const stats = await tracker.getStats();
371
- console.log(`\nFound ${proposals.length} new proposals, ${timelockOps.length} new ops | ` +
372
- `Incomplete: ${stats.proposals.active} proposals, ${stats.timelocks.active} timelocks | ` +
373
- `Tracked: ${result.tracked}, Prepared: ${result.prepared}` +
374
- (electionsSkipped > 0 ? ` (${electionsSkipped} elections skipped)` : ""));
407
+ // Get checkpoints and calculate filtered stats based on enabled targets
408
+ const checkpoints = await tracker.getAllCheckpoints();
409
+ const stats = (0, cli_1.calculateFilteredStats)(checkpoints, discoveryTargets);
410
+ // Build summary based on what was tracked this run
411
+ if (electionsOnly) {
412
+ // Elections-only mode: count from tracked elections, not cache
413
+ const electionComplete = elections.filter((e) => e.phase === "COMPLETED").length;
414
+ console.log(`\nTracked ${elections.length} elections (${electionComplete}/${elections.length} complete)`);
415
+ }
416
+ else {
417
+ // Discovery mode: show stats for tracked types only
418
+ const electionSummary = trackingElections && elections.length > 0
419
+ ? `, ${elections.length} elections (${stats.elections.complete}/${stats.elections.total} complete)`
420
+ : "";
421
+ // Build incomplete stats for tracked types
422
+ const incompleteParts = [];
423
+ if (trackingProposals && stats.proposals.active > 0) {
424
+ incompleteParts.push(`${stats.proposals.active} proposals`);
425
+ }
426
+ if (trackingTimelocks && stats.timelocks.active > 0) {
427
+ incompleteParts.push(`${stats.timelocks.active} timelocks`);
428
+ }
429
+ const incompleteSummary = incompleteParts.length > 0 ? ` | Incomplete: ${incompleteParts.join(", ")}` : "";
430
+ console.log(`\nFound ${proposals.length} new proposals, ${timelockOps.length} new ops${electionSummary}${incompleteSummary} | ` +
431
+ `Tracked: ${result.tracked}, Prepared: ${result.prepared}` +
432
+ (electionsSkipped > 0 ? ` (${electionsSkipped} elections skipped)` : ""));
433
+ }
375
434
  if (opts.jsonOutput) {
376
- const checkpoints = await tracker.getAllCheckpoints();
377
- (0, json_state_1.writeDashboardState)((0, json_state_1.buildDashboardState)(checkpoints), opts.jsonOutput);
435
+ const filteredCheckpoints = (0, cli_1.filterCheckpointsByTargets)(checkpoints, discoveryTargets);
436
+ (0, json_state_1.writeDashboardState)((0, json_state_1.buildDashboardState)(filteredCheckpoints), opts.jsonOutput);
378
437
  if (opts.verbose)
379
438
  console.log(`JSON state written to ${opts.jsonOutput}`);
380
439
  }
@@ -407,6 +466,8 @@ runCmd
407
466
  intervalMs,
408
467
  healthCheckUrl: opts.healthCheckUrl,
409
468
  });
469
+ // Flush cache at end (single write instead of per-item)
470
+ await cache?.flush();
410
471
  });
411
472
  // ============================================================================
412
473
  // Track Command
@@ -430,6 +491,7 @@ trackCmd
430
491
  .action(async (txHash, opts) => {
431
492
  if (opts.verbose)
432
493
  debug_1.default.enable("gov-tracker:*");
494
+ (0, cli_1.validateCliOptions)(opts, "track");
433
495
  (0, cli_1.requirePrivateKeyForWrite)(opts);
434
496
  try {
435
497
  const providers = (0, cli_1.createProvidersFromOptions)(opts);
@@ -443,14 +505,19 @@ trackCmd
443
505
  if (opts.force)
444
506
  console.log(`Force: ignoring cached data`);
445
507
  }
446
- // Use cache unless --force or --no-cache is specified
447
- const cachePath = opts.noCache ? undefined : opts.force ? undefined : opts.cache;
508
+ // Use cache unless --no-cache is specified
509
+ // --force clears the cache entry before tracking but still writes results
510
+ const cachePath = opts.noCache ? undefined : opts.cache;
448
511
  const tracker = (0, index_1.createTracker)({
449
512
  ...providers,
450
513
  cachePath,
451
514
  chunkingConfig,
452
515
  onProgress: opts.verbose ? createProgressCallback() : undefined,
453
516
  });
517
+ // Clear all cache entries for this tx if --force to ensure fresh tracking
518
+ if (opts.force && cachePath) {
519
+ await tracker.clearTxCacheEntries(txHash);
520
+ }
454
521
  let calldatas = [];
455
522
  let targets = [];
456
523
  let values = [];
@@ -483,11 +550,16 @@ trackCmd
483
550
  prepareCompleted: opts.prepareCompleted,
484
551
  preparePending: opts.preparePending,
485
552
  }, providers);
553
+ if (results.length === 0) {
554
+ console.error("No proposal or timelock operation found in transaction");
555
+ process.exit(1);
556
+ }
486
557
  // Format tracking output
487
- results.forEach((r, i) => {
558
+ for (let i = 0; i < results.length; i++) {
559
+ const r = results[i];
488
560
  const label = results.length > 1 ? `Operation ${i + 1}/${results.length}` : undefined;
489
- console.log((0, cli_1.formatTrackingResult)(r, label));
490
- });
561
+ (0, cli_1.displayTrackingResult)(r, label);
562
+ }
491
563
  // Show all prepared transactions
492
564
  if (preparedTransactions.length > 0) {
493
565
  console.log(`\n${(0, cli_1.formatMultiplePreparedTransactions)(preparedTransactions)}`);
@@ -529,12 +601,13 @@ trackCmd
529
601
  break;
530
602
  console.log(`\nRe-tracking to find next stages...`);
531
603
  const retracked = await (0, cli_1.trackAndPrepare)(tracker, txHash, { prepare: true }, providers);
532
- retracked.results.forEach((r, i) => {
604
+ for (let i = 0; i < retracked.results.length; i++) {
605
+ const r = retracked.results[i];
533
606
  const label = retracked.results.length > 1
534
607
  ? `Operation ${i + 1}/${retracked.results.length}`
535
608
  : undefined;
536
- console.log((0, cli_1.formatTrackingResult)(r, label));
537
- });
609
+ (0, cli_1.displayTrackingResult)(r, label);
610
+ }
538
611
  if (retracked.preparedTransactions.length > 0) {
539
612
  console.log(`\n${(0, cli_1.formatMultiplePreparedTransactions)(retracked.preparedTransactions)}`);
540
613
  }
@@ -590,16 +663,19 @@ program
590
663
  .addOption(cli_1.cacheOptions.cache(DEFAULT_CACHE_PATH))
591
664
  .option("--json", "Output as JSON")
592
665
  .action(async (opts) => {
593
- const { watermarks, checkpoints } = await index_1.ProposalStageTracker.readCacheStatus(opts.cache);
666
+ const { watermarks, checkpoints, elections } = await index_1.ProposalStageTracker.readCacheStatus(opts.cache);
594
667
  if (opts.json) {
595
668
  const checkpointsObj = {};
596
669
  for (const [key, checkpoint] of checkpoints) {
597
670
  checkpointsObj[key] = checkpoint;
598
671
  }
672
+ for (const [index, checkpoint] of elections) {
673
+ checkpointsObj[`election:${index}`] = checkpoint;
674
+ }
599
675
  console.log(JSON.stringify({ watermarks, checkpoints: checkpointsObj }, null, 2));
600
676
  }
601
677
  else {
602
- console.log((0, cli_1.formatCacheStatus)(checkpoints));
678
+ console.log((0, cli_1.formatCacheStatus)(checkpoints, elections));
603
679
  }
604
680
  });
605
681
  // ============================================================================
@@ -607,19 +683,181 @@ program
607
683
  // ============================================================================
608
684
  const electionCmd = program
609
685
  .command("election")
610
- .description("Check Security Council election status");
686
+ .description("Check Security Council election status and track elections");
611
687
  (0, cli_1.addOptions)(electionCmd, cli_1.rpcOptions);
612
688
  (0, cli_1.addOptions)(electionCmd, cli_1.loopOptions);
689
+ (0, cli_1.addOptions)(electionCmd, cli_1.executionOptions);
613
690
  electionCmd
691
+ .addOption(cli_1.cacheOptions.cache(DEFAULT_CACHE_PATH))
692
+ .addOption(cli_1.cacheOptions.noCache)
693
+ .addOption(cli_1.cacheOptions.force)
614
694
  .addOption(cli_1.verboseOption)
615
- .option("--write", "Create election if ready (requires --private-key)")
616
- .addOption(new Option("--private-key <key>", "Private key for execution").env("PRIVATE_KEY"))
695
+ .option("--list", "List all elections with their statuses")
696
+ .option("--track <index>", "Track a specific election by index")
697
+ .option("--details", "Show detailed nominee/member info (use with --track)")
617
698
  .action(async (opts) => {
618
699
  if (opts.verbose)
619
700
  debug_1.default.enable("gov-tracker:*");
620
701
  (0, cli_1.requirePrivateKeyForWrite)(opts);
621
702
  const providers = (0, cli_1.createProvidersFromOptions)(opts);
622
703
  const signer = opts.write ? (0, cli_1.createSigner)(opts.privateKey) : null;
704
+ // Create tracker with cache (unless --no-cache)
705
+ const cachePath = opts.noCache ? undefined : opts.cache;
706
+ const tracker = (0, index_1.createTracker)({
707
+ ...providers,
708
+ cachePath,
709
+ });
710
+ // Import election tracking functions for detailed queries
711
+ const { getNomineeElectionDetails, getMemberElectionDetails, prepareMemberElectionTrigger, prepareMemberElectionExecution, } = await Promise.resolve().then(() => __importStar(require("../index")));
712
+ // --list: Show all elections (uses cached data for completed elections)
713
+ if (opts.list) {
714
+ console.log("Fetching all elections...\n");
715
+ // Use --force to bypass cache and re-track all elections
716
+ const elections = await tracker.trackAllElections({ force: opts.force });
717
+ if (elections.length === 0) {
718
+ console.log("No elections found.");
719
+ return;
720
+ }
721
+ console.log(`=== Security Council Elections (${elections.length} total) ===\n`);
722
+ for (const election of elections) {
723
+ const cohortName = election.cohort === 0 ? "First" : "Second";
724
+ console.log(`Election #${election.electionIndex}`);
725
+ console.log(` Phase: ${election.phase}`);
726
+ console.log(` Cohort: ${cohortName} (${election.cohort})`);
727
+ console.log(` Compliant Nominees: ${election.compliantNomineeCount}/${election.targetNomineeCount}`);
728
+ if (election.nomineeProposalId) {
729
+ console.log(` Nominee Proposal: ${election.nomineeProposalState}`);
730
+ }
731
+ if (election.memberProposalId) {
732
+ console.log(` Member Proposal: ${election.memberProposalState}`);
733
+ }
734
+ if (election.canProceedToMemberPhase) {
735
+ console.log(` → Ready to trigger member election`);
736
+ }
737
+ if (election.canExecuteMember) {
738
+ console.log(` → Ready to execute member election`);
739
+ }
740
+ console.log("");
741
+ }
742
+ return;
743
+ }
744
+ // --track <index>: Track specific election (uses cache unless --force)
745
+ if (opts.track !== undefined) {
746
+ const electionIndex = parseInt(opts.track, 10);
747
+ console.log(`Tracking election #${electionIndex}...\n`);
748
+ const election = await tracker.trackElection(electionIndex, { force: opts.force });
749
+ // Use shared formatter for consistent output with stages
750
+ console.log((0, cli_1.formatElectionResult)(election));
751
+ // Show detailed info if requested
752
+ if (opts.details) {
753
+ console.log(`\n--- Detailed Information ---`);
754
+ if (election.nomineeProposalId) {
755
+ const nomineeDetails = await getNomineeElectionDetails(electionIndex, providers.l2Provider);
756
+ if (nomineeDetails) {
757
+ console.log(`\nNominee Election Details:`);
758
+ console.log(` Contenders: ${nomineeDetails.contenders.length}`);
759
+ console.log(` Total Nominees: ${nomineeDetails.nominees.length}`);
760
+ console.log(` Compliant: ${nomineeDetails.compliantNominees.length}`);
761
+ console.log(` Excluded: ${nomineeDetails.excludedNominees.length}`);
762
+ console.log(` Quorum Threshold: ${nomineeDetails.quorumThreshold.toString()} votes`);
763
+ if (nomineeDetails.contenders.length > 0) {
764
+ console.log(`\n Registered Contenders (${nomineeDetails.contenders.length}):`);
765
+ for (const contender of nomineeDetails.contenders) {
766
+ const extra = opts.verbose ? ` (block ${contender.registeredAtBlock})` : "";
767
+ console.log(` ${contender.address}${extra}`);
768
+ }
769
+ }
770
+ if (nomineeDetails.compliantNominees.length > 0) {
771
+ console.log(`\n Qualified Nominees (${nomineeDetails.compliantNominees.length}):`);
772
+ for (const nominee of nomineeDetails.compliantNominees) {
773
+ const extra = opts.verbose && nominee.nominatedAtBlock
774
+ ? ` (block ${nominee.nominatedAtBlock})`
775
+ : "";
776
+ console.log(` ${nominee.address}: ${nominee.votesReceived.toString()} votes${extra}`);
777
+ }
778
+ }
779
+ if (nomineeDetails.excludedNominees.length > 0) {
780
+ console.log(`\n Excluded Nominees (${nomineeDetails.excludedNominees.length}):`);
781
+ for (const nominee of nomineeDetails.excludedNominees) {
782
+ const extra = opts.verbose && nominee.excludedAtBlock
783
+ ? ` (block ${nominee.excludedAtBlock})`
784
+ : "";
785
+ console.log(` ${nominee.address}: excluded${extra}`);
786
+ }
787
+ }
788
+ }
789
+ }
790
+ if (election.memberProposalId) {
791
+ const memberDetails = await getMemberElectionDetails(electionIndex, providers.l2Provider);
792
+ if (memberDetails) {
793
+ console.log(`\nMember Election Details:`);
794
+ console.log(` Full Weight Deadline: block ${memberDetails.fullWeightDeadline}`);
795
+ console.log(` Proposal Deadline: block ${memberDetails.proposalDeadline}`);
796
+ if (memberDetails.nominees.length > 0) {
797
+ console.log(`\n Candidates by Weight (${memberDetails.nominees.length}):`);
798
+ for (const nominee of memberDetails.nominees) {
799
+ const winnerTag = nominee.isWinner ? " [WINNER]" : "";
800
+ console.log(` #${nominee.rank} ${nominee.address}: ${nominee.weightReceived.toString()}${winnerTag}`);
801
+ }
802
+ }
803
+ if (memberDetails.winners.length > 0) {
804
+ console.log(`\n Elected Members (${memberDetails.winners.length}):`);
805
+ for (const winner of memberDetails.winners) {
806
+ console.log(` ${winner}`);
807
+ }
808
+ }
809
+ }
810
+ }
811
+ }
812
+ function getActionStatus(isReady, isPending, isCompleted) {
813
+ if (isReady)
814
+ return "READY";
815
+ if (isCompleted)
816
+ return "COMPLETED";
817
+ if (isPending)
818
+ return "PENDING";
819
+ return "PENDING";
820
+ }
821
+ // Should we show/prepare this action based on flags?
822
+ function shouldShowAction(status) {
823
+ // Always show READY actions
824
+ if (status === "READY")
825
+ return true;
826
+ // Otherwise, only if --prepare* flags match the status
827
+ if (opts.preparePending && status === "PENDING")
828
+ return true;
829
+ if (opts.prepareCompleted && status === "COMPLETED")
830
+ return true;
831
+ return false;
832
+ }
833
+ // Helper to prepare and optionally execute an election action
834
+ async function handleElectionAction(actionName, prepareFunc, status) {
835
+ if (!shouldShowAction(status))
836
+ return;
837
+ console.log(`\n[ACTION] ${actionName} (${status})`);
838
+ const prepared = await prepareFunc();
839
+ if (!prepared)
840
+ return;
841
+ console.log((0, cli_1.formatDryRun)(prepared));
842
+ if (signer && opts.write && status === "READY") {
843
+ const execResult = await (0, cli_1.executeTransaction)(prepared, signer, providers);
844
+ if (execResult.success) {
845
+ console.log(`\n[EXECUTED] ${actionName} succeeded! Tx: ${execResult.txHash}`);
846
+ }
847
+ else {
848
+ console.error(`\n[ERROR] Execution failed: ${execResult.error}`);
849
+ }
850
+ }
851
+ }
852
+ // Trigger member election
853
+ const triggerStatus = getActionStatus(election.canProceedToMemberPhase, election.phase === "VETTING_PERIOD" && !election.canProceedToMemberPhase, election.memberProposalId !== null);
854
+ await handleElectionAction("Trigger member election", () => prepareMemberElectionTrigger(election, providers.l2Provider), triggerStatus);
855
+ // Execute member election
856
+ const executeStatus = getActionStatus(election.canExecuteMember, election.phase === "MEMBER_ELECTION" && !election.canExecuteMember, election.phase === "COMPLETED");
857
+ await handleElectionAction("Execute member election - install new council", () => prepareMemberElectionExecution(election, providers.l2Provider), executeStatus);
858
+ return;
859
+ }
860
+ // Default: Check election status (original behavior)
623
861
  async function checkElection() {
624
862
  try {
625
863
  const result = await (0, election_check_1.checkAndExecuteElection)(providers, signer, {