@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.
- package/README.md +201 -0
- package/dist/abis/DirectDemocracyVotingNew.d.ts +749 -0
- package/dist/abis/DirectDemocracyVotingNew.js +961 -0
- package/dist/abis/ERC20.d.ts +90 -0
- package/dist/abis/ERC20.js +125 -0
- package/dist/abis/EducationHubNew.d.ts +590 -0
- package/dist/abis/EducationHubNew.js +762 -0
- package/dist/abis/EligibilityModuleNew.d.ts +1615 -0
- package/dist/abis/EligibilityModuleNew.js +2082 -0
- package/dist/abis/Executor.d.ts +619 -0
- package/dist/abis/Executor.js +796 -0
- package/dist/abis/HybridVotingNew.d.ts +898 -0
- package/dist/abis/HybridVotingNew.js +1149 -0
- package/dist/abis/ImplementationRegistry.d.ts +270 -0
- package/dist/abis/ImplementationRegistry.js +355 -0
- package/dist/abis/OrgDeployerNew.d.ts +1275 -0
- package/dist/abis/OrgDeployerNew.js +1630 -0
- package/dist/abis/OrgRegistry.d.ts +670 -0
- package/dist/abis/OrgRegistry.js +868 -0
- package/dist/abis/ParticipationToken.d.ts +1086 -0
- package/dist/abis/ParticipationToken.js +1415 -0
- package/dist/abis/PasskeyAccount.d.ts +788 -0
- package/dist/abis/PasskeyAccount.js +1019 -0
- package/dist/abis/PasskeyAccountFactory.d.ts +344 -0
- package/dist/abis/PasskeyAccountFactory.js +449 -0
- package/dist/abis/PaymasterHub.d.ts +1603 -0
- package/dist/abis/PaymasterHub.js +2048 -0
- package/dist/abis/PaymentManager.d.ts +520 -0
- package/dist/abis/PaymentManager.js +671 -0
- package/dist/abis/PoaManager.d.ts +344 -0
- package/dist/abis/PoaManager.js +449 -0
- package/dist/abis/QuickJoinNew.d.ts +855 -0
- package/dist/abis/QuickJoinNew.js +1098 -0
- package/dist/abis/TaskManagerNew.d.ts +1236 -0
- package/dist/abis/TaskManagerNew.js +1573 -0
- package/dist/abis/ToggleModule.d.ts +193 -0
- package/dist/abis/ToggleModule.js +255 -0
- package/dist/abis/UniversalAccountRegistry.d.ts +577 -0
- package/dist/abis/UniversalAccountRegistry.js +750 -0
- package/dist/abis/ZkEmailInvites.d.ts +823 -0
- package/dist/abis/ZkEmailInvites.js +1060 -0
- package/dist/abis/external/AaveGovernanceV2.d.ts +128 -0
- package/dist/abis/external/AaveGovernanceV2.js +177 -0
- package/dist/abis/external/AaveGovernanceV3.d.ts +160 -0
- package/dist/abis/external/AaveGovernanceV3.js +220 -0
- package/dist/abis/external/AragonVoting.d.ts +107 -0
- package/dist/abis/external/AragonVoting.js +150 -0
- package/dist/abis/external/CurveGaugeController.d.ts +95 -0
- package/dist/abis/external/CurveGaugeController.js +130 -0
- package/dist/abis/external/CurveVotingEscrow.d.ts +86 -0
- package/dist/abis/external/CurveVotingEscrow.js +116 -0
- package/dist/abis/external/GovernorAlpha.d.ts +172 -0
- package/dist/abis/external/GovernorAlpha.js +228 -0
- package/dist/abis/external/MakerDAOChief.d.ts +92 -0
- package/dist/abis/external/MakerDAOChief.js +129 -0
- package/dist/abis/external/OZGovernor.d.ts +227 -0
- package/dist/abis/external/OZGovernor.js +317 -0
- package/dist/abis/external/SolidlyVotingEscrow.d.ts +196 -0
- package/dist/abis/external/SolidlyVotingEscrow.js +260 -0
- package/dist/abis/index.d.ts +62 -0
- package/dist/abis/index.js +98 -0
- package/dist/chains.d.ts +150 -0
- package/dist/chains.js +320 -0
- package/dist/context.d.ts +48 -0
- package/dist/context.js +38 -0
- package/dist/contracts.d.ts +24 -0
- package/dist/contracts.js +41 -0
- package/dist/encoding.d.ts +58 -0
- package/dist/encoding.js +243 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.js +4 -0
- package/dist/error-catalog.d.ts +41 -0
- package/dist/error-catalog.js +941 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +60 -0
- package/dist/execute/ethers.d.ts +93 -0
- package/dist/execute/ethers.js +323 -0
- package/dist/execute/index.d.ts +7 -0
- package/dist/execute/index.js +23 -0
- package/dist/execute/sponsored.d.ts +68 -0
- package/dist/execute/sponsored.js +277 -0
- package/dist/exit-codes.d.ts +18 -0
- package/dist/exit-codes.js +21 -0
- package/dist/format.d.ts +21 -0
- package/dist/format.js +89 -0
- package/dist/graph/client.d.ts +298 -0
- package/dist/graph/client.js +702 -0
- package/dist/graph/documents/activity.d.ts +9 -0
- package/dist/graph/documents/activity.js +164 -0
- package/dist/graph/documents/beacons.d.ts +172 -0
- package/dist/graph/documents/beacons.js +345 -0
- package/dist/graph/documents/index.d.ts +21 -0
- package/dist/graph/documents/index.js +57 -0
- package/dist/graph/documents/infrastructure.d.ts +30 -0
- package/dist/graph/documents/infrastructure.js +34 -0
- package/dist/graph/documents/org.d.ts +15 -0
- package/dist/graph/documents/org.js +249 -0
- package/dist/graph/documents/paymaster.d.ts +95 -0
- package/dist/graph/documents/paymaster.js +101 -0
- package/dist/graph/documents/role.d.ts +6 -0
- package/dist/graph/documents/role.js +41 -0
- package/dist/graph/documents/roles.d.ts +50 -0
- package/dist/graph/documents/roles.js +112 -0
- package/dist/graph/documents/task.d.ts +84 -0
- package/dist/graph/documents/task.js +236 -0
- package/dist/graph/documents/token.d.ts +13 -0
- package/dist/graph/documents/token.js +126 -0
- package/dist/graph/documents/treasury.d.ts +39 -0
- package/dist/graph/documents/treasury.js +136 -0
- package/dist/graph/documents/user.d.ts +120 -0
- package/dist/graph/documents/user.js +302 -0
- package/dist/graph/documents/voting-classes.d.ts +69 -0
- package/dist/graph/documents/voting-classes.js +149 -0
- package/dist/graph/documents/voting.d.ts +28 -0
- package/dist/graph/documents/voting.js +163 -0
- package/dist/graph/documents/vouch.d.ts +79 -0
- package/dist/graph/documents/vouch.js +171 -0
- package/dist/graph/documents/zkemail.d.ts +63 -0
- package/dist/graph/documents/zkemail.js +188 -0
- package/dist/graph/index.d.ts +2 -0
- package/dist/graph/index.js +41 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +92 -0
- package/dist/ipfs.d.ts +39 -0
- package/dist/ipfs.js +214 -0
- package/dist/label-aliases.d.ts +31 -0
- package/dist/label-aliases.js +70 -0
- package/dist/metadata/education.d.ts +30 -0
- package/dist/metadata/education.js +31 -0
- package/dist/metadata/index.d.ts +12 -0
- package/dist/metadata/index.js +48 -0
- package/dist/metadata/org.d.ts +92 -0
- package/dist/metadata/org.js +93 -0
- package/dist/metadata/proposal.d.ts +44 -0
- package/dist/metadata/proposal.js +45 -0
- package/dist/metadata/role.d.ts +38 -0
- package/dist/metadata/role.js +35 -0
- package/dist/metadata/task.d.ts +108 -0
- package/dist/metadata/task.js +81 -0
- package/dist/metadata/token.d.ts +32 -0
- package/dist/metadata/token.js +36 -0
- package/dist/metadata/user.d.ts +63 -0
- package/dist/metadata/user.js +70 -0
- package/dist/multicall.d.ts +32 -0
- package/dist/multicall.js +96 -0
- package/dist/payout.d.ts +59 -0
- package/dist/payout.js +96 -0
- package/dist/perms.d.ts +47 -0
- package/dist/perms.js +120 -0
- package/dist/preflight.d.ts +72 -0
- package/dist/preflight.js +269 -0
- package/dist/reads/education.d.ts +79 -0
- package/dist/reads/education.js +122 -0
- package/dist/reads/eligibility.d.ts +377 -0
- package/dist/reads/eligibility.js +687 -0
- package/dist/reads/index.d.ts +16 -0
- package/dist/reads/index.js +55 -0
- package/dist/reads/org.d.ts +258 -0
- package/dist/reads/org.js +220 -0
- package/dist/reads/paymaster.d.ts +147 -0
- package/dist/reads/paymaster.js +235 -0
- package/dist/reads/project.d.ts +33 -0
- package/dist/reads/project.js +62 -0
- package/dist/reads/resolve.d.ts +34 -0
- package/dist/reads/resolve.js +64 -0
- package/dist/reads/task.d.ts +286 -0
- package/dist/reads/task.js +280 -0
- package/dist/reads/token.d.ts +94 -0
- package/dist/reads/token.js +75 -0
- package/dist/reads/treasury.d.ts +222 -0
- package/dist/reads/treasury.js +241 -0
- package/dist/reads/user.d.ts +229 -0
- package/dist/reads/user.js +155 -0
- package/dist/reads/vote.d.ts +296 -0
- package/dist/reads/vote.js +392 -0
- package/dist/reads/zkemail.d.ts +117 -0
- package/dist/reads/zkemail.js +217 -0
- package/dist/similarity.d.ts +36 -0
- package/dist/similarity.js +67 -0
- package/dist/sponsorship-config.d.ts +47 -0
- package/dist/sponsorship-config.js +41 -0
- package/dist/stats.d.ts +11 -0
- package/dist/stats.js +27 -0
- package/dist/task-lens.d.ts +113 -0
- package/dist/task-lens.js +210 -0
- package/dist/tx/education.d.ts +169 -0
- package/dist/tx/education.js +344 -0
- package/dist/tx/eligibility.d.ts +511 -0
- package/dist/tx/eligibility.js +818 -0
- package/dist/tx/governance.d.ts +85 -0
- package/dist/tx/governance.js +87 -0
- package/dist/tx/index.d.ts +17 -0
- package/dist/tx/index.js +56 -0
- package/dist/tx/intent.d.ts +67 -0
- package/dist/tx/intent.js +48 -0
- package/dist/tx/org.d.ts +321 -0
- package/dist/tx/org.js +544 -0
- package/dist/tx/paymaster.d.ts +140 -0
- package/dist/tx/paymaster.js +251 -0
- package/dist/tx/project.d.ts +146 -0
- package/dist/tx/project.js +217 -0
- package/dist/tx/task.d.ts +480 -0
- package/dist/tx/task.js +1149 -0
- package/dist/tx/token.d.ts +96 -0
- package/dist/tx/token.js +165 -0
- package/dist/tx/treasury.d.ts +474 -0
- package/dist/tx/treasury.js +911 -0
- package/dist/tx/user.d.ts +177 -0
- package/dist/tx/user.js +292 -0
- package/dist/tx/vote.d.ts +332 -0
- package/dist/tx/vote.js +762 -0
- package/dist/tx/zkemail.d.ts +90 -0
- package/dist/tx/zkemail.js +210 -0
- package/dist/validation.d.ts +8 -0
- package/dist/validation.js +39 -0
- package/dist/version.d.ts +142 -0
- package/dist/version.js +257 -0
- package/dist/zkemail.d.ts +161 -0
- package/dist/zkemail.js +299 -0
- 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
|
+
}
|