@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,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZK Email transaction builders — port of `pop zkemail propose-allowlist`
|
|
3
|
+
* (src/commands/zkemail/propose-allowlist.ts).
|
|
4
|
+
*
|
|
5
|
+
* `ZkEmailInvites.setActiveAllowlist(bytes32 root, bytes32 cidDigest)` is
|
|
6
|
+
* onlyExecutor, so it cannot be sent directly by a member wallet — it goes
|
|
7
|
+
* through a HybridVoting proposal whose option 0 executes the call from the
|
|
8
|
+
* Executor. Same shape as the treasury governance wraps (./governance).
|
|
9
|
+
*
|
|
10
|
+
* Allowlist BUILDING is not here: the browser-pure ../zkemail module
|
|
11
|
+
* (buildAllowlist, treeFromDoc, assertRootMatches, summarize, domainHash,
|
|
12
|
+
* emailHash) already owns the Poseidon-hashed merkle construction —
|
|
13
|
+
* `pop zkemail build-allowlist` is that module plus file I/O and an optional
|
|
14
|
+
* pin, so hosts compose buildAllowlist + pinJson + ipfsCidToBytes32 directly.
|
|
15
|
+
*/
|
|
16
|
+
import { ethers } from 'ethers';
|
|
17
|
+
import type { TxIntent } from './intent';
|
|
18
|
+
import type { PopContext } from '../context';
|
|
19
|
+
/**
|
|
20
|
+
* Normalize --root to bytes32. A zero root would make the module dormant and
|
|
21
|
+
* revert every claim, so it is rejected. Port of the guard in
|
|
22
|
+
* src/commands/zkemail/propose-allowlist.ts.
|
|
23
|
+
*/
|
|
24
|
+
export declare function normalizeAllowlistRoot(root: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Normalize --cid to the bytes32 digest the contract stores + the CIDv0 form.
|
|
27
|
+
*
|
|
28
|
+
* ipfsCidToBytes32 falls back to keccak256(string) for anything that is
|
|
29
|
+
* neither a Qm… CIDv0 nor a 0x-bytes32, which yields a non-zero digest that
|
|
30
|
+
* sails past a HashZero check and commits a dead pointer on-chain. Accept only
|
|
31
|
+
* the two forms the contract can round-trip. Port of the guard in
|
|
32
|
+
* src/commands/zkemail/propose-allowlist.ts.
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizeAllowlistCid(cid: string): {
|
|
35
|
+
cidDigest: string;
|
|
36
|
+
cidV0: string | null;
|
|
37
|
+
};
|
|
38
|
+
export interface SetAllowlistProposalArgs {
|
|
39
|
+
hybridVotingAddress: string;
|
|
40
|
+
zkEmailInvitesAddress: string;
|
|
41
|
+
/** Normalized bytes32 (see normalizeAllowlistRoot). */
|
|
42
|
+
root: string;
|
|
43
|
+
/** bytes32 multihash digest (see normalizeAllowlistCid). */
|
|
44
|
+
cidDigest: string;
|
|
45
|
+
descriptionHash: string;
|
|
46
|
+
durationMinutes: ethers.BigNumberish;
|
|
47
|
+
orgId?: string;
|
|
48
|
+
summary?: Record<string, unknown>;
|
|
49
|
+
ipfs?: {
|
|
50
|
+
cid: string;
|
|
51
|
+
metadata: unknown;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Port of `pop zkemail propose-allowlist` —
|
|
56
|
+
* src/commands/zkemail/propose-allowlist.ts. HybridVoting.createProposal
|
|
57
|
+
* wrapping [[zkEmailInvites, 0, setActiveAllowlist(root, cidDigest)], []]
|
|
58
|
+
* (setActiveAllowlist is onlyExecutor).
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildSetAllowlistProposal(a: SetAllowlistProposalArgs): TxIntent;
|
|
61
|
+
export interface ProposeAllowlistParams {
|
|
62
|
+
org: string;
|
|
63
|
+
/** Merkle root from build-allowlist (../zkemail buildAllowlist). */
|
|
64
|
+
root: string;
|
|
65
|
+
/** IPFS CIDv0 (Qm…) of the pinned allowlist file, or its 0x-bytes32 digest. */
|
|
66
|
+
cid: string;
|
|
67
|
+
/** Voting window in minutes (CLI default 1440 = 24h). */
|
|
68
|
+
durationMinutes?: number;
|
|
69
|
+
/** Skip fetching the pinned file to confirm it reproduces --root (not recommended). */
|
|
70
|
+
skipVerify?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolved port of `pop zkemail propose-allowlist` —
|
|
74
|
+
* src/commands/zkemail/propose-allowlist.ts.
|
|
75
|
+
*
|
|
76
|
+
* Guards replicated from the CLI:
|
|
77
|
+
* - root != HashZero; --cid restricted to CIDv0 or 0x-bytes32 (the keccak
|
|
78
|
+
* fallback of ipfsCidToBytes32 would commit a dead pointer)
|
|
79
|
+
* - unless skipVerify, the pinned file is fetched and assertRootMatches
|
|
80
|
+
* confirms it reproduces the root being committed (a mismatch costs a full
|
|
81
|
+
* governance cycle to undo)
|
|
82
|
+
* - the CURRENT root (display + metadata) comes from the indexed activeRoot,
|
|
83
|
+
* identity-guarded, with merkleRoot() as the on-chain fallback
|
|
84
|
+
* - module.executor() vs the org's Executor mismatch fails fast — the
|
|
85
|
+
* Unauthorized revert would otherwise only surface AFTER the full vote.
|
|
86
|
+
* `executor` deliberately stays on-chain (it gates that check, the one
|
|
87
|
+
* place subgraph lag is unacceptable in this command), so the check runs
|
|
88
|
+
* only when ctx.provider is supplied.
|
|
89
|
+
*/
|
|
90
|
+
export declare function proposeAllowlistIntent(ctx: PopContext, params: ProposeAllowlistParams): Promise<TxIntent>;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ZK Email transaction builders — port of `pop zkemail propose-allowlist`
|
|
4
|
+
* (src/commands/zkemail/propose-allowlist.ts).
|
|
5
|
+
*
|
|
6
|
+
* `ZkEmailInvites.setActiveAllowlist(bytes32 root, bytes32 cidDigest)` is
|
|
7
|
+
* onlyExecutor, so it cannot be sent directly by a member wallet — it goes
|
|
8
|
+
* through a HybridVoting proposal whose option 0 executes the call from the
|
|
9
|
+
* Executor. Same shape as the treasury governance wraps (./governance).
|
|
10
|
+
*
|
|
11
|
+
* Allowlist BUILDING is not here: the browser-pure ../zkemail module
|
|
12
|
+
* (buildAllowlist, treeFromDoc, assertRootMatches, summarize, domainHash,
|
|
13
|
+
* emailHash) already owns the Poseidon-hashed merkle construction —
|
|
14
|
+
* `pop zkemail build-allowlist` is that module plus file I/O and an optional
|
|
15
|
+
* pin, so hosts compose buildAllowlist + pinJson + ipfsCidToBytes32 directly.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.normalizeAllowlistRoot = normalizeAllowlistRoot;
|
|
19
|
+
exports.normalizeAllowlistCid = normalizeAllowlistCid;
|
|
20
|
+
exports.buildSetAllowlistProposal = buildSetAllowlistProposal;
|
|
21
|
+
exports.proposeAllowlistIntent = proposeAllowlistIntent;
|
|
22
|
+
const ethers_1 = require("ethers");
|
|
23
|
+
const contracts_1 = require("../contracts");
|
|
24
|
+
const governance_1 = require("./governance");
|
|
25
|
+
const resolve_1 = require("../reads/resolve");
|
|
26
|
+
const zkemail_1 = require("../reads/zkemail");
|
|
27
|
+
const proposal_1 = require("../metadata/proposal");
|
|
28
|
+
const ipfs_1 = require("../ipfs");
|
|
29
|
+
const encoding_1 = require("../encoding");
|
|
30
|
+
const zkemail_2 = require("../zkemail");
|
|
31
|
+
const errors_1 = require("../errors");
|
|
32
|
+
const exit_codes_1 = require("../exit-codes");
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Pure input normalization (the CLI's guards, verbatim messages)
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
/**
|
|
37
|
+
* Normalize --root to bytes32. A zero root would make the module dormant and
|
|
38
|
+
* revert every claim, so it is rejected. Port of the guard in
|
|
39
|
+
* src/commands/zkemail/propose-allowlist.ts.
|
|
40
|
+
*/
|
|
41
|
+
function normalizeAllowlistRoot(root) {
|
|
42
|
+
let normalized;
|
|
43
|
+
try {
|
|
44
|
+
normalized = ethers_1.ethers.utils.hexZeroPad(ethers_1.ethers.utils.hexlify(root), 32);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new errors_1.CliError(`--root "${root}" is not a valid bytes32.`, exit_codes_1.EXIT.USAGE);
|
|
48
|
+
}
|
|
49
|
+
if (normalized === ethers_1.ethers.constants.HashZero) {
|
|
50
|
+
throw new errors_1.CliError('A zero root would make the module dormant and revert every claim.', exit_codes_1.EXIT.USAGE, 'Pass the root printed by pop zkemail build-allowlist.');
|
|
51
|
+
}
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Normalize --cid to the bytes32 digest the contract stores + the CIDv0 form.
|
|
56
|
+
*
|
|
57
|
+
* ipfsCidToBytes32 falls back to keccak256(string) for anything that is
|
|
58
|
+
* neither a Qm… CIDv0 nor a 0x-bytes32, which yields a non-zero digest that
|
|
59
|
+
* sails past a HashZero check and commits a dead pointer on-chain. Accept only
|
|
60
|
+
* the two forms the contract can round-trip. Port of the guard in
|
|
61
|
+
* src/commands/zkemail/propose-allowlist.ts.
|
|
62
|
+
*/
|
|
63
|
+
function normalizeAllowlistCid(cid) {
|
|
64
|
+
const isCidV0 = /^Qm[1-9A-HJ-NP-Za-km-z]{44}$/.test(cid);
|
|
65
|
+
const isBytes32 = /^0x[0-9a-fA-F]{64}$/.test(cid);
|
|
66
|
+
if (!isCidV0 && !isBytes32) {
|
|
67
|
+
throw new errors_1.CliError(`--cid "${cid}" is not a CIDv0 (Qm…) or a 0x bytes32 digest.`, exit_codes_1.EXIT.USAGE, cid.startsWith('baf')
|
|
68
|
+
? 'That looks like a CIDv1. The contract stores a raw 32-byte multihash digest, so the '
|
|
69
|
+
+ 'file must be pinned as CIDv0 — re-pin with --cid-version=0 (pop pins CIDv0 by default).'
|
|
70
|
+
: 'Pass the CID printed by pop zkemail build-allowlist --pin.');
|
|
71
|
+
}
|
|
72
|
+
const cidDigest = (0, encoding_1.ipfsCidToBytes32)(cid);
|
|
73
|
+
if (cidDigest === ethers_1.ethers.constants.HashZero) {
|
|
74
|
+
throw new errors_1.CliError(`--cid "${cid}" did not decode to a valid digest.`, exit_codes_1.EXIT.USAGE);
|
|
75
|
+
}
|
|
76
|
+
return { cidDigest, cidV0: isCidV0 ? cid : (0, encoding_1.bytes32ToIpfsCid)(cidDigest) };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Port of `pop zkemail propose-allowlist` —
|
|
80
|
+
* src/commands/zkemail/propose-allowlist.ts. HybridVoting.createProposal
|
|
81
|
+
* wrapping [[zkEmailInvites, 0, setActiveAllowlist(root, cidDigest)], []]
|
|
82
|
+
* (setActiveAllowlist is onlyExecutor).
|
|
83
|
+
*/
|
|
84
|
+
function buildSetAllowlistProposal(a) {
|
|
85
|
+
const iface = new ethers_1.ethers.utils.Interface((0, contracts_1.getAbi)('ZkEmailInvites'));
|
|
86
|
+
const call = iface.encodeFunctionData('setActiveAllowlist', [a.root, a.cidDigest]);
|
|
87
|
+
const calls = [
|
|
88
|
+
{ target: a.zkEmailInvitesAddress, value: ethers_1.ethers.BigNumber.from(0), calldata: call },
|
|
89
|
+
];
|
|
90
|
+
return (0, governance_1.buildGovernanceProposal)({
|
|
91
|
+
votingAddress: a.hybridVotingAddress,
|
|
92
|
+
votingAbiName: 'HybridVotingNew',
|
|
93
|
+
title: 'Set ZK Email invite allowlist',
|
|
94
|
+
descriptionHash: a.descriptionHash,
|
|
95
|
+
durationMinutes: a.durationMinutes,
|
|
96
|
+
numOptions: 2,
|
|
97
|
+
batches: [calls, []], // option 0 = set, option 1 = keep current
|
|
98
|
+
hatIds: [],
|
|
99
|
+
domain: 'zkemail',
|
|
100
|
+
action: 'propose-allowlist',
|
|
101
|
+
orgId: a.orgId,
|
|
102
|
+
summary: a.summary ?? { module: a.zkEmailInvitesAddress, newRoot: a.root },
|
|
103
|
+
ipfs: a.ipfs,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolved port of `pop zkemail propose-allowlist` —
|
|
108
|
+
* src/commands/zkemail/propose-allowlist.ts.
|
|
109
|
+
*
|
|
110
|
+
* Guards replicated from the CLI:
|
|
111
|
+
* - root != HashZero; --cid restricted to CIDv0 or 0x-bytes32 (the keccak
|
|
112
|
+
* fallback of ipfsCidToBytes32 would commit a dead pointer)
|
|
113
|
+
* - unless skipVerify, the pinned file is fetched and assertRootMatches
|
|
114
|
+
* confirms it reproduces the root being committed (a mismatch costs a full
|
|
115
|
+
* governance cycle to undo)
|
|
116
|
+
* - the CURRENT root (display + metadata) comes from the indexed activeRoot,
|
|
117
|
+
* identity-guarded, with merkleRoot() as the on-chain fallback
|
|
118
|
+
* - module.executor() vs the org's Executor mismatch fails fast — the
|
|
119
|
+
* Unauthorized revert would otherwise only surface AFTER the full vote.
|
|
120
|
+
* `executor` deliberately stays on-chain (it gates that check, the one
|
|
121
|
+
* place subgraph lag is unacceptable in this command), so the check runs
|
|
122
|
+
* only when ctx.provider is supplied.
|
|
123
|
+
*/
|
|
124
|
+
async function proposeAllowlistIntent(ctx, params) {
|
|
125
|
+
const durationMinutes = params.durationMinutes ?? 1440;
|
|
126
|
+
const modules = await (0, resolve_1.resolveOrgModules)(ctx.client, params.org, ctx.chainId);
|
|
127
|
+
const orgId = modules.orgId;
|
|
128
|
+
const moduleAddr = modules.zkEmailInvitesAddress;
|
|
129
|
+
if (!moduleAddr) {
|
|
130
|
+
throw new errors_1.CliError(`Org ${orgId} has no ZkEmailInvites module.`, exit_codes_1.EXIT.PRECONDITION, 'ZK Email invites are opt-in per org — the module is wired at deploy time via '
|
|
131
|
+
+ 'OrgDeployer.deployFullOrgWithZkEmail.');
|
|
132
|
+
}
|
|
133
|
+
const hybridVotingAddr = modules.hybridVotingAddress;
|
|
134
|
+
if (!hybridVotingAddr) {
|
|
135
|
+
throw new errors_1.CliError(`Org "${orgId}" has no HybridVoting module, so an executor-gated call cannot be proposed.`, exit_codes_1.EXIT.PRECONDITION);
|
|
136
|
+
}
|
|
137
|
+
// Normalize the root and CID.
|
|
138
|
+
const root = normalizeAllowlistRoot(params.root);
|
|
139
|
+
const { cidDigest, cidV0 } = normalizeAllowlistCid(params.cid);
|
|
140
|
+
// Verify the pinned file actually reproduces the root being committed.
|
|
141
|
+
let entrySummary = '(not verified)';
|
|
142
|
+
if (!params.skipVerify) {
|
|
143
|
+
const doc = await (0, ipfs_1.fetchJson)(cidV0 || params.cid, ctx.ipfs);
|
|
144
|
+
if (!doc) {
|
|
145
|
+
throw new errors_1.CliError(`Could not fetch allowlist ${cidV0 || params.cid} from IPFS.`, exit_codes_1.EXIT.INFRA, 'Pin the file before proposing, or pass --skip-verify if you are certain it is retrievable.');
|
|
146
|
+
}
|
|
147
|
+
// Mismatch here means --root and --cid disagree; committing them would cost a full
|
|
148
|
+
// governance cycle to undo, so fail before the proposal exists.
|
|
149
|
+
(0, zkemail_2.assertRootMatches)(doc, root, 'root you are proposing (--root)');
|
|
150
|
+
const s = (0, zkemail_2.summarize)(doc);
|
|
151
|
+
entrySummary = `${doc.entries?.length ?? 0} entries (${s.domains.length} domain, ${s.emails.length} specific)`;
|
|
152
|
+
}
|
|
153
|
+
// Read the current commit so the diff is visible in the metadata.
|
|
154
|
+
//
|
|
155
|
+
// The root is DISPLAY ONLY (the proposal description), so it comes from the
|
|
156
|
+
// indexed ZkEmailInvites.activeRoot. A module row that exists with a null
|
|
157
|
+
// activeRoot is dormant, exactly what merkleRoot() == 0 means on-chain; a
|
|
158
|
+
// MISSING row means "not indexed yet" and falls back to the contract so a
|
|
159
|
+
// brand-new module still renders its real root.
|
|
160
|
+
const indexedRoot = await (0, zkemail_1.fetchIndexedActiveRoot)(ctx.client, orgId, moduleAddr, ctx.chainId);
|
|
161
|
+
let currentRoot;
|
|
162
|
+
let moduleExecutor = null;
|
|
163
|
+
if (ctx.provider) {
|
|
164
|
+
const readModule = new ethers_1.ethers.Contract(moduleAddr, (0, contracts_1.getAbi)('ZkEmailInvites'), ctx.provider);
|
|
165
|
+
[currentRoot, moduleExecutor] = await Promise.all([
|
|
166
|
+
indexedRoot != null ? Promise.resolve(indexedRoot) : readModule.merkleRoot(),
|
|
167
|
+
readModule.executor(),
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
else if (indexedRoot != null) {
|
|
171
|
+
currentRoot = indexedRoot;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
throw new errors_1.CliError('The module is not indexed yet and no provider was supplied — cannot read the current allowlist root.', exit_codes_1.EXIT.INFRA, 'Pass a provider in the context, or retry once the subgraph has indexed the module.');
|
|
175
|
+
}
|
|
176
|
+
// setActiveAllowlist is onlyExecutor, and the proposal executes from the org's Executor.
|
|
177
|
+
// If the module answers to a different one the call reverts Unauthorized only AFTER the
|
|
178
|
+
// full voting window — check now, while it is still free.
|
|
179
|
+
const orgExecutor = modules.executorAddress;
|
|
180
|
+
if (moduleExecutor && orgExecutor && moduleExecutor.toLowerCase() !== orgExecutor.toLowerCase()) {
|
|
181
|
+
throw new errors_1.CliError(`The ZkEmailInvites module answers to executor ${moduleExecutor}, but this org's Executor `
|
|
182
|
+
+ `is ${orgExecutor}. A proposal from this org could never execute the call.`, exit_codes_1.EXIT.PRECONDITION, 'The module was deployed against a different executor, or the executor was rotated. '
|
|
183
|
+
+ 'Re-wire the module before proposing.');
|
|
184
|
+
}
|
|
185
|
+
const metadata = (0, proposal_1.buildProposalMetadata)({
|
|
186
|
+
description: `Set the ZK Email invite allowlist to root ${root} (IPFS ${cidV0 ?? params.cid}). `
|
|
187
|
+
+ `${entrySummary}. Replaces ${currentRoot === ethers_1.ethers.constants.HashZero ? 'no active allowlist' : currentRoot}. `
|
|
188
|
+
+ 'Anyone who can prove a DKIM-signed email from an allowlisted domain or address may then claim the listed role hats.',
|
|
189
|
+
optionNames: ['Set this allowlist', 'Keep the current allowlist'],
|
|
190
|
+
});
|
|
191
|
+
const metaCid = await (0, ipfs_1.pinJson)((0, proposal_1.serializeProposalMetadata)(metadata), ctx.ipfs);
|
|
192
|
+
const descriptionHash = (0, encoding_1.ipfsCidToBytes32)(metaCid);
|
|
193
|
+
return buildSetAllowlistProposal({
|
|
194
|
+
hybridVotingAddress: hybridVotingAddr,
|
|
195
|
+
zkEmailInvitesAddress: moduleAddr,
|
|
196
|
+
root,
|
|
197
|
+
cidDigest,
|
|
198
|
+
descriptionHash,
|
|
199
|
+
durationMinutes,
|
|
200
|
+
orgId,
|
|
201
|
+
summary: {
|
|
202
|
+
module: moduleAddr,
|
|
203
|
+
newRoot: root,
|
|
204
|
+
cid: cidV0 ?? params.cid,
|
|
205
|
+
currentRoot,
|
|
206
|
+
allowlist: entrySummary,
|
|
207
|
+
},
|
|
208
|
+
ipfs: { cid: metaCid, metadata },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Validation
|
|
3
|
+
*/
|
|
4
|
+
export declare function requireArg<T>(value: T | undefined, name: string): T;
|
|
5
|
+
export declare function requireAddress(address: string | undefined, name: string): string;
|
|
6
|
+
export declare function requirePositiveNumber(value: number | string, name: string): number;
|
|
7
|
+
/** Username: 3-32 chars, alphanumeric + underscores only (matches contract/frontend rules) */
|
|
8
|
+
export declare function requireValidUsername(username: string): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Input Validation
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.requireArg = requireArg;
|
|
7
|
+
exports.requireAddress = requireAddress;
|
|
8
|
+
exports.requirePositiveNumber = requirePositiveNumber;
|
|
9
|
+
exports.requireValidUsername = requireValidUsername;
|
|
10
|
+
const ethers_1 = require("ethers");
|
|
11
|
+
function requireArg(value, name) {
|
|
12
|
+
if (value === undefined || value === null || value === '') {
|
|
13
|
+
throw new Error(`Missing required argument: --${name}`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function requireAddress(address, name) {
|
|
18
|
+
if (!address || !ethers_1.ethers.utils.isAddress(address)) {
|
|
19
|
+
throw new Error(`Invalid address for --${name}: ${address}`);
|
|
20
|
+
}
|
|
21
|
+
return ethers_1.ethers.utils.getAddress(address);
|
|
22
|
+
}
|
|
23
|
+
function requirePositiveNumber(value, name) {
|
|
24
|
+
const num = typeof value === 'string' ? parseFloat(value) : value;
|
|
25
|
+
if (isNaN(num) || num <= 0 || !isFinite(num)) {
|
|
26
|
+
throw new Error(`--${name} must be a positive finite number, got: ${value}`);
|
|
27
|
+
}
|
|
28
|
+
return num;
|
|
29
|
+
}
|
|
30
|
+
/** Username: 3-32 chars, alphanumeric + underscores only (matches contract/frontend rules) */
|
|
31
|
+
function requireValidUsername(username) {
|
|
32
|
+
if (!username || username.length < 3 || username.length > 32) {
|
|
33
|
+
throw new Error('Username must be 3-32 characters');
|
|
34
|
+
}
|
|
35
|
+
if (!/^[a-zA-Z0-9_]+$/.test(username)) {
|
|
36
|
+
throw new Error('Username can only contain letters, numbers, and underscores');
|
|
37
|
+
}
|
|
38
|
+
return username;
|
|
39
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation Version Detection
|
|
3
|
+
* Resolves a proxy's implementation (EIP-1967 beacon / implementation slots,
|
|
4
|
+
* SwitchableBeacon.implementation()) and probes the implementation bytecode
|
|
5
|
+
* for feature selectors so commands can degrade gracefully on orgs whose
|
|
6
|
+
* TaskManager is pinned to an older version.
|
|
7
|
+
*
|
|
8
|
+
* Verified against contracts repo origin/main:
|
|
9
|
+
* - src/SwitchableBeacon.sol — `implementation()` (IBeacon accessor)
|
|
10
|
+
* - src/TaskManager.sol — createTask (9-arg v6), createTasksBatch(bytes32,
|
|
11
|
+
* CreateTaskInput[]), updateTaskMetadata, setFolders signatures and the
|
|
12
|
+
* CreateTaskInput struct field order.
|
|
13
|
+
* - src/TaskManager.sol v7 (contracts PR #187) — unclaimTask(uint256).
|
|
14
|
+
*
|
|
15
|
+
* RPC COST (2026-07): this module is on the hot path of task
|
|
16
|
+
* list/create/create-batch/update/edit-meta/folders, and each call used to cost
|
|
17
|
+
* eth_getStorageAt + eth_call(implementation) + eth_getCode — the last of which
|
|
18
|
+
* ships ~20 KB of hex. Both halves now have a cheaper first choice:
|
|
19
|
+
*
|
|
20
|
+
* 1. Beacon address — the subgraph knows it (RegisteredContract.beacon).
|
|
21
|
+
* Requires the caller to pass `orgId` AND a `client` (the injected
|
|
22
|
+
* GraphClient); without either the EIP-1967 slot walk runs exactly as
|
|
23
|
+
* before. The implementation is still fetched FROM the beacon over RPC,
|
|
24
|
+
* because this function gates writes (see
|
|
25
|
+
* beaconImplementationViaSubgraph).
|
|
26
|
+
* 2. Feature set — memoized per implementation ADDRESS (see
|
|
27
|
+
* KNOWN_TM_IMPLEMENTATION_FEATURES). Deployed bytecode at an address is
|
|
28
|
+
* immutable, so this is a cache of a fact, not a guess about a version
|
|
29
|
+
* string. An unrecognised address falls back to the bytecode scan, so the
|
|
30
|
+
* table can only ever remove work — never invent a feature.
|
|
31
|
+
*/
|
|
32
|
+
import { ethers } from 'ethers';
|
|
33
|
+
import type { GraphClient } from './graph/client';
|
|
34
|
+
import type { OrgBeaconSnapshot } from './graph/documents/beacons';
|
|
35
|
+
/** EIP-1967 beacon slot: keccak256('eip1967.proxy.beacon') - 1 */
|
|
36
|
+
export declare const EIP1967_BEACON_SLOT = "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50";
|
|
37
|
+
/** EIP-1967 implementation slot: keccak256('eip1967.proxy.implementation') - 1 */
|
|
38
|
+
export declare const EIP1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
39
|
+
export interface TaskManagerFeatures {
|
|
40
|
+
/** v6 createTask with absoluteDeadline + completionWindow params */
|
|
41
|
+
deadlines: boolean;
|
|
42
|
+
/** createTasksBatch(bytes32, CreateTaskInput[]) */
|
|
43
|
+
batchCreate: boolean;
|
|
44
|
+
/** updateTaskMetadata(uint256, bytes, bytes32) */
|
|
45
|
+
editMeta: boolean;
|
|
46
|
+
/** setFolders(bytes32, bytes32) */
|
|
47
|
+
folders: boolean;
|
|
48
|
+
/** pre-v6 7-arg createTask (no deadline params) */
|
|
49
|
+
legacyCreate7: boolean;
|
|
50
|
+
/** v7 unclaimTask(uint256) — release a CLAIMED task back to the pool */
|
|
51
|
+
unclaim: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Human-readable fragments whose runtime-computed selectors identify each
|
|
55
|
+
* feature in implementation bytecode. Signatures verified against contracts
|
|
56
|
+
* repo origin/main src/TaskManager.sol (CreateTaskInput field order:
|
|
57
|
+
* payout, title, metadataHash, bountyToken, bountyPayout,
|
|
58
|
+
* requiresApplication, absoluteDeadline, completionWindow).
|
|
59
|
+
*/
|
|
60
|
+
export declare const TM_FEATURE_FRAGMENTS: Record<keyof TaskManagerFeatures, string>;
|
|
61
|
+
/**
|
|
62
|
+
* Legacy (pre-v6) TaskManager write fragments, for building a fallback
|
|
63
|
+
* Interface when the org's implementation predates the deadline fields.
|
|
64
|
+
*/
|
|
65
|
+
export declare const LEGACY_TM_FRAGMENTS: string[];
|
|
66
|
+
/**
|
|
67
|
+
* Compute the 4-byte selector for a human-readable function fragment at
|
|
68
|
+
* runtime (never hardcode selector hex — signatures drift, keccak does not).
|
|
69
|
+
*/
|
|
70
|
+
export declare function computeSelector(fragment: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Resolve the implementation behind `proxy`:
|
|
73
|
+
* 1. EIP-1967 beacon slot set → call implementation() on the beacon
|
|
74
|
+
* (works for SwitchableBeacon and any IBeacon).
|
|
75
|
+
* 2. EIP-1967 implementation slot set → that address.
|
|
76
|
+
* 3. Neither → `proxy` is not a proxy; return it (checksummed) as-is.
|
|
77
|
+
*/
|
|
78
|
+
export declare function getImplementation(provider: ethers.providers.Provider, proxy: string): Promise<string>;
|
|
79
|
+
/** keccak256("TaskManager") — the PoaManager/OrgRegistry module type id. */
|
|
80
|
+
export declare const TASK_MANAGER_TYPE_ID: string;
|
|
81
|
+
/** Run the selector scan over already-fetched implementation bytecode. */
|
|
82
|
+
export declare function featuresFromBytecode(code: string): TaskManagerFeatures;
|
|
83
|
+
/**
|
|
84
|
+
* Memoized feature scan per TaskManager IMPLEMENTATION ADDRESS (lowercased).
|
|
85
|
+
*
|
|
86
|
+
* Deliberately keyed by address, not by version string. A version→feature table
|
|
87
|
+
* is a guess that fails silently when a release changes shape; deployed
|
|
88
|
+
* bytecode at an address is immutable, so each row below is a recorded fact.
|
|
89
|
+
* Every row was produced by running `featuresFromBytecode` against live
|
|
90
|
+
* eth_getCode on 2026-07-30 (v7 row added 2026-08-02) — on BOTH Gnosis and
|
|
91
|
+
* Arbitrum, which return byte-identical code at these addresses (deterministic
|
|
92
|
+
* deploys), so a row is not chain-specific. The version comment is
|
|
93
|
+
* documentation only; nothing keys off it.
|
|
94
|
+
*
|
|
95
|
+
* FAIL CLOSED: an address that is not listed here is scanned over RPC. Adding a
|
|
96
|
+
* new TaskManager release is optional — omitting it costs one eth_getCode, it
|
|
97
|
+
* never mis-reports a feature.
|
|
98
|
+
*/
|
|
99
|
+
export declare const KNOWN_TM_IMPLEMENTATION_FEATURES: Record<string, TaskManagerFeatures>;
|
|
100
|
+
export interface DetectFeaturesOptions {
|
|
101
|
+
/**
|
|
102
|
+
* Org id. When supplied (together with `client`), the proxy's BEACON address
|
|
103
|
+
* comes from the subgraph (RegisteredContract.beacon) instead of an EIP-1967
|
|
104
|
+
* slot read; the implementation is still fetched from that beacon over RPC.
|
|
105
|
+
* Omit it and behaviour is exactly as before.
|
|
106
|
+
*/
|
|
107
|
+
orgId?: string;
|
|
108
|
+
/** Force the pure-RPC path (offline use / tests). */
|
|
109
|
+
skipSubgraph?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Injected GraphClient for the beacon-snapshot read. Core never constructs
|
|
112
|
+
* one itself; without a client the subgraph shortcut is skipped and the
|
|
113
|
+
* EIP-1967 slot walk runs — identical to skipSubgraph.
|
|
114
|
+
*/
|
|
115
|
+
client?: GraphClient;
|
|
116
|
+
/**
|
|
117
|
+
* Alternative injection: a snapshot fetcher. Takes precedence over `client`.
|
|
118
|
+
* Hosts that already wrap the snapshot read in their own module (the CLI's
|
|
119
|
+
* src/queries/beacons, which tests mock) inject that wrapper here so the
|
|
120
|
+
* seam stays interceptable.
|
|
121
|
+
*/
|
|
122
|
+
fetchBeaconSnapshot?: (orgId: string, chainId: number) => Promise<OrgBeaconSnapshot>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Probe a TaskManager proxy's implementation for feature selectors.
|
|
126
|
+
*
|
|
127
|
+
* Resolution order (each step falls back to the next, never guesses):
|
|
128
|
+
* implementation: subgraph (needs `opts.orgId` + `opts.client`) → EIP-1967
|
|
129
|
+
* slot walk
|
|
130
|
+
* features: KNOWN_TM_IMPLEMENTATION_FEATURES → eth_getCode scan
|
|
131
|
+
*
|
|
132
|
+
* Results are cached per `chainId:proxy` for the process lifetime. Pass
|
|
133
|
+
* `chainId` if known to skip the getNetwork round-trip.
|
|
134
|
+
*/
|
|
135
|
+
export declare function detectTaskManagerFeatures(provider: ethers.providers.Provider, proxy: string, chainId?: number, opts?: DetectFeaturesOptions): Promise<TaskManagerFeatures>;
|
|
136
|
+
/**
|
|
137
|
+
* Standard degradation message for commands that hit an org whose
|
|
138
|
+
* implementation predates a feature.
|
|
139
|
+
*/
|
|
140
|
+
export declare function featureUnavailable(feature: string, needsVersion: string, remedy: string): string;
|
|
141
|
+
/** Test-only: reset the module-level feature cache. */
|
|
142
|
+
export declare function _clearVersionCacheForTest(): void;
|