@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
package/dist/version.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Implementation Version Detection
|
|
4
|
+
* Resolves a proxy's implementation (EIP-1967 beacon / implementation slots,
|
|
5
|
+
* SwitchableBeacon.implementation()) and probes the implementation bytecode
|
|
6
|
+
* for feature selectors so commands can degrade gracefully on orgs whose
|
|
7
|
+
* TaskManager is pinned to an older version.
|
|
8
|
+
*
|
|
9
|
+
* Verified against contracts repo origin/main:
|
|
10
|
+
* - src/SwitchableBeacon.sol — `implementation()` (IBeacon accessor)
|
|
11
|
+
* - src/TaskManager.sol — createTask (9-arg v6), createTasksBatch(bytes32,
|
|
12
|
+
* CreateTaskInput[]), updateTaskMetadata, setFolders signatures and the
|
|
13
|
+
* CreateTaskInput struct field order.
|
|
14
|
+
* - src/TaskManager.sol v7 (contracts PR #187) — unclaimTask(uint256).
|
|
15
|
+
*
|
|
16
|
+
* RPC COST (2026-07): this module is on the hot path of task
|
|
17
|
+
* list/create/create-batch/update/edit-meta/folders, and each call used to cost
|
|
18
|
+
* eth_getStorageAt + eth_call(implementation) + eth_getCode — the last of which
|
|
19
|
+
* ships ~20 KB of hex. Both halves now have a cheaper first choice:
|
|
20
|
+
*
|
|
21
|
+
* 1. Beacon address — the subgraph knows it (RegisteredContract.beacon).
|
|
22
|
+
* Requires the caller to pass `orgId` AND a `client` (the injected
|
|
23
|
+
* GraphClient); without either the EIP-1967 slot walk runs exactly as
|
|
24
|
+
* before. The implementation is still fetched FROM the beacon over RPC,
|
|
25
|
+
* because this function gates writes (see
|
|
26
|
+
* beaconImplementationViaSubgraph).
|
|
27
|
+
* 2. Feature set — memoized per implementation ADDRESS (see
|
|
28
|
+
* KNOWN_TM_IMPLEMENTATION_FEATURES). Deployed bytecode at an address is
|
|
29
|
+
* immutable, so this is a cache of a fact, not a guess about a version
|
|
30
|
+
* string. An unrecognised address falls back to the bytecode scan, so the
|
|
31
|
+
* table can only ever remove work — never invent a feature.
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.KNOWN_TM_IMPLEMENTATION_FEATURES = exports.TASK_MANAGER_TYPE_ID = exports.LEGACY_TM_FRAGMENTS = exports.TM_FEATURE_FRAGMENTS = exports.EIP1967_IMPLEMENTATION_SLOT = exports.EIP1967_BEACON_SLOT = void 0;
|
|
35
|
+
exports.computeSelector = computeSelector;
|
|
36
|
+
exports.getImplementation = getImplementation;
|
|
37
|
+
exports.featuresFromBytecode = featuresFromBytecode;
|
|
38
|
+
exports.detectTaskManagerFeatures = detectTaskManagerFeatures;
|
|
39
|
+
exports.featureUnavailable = featureUnavailable;
|
|
40
|
+
exports._clearVersionCacheForTest = _clearVersionCacheForTest;
|
|
41
|
+
const ethers_1 = require("ethers");
|
|
42
|
+
const beacons_1 = require("./graph/documents/beacons");
|
|
43
|
+
/** EIP-1967 beacon slot: keccak256('eip1967.proxy.beacon') - 1 */
|
|
44
|
+
exports.EIP1967_BEACON_SLOT = '0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50';
|
|
45
|
+
/** EIP-1967 implementation slot: keccak256('eip1967.proxy.implementation') - 1 */
|
|
46
|
+
exports.EIP1967_IMPLEMENTATION_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
|
|
47
|
+
const BEACON_IFACE = new ethers_1.ethers.utils.Interface([
|
|
48
|
+
'function implementation() view returns (address)',
|
|
49
|
+
]);
|
|
50
|
+
/**
|
|
51
|
+
* Human-readable fragments whose runtime-computed selectors identify each
|
|
52
|
+
* feature in implementation bytecode. Signatures verified against contracts
|
|
53
|
+
* repo origin/main src/TaskManager.sol (CreateTaskInput field order:
|
|
54
|
+
* payout, title, metadataHash, bountyToken, bountyPayout,
|
|
55
|
+
* requiresApplication, absoluteDeadline, completionWindow).
|
|
56
|
+
*/
|
|
57
|
+
exports.TM_FEATURE_FRAGMENTS = {
|
|
58
|
+
deadlines: 'function createTask(uint256 payout, bytes title, bytes32 metadataHash, bytes32 pid, address bountyToken, uint256 bountyPayout, bool requiresApplication, uint48 absoluteDeadline, uint32 completionWindow)',
|
|
59
|
+
batchCreate: 'function createTasksBatch(bytes32 pid, tuple(uint256 payout, bytes title, bytes32 metadataHash, address bountyToken, uint256 bountyPayout, bool requiresApplication, uint48 absoluteDeadline, uint32 completionWindow)[] tasks) returns (uint256[] taskIds)',
|
|
60
|
+
editMeta: 'function updateTaskMetadata(uint256 id, bytes newTitle, bytes32 newMetadataHash)',
|
|
61
|
+
folders: 'function setFolders(bytes32 expectedCurrentRoot, bytes32 newRoot)',
|
|
62
|
+
legacyCreate7: 'function createTask(uint256 payout, bytes title, bytes32 metadataHash, bytes32 pid, address bountyToken, uint256 bountyPayout, bool requiresApplication)',
|
|
63
|
+
unclaim: 'function unclaimTask(uint256 id)',
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Legacy (pre-v6) TaskManager write fragments, for building a fallback
|
|
67
|
+
* Interface when the org's implementation predates the deadline fields.
|
|
68
|
+
*/
|
|
69
|
+
exports.LEGACY_TM_FRAGMENTS = [
|
|
70
|
+
exports.TM_FEATURE_FRAGMENTS.legacyCreate7,
|
|
71
|
+
'function updateTask(uint256 id, uint256 newPayout, bytes newTitle, bytes32 newMetadataHash, address newBountyToken, uint256 newBountyPayout)',
|
|
72
|
+
// TaskCreated event (identical shape pre-v6 and v6 — verified against the
|
|
73
|
+
// pre-sync ABI at f3b7216~1) so executeTx's receipt log parsing can still
|
|
74
|
+
// extract the created task id on the legacy path.
|
|
75
|
+
'event TaskCreated(uint256 indexed id, bytes32 indexed project, uint256 payout, address bountyToken, uint256 bountyPayout, bool requiresApplication, bytes title, bytes32 metadataHash)',
|
|
76
|
+
];
|
|
77
|
+
const selectorCache = new Map();
|
|
78
|
+
/**
|
|
79
|
+
* Compute the 4-byte selector for a human-readable function fragment at
|
|
80
|
+
* runtime (never hardcode selector hex — signatures drift, keccak does not).
|
|
81
|
+
*/
|
|
82
|
+
function computeSelector(fragment) {
|
|
83
|
+
let selector = selectorCache.get(fragment);
|
|
84
|
+
if (selector)
|
|
85
|
+
return selector;
|
|
86
|
+
const iface = new ethers_1.ethers.utils.Interface([fragment]);
|
|
87
|
+
const signature = Object.keys(iface.functions)[0];
|
|
88
|
+
selector = iface.getSighash(iface.functions[signature]);
|
|
89
|
+
selectorCache.set(fragment, selector);
|
|
90
|
+
return selector;
|
|
91
|
+
}
|
|
92
|
+
/** Extract a right-aligned address from a 32-byte storage word (null if zero). */
|
|
93
|
+
function slotToAddress(word) {
|
|
94
|
+
if (!word)
|
|
95
|
+
return null;
|
|
96
|
+
const padded = ethers_1.ethers.utils.hexZeroPad(word, 32);
|
|
97
|
+
if (ethers_1.ethers.BigNumber.from(padded).isZero())
|
|
98
|
+
return null;
|
|
99
|
+
return ethers_1.ethers.utils.getAddress(ethers_1.ethers.utils.hexDataSlice(padded, 12));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the implementation behind `proxy`:
|
|
103
|
+
* 1. EIP-1967 beacon slot set → call implementation() on the beacon
|
|
104
|
+
* (works for SwitchableBeacon and any IBeacon).
|
|
105
|
+
* 2. EIP-1967 implementation slot set → that address.
|
|
106
|
+
* 3. Neither → `proxy` is not a proxy; return it (checksummed) as-is.
|
|
107
|
+
*/
|
|
108
|
+
async function getImplementation(provider, proxy) {
|
|
109
|
+
const beacon = slotToAddress(await provider.getStorageAt(proxy, exports.EIP1967_BEACON_SLOT));
|
|
110
|
+
if (beacon) {
|
|
111
|
+
const raw = await provider.call({
|
|
112
|
+
to: beacon,
|
|
113
|
+
data: BEACON_IFACE.encodeFunctionData('implementation'),
|
|
114
|
+
});
|
|
115
|
+
return BEACON_IFACE.decodeFunctionResult('implementation', raw)[0];
|
|
116
|
+
}
|
|
117
|
+
const impl = slotToAddress(await provider.getStorageAt(proxy, exports.EIP1967_IMPLEMENTATION_SLOT));
|
|
118
|
+
if (impl)
|
|
119
|
+
return impl;
|
|
120
|
+
return ethers_1.ethers.utils.getAddress(proxy);
|
|
121
|
+
}
|
|
122
|
+
/** keccak256("TaskManager") — the PoaManager/OrgRegistry module type id. */
|
|
123
|
+
exports.TASK_MANAGER_TYPE_ID = ethers_1.ethers.utils.id('TaskManager');
|
|
124
|
+
/** Run the selector scan over already-fetched implementation bytecode. */
|
|
125
|
+
function featuresFromBytecode(code) {
|
|
126
|
+
const lowered = (code || '').toLowerCase();
|
|
127
|
+
const has = (fragment) => lowered.includes(computeSelector(fragment).slice(2).toLowerCase());
|
|
128
|
+
return {
|
|
129
|
+
deadlines: has(exports.TM_FEATURE_FRAGMENTS.deadlines),
|
|
130
|
+
batchCreate: has(exports.TM_FEATURE_FRAGMENTS.batchCreate),
|
|
131
|
+
editMeta: has(exports.TM_FEATURE_FRAGMENTS.editMeta),
|
|
132
|
+
folders: has(exports.TM_FEATURE_FRAGMENTS.folders),
|
|
133
|
+
legacyCreate7: has(exports.TM_FEATURE_FRAGMENTS.legacyCreate7),
|
|
134
|
+
unclaim: has(exports.TM_FEATURE_FRAGMENTS.unclaim),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Memoized feature scan per TaskManager IMPLEMENTATION ADDRESS (lowercased).
|
|
139
|
+
*
|
|
140
|
+
* Deliberately keyed by address, not by version string. A version→feature table
|
|
141
|
+
* is a guess that fails silently when a release changes shape; deployed
|
|
142
|
+
* bytecode at an address is immutable, so each row below is a recorded fact.
|
|
143
|
+
* Every row was produced by running `featuresFromBytecode` against live
|
|
144
|
+
* eth_getCode on 2026-07-30 (v7 row added 2026-08-02) — on BOTH Gnosis and
|
|
145
|
+
* Arbitrum, which return byte-identical code at these addresses (deterministic
|
|
146
|
+
* deploys), so a row is not chain-specific. The version comment is
|
|
147
|
+
* documentation only; nothing keys off it.
|
|
148
|
+
*
|
|
149
|
+
* FAIL CLOSED: an address that is not listed here is scanned over RPC. Adding a
|
|
150
|
+
* new TaskManager release is optional — omitting it costs one eth_getCode, it
|
|
151
|
+
* never mis-reports a feature.
|
|
152
|
+
*/
|
|
153
|
+
exports.KNOWN_TM_IMPLEMENTATION_FEATURES = {
|
|
154
|
+
// TaskManager v2
|
|
155
|
+
'0xe5ce83cc15360d1948b70e699cd0fa779af320b7': { deadlines: false, batchCreate: false, editMeta: false, folders: false, legacyCreate7: true, unclaim: false },
|
|
156
|
+
// TaskManager v4 — setFolders introduced
|
|
157
|
+
'0xd1721e7bb458c21485cbc7175a557c23bb4be358': { deadlines: false, batchCreate: false, editMeta: false, folders: true, legacyCreate7: true, unclaim: false },
|
|
158
|
+
// TaskManager v5 — updateTaskMetadata introduced
|
|
159
|
+
'0xd388953eee145247e1f8a51c5a0ddefc2c3db915': { deadlines: false, batchCreate: false, editMeta: true, folders: true, legacyCreate7: true, unclaim: false },
|
|
160
|
+
// TaskManager v6 — deadlines + createTasksBatch; 7-arg createTask removed
|
|
161
|
+
'0x7833c4670c42dbce1a7ab1bab7e7baf0a982ff57': { deadlines: true, batchCreate: true, editMeta: true, folders: true, legacyCreate7: false, unclaim: false },
|
|
162
|
+
// TaskManager v7 — unclaimTask (release a claim back to the pool).
|
|
163
|
+
// Verified 2026-08-02: eth_getCode returns byte-identical code on Gnosis AND
|
|
164
|
+
// Arbitrum (same sha256) containing selector 0x6103955a, and the global
|
|
165
|
+
// TaskManager beacon on both chains reports version "v7" pointing here with
|
|
166
|
+
// every live SwitchableBeacon in Mirror mode — so all orgs resolve here today.
|
|
167
|
+
'0xcfae1dadf1a48b363aad3bbb8f94f67bb3785988': { deadlines: true, batchCreate: true, editMeta: true, folders: true, legacyCreate7: false, unclaim: true },
|
|
168
|
+
};
|
|
169
|
+
const featureCache = new Map();
|
|
170
|
+
const chainIdCache = new WeakMap();
|
|
171
|
+
async function resolveChainId(provider) {
|
|
172
|
+
const cached = chainIdCache.get(provider);
|
|
173
|
+
if (cached !== undefined)
|
|
174
|
+
return cached;
|
|
175
|
+
const network = await provider.getNetwork();
|
|
176
|
+
chainIdCache.set(provider, network.chainId);
|
|
177
|
+
return network.chainId;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Resolve the implementation behind a TaskManager proxy using the subgraph for
|
|
181
|
+
* the part that is safe to index, and RPC for the part that is not.
|
|
182
|
+
*
|
|
183
|
+
* The subgraph supplies RegisteredContract.beacon — the proxy's EIP-1967 beacon
|
|
184
|
+
* address, verified byte-identical to eth_getStorageAt on live Gnosis (org
|
|
185
|
+
* Test6: 0x4af43d51…d4da7c5 from both). That value is written once at org
|
|
186
|
+
* deployment and effectively never moves, so indexing it carries no staleness
|
|
187
|
+
* risk and removes the eth_getStorageAt.
|
|
188
|
+
*
|
|
189
|
+
* The implementation ITSELF is still read from the beacon over RPC, on purpose.
|
|
190
|
+
* detectTaskManagerFeatures gates writes (createTask arity, setFolders,
|
|
191
|
+
* updateTaskMetadata): if a beacon upgrade landed inside the indexing window,
|
|
192
|
+
* a subgraph-served implementation would pick the wrong calldata shape and
|
|
193
|
+
* broadcast a doomed transaction. Beacon.currentImplementation is used for the
|
|
194
|
+
* read-only version panel, never here.
|
|
195
|
+
*
|
|
196
|
+
* Returns null when the beacon is not indexed (or no GraphClient was injected)
|
|
197
|
+
* — the caller then walks the EIP-1967 slots exactly as before.
|
|
198
|
+
*/
|
|
199
|
+
async function beaconImplementationViaSubgraph(provider, chainId, opts) {
|
|
200
|
+
if (!opts?.orgId || opts.skipSubgraph)
|
|
201
|
+
return null;
|
|
202
|
+
if (!opts.client && !opts.fetchBeaconSnapshot)
|
|
203
|
+
return null;
|
|
204
|
+
try {
|
|
205
|
+
const snapshot = opts.fetchBeaconSnapshot
|
|
206
|
+
? await opts.fetchBeaconSnapshot(opts.orgId, chainId)
|
|
207
|
+
: await (0, beacons_1.fetchOrgBeaconSnapshot)(opts.client, opts.orgId, chainId);
|
|
208
|
+
const beacon = (0, beacons_1.beaconAddressFromSnapshot)(snapshot, exports.TASK_MANAGER_TYPE_ID);
|
|
209
|
+
if (!beacon)
|
|
210
|
+
return null;
|
|
211
|
+
const raw = await provider.call({
|
|
212
|
+
to: beacon,
|
|
213
|
+
data: BEACON_IFACE.encodeFunctionData('implementation'),
|
|
214
|
+
});
|
|
215
|
+
return BEACON_IFACE.decodeFunctionResult('implementation', raw)[0];
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Probe a TaskManager proxy's implementation for feature selectors.
|
|
223
|
+
*
|
|
224
|
+
* Resolution order (each step falls back to the next, never guesses):
|
|
225
|
+
* implementation: subgraph (needs `opts.orgId` + `opts.client`) → EIP-1967
|
|
226
|
+
* slot walk
|
|
227
|
+
* features: KNOWN_TM_IMPLEMENTATION_FEATURES → eth_getCode scan
|
|
228
|
+
*
|
|
229
|
+
* Results are cached per `chainId:proxy` for the process lifetime. Pass
|
|
230
|
+
* `chainId` if known to skip the getNetwork round-trip.
|
|
231
|
+
*/
|
|
232
|
+
async function detectTaskManagerFeatures(provider, proxy, chainId, opts) {
|
|
233
|
+
const resolvedChainId = chainId ?? await resolveChainId(provider);
|
|
234
|
+
const cacheKey = `${resolvedChainId}:${proxy.toLowerCase()}`;
|
|
235
|
+
const cached = featureCache.get(cacheKey);
|
|
236
|
+
if (cached)
|
|
237
|
+
return cached;
|
|
238
|
+
const impl = await beaconImplementationViaSubgraph(provider, resolvedChainId, opts)
|
|
239
|
+
?? await getImplementation(provider, proxy);
|
|
240
|
+
const known = exports.KNOWN_TM_IMPLEMENTATION_FEATURES[impl.toLowerCase()];
|
|
241
|
+
const features = known
|
|
242
|
+
? { ...known }
|
|
243
|
+
: featuresFromBytecode(await provider.getCode(impl));
|
|
244
|
+
featureCache.set(cacheKey, features);
|
|
245
|
+
return features;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Standard degradation message for commands that hit an org whose
|
|
249
|
+
* implementation predates a feature.
|
|
250
|
+
*/
|
|
251
|
+
function featureUnavailable(feature, needsVersion, remedy) {
|
|
252
|
+
return `${feature} is unavailable: this org's TaskManager implementation predates ${needsVersion}. ${remedy}`;
|
|
253
|
+
}
|
|
254
|
+
/** Test-only: reset the module-level feature cache. */
|
|
255
|
+
function _clearVersionCacheForTest() {
|
|
256
|
+
featureCache.clear();
|
|
257
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZK Email allowlist — build / parse / prove.
|
|
3
|
+
*
|
|
4
|
+
* Port of the frontend's `poa-app/src/lib/zkemail/allowlist.js`. That file names the
|
|
5
|
+
* CLI as an intended caller ("Applied for EVERY caller of buildAllowlist (editor, CLI,
|
|
6
|
+
* forge tooling)"), so this module reproduces it exactly rather than reinventing it.
|
|
7
|
+
*
|
|
8
|
+
* CRITICAL coordination — every one of these is load-bearing, and a mismatch produces a
|
|
9
|
+
* silently UNCLAIMABLE allowlist (fail-closed: no funds at risk, but a broken invite):
|
|
10
|
+
*
|
|
11
|
+
* - Merkle leaf == `ZkEmailInvites._leaf` == OpenZeppelin StandardMerkleTree over
|
|
12
|
+
* ['uint8','bytes32','uint256[]'] = [kind, id, hatIds], kind 0=domain, 1=email.
|
|
13
|
+
* We use @openzeppelin/merkle-tree rather than a hand-rolled tree because the doc
|
|
14
|
+
* carries a `treeDump` that the frontend reloads with StandardMerkleTree.load().
|
|
15
|
+
* - domain id = Poseidon(packBytes(ascii-lower(domain), 192)) == the circuit's
|
|
16
|
+
* `fromDomainHash` signal and the PoaDKIMRegistry key. NOT keccak.
|
|
17
|
+
* - email id = Poseidon(packBytes(ascii-lower(address), 192)) == PopRoleClaimV2.circom's
|
|
18
|
+
* `emailHash` public signal.
|
|
19
|
+
*
|
|
20
|
+
* Conformance is pinned by test/lib/zkemail.test.ts, which checks these helpers against the
|
|
21
|
+
* three Poseidon constants and the live allowlist root baked into the contracts repo's
|
|
22
|
+
* script/zkemail/CeremonyDeployTest6Gnosis.s.sol (verified equal to the deployed Gnosis root).
|
|
23
|
+
*/
|
|
24
|
+
export declare const ALLOWLIST_SCHEMA = "poa.zkemail.allowlist/1";
|
|
25
|
+
export declare const LEAF_TYPES: readonly ["uint8", "bytes32", "uint256[]"];
|
|
26
|
+
export declare const LEAF_KIND_DOMAIN = 0;
|
|
27
|
+
export declare const LEAF_KIND_EMAIL = 1;
|
|
28
|
+
export type EntryType = 'domain' | 'email';
|
|
29
|
+
export interface AllowlistInputEntry {
|
|
30
|
+
type: EntryType;
|
|
31
|
+
identifier: string;
|
|
32
|
+
hatIds: Array<string | bigint | number>;
|
|
33
|
+
roleIndexes?: number[];
|
|
34
|
+
}
|
|
35
|
+
export interface AllowlistDocEntry {
|
|
36
|
+
type: EntryType;
|
|
37
|
+
identifier: string;
|
|
38
|
+
emailHash?: string;
|
|
39
|
+
domainHash?: string;
|
|
40
|
+
hatIds: string[];
|
|
41
|
+
roleIndexes: number[];
|
|
42
|
+
}
|
|
43
|
+
export interface AllowlistDoc {
|
|
44
|
+
schema: string;
|
|
45
|
+
orgId: string;
|
|
46
|
+
root: string;
|
|
47
|
+
leafTypes: string[];
|
|
48
|
+
entries: AllowlistDocEntry[];
|
|
49
|
+
treeDump: any;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* ASCII-only lowercase — EXACTLY mirrors the circuit's `ToLower` and the contract's `_lower`,
|
|
53
|
+
* both of which transform only bytes 0x41–0x5A. JS String.prototype.toLowerCase() is
|
|
54
|
+
* Unicode-aware and would additionally lowercase non-ASCII letters, producing a leaf the
|
|
55
|
+
* on-chain path can never reproduce. Trim, then ASCII-lowercase.
|
|
56
|
+
*/
|
|
57
|
+
export declare function norm(s: unknown): string;
|
|
58
|
+
/**
|
|
59
|
+
* An identifier that isn't pure printable ASCII can never round-trip through the circuit
|
|
60
|
+
* (its From-address regex + PackBytes are ASCII) or the contract, so it would build an
|
|
61
|
+
* unclaimable leaf. Reject at build time so the operator learns immediately, not the claimer later.
|
|
62
|
+
*/
|
|
63
|
+
export declare function isPrintableAscii(s: unknown): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Poseidon commitment = Poseidon(packBytes(ascii-lower(s) zero-padded to 192), 7).
|
|
66
|
+
*
|
|
67
|
+
* This is the exact commitment the circuits compute: `emailHash` over the From ADDRESS and
|
|
68
|
+
* `fromDomainHash` over the From DOMAIN. Both allowlist leaf ids use it, and each equals the
|
|
69
|
+
* corresponding on-chain proof signal / DKIM registry key.
|
|
70
|
+
*
|
|
71
|
+
* The 192-byte pad and little-endian 31-byte packing MUST equal EMAX/PackBytes in the circuit.
|
|
72
|
+
*
|
|
73
|
+
* The frontend calls circomlibjs `buildPoseidon()`; we use `poseidon-lite`, which is pure JS
|
|
74
|
+
* (no wasm, no worker threads — circomlibjs logs a worker TypeError to stderr on every call,
|
|
75
|
+
* which would corrupt `--json` output). Equivalence is not assumed: the conformance test pins
|
|
76
|
+
* this against the three production domain hashes and the live on-chain root.
|
|
77
|
+
* The fixed arity-7 entry point also enforces CHUNKS === 7.
|
|
78
|
+
*
|
|
79
|
+
* Synchronous, unlike the frontend's — theirs is async only because circomlibjs needs a wasm
|
|
80
|
+
* build step. The value is what has to match, not the signature.
|
|
81
|
+
*/
|
|
82
|
+
export declare function poseidonCommit(str: string): string;
|
|
83
|
+
/** domainHash(domain) — domain-leaf id == the circuit's `fromDomainHash` / PoaDKIMRegistry key. */
|
|
84
|
+
export declare function domainHash(domain: string): string;
|
|
85
|
+
/** emailHash(address) — email-leaf id == PopRoleClaimV2.circom's `emailHash` signal. */
|
|
86
|
+
export declare function emailHash(address: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Build the allowlist JSON + merkle tree.
|
|
89
|
+
*
|
|
90
|
+
* Returns the doc exactly as the frontend writes it, including `treeDump` so a claimant can
|
|
91
|
+
* reload the identical tree without recomputing Poseidon hashes.
|
|
92
|
+
*/
|
|
93
|
+
export declare function buildAllowlist(args: {
|
|
94
|
+
orgId: string;
|
|
95
|
+
entries: AllowlistInputEntry[];
|
|
96
|
+
}): Promise<{
|
|
97
|
+
doc: AllowlistDoc;
|
|
98
|
+
json: string;
|
|
99
|
+
tree: any;
|
|
100
|
+
root: string;
|
|
101
|
+
}>;
|
|
102
|
+
/** Reload the exact tree from a fetched allowlist doc (prefers the dumped tree). */
|
|
103
|
+
export declare function treeFromDoc(doc: any): any;
|
|
104
|
+
/**
|
|
105
|
+
* Recompute the root from a doc and assert it matches `expectedRoot` (anti-swapped-CID guard).
|
|
106
|
+
*
|
|
107
|
+
* `label` names what `expectedRoot` is, because the two call sites mean different things:
|
|
108
|
+
* inspecting an active allowlist compares against the on-chain commit, whereas proposing one
|
|
109
|
+
* compares against a root that is not on-chain yet.
|
|
110
|
+
*/
|
|
111
|
+
export declare function assertRootMatches(doc: any, expectedRoot: string, label?: string): any;
|
|
112
|
+
/** Merkle proof + hatIds for a DOMAIN entry, or null if the domain is not in the allowlist. */
|
|
113
|
+
export declare function proofForDomain(tree: any, domain: string): {
|
|
114
|
+
hatIds: string[];
|
|
115
|
+
proof: string[];
|
|
116
|
+
} | null;
|
|
117
|
+
/** Merkle proof + hatIds for a SPECIFIC-address entry by its emailHash, or null. */
|
|
118
|
+
export declare function proofForEmailHash(tree: any, eHash: string): {
|
|
119
|
+
hatIds: string[];
|
|
120
|
+
proof: string[];
|
|
121
|
+
} | null;
|
|
122
|
+
/** Human-readable summary: which domains + which specific addresses are invited. */
|
|
123
|
+
export declare function summarize(doc: any): {
|
|
124
|
+
domains: string[];
|
|
125
|
+
emails: string[];
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Parse the CLI's allowlist input format into entries.
|
|
129
|
+
*
|
|
130
|
+
* Accepts either a full allowlist doc (re-publish an existing one) or a bare array, and
|
|
131
|
+
* normalizes `hatIds` from hex strings / decimals / numbers.
|
|
132
|
+
*/
|
|
133
|
+
export declare function parseEntriesFile(raw: string, filePath: string): AllowlistInputEntry[];
|
|
134
|
+
/** One indexed DKIM key, as served by the subgraph's DkimKey entity. */
|
|
135
|
+
export interface IndexedDkimKey {
|
|
136
|
+
domainHash: string;
|
|
137
|
+
keyHash?: string;
|
|
138
|
+
valid: boolean;
|
|
139
|
+
validUntil: string | number | bigint | null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Is any currently-usable DKIM key indexed for `domainLeafId`?
|
|
143
|
+
*
|
|
144
|
+
* Reproduces PoaDKIMRegistry.isKeyHashValid rather than trusting the indexed `valid` flag
|
|
145
|
+
* alone. That flag is the registry's boolean at last-event time and does NOT track a
|
|
146
|
+
* time-bounded expiry lapsing, because no event fires when one does — so a key can be
|
|
147
|
+
* `valid: true` on the index and already rejected on-chain.
|
|
148
|
+
*
|
|
149
|
+
* The sentinels are counter-intuitive and worth stating plainly:
|
|
150
|
+
* validUntil == 0 -> unset or REVOKED (never "permanent")
|
|
151
|
+
* validUntil == 2^256 - 1 -> NO_EXPIRY, valid until explicitly revoked
|
|
152
|
+
* anything else -> unix cut-off, compare against now
|
|
153
|
+
*
|
|
154
|
+
* 2^256-1 overflows a JS number, so every comparison goes through BigNumber.
|
|
155
|
+
*
|
|
156
|
+
* Returns null when the registry itself is not indexed (e.g. a chain where it is not
|
|
157
|
+
* configured), which must stay distinguishable from "indexed, and this domain has no key".
|
|
158
|
+
*/
|
|
159
|
+
export declare function dkimSeededFromIndex(registry: {
|
|
160
|
+
keys?: IndexedDkimKey[] | null;
|
|
161
|
+
} | null | undefined, domainLeafId: string, nowSeconds?: number): boolean | null;
|