@poa-box/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/README.md +201 -0
  2. package/dist/abis/DirectDemocracyVotingNew.d.ts +749 -0
  3. package/dist/abis/DirectDemocracyVotingNew.js +961 -0
  4. package/dist/abis/ERC20.d.ts +90 -0
  5. package/dist/abis/ERC20.js +125 -0
  6. package/dist/abis/EducationHubNew.d.ts +590 -0
  7. package/dist/abis/EducationHubNew.js +762 -0
  8. package/dist/abis/EligibilityModuleNew.d.ts +1615 -0
  9. package/dist/abis/EligibilityModuleNew.js +2082 -0
  10. package/dist/abis/Executor.d.ts +619 -0
  11. package/dist/abis/Executor.js +796 -0
  12. package/dist/abis/HybridVotingNew.d.ts +898 -0
  13. package/dist/abis/HybridVotingNew.js +1149 -0
  14. package/dist/abis/ImplementationRegistry.d.ts +270 -0
  15. package/dist/abis/ImplementationRegistry.js +355 -0
  16. package/dist/abis/OrgDeployerNew.d.ts +1275 -0
  17. package/dist/abis/OrgDeployerNew.js +1630 -0
  18. package/dist/abis/OrgRegistry.d.ts +670 -0
  19. package/dist/abis/OrgRegistry.js +868 -0
  20. package/dist/abis/ParticipationToken.d.ts +1086 -0
  21. package/dist/abis/ParticipationToken.js +1415 -0
  22. package/dist/abis/PasskeyAccount.d.ts +788 -0
  23. package/dist/abis/PasskeyAccount.js +1019 -0
  24. package/dist/abis/PasskeyAccountFactory.d.ts +344 -0
  25. package/dist/abis/PasskeyAccountFactory.js +449 -0
  26. package/dist/abis/PaymasterHub.d.ts +1603 -0
  27. package/dist/abis/PaymasterHub.js +2048 -0
  28. package/dist/abis/PaymentManager.d.ts +520 -0
  29. package/dist/abis/PaymentManager.js +671 -0
  30. package/dist/abis/PoaManager.d.ts +344 -0
  31. package/dist/abis/PoaManager.js +449 -0
  32. package/dist/abis/QuickJoinNew.d.ts +855 -0
  33. package/dist/abis/QuickJoinNew.js +1098 -0
  34. package/dist/abis/TaskManagerNew.d.ts +1236 -0
  35. package/dist/abis/TaskManagerNew.js +1573 -0
  36. package/dist/abis/ToggleModule.d.ts +193 -0
  37. package/dist/abis/ToggleModule.js +255 -0
  38. package/dist/abis/UniversalAccountRegistry.d.ts +577 -0
  39. package/dist/abis/UniversalAccountRegistry.js +750 -0
  40. package/dist/abis/ZkEmailInvites.d.ts +823 -0
  41. package/dist/abis/ZkEmailInvites.js +1060 -0
  42. package/dist/abis/external/AaveGovernanceV2.d.ts +128 -0
  43. package/dist/abis/external/AaveGovernanceV2.js +177 -0
  44. package/dist/abis/external/AaveGovernanceV3.d.ts +160 -0
  45. package/dist/abis/external/AaveGovernanceV3.js +220 -0
  46. package/dist/abis/external/AragonVoting.d.ts +107 -0
  47. package/dist/abis/external/AragonVoting.js +150 -0
  48. package/dist/abis/external/CurveGaugeController.d.ts +95 -0
  49. package/dist/abis/external/CurveGaugeController.js +130 -0
  50. package/dist/abis/external/CurveVotingEscrow.d.ts +86 -0
  51. package/dist/abis/external/CurveVotingEscrow.js +116 -0
  52. package/dist/abis/external/GovernorAlpha.d.ts +172 -0
  53. package/dist/abis/external/GovernorAlpha.js +228 -0
  54. package/dist/abis/external/MakerDAOChief.d.ts +92 -0
  55. package/dist/abis/external/MakerDAOChief.js +129 -0
  56. package/dist/abis/external/OZGovernor.d.ts +227 -0
  57. package/dist/abis/external/OZGovernor.js +317 -0
  58. package/dist/abis/external/SolidlyVotingEscrow.d.ts +196 -0
  59. package/dist/abis/external/SolidlyVotingEscrow.js +260 -0
  60. package/dist/abis/index.d.ts +62 -0
  61. package/dist/abis/index.js +98 -0
  62. package/dist/chains.d.ts +150 -0
  63. package/dist/chains.js +320 -0
  64. package/dist/context.d.ts +48 -0
  65. package/dist/context.js +38 -0
  66. package/dist/contracts.d.ts +24 -0
  67. package/dist/contracts.js +41 -0
  68. package/dist/encoding.d.ts +58 -0
  69. package/dist/encoding.js +243 -0
  70. package/dist/env.d.ts +10 -0
  71. package/dist/env.js +4 -0
  72. package/dist/error-catalog.d.ts +41 -0
  73. package/dist/error-catalog.js +941 -0
  74. package/dist/errors.d.ts +27 -0
  75. package/dist/errors.js +60 -0
  76. package/dist/execute/ethers.d.ts +93 -0
  77. package/dist/execute/ethers.js +323 -0
  78. package/dist/execute/index.d.ts +7 -0
  79. package/dist/execute/index.js +23 -0
  80. package/dist/execute/sponsored.d.ts +68 -0
  81. package/dist/execute/sponsored.js +277 -0
  82. package/dist/exit-codes.d.ts +18 -0
  83. package/dist/exit-codes.js +21 -0
  84. package/dist/format.d.ts +21 -0
  85. package/dist/format.js +89 -0
  86. package/dist/graph/client.d.ts +298 -0
  87. package/dist/graph/client.js +702 -0
  88. package/dist/graph/documents/activity.d.ts +9 -0
  89. package/dist/graph/documents/activity.js +164 -0
  90. package/dist/graph/documents/beacons.d.ts +172 -0
  91. package/dist/graph/documents/beacons.js +345 -0
  92. package/dist/graph/documents/index.d.ts +21 -0
  93. package/dist/graph/documents/index.js +57 -0
  94. package/dist/graph/documents/infrastructure.d.ts +30 -0
  95. package/dist/graph/documents/infrastructure.js +34 -0
  96. package/dist/graph/documents/org.d.ts +15 -0
  97. package/dist/graph/documents/org.js +249 -0
  98. package/dist/graph/documents/paymaster.d.ts +95 -0
  99. package/dist/graph/documents/paymaster.js +101 -0
  100. package/dist/graph/documents/role.d.ts +6 -0
  101. package/dist/graph/documents/role.js +41 -0
  102. package/dist/graph/documents/roles.d.ts +50 -0
  103. package/dist/graph/documents/roles.js +112 -0
  104. package/dist/graph/documents/task.d.ts +84 -0
  105. package/dist/graph/documents/task.js +236 -0
  106. package/dist/graph/documents/token.d.ts +13 -0
  107. package/dist/graph/documents/token.js +126 -0
  108. package/dist/graph/documents/treasury.d.ts +39 -0
  109. package/dist/graph/documents/treasury.js +136 -0
  110. package/dist/graph/documents/user.d.ts +120 -0
  111. package/dist/graph/documents/user.js +302 -0
  112. package/dist/graph/documents/voting-classes.d.ts +69 -0
  113. package/dist/graph/documents/voting-classes.js +149 -0
  114. package/dist/graph/documents/voting.d.ts +28 -0
  115. package/dist/graph/documents/voting.js +163 -0
  116. package/dist/graph/documents/vouch.d.ts +79 -0
  117. package/dist/graph/documents/vouch.js +171 -0
  118. package/dist/graph/documents/zkemail.d.ts +63 -0
  119. package/dist/graph/documents/zkemail.js +188 -0
  120. package/dist/graph/index.d.ts +2 -0
  121. package/dist/graph/index.js +41 -0
  122. package/dist/index.d.ts +49 -0
  123. package/dist/index.js +92 -0
  124. package/dist/ipfs.d.ts +39 -0
  125. package/dist/ipfs.js +214 -0
  126. package/dist/label-aliases.d.ts +31 -0
  127. package/dist/label-aliases.js +70 -0
  128. package/dist/metadata/education.d.ts +30 -0
  129. package/dist/metadata/education.js +31 -0
  130. package/dist/metadata/index.d.ts +12 -0
  131. package/dist/metadata/index.js +48 -0
  132. package/dist/metadata/org.d.ts +92 -0
  133. package/dist/metadata/org.js +93 -0
  134. package/dist/metadata/proposal.d.ts +44 -0
  135. package/dist/metadata/proposal.js +45 -0
  136. package/dist/metadata/role.d.ts +38 -0
  137. package/dist/metadata/role.js +35 -0
  138. package/dist/metadata/task.d.ts +108 -0
  139. package/dist/metadata/task.js +81 -0
  140. package/dist/metadata/token.d.ts +32 -0
  141. package/dist/metadata/token.js +36 -0
  142. package/dist/metadata/user.d.ts +63 -0
  143. package/dist/metadata/user.js +70 -0
  144. package/dist/multicall.d.ts +32 -0
  145. package/dist/multicall.js +96 -0
  146. package/dist/payout.d.ts +59 -0
  147. package/dist/payout.js +96 -0
  148. package/dist/perms.d.ts +47 -0
  149. package/dist/perms.js +120 -0
  150. package/dist/preflight.d.ts +72 -0
  151. package/dist/preflight.js +269 -0
  152. package/dist/reads/education.d.ts +79 -0
  153. package/dist/reads/education.js +122 -0
  154. package/dist/reads/eligibility.d.ts +377 -0
  155. package/dist/reads/eligibility.js +687 -0
  156. package/dist/reads/index.d.ts +16 -0
  157. package/dist/reads/index.js +55 -0
  158. package/dist/reads/org.d.ts +258 -0
  159. package/dist/reads/org.js +220 -0
  160. package/dist/reads/paymaster.d.ts +147 -0
  161. package/dist/reads/paymaster.js +235 -0
  162. package/dist/reads/project.d.ts +33 -0
  163. package/dist/reads/project.js +62 -0
  164. package/dist/reads/resolve.d.ts +34 -0
  165. package/dist/reads/resolve.js +64 -0
  166. package/dist/reads/task.d.ts +286 -0
  167. package/dist/reads/task.js +280 -0
  168. package/dist/reads/token.d.ts +94 -0
  169. package/dist/reads/token.js +75 -0
  170. package/dist/reads/treasury.d.ts +222 -0
  171. package/dist/reads/treasury.js +241 -0
  172. package/dist/reads/user.d.ts +229 -0
  173. package/dist/reads/user.js +155 -0
  174. package/dist/reads/vote.d.ts +296 -0
  175. package/dist/reads/vote.js +392 -0
  176. package/dist/reads/zkemail.d.ts +117 -0
  177. package/dist/reads/zkemail.js +217 -0
  178. package/dist/similarity.d.ts +36 -0
  179. package/dist/similarity.js +67 -0
  180. package/dist/sponsorship-config.d.ts +47 -0
  181. package/dist/sponsorship-config.js +41 -0
  182. package/dist/stats.d.ts +11 -0
  183. package/dist/stats.js +27 -0
  184. package/dist/task-lens.d.ts +113 -0
  185. package/dist/task-lens.js +210 -0
  186. package/dist/tx/education.d.ts +169 -0
  187. package/dist/tx/education.js +344 -0
  188. package/dist/tx/eligibility.d.ts +511 -0
  189. package/dist/tx/eligibility.js +818 -0
  190. package/dist/tx/governance.d.ts +85 -0
  191. package/dist/tx/governance.js +87 -0
  192. package/dist/tx/index.d.ts +17 -0
  193. package/dist/tx/index.js +56 -0
  194. package/dist/tx/intent.d.ts +67 -0
  195. package/dist/tx/intent.js +48 -0
  196. package/dist/tx/org.d.ts +321 -0
  197. package/dist/tx/org.js +544 -0
  198. package/dist/tx/paymaster.d.ts +140 -0
  199. package/dist/tx/paymaster.js +251 -0
  200. package/dist/tx/project.d.ts +146 -0
  201. package/dist/tx/project.js +217 -0
  202. package/dist/tx/task.d.ts +480 -0
  203. package/dist/tx/task.js +1149 -0
  204. package/dist/tx/token.d.ts +96 -0
  205. package/dist/tx/token.js +165 -0
  206. package/dist/tx/treasury.d.ts +474 -0
  207. package/dist/tx/treasury.js +911 -0
  208. package/dist/tx/user.d.ts +177 -0
  209. package/dist/tx/user.js +292 -0
  210. package/dist/tx/vote.d.ts +332 -0
  211. package/dist/tx/vote.js +762 -0
  212. package/dist/tx/zkemail.d.ts +90 -0
  213. package/dist/tx/zkemail.js +210 -0
  214. package/dist/validation.d.ts +8 -0
  215. package/dist/validation.js +39 -0
  216. package/dist/version.d.ts +142 -0
  217. package/dist/version.js +257 -0
  218. package/dist/zkemail.d.ts +161 -0
  219. package/dist/zkemail.js +299 -0
  220. package/package.json +130 -0
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ /**
3
+ * Pre-flight Checks
4
+ * Read-only validations that run before a write command sends its
5
+ * transaction, so predictable failures surface as one actionable message
6
+ * instead of an on-chain revert (or a silent wrong-state write). Contract
7
+ * reads batch through Multicall3 in a single RPC round-trip; local checks
8
+ * run concurrently. ALL failures are collected before throwing.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.runPreflight = runPreflight;
12
+ exports.checkGasBalance = checkGasBalance;
13
+ exports.checkHasHat = checkHasHat;
14
+ exports.checkUsernameFree = checkUsernameFree;
15
+ exports.checkTaskStatus = checkTaskStatus;
16
+ exports.checkProposalActive = checkProposalActive;
17
+ const ethers_1 = require("ethers");
18
+ const errors_1 = require("./errors");
19
+ const multicall_1 = require("./multicall");
20
+ const contracts_1 = require("./contracts");
21
+ const format_1 = require("./format");
22
+ const task_lens_1 = require("./task-lens");
23
+ function formatFailure(f) {
24
+ let line = ` ✗ ${f.label}`;
25
+ if (f.detail)
26
+ line += `: ${f.detail}`;
27
+ if (f.suggestion)
28
+ line += ` (${f.suggestion})`;
29
+ return line;
30
+ }
31
+ /**
32
+ * Run all checks (batched calls + concurrent locals), collect every failure,
33
+ * and throw a single PreconditionError listing them. `opts.skip` (from a
34
+ * --skip-checks flag) bypasses everything.
35
+ */
36
+ async function runPreflight(provider, checks, opts) {
37
+ if (opts?.skip || checks.length === 0)
38
+ return;
39
+ const callIndexes = [];
40
+ const calls = [];
41
+ const localIndexes = [];
42
+ checks.forEach((check, i) => {
43
+ if (check.call) {
44
+ callIndexes.push(i);
45
+ calls.push(check.call);
46
+ }
47
+ else if (check.local) {
48
+ localIndexes.push(i);
49
+ }
50
+ });
51
+ const [callResults, localResults] = await Promise.all([
52
+ (0, multicall_1.tryAggregate)(provider, calls),
53
+ Promise.all(localIndexes.map(async (i) => {
54
+ try {
55
+ return await checks[i].local();
56
+ }
57
+ catch (err) {
58
+ return { ok: false, detail: err?.message || String(err) };
59
+ }
60
+ })),
61
+ ]);
62
+ const results = new Array(checks.length);
63
+ callIndexes.forEach((checkIdx, i) => {
64
+ const check = checks[checkIdx];
65
+ const { success, returnData } = callResults[i];
66
+ try {
67
+ results[checkIdx] = check.interpret
68
+ ? check.interpret(returnData, success)
69
+ : { ok: success, detail: success ? undefined : 'call reverted' };
70
+ }
71
+ catch (err) {
72
+ results[checkIdx] = { ok: false, detail: `could not interpret result: ${err?.message || err}` };
73
+ }
74
+ });
75
+ localIndexes.forEach((checkIdx, i) => {
76
+ results[checkIdx] = localResults[i];
77
+ });
78
+ const failures = [];
79
+ checks.forEach((check, i) => {
80
+ const result = results[i];
81
+ if (result && !result.ok) {
82
+ failures.push({ label: check.label, detail: result.detail, suggestion: result.suggestion });
83
+ }
84
+ });
85
+ if (failures.length > 0) {
86
+ throw new errors_1.PreconditionError(`Pre-flight checks failed:\n${failures.map(formatFailure).join('\n')}`);
87
+ }
88
+ }
89
+ const DEFAULT_MIN_GAS_WEI = ethers_1.ethers.utils.parseEther('0.0001');
90
+ /**
91
+ * Wallet holds at least `minWei` (default 0.0001 ether) of the chain's
92
+ * native gas token, read via Multicall3.getEthBalance. Pass the resolved
93
+ * sponsorship config as `sponsored` to skip the check for sponsored wallets
94
+ * (the CLI resolves it from env via resolveSponsoredConfig).
95
+ */
96
+ function checkGasBalance(address, minWei, sponsored) {
97
+ const min = minWei ?? DEFAULT_MIN_GAS_WEI;
98
+ // Sponsored writes (EIP-7702 / ERC-4337) don't spend the EOA's own gas, so a
99
+ // zero balance is expected and fine — the whole point of the "members
100
+ // transact without ever holding gas" path. When sponsorship is configured,
101
+ // skip the balance requirement rather than forcing --no-preflight. (If the
102
+ // EOA isn't actually delegated, executeContractTx falls back to a direct tx
103
+ // whose own gas estimation surfaces the shortfall with a decoded error.)
104
+ if (sponsored !== undefined) {
105
+ return { label: 'gas balance', local: () => ({ ok: true }) };
106
+ }
107
+ return {
108
+ label: 'gas balance',
109
+ call: (0, multicall_1.getEthBalanceCall)(address),
110
+ interpret: (returnData, success) => {
111
+ if (!success || !returnData || returnData === '0x') {
112
+ return { ok: false, detail: 'could not read wallet balance', suggestion: 'check RPC connectivity' };
113
+ }
114
+ const balance = (0, multicall_1.decodeEthBalance)(returnData);
115
+ if (balance.gte(min))
116
+ return { ok: true };
117
+ return {
118
+ ok: false,
119
+ detail: `wallet holds ${(0, format_1.formatToken)(balance)} native gas token, below the ${(0, format_1.formatToken)(min)} minimum`,
120
+ suggestion: `fund ${address} with gas before retrying`,
121
+ };
122
+ },
123
+ };
124
+ }
125
+ const HATS_IFACE = new ethers_1.ethers.utils.Interface([
126
+ 'function balanceOf(address wearer, uint256 hatId) view returns (uint256 balance)',
127
+ ]);
128
+ /** Wearer holds the given hat (IHats balanceOf(wearer, hatId) > 0). */
129
+ function checkHasHat(hatsAddress, wearer, hatId) {
130
+ const id = ethers_1.ethers.BigNumber.from(hatId);
131
+ return {
132
+ label: 'role (hat)',
133
+ call: { to: hatsAddress, data: HATS_IFACE.encodeFunctionData('balanceOf', [wearer, id]) },
134
+ interpret: (returnData, success) => {
135
+ if (!success || !returnData || returnData === '0x') {
136
+ return { ok: false, detail: 'could not read hat balance', suggestion: 'verify the Hats contract address' };
137
+ }
138
+ const balance = HATS_IFACE.decodeFunctionResult('balanceOf', returnData)[0];
139
+ if (!balance.isZero())
140
+ return { ok: true };
141
+ return {
142
+ ok: false,
143
+ detail: `${wearer} does not wear hat ${id.toString()}`,
144
+ suggestion: 'list org roles with pop org roles, then request one via pop role apply',
145
+ };
146
+ },
147
+ };
148
+ }
149
+ let uarInterface = null;
150
+ function getUarInterface() {
151
+ if (!uarInterface) {
152
+ uarInterface = new ethers_1.ethers.utils.Interface((0, contracts_1.getAbi)('UniversalAccountRegistry'));
153
+ }
154
+ return uarInterface;
155
+ }
156
+ /** Username is unregistered (UAR getAddressOfUsername returns the zero address). */
157
+ function checkUsernameFree(registryAddr, username) {
158
+ return {
159
+ label: `username "${username}"`,
160
+ call: {
161
+ to: registryAddr,
162
+ data: getUarInterface().encodeFunctionData('getAddressOfUsername', [username]),
163
+ },
164
+ interpret: (returnData, success) => {
165
+ if (!success || !returnData || returnData === '0x') {
166
+ return { ok: false, detail: 'could not query the account registry', suggestion: 'verify the registry address' };
167
+ }
168
+ const holder = getUarInterface().decodeFunctionResult('getAddressOfUsername', returnData)[0];
169
+ if (holder === ethers_1.ethers.constants.AddressZero)
170
+ return { ok: true };
171
+ return {
172
+ ok: false,
173
+ detail: `already registered to ${holder}`,
174
+ suggestion: 'pick a different username',
175
+ };
176
+ },
177
+ };
178
+ }
179
+ /**
180
+ * Task is in one of `allowedStatuses` (authoritative on-chain read via
181
+ * TaskManager.getLensData). Options:
182
+ * - claimerMustBe: task.claimer must equal this address (case-insensitive)
183
+ * - allowExpiredTakeover: a CLAIMED task outside allowedStatuses still passes
184
+ * when its claim deadline has lapsed (deriveClaimState === 'expired-claimable')
185
+ * - now: injectable unix timestamp for deadline math (tests)
186
+ */
187
+ function checkTaskStatus(taskManagerAddr, taskId, allowedStatuses, opts) {
188
+ const id = ethers_1.ethers.BigNumber.from(taskId);
189
+ return {
190
+ label: `task ${id.toString()} status`,
191
+ call: {
192
+ to: taskManagerAddr,
193
+ data: (0, task_lens_1.encodeLensCall)(task_lens_1.STORAGE_KEYS.TASK_INFO, ethers_1.ethers.utils.defaultAbiCoder.encode(['uint256'], [id])),
194
+ },
195
+ interpret: (returnData, success) => {
196
+ if (!success || !returnData || returnData === '0x') {
197
+ return {
198
+ ok: false,
199
+ detail: 'could not read the task on-chain — it may not exist',
200
+ suggestion: 'check the task ID with pop task list',
201
+ };
202
+ }
203
+ let task;
204
+ try {
205
+ task = (0, task_lens_1.decodeTaskInfo)((0, task_lens_1.decodeLensResult)(returnData));
206
+ }
207
+ catch {
208
+ return { ok: false, detail: 'could not decode on-chain task data', suggestion: 'check the task ID' };
209
+ }
210
+ if (!allowedStatuses.includes(task.status)) {
211
+ if (opts?.allowExpiredTakeover && task.status === task_lens_1.TASK_STATUS.CLAIMED) {
212
+ if ((0, task_lens_1.deriveClaimState)(task, opts.now) === 'expired-claimable') {
213
+ return { ok: true, detail: `previous claim by ${task.claimer} expired — takeover allowed` };
214
+ }
215
+ return {
216
+ ok: false,
217
+ detail: `claimed by ${task.claimer} and the claim deadline has not passed`,
218
+ suggestion: 'wait for the claim to expire or pick another task',
219
+ };
220
+ }
221
+ return {
222
+ ok: false,
223
+ detail: `status is ${(0, task_lens_1.taskStatusName)(task.status)}, expected ${allowedStatuses.map(task_lens_1.taskStatusName).join(' or ')}`,
224
+ suggestion: 'inspect the task with pop task view',
225
+ };
226
+ }
227
+ if (opts?.claimerMustBe && task.claimer.toLowerCase() !== opts.claimerMustBe.toLowerCase()) {
228
+ return {
229
+ ok: false,
230
+ detail: `claimed by ${task.claimer}, not ${opts.claimerMustBe}`,
231
+ suggestion: 'only the current claimer can perform this action',
232
+ };
233
+ }
234
+ return { ok: true };
235
+ },
236
+ };
237
+ }
238
+ const VOTING_IFACE = new ethers_1.ethers.utils.Interface([
239
+ 'function proposalsCount() view returns (uint256 count)',
240
+ ]);
241
+ /**
242
+ * Proposal ID exists on the voting contract (proposalsCount() > id).
243
+ *
244
+ * Kept on RPC on purpose. Counting Proposal/DDVProposal rows in the subgraph
245
+ * would be semantically equivalent, but it saves nothing and costs something:
246
+ * this check's `call` rides inside the SAME Multicall3 batch as the gas-balance
247
+ * check that every write already performs, so dropping it removes zero RPC
248
+ * round-trips while adding a subgraph round-trip. Worse, the propose→cast
249
+ * sequence agents run means the proposal is frequently seconds old, and an
250
+ * un-indexed row would block a perfectly legitimate vote. Net: strictly worse.
251
+ */
252
+ function checkProposalActive(votingAddr, proposalId) {
253
+ return {
254
+ label: `proposal ${proposalId}`,
255
+ call: { to: votingAddr, data: VOTING_IFACE.encodeFunctionData('proposalsCount') },
256
+ interpret: (returnData, success) => {
257
+ if (!success || !returnData || returnData === '0x') {
258
+ return { ok: false, detail: 'could not read proposalsCount', suggestion: 'verify the voting contract address' };
259
+ }
260
+ const count = VOTING_IFACE.decodeFunctionResult('proposalsCount', returnData)[0];
261
+ if (count.gt(proposalId))
262
+ return { ok: true };
263
+ const detail = count.isZero()
264
+ ? `proposal ${proposalId} does not exist — no proposals have been created yet`
265
+ : `proposal ${proposalId} does not exist — proposal IDs run 0 to ${count.sub(1).toString()}`;
266
+ return { ok: false, detail, suggestion: 'list proposals with pop vote list' };
267
+ },
268
+ };
269
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Education reads — typed wrappers over the education subgraph documents.
3
+ *
4
+ * Ports the read layer of:
5
+ * - `pop education list` — src/commands/education/list.ts
6
+ * - `pop education update` — src/commands/education/update.ts (read-merge query)
7
+ * - `pop education remove` — src/commands/education/remove.ts (title lookup)
8
+ *
9
+ * NOTE: the chain stores only {answerHash, payout, exists} per module; title
10
+ * and contentHash are EVENT-ONLY, so the subgraph is the sole source for
11
+ * current metadata. A missing row can mean "not indexed yet", not
12
+ * "nonexistent" — existence is only provable on-chain via getModule
13
+ * (which reverts ModuleUnknown).
14
+ */
15
+ import type { GraphClient } from '../graph/client';
16
+ /** Indexed education-module metadata (from the pinned IPFS doc). */
17
+ export interface EducationModuleMetadataRow {
18
+ description: string | null;
19
+ link: string | null;
20
+ quiz: string[] | null;
21
+ /** JSON-serialized string[][] — parse before use. */
22
+ answersJson: string | null;
23
+ }
24
+ export interface EducationCompletionRow {
25
+ learner: string;
26
+ completedAt: string;
27
+ }
28
+ /** Raw subgraph EducationModule row (`pop education list` field set). */
29
+ export interface EducationModuleRow {
30
+ id: string;
31
+ moduleId: string;
32
+ title: string | null;
33
+ contentHash: string;
34
+ metadata: EducationModuleMetadataRow | null;
35
+ /** PT wei as a decimal string. */
36
+ payout: string;
37
+ status: string;
38
+ createdAt: string;
39
+ completions: EducationCompletionRow[];
40
+ }
41
+ /** Verbatim from src/commands/education/list.ts. */
42
+ export declare const FETCH_EDUCATION_DATA = "\n query FetchEducationData($orgId: Bytes!) {\n organization(id: $orgId) {\n id\n educationHub {\n id\n modules(first: 50) {\n id\n moduleId\n title\n contentHash\n metadata {\n description\n link\n quiz\n answersJson\n }\n payout\n status\n createdAt\n completions {\n learner\n completedAt\n }\n }\n }\n }\n }\n";
43
+ /**
44
+ * Same field set as list.ts (schema-safe); title/contentHash for the merge.
45
+ * Verbatim from src/commands/education/update.ts.
46
+ */
47
+ export declare const FETCH_MODULES_FOR_UPDATE = "\n query FetchModulesForUpdate($orgId: Bytes!) {\n organization(id: $orgId) {\n id\n educationHub {\n id\n modules(first: 100) {\n id\n moduleId\n title\n contentHash\n payout\n metadata {\n description\n link\n quiz\n answersJson\n }\n }\n }\n }\n }\n";
48
+ /**
49
+ * Best-effort title lookup so a destructive confirm can name the module.
50
+ * Verbatim from src/commands/education/remove.ts.
51
+ */
52
+ export declare const FETCH_MODULE_TITLE = "\n query FetchModuleTitle($orgId: Bytes!) {\n organization(id: $orgId) {\n educationHub {\n modules(first: 100) {\n moduleId\n title\n }\n }\n }\n }\n";
53
+ /**
54
+ * Port of the `pop education list` read — src/commands/education/list.ts.
55
+ * Returns the raw module rows ([] when the org has no education hub or no
56
+ * modules indexed yet).
57
+ */
58
+ export declare function listEducationModules(client: GraphClient, orgId: string, chainId?: number): Promise<EducationModuleRow[]>;
59
+ /** Row shape FETCH_MODULES_FOR_UPDATE returns (no status/completions). */
60
+ export interface EducationModuleForUpdateRow {
61
+ id: string;
62
+ moduleId: string;
63
+ title: string | null;
64
+ contentHash: string;
65
+ payout: string;
66
+ metadata: EducationModuleMetadataRow | null;
67
+ }
68
+ /**
69
+ * The indexed row `pop education update` merges from — null when the module
70
+ * is not indexed (subgraph lag) OR does not exist; only on-chain getModule
71
+ * distinguishes the two.
72
+ */
73
+ export declare function getModuleForUpdate(client: GraphClient, orgId: string, moduleId: string | number, chainId?: number): Promise<EducationModuleForUpdateRow | null>;
74
+ /**
75
+ * Best-effort module title (`pop education remove` confirm summary).
76
+ * Returns undefined when not indexed; throws on transport failure — callers
77
+ * that only want a label should catch.
78
+ */
79
+ export declare function getModuleTitle(client: GraphClient, orgId: string, moduleId: string | number, chainId?: number): Promise<string | undefined>;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /**
3
+ * Education reads — typed wrappers over the education subgraph documents.
4
+ *
5
+ * Ports the read layer of:
6
+ * - `pop education list` — src/commands/education/list.ts
7
+ * - `pop education update` — src/commands/education/update.ts (read-merge query)
8
+ * - `pop education remove` — src/commands/education/remove.ts (title lookup)
9
+ *
10
+ * NOTE: the chain stores only {answerHash, payout, exists} per module; title
11
+ * and contentHash are EVENT-ONLY, so the subgraph is the sole source for
12
+ * current metadata. A missing row can mean "not indexed yet", not
13
+ * "nonexistent" — existence is only provable on-chain via getModule
14
+ * (which reverts ModuleUnknown).
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FETCH_MODULE_TITLE = exports.FETCH_MODULES_FOR_UPDATE = exports.FETCH_EDUCATION_DATA = void 0;
18
+ exports.listEducationModules = listEducationModules;
19
+ exports.getModuleForUpdate = getModuleForUpdate;
20
+ exports.getModuleTitle = getModuleTitle;
21
+ /** Verbatim from src/commands/education/list.ts. */
22
+ exports.FETCH_EDUCATION_DATA = `
23
+ query FetchEducationData($orgId: Bytes!) {
24
+ organization(id: $orgId) {
25
+ id
26
+ educationHub {
27
+ id
28
+ modules(first: 50) {
29
+ id
30
+ moduleId
31
+ title
32
+ contentHash
33
+ metadata {
34
+ description
35
+ link
36
+ quiz
37
+ answersJson
38
+ }
39
+ payout
40
+ status
41
+ createdAt
42
+ completions {
43
+ learner
44
+ completedAt
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ `;
51
+ /**
52
+ * Same field set as list.ts (schema-safe); title/contentHash for the merge.
53
+ * Verbatim from src/commands/education/update.ts.
54
+ */
55
+ exports.FETCH_MODULES_FOR_UPDATE = `
56
+ query FetchModulesForUpdate($orgId: Bytes!) {
57
+ organization(id: $orgId) {
58
+ id
59
+ educationHub {
60
+ id
61
+ modules(first: 100) {
62
+ id
63
+ moduleId
64
+ title
65
+ contentHash
66
+ payout
67
+ metadata {
68
+ description
69
+ link
70
+ quiz
71
+ answersJson
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ `;
78
+ /**
79
+ * Best-effort title lookup so a destructive confirm can name the module.
80
+ * Verbatim from src/commands/education/remove.ts.
81
+ */
82
+ exports.FETCH_MODULE_TITLE = `
83
+ query FetchModuleTitle($orgId: Bytes!) {
84
+ organization(id: $orgId) {
85
+ educationHub {
86
+ modules(first: 100) {
87
+ moduleId
88
+ title
89
+ }
90
+ }
91
+ }
92
+ }
93
+ `;
94
+ /**
95
+ * Port of the `pop education list` read — src/commands/education/list.ts.
96
+ * Returns the raw module rows ([] when the org has no education hub or no
97
+ * modules indexed yet).
98
+ */
99
+ async function listEducationModules(client, orgId, chainId) {
100
+ const result = await client.query(exports.FETCH_EDUCATION_DATA, { orgId }, chainId);
101
+ return result.organization?.educationHub?.modules || [];
102
+ }
103
+ /**
104
+ * The indexed row `pop education update` merges from — null when the module
105
+ * is not indexed (subgraph lag) OR does not exist; only on-chain getModule
106
+ * distinguishes the two.
107
+ */
108
+ async function getModuleForUpdate(client, orgId, moduleId, chainId) {
109
+ const result = await client.query(exports.FETCH_MODULES_FOR_UPDATE, { orgId }, chainId);
110
+ const modules = result.organization?.educationHub?.modules || [];
111
+ return modules.find((m) => String(m.moduleId) === String(moduleId)) || null;
112
+ }
113
+ /**
114
+ * Best-effort module title (`pop education remove` confirm summary).
115
+ * Returns undefined when not indexed; throws on transport failure — callers
116
+ * that only want a label should catch.
117
+ */
118
+ async function getModuleTitle(client, orgId, moduleId, chainId) {
119
+ const result = await client.query(exports.FETCH_MODULE_TITLE, { orgId }, chainId);
120
+ const modules = result.organization?.educationHub?.modules || [];
121
+ return modules.find((m) => String(m.moduleId) === String(moduleId))?.title ?? undefined;
122
+ }