@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/tx/org.js
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Org write builders.
|
|
4
|
+
*
|
|
5
|
+
* Ports:
|
|
6
|
+
* - `pop org deploy` — src/commands/org/deploy.ts
|
|
7
|
+
* - `pop org update-metadata` — src/commands/org/update-metadata.ts
|
|
8
|
+
* - `pop org set-metadata-admin` — src/commands/org/set-metadata-admin.ts
|
|
9
|
+
*
|
|
10
|
+
* Level-1 builders are pure (sync, zero I/O) and own ALL argument encoding.
|
|
11
|
+
* Level-2 `<action>Intent(ctx, params)` builders resolve addresses via the
|
|
12
|
+
* subgraph, pin metadata to IPFS, and delegate to Level 1.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.DEPLOY_FULL_ORG_GAS_LIMIT = exports.REGISTER_ACCOUNT_TYPES = void 0;
|
|
16
|
+
exports.indicesToBitmap = indicesToBitmap;
|
|
17
|
+
exports.assertRoleIndices = assertRoleIndices;
|
|
18
|
+
exports.deriveOrgId = deriveOrgId;
|
|
19
|
+
exports.registerAccountDomain = registerAccountDomain;
|
|
20
|
+
exports.signRegisterAccount = signRegisterAccount;
|
|
21
|
+
exports.buildDeploymentParams = buildDeploymentParams;
|
|
22
|
+
exports.buildDeployFullOrg = buildDeployFullOrg;
|
|
23
|
+
exports.deployFullOrgIntent = deployFullOrgIntent;
|
|
24
|
+
exports.buildUpdateOrgMetaAsAdmin = buildUpdateOrgMetaAsAdmin;
|
|
25
|
+
exports.updateOrgMetaIntent = updateOrgMetaIntent;
|
|
26
|
+
exports.setMetadataAdminTitle = setMetadataAdminTitle;
|
|
27
|
+
exports.buildSetMetadataAdminProposal = buildSetMetadataAdminProposal;
|
|
28
|
+
exports.setMetadataAdminIntent = setMetadataAdminIntent;
|
|
29
|
+
const ethers_1 = require("ethers");
|
|
30
|
+
const contracts_1 = require("../contracts");
|
|
31
|
+
const encoding_1 = require("../encoding");
|
|
32
|
+
const ipfs_1 = require("../ipfs");
|
|
33
|
+
const errors_1 = require("../errors");
|
|
34
|
+
const exit_codes_1 = require("../exit-codes");
|
|
35
|
+
const resolve_1 = require("../reads/resolve");
|
|
36
|
+
const infrastructure_1 = require("../graph/documents/infrastructure");
|
|
37
|
+
const org_1 = require("../graph/documents/org");
|
|
38
|
+
const org_2 = require("../metadata/org");
|
|
39
|
+
const proposal_1 = require("../metadata/proposal");
|
|
40
|
+
const governance_1 = require("./governance");
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Shared infra resolution
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
/**
|
|
45
|
+
* OrgRegistry proxy address — global infrastructure, not an org module.
|
|
46
|
+
* Same lookup + error as `pop org update-metadata` / `pop org set-metadata-admin`.
|
|
47
|
+
*/
|
|
48
|
+
async function resolveOrgRegistryAddress(ctx) {
|
|
49
|
+
const infra = await ctx.client.query(infrastructure_1.FETCH_INFRASTRUCTURE_ADDRESSES, {}, ctx.chainId);
|
|
50
|
+
const orgRegistryAddr = infra.poaManagerContracts?.[0]?.orgRegistryProxy;
|
|
51
|
+
if (!orgRegistryAddr) {
|
|
52
|
+
throw new errors_1.CliError('Could not resolve OrgRegistry address from subgraph', exit_codes_1.EXIT.INFRA);
|
|
53
|
+
}
|
|
54
|
+
return orgRegistryAddr;
|
|
55
|
+
}
|
|
56
|
+
/** Port of indicesToBitmap in src/commands/org/deploy.ts. */
|
|
57
|
+
function indicesToBitmap(indices) {
|
|
58
|
+
let bitmap = ethers_1.ethers.BigNumber.from(0);
|
|
59
|
+
for (const i of indices) {
|
|
60
|
+
bitmap = bitmap.or(ethers_1.ethers.BigNumber.from(1).shl(i));
|
|
61
|
+
}
|
|
62
|
+
return bitmap;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reject role indices that do not name a role in the config.
|
|
66
|
+
*
|
|
67
|
+
* RoleResolver reverts UnregisteredRole(roleIdx) when a bitmap bit has no registered hat
|
|
68
|
+
* (audit M-09) — previously hat 0 was silently stored as an "authorized" hat and the deploy
|
|
69
|
+
* succeeded with a broken allowlist. Either way the config is wrong, and we know roles.length
|
|
70
|
+
* here, so name the offending field instead of surfacing a bare index from gas estimation
|
|
71
|
+
* (older deployers) or an aborted deploy (upgraded ones).
|
|
72
|
+
*/
|
|
73
|
+
function assertRoleIndices(field, indices, roleCount) {
|
|
74
|
+
for (const i of indices ?? []) {
|
|
75
|
+
if (!Number.isInteger(i) || i < 0 || i >= roleCount) {
|
|
76
|
+
throw new errors_1.CliError(`${field} references role index ${i}, but the config defines ${roleCount} role(s) (valid: 0-${roleCount - 1}).`, exit_codes_1.EXIT.USAGE, 'Role indices are positions in the config\'s `roles` array — renumber them after adding '
|
|
77
|
+
+ 'or removing a role.');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Derive the deterministic orgId + normalized name the deployer uses:
|
|
83
|
+
* orgId = keccak256(orgName.toLowerCase().replace(/\s+/g, '-')).
|
|
84
|
+
* The normalized name is also the default deployer username.
|
|
85
|
+
*/
|
|
86
|
+
function deriveOrgId(orgName) {
|
|
87
|
+
const normalizedName = orgName.toLowerCase().replace(/\s+/g, '-');
|
|
88
|
+
return {
|
|
89
|
+
orgId: ethers_1.ethers.utils.keccak256(ethers_1.ethers.utils.toUtf8Bytes(normalizedName)),
|
|
90
|
+
normalizedName,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** EIP-712 type set for UniversalAccountRegistry registration. */
|
|
94
|
+
exports.REGISTER_ACCOUNT_TYPES = {
|
|
95
|
+
RegisterAccount: [
|
|
96
|
+
{ name: 'user', type: 'address' },
|
|
97
|
+
{ name: 'username', type: 'string' },
|
|
98
|
+
{ name: 'nonce', type: 'uint256' },
|
|
99
|
+
{ name: 'deadline', type: 'uint256' },
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
/** EIP-712 domain for UniversalAccountRegistry registration. */
|
|
103
|
+
function registerAccountDomain(chainId, registryAddress) {
|
|
104
|
+
return {
|
|
105
|
+
name: 'UniversalAccountRegistry',
|
|
106
|
+
version: '1',
|
|
107
|
+
chainId,
|
|
108
|
+
verifyingContract: registryAddress,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Port of the EIP-712 RegisterAccount signature step in `pop org deploy`
|
|
113
|
+
* (src/commands/org/deploy.ts): registration happens INSIDE deployFullOrg via
|
|
114
|
+
* this signature — it is not a separate tx. Kept as a separate function taking
|
|
115
|
+
* a Signer so hosts control key custody; everything else here is pure data.
|
|
116
|
+
*/
|
|
117
|
+
async function signRegisterAccount(signer, params) {
|
|
118
|
+
const domain = registerAccountDomain(params.chainId, params.registryAddress);
|
|
119
|
+
const message = {
|
|
120
|
+
user: params.user,
|
|
121
|
+
username: params.username,
|
|
122
|
+
nonce: ethers_1.ethers.BigNumber.from(params.nonce).toString(),
|
|
123
|
+
deadline: params.deadline,
|
|
124
|
+
};
|
|
125
|
+
return signer._signTypedData(domain, exports.REGISTER_ACCOUNT_TYPES, message);
|
|
126
|
+
}
|
|
127
|
+
/** Recommended gasLimit for deployFullOrg — the CLI always sends 15,000,000. */
|
|
128
|
+
exports.DEPLOY_FULL_ORG_GAS_LIMIT = 15000000;
|
|
129
|
+
/**
|
|
130
|
+
* Pure assembly of the DeploymentParams struct — port of the param build in
|
|
131
|
+
* `pop org deploy` (src/commands/org/deploy.ts:257-403), byte-for-byte the
|
|
132
|
+
* same encoding. Validates taskManagerPerms lengths and every roleAssignments
|
|
133
|
+
* index exactly like the CLI (both gate the broadcast).
|
|
134
|
+
*
|
|
135
|
+
* ABI field order (verified against src/abi/OrgDeployerNew.json +
|
|
136
|
+
* contracts origin/main src/OrgDeployer.sol DeploymentParams):
|
|
137
|
+
* orgId, orgName, metadataHash, registryAddr, deployerAddress,
|
|
138
|
+
* deployerUsername, regDeadline, regNonce, regSignature, autoUpgrade,
|
|
139
|
+
* hybridThresholdPct, ddThresholdPct, hybridClasses, ddInitialTargets,
|
|
140
|
+
* roles, roleAssignments, metadataAdminRoleIndex, passkeyEnabled,
|
|
141
|
+
* educationHubConfig, bootstrap, paymasterConfig, taskManagerPerms
|
|
142
|
+
*/
|
|
143
|
+
function buildDeploymentParams(config, resolved) {
|
|
144
|
+
// Build hybrid voting classes
|
|
145
|
+
// ABI: strategy(uint8), slicePct(uint8), quadratic(bool), minBalance(uint256), asset(address), hatIds(uint256[])
|
|
146
|
+
const hybridClasses = config.hybridVoting.classes.map((c) => [
|
|
147
|
+
c.strategy === 'DIRECT' ? 0 : 1, // uint8
|
|
148
|
+
c.slicePct, // uint8 (0-100)
|
|
149
|
+
c.quadratic || false, // bool
|
|
150
|
+
c.minBalance ? ethers_1.ethers.utils.parseUnits(c.minBalance, 18) : 0, // uint256
|
|
151
|
+
c.asset || ethers_1.ethers.constants.AddressZero, // address
|
|
152
|
+
c.hatIds || [], // uint256[]
|
|
153
|
+
]);
|
|
154
|
+
// Build role configs
|
|
155
|
+
// ABI: name(string), image(string), metadataCID(bytes32), canVote(bool),
|
|
156
|
+
// vouching(tuple), defaults(tuple), hierarchy(tuple), distribution(tuple), hatConfig(tuple)
|
|
157
|
+
const MAX_UINT32 = 4294967295; // 2^32 - 1
|
|
158
|
+
const roles = config.roles.map((r) => [
|
|
159
|
+
r.name, // string
|
|
160
|
+
r.image || '', // string
|
|
161
|
+
ethers_1.ethers.constants.HashZero, // bytes32 metadataCID
|
|
162
|
+
r.canVote, // bool
|
|
163
|
+
[
|
|
164
|
+
r.vouching?.enabled || false,
|
|
165
|
+
r.vouching?.quorum || 0,
|
|
166
|
+
r.vouching?.voucherRoleIndex ?? ethers_1.ethers.constants.MaxUint256,
|
|
167
|
+
r.vouching?.combineWithHierarchy || false,
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
r.defaults?.eligible ?? true,
|
|
171
|
+
r.defaults?.standing ?? true,
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
r.hierarchy?.adminRoleIndex ?? ethers_1.ethers.constants.MaxUint256,
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
r.distribution?.mintToDeployer ?? true,
|
|
178
|
+
r.distribution?.additionalWearers || [],
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
r.hatConfig?.maxSupply ?? MAX_UINT32, // uint32, NOT uint256
|
|
182
|
+
r.hatConfig?.mutableHat ?? true,
|
|
183
|
+
],
|
|
184
|
+
]);
|
|
185
|
+
// Build role assignment bitmaps
|
|
186
|
+
const ra = config.roleAssignments;
|
|
187
|
+
// Every index must name a real role BEFORE anything is broadcast — a stale index is
|
|
188
|
+
// trivially produced by editing `roles` without renumbering `roleAssignments`.
|
|
189
|
+
const roleCount = config.roles.length;
|
|
190
|
+
assertRoleIndices('roleAssignments.quickJoinRoles', ra.quickJoinRoles, roleCount);
|
|
191
|
+
assertRoleIndices('roleAssignments.tokenMemberRoles', ra.tokenMemberRoles, roleCount);
|
|
192
|
+
assertRoleIndices('roleAssignments.tokenApproverRoles', ra.tokenApproverRoles, roleCount);
|
|
193
|
+
assertRoleIndices('roleAssignments.taskCreatorRoles', ra.taskCreatorRoles, roleCount);
|
|
194
|
+
assertRoleIndices('roleAssignments.educationCreatorRoles', ra.educationCreatorRoles, roleCount);
|
|
195
|
+
assertRoleIndices('roleAssignments.educationMemberRoles', ra.educationMemberRoles, roleCount);
|
|
196
|
+
assertRoleIndices('roleAssignments.hybridProposalCreatorRoles', ra.hybridProposalCreatorRoles, roleCount);
|
|
197
|
+
assertRoleIndices('roleAssignments.ddVotingRoles', ra.ddVotingRoles, roleCount);
|
|
198
|
+
assertRoleIndices('roleAssignments.ddCreatorRoles', ra.ddCreatorRoles, roleCount);
|
|
199
|
+
const roleAssignments = [
|
|
200
|
+
indicesToBitmap(ra.quickJoinRoles),
|
|
201
|
+
indicesToBitmap(ra.tokenMemberRoles),
|
|
202
|
+
indicesToBitmap(ra.tokenApproverRoles),
|
|
203
|
+
indicesToBitmap(ra.taskCreatorRoles),
|
|
204
|
+
indicesToBitmap(ra.educationCreatorRoles || []),
|
|
205
|
+
indicesToBitmap(ra.educationMemberRoles || []),
|
|
206
|
+
indicesToBitmap(ra.hybridProposalCreatorRoles),
|
|
207
|
+
indicesToBitmap(ra.ddVotingRoles),
|
|
208
|
+
indicesToBitmap(ra.ddCreatorRoles),
|
|
209
|
+
];
|
|
210
|
+
// Build paymaster config
|
|
211
|
+
// ABI: operatorRoleIndex(uint256), autoWhitelistContracts(bool),
|
|
212
|
+
// maxFeePerGas(uint256), maxPriorityFeePerGas(uint256),
|
|
213
|
+
// maxCallGas(uint32), maxVerificationGas(uint32), maxPreVerificationGas(uint32),
|
|
214
|
+
// defaultBudgetCapPerEpoch(uint128), defaultBudgetEpochLen(uint32)
|
|
215
|
+
const pm = config.paymaster;
|
|
216
|
+
const paymasterConfig = pm ? [
|
|
217
|
+
pm.operatorRoleIndex, // uint256
|
|
218
|
+
true, // bool
|
|
219
|
+
ethers_1.ethers.utils.parseUnits(pm.maxFeePerGas, 'gwei'), // uint256
|
|
220
|
+
ethers_1.ethers.utils.parseUnits(pm.maxPriorityFeePerGas, 'gwei'), // uint256
|
|
221
|
+
500000, // uint32 maxCallGas
|
|
222
|
+
500000, // uint32 maxVerificationGas
|
|
223
|
+
100000, // uint32 maxPreVerificationGas
|
|
224
|
+
ethers_1.ethers.utils.parseEther(pm.defaultBudgetCapPerEpoch), // uint128
|
|
225
|
+
pm.defaultBudgetEpochLen, // uint32
|
|
226
|
+
] : [
|
|
227
|
+
ethers_1.ethers.constants.MaxUint256, // operatorRoleIndex = MaxUint256 disables paymaster
|
|
228
|
+
false, // autoWhitelistContracts
|
|
229
|
+
0, // maxFeePerGas
|
|
230
|
+
0, // maxPriorityFeePerGas
|
|
231
|
+
0, // maxCallGas
|
|
232
|
+
0, // maxVerificationGas
|
|
233
|
+
0, // maxPreVerificationGas
|
|
234
|
+
0, // defaultBudgetCapPerEpoch
|
|
235
|
+
0, // defaultBudgetEpochLen
|
|
236
|
+
];
|
|
237
|
+
// Optional org-wide TaskManager ROLE_PERM grants
|
|
238
|
+
// (OrgDeployer.TaskManagerPermConfig: roleIndices[] + masks[] — empty
|
|
239
|
+
// arrays skip the bootstrapGlobalPerms step entirely; verified against
|
|
240
|
+
// contracts origin/main src/OrgDeployer.sol).
|
|
241
|
+
const tmPerms = config.taskManagerPerms;
|
|
242
|
+
if (tmPerms && (tmPerms.roleIndices?.length || 0) !== (tmPerms.masks?.length || 0)) {
|
|
243
|
+
throw new errors_1.CliError('Config invalid: taskManagerPerms.roleIndices and taskManagerPerms.masks must be the same length.', exit_codes_1.EXIT.USAGE);
|
|
244
|
+
}
|
|
245
|
+
const taskManagerPerms = [
|
|
246
|
+
tmPerms?.roleIndices || [], // uint256[] roleIndices
|
|
247
|
+
tmPerms?.masks || [], // uint8[] masks
|
|
248
|
+
];
|
|
249
|
+
return [
|
|
250
|
+
resolved.orgId, // bytes32
|
|
251
|
+
config.orgName, // string
|
|
252
|
+
resolved.metadataHash, // bytes32
|
|
253
|
+
resolved.registryAddr, // address
|
|
254
|
+
resolved.deployerAddress, // address deployerAddress
|
|
255
|
+
resolved.deployerUsername, // string deployerUsername
|
|
256
|
+
resolved.regDeadline, // uint256
|
|
257
|
+
resolved.regNonce, // uint256
|
|
258
|
+
resolved.regSignature, // bytes
|
|
259
|
+
config.autoUpgrade ?? true, // bool
|
|
260
|
+
config.hybridVoting.thresholdPct, // uint8
|
|
261
|
+
config.directDemocracy?.thresholdPct || 51, // uint8
|
|
262
|
+
hybridClasses, // ClassConfig[]
|
|
263
|
+
[], // address[] ddInitialTargets
|
|
264
|
+
roles, // RoleConfig[]
|
|
265
|
+
roleAssignments, // RoleAssignments struct
|
|
266
|
+
config.metadataAdminRoleIndex ?? ethers_1.ethers.constants.MaxUint256, // uint256
|
|
267
|
+
true, // bool passkeyEnabled
|
|
268
|
+
[config.educationHub?.enabled ?? true], // EducationHubConfig struct
|
|
269
|
+
[[], []], // BootstrapConfig struct (projects, tasks)
|
|
270
|
+
paymasterConfig, // PaymasterConfig struct
|
|
271
|
+
taskManagerPerms, // TaskManagerPermConfig struct (roleIndices, masks)
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Port of `pop org deploy` — src/commands/org/deploy.ts.
|
|
276
|
+
* OrgDeployer.deployFullOrg([DeploymentParams]) — the single on-chain write,
|
|
277
|
+
* which internally deploys every org module (registration happens inside it
|
|
278
|
+
* via the EIP-712 signature carried in the params).
|
|
279
|
+
*
|
|
280
|
+
* NOTE: the CLI always sends this with gasLimit 15,000,000
|
|
281
|
+
* (DEPLOY_FULL_ORG_GAS_LIMIT) — pass it in the executor's options; TxIntent
|
|
282
|
+
* carries no gas policy.
|
|
283
|
+
*/
|
|
284
|
+
function buildDeployFullOrg(a) {
|
|
285
|
+
return {
|
|
286
|
+
to: a.orgDeployerAddress,
|
|
287
|
+
abi: (0, contracts_1.getAbi)('OrgDeployerNew'),
|
|
288
|
+
method: 'deployFullOrg',
|
|
289
|
+
args: [a.deployParams],
|
|
290
|
+
value: a.value,
|
|
291
|
+
meta: {
|
|
292
|
+
domain: 'org',
|
|
293
|
+
action: 'deploy',
|
|
294
|
+
orgId: a.orgId,
|
|
295
|
+
summary: {
|
|
296
|
+
orgId: a.orgId,
|
|
297
|
+
orgName: a.orgName,
|
|
298
|
+
gasLimit: exports.DEPLOY_FULL_ORG_GAS_LIMIT,
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Resolved builder for `pop org deploy` — src/commands/org/deploy.ts.
|
|
305
|
+
*
|
|
306
|
+
* Performs the CLI's pre-steps in the same order: config validation,
|
|
307
|
+
* infrastructure address resolution (orgDeployerProxy +
|
|
308
|
+
* globalAccountRegistryProxy), org metadata pin, on-chain
|
|
309
|
+
* UniversalAccountRegistry.nonces() read (requires ctx.provider), and the
|
|
310
|
+
* EIP-712 RegisterAccount signature — then delegates to buildDeployFullOrg.
|
|
311
|
+
*/
|
|
312
|
+
async function deployFullOrgIntent(ctx, params) {
|
|
313
|
+
const config = params.config;
|
|
314
|
+
// ── Fail fast on config problems before any network work ───────────
|
|
315
|
+
if (!config.orgName)
|
|
316
|
+
throw new errors_1.CliError('Config missing: orgName', exit_codes_1.EXIT.USAGE);
|
|
317
|
+
if (!config.roles?.length)
|
|
318
|
+
throw new errors_1.CliError('Config missing: roles', exit_codes_1.EXIT.USAGE);
|
|
319
|
+
if (!config.hybridVoting)
|
|
320
|
+
throw new errors_1.CliError('Config missing: hybridVoting', exit_codes_1.EXIT.USAGE);
|
|
321
|
+
if (!ctx.provider) {
|
|
322
|
+
throw new errors_1.CliError('deployFullOrgIntent requires ctx.provider (UniversalAccountRegistry.nonces read)', exit_codes_1.EXIT.USAGE);
|
|
323
|
+
}
|
|
324
|
+
if (ctx.chainId === undefined) {
|
|
325
|
+
throw new errors_1.CliError('deployFullOrgIntent requires ctx.chainId (EIP-712 domain)', exit_codes_1.EXIT.USAGE);
|
|
326
|
+
}
|
|
327
|
+
// Resolve infrastructure addresses
|
|
328
|
+
const infra = await ctx.client.query(infrastructure_1.FETCH_INFRASTRUCTURE_ADDRESSES, {}, ctx.chainId);
|
|
329
|
+
const orgDeployerAddr = infra.poaManagerContracts?.[0]?.orgDeployerProxy;
|
|
330
|
+
const registryAddr = infra.poaManagerContracts?.[0]?.globalAccountRegistryProxy;
|
|
331
|
+
if (!orgDeployerAddr)
|
|
332
|
+
throw new errors_1.CliError('Could not resolve OrgDeployer address', exit_codes_1.EXIT.INFRA);
|
|
333
|
+
if (!registryAddr)
|
|
334
|
+
throw new errors_1.CliError('Could not resolve UniversalAccountRegistry address', exit_codes_1.EXIT.INFRA);
|
|
335
|
+
// Generate orgId: keccak256(orgName.toLowerCase().replace(/\s+/g, '-'))
|
|
336
|
+
const { orgId, normalizedName } = deriveOrgId(config.orgName);
|
|
337
|
+
const deployerUsername = config.deployerUsername || normalizedName;
|
|
338
|
+
// Upload org metadata to IPFS
|
|
339
|
+
const metadata = (0, org_2.buildOrgDeployMetadata)({
|
|
340
|
+
description: config.description,
|
|
341
|
+
links: config.links,
|
|
342
|
+
});
|
|
343
|
+
const metaCid = await (0, ipfs_1.pinJson)((0, org_2.serializeOrgMetadata)(metadata), ctx.ipfs);
|
|
344
|
+
const metadataHash = (0, encoding_1.ipfsCidToBytes32)(metaCid);
|
|
345
|
+
// Get registration nonce for deployer
|
|
346
|
+
const registryContract = (0, contracts_1.createReadContract)(registryAddr, 'UniversalAccountRegistry', ctx.provider);
|
|
347
|
+
const regNonce = await registryContract.nonces(params.deployerAddress);
|
|
348
|
+
// 15 min validity by default — see DeployFullOrgParams.regDeadline.
|
|
349
|
+
const regDeadline = params.regDeadline ?? Math.floor(Date.now() / 1000) + 900;
|
|
350
|
+
// Sign EIP-712 registration message
|
|
351
|
+
const regSignature = await signRegisterAccount(params.signer, {
|
|
352
|
+
registryAddress: registryAddr,
|
|
353
|
+
chainId: ctx.chainId,
|
|
354
|
+
user: params.deployerAddress,
|
|
355
|
+
username: deployerUsername,
|
|
356
|
+
nonce: regNonce,
|
|
357
|
+
deadline: regDeadline,
|
|
358
|
+
});
|
|
359
|
+
const deployParams = buildDeploymentParams(config, {
|
|
360
|
+
orgId,
|
|
361
|
+
metadataHash,
|
|
362
|
+
registryAddr,
|
|
363
|
+
deployerAddress: params.deployerAddress,
|
|
364
|
+
deployerUsername,
|
|
365
|
+
regDeadline,
|
|
366
|
+
regNonce,
|
|
367
|
+
regSignature,
|
|
368
|
+
});
|
|
369
|
+
const txValue = config.paymaster?.funding
|
|
370
|
+
? ethers_1.ethers.utils.parseEther(config.paymaster.funding)
|
|
371
|
+
: undefined;
|
|
372
|
+
const intent = buildDeployFullOrg({
|
|
373
|
+
orgDeployerAddress: orgDeployerAddr,
|
|
374
|
+
deployParams,
|
|
375
|
+
value: txValue,
|
|
376
|
+
orgId,
|
|
377
|
+
orgName: config.orgName,
|
|
378
|
+
});
|
|
379
|
+
intent.meta.ipfs = { cid: metaCid, metadata };
|
|
380
|
+
return intent;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Port of `pop org update-metadata` — src/commands/org/update-metadata.ts.
|
|
384
|
+
* OrgRegistry.updateOrgMetaAsAdmin(orgId, stringToBytes(name), metadataHash).
|
|
385
|
+
* Caller must wear the org's metadata-admin hat (topHat fallback) — revert
|
|
386
|
+
* NotOrgMetadataAdmin otherwise. FULL OVERWRITE of name + metadata hash.
|
|
387
|
+
*/
|
|
388
|
+
function buildUpdateOrgMetaAsAdmin(a) {
|
|
389
|
+
return {
|
|
390
|
+
to: a.orgRegistryAddress,
|
|
391
|
+
abi: (0, contracts_1.getAbi)('OrgRegistry'),
|
|
392
|
+
method: 'updateOrgMetaAsAdmin',
|
|
393
|
+
args: [a.orgId, (0, encoding_1.stringToBytes)(a.name), a.metadataHash],
|
|
394
|
+
meta: {
|
|
395
|
+
domain: 'org',
|
|
396
|
+
action: 'update-metadata',
|
|
397
|
+
orgId: a.orgId,
|
|
398
|
+
summary: { name: a.name, metadataHash: a.metadataHash },
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Resolved builder for `pop org update-metadata` — src/commands/org/update-metadata.ts.
|
|
404
|
+
*
|
|
405
|
+
* Merge base is the RAW IPFS doc (fetchJson of the current metadataHash),
|
|
406
|
+
* spread FIRST so unknown keys (e.g. zkEmailAllowlist) and frontend key order
|
|
407
|
+
* survive; refuses (INFRA) when IPFS is unreachable but the subgraph shows
|
|
408
|
+
* metadata exists — proceeding would drop any key the CLI does not model.
|
|
409
|
+
*/
|
|
410
|
+
async function updateOrgMetaIntent(ctx, params) {
|
|
411
|
+
if (!params.name && !params.description && !params.logo && !params.logoCid && !params.links
|
|
412
|
+
&& params.backgroundColor === undefined && params.hideTreasury === undefined) {
|
|
413
|
+
throw new errors_1.CliError('At least one metadata field must be provided.', exit_codes_1.EXIT.USAGE, 'Available: --name, --description, --logo, --links, --background-color, --hide-treasury');
|
|
414
|
+
}
|
|
415
|
+
const orgId = await (0, resolve_1.resolveOrgId)(ctx.client, params.org, ctx.chainId);
|
|
416
|
+
// Resolve OrgRegistry address (global infrastructure, not an org module)
|
|
417
|
+
const orgRegistryAddr = await resolveOrgRegistryAddress(ctx);
|
|
418
|
+
// Fetch existing metadata to preserve fields not being updated.
|
|
419
|
+
const existing = await ctx.client.query(org_1.FETCH_ORG_FULL_DATA, { orgId }, ctx.chainId);
|
|
420
|
+
if (!existing.organization) {
|
|
421
|
+
throw new errors_1.CliError(`Organization ${orgId} is not indexed on this chain.`, exit_codes_1.EXIT.INFRA, 'This write overwrites the org name and metadata wholesale; refusing to send it against '
|
|
422
|
+
+ 'an unknown org would otherwise blank both. Check --chain, or retry once indexed.');
|
|
423
|
+
}
|
|
424
|
+
const subgraphMeta = existing.organization.metadata || {};
|
|
425
|
+
const currentName = existing.organization.name || '';
|
|
426
|
+
const currentMetadataHash = existing.organization.metadataHash;
|
|
427
|
+
// The RAW IPFS doc — not the subgraph's typed projection — is the merge base
|
|
428
|
+
// (see mergeOrgMetadata for why).
|
|
429
|
+
let rawMeta = null;
|
|
430
|
+
if (currentMetadataHash && currentMetadataHash !== ethers_1.ethers.constants.HashZero) {
|
|
431
|
+
try {
|
|
432
|
+
rawMeta = await (0, ipfs_1.fetchJson)(currentMetadataHash, ctx.ipfs);
|
|
433
|
+
}
|
|
434
|
+
catch { /* handled below */ }
|
|
435
|
+
}
|
|
436
|
+
if (!rawMeta && Object.keys(subgraphMeta).length > 0) {
|
|
437
|
+
throw new errors_1.CliError('Could not fetch the org\'s current metadata from IPFS, so unknown fields cannot be preserved.', exit_codes_1.EXIT.INFRA, 'This write is a full overwrite — proceeding would drop any metadata key the CLI does not '
|
|
438
|
+
+ 'model. Retry once IPFS responds.');
|
|
439
|
+
}
|
|
440
|
+
const currentMeta = rawMeta ?? subgraphMeta;
|
|
441
|
+
// Upload logo to IPFS if provided
|
|
442
|
+
let logoCid = params.logoCid ?? null;
|
|
443
|
+
if (params.logo) {
|
|
444
|
+
logoCid = await (0, ipfs_1.pinFile)(params.logo, ctx.ipfs);
|
|
445
|
+
}
|
|
446
|
+
const metadata = (0, org_2.mergeOrgMetadata)(currentMeta, {
|
|
447
|
+
description: params.description,
|
|
448
|
+
links: params.links,
|
|
449
|
+
logoCid,
|
|
450
|
+
backgroundColor: params.backgroundColor,
|
|
451
|
+
hideTreasury: params.hideTreasury,
|
|
452
|
+
});
|
|
453
|
+
// If name not provided, keep the current name (full-overwrite write).
|
|
454
|
+
const nameToSend = params.name || currentName;
|
|
455
|
+
const metaCid = await (0, ipfs_1.pinJson)((0, org_2.serializeOrgMetadata)(metadata), ctx.ipfs);
|
|
456
|
+
const metadataHash = (0, encoding_1.ipfsCidToBytes32)(metaCid);
|
|
457
|
+
const intent = buildUpdateOrgMetaAsAdmin({
|
|
458
|
+
orgRegistryAddress: orgRegistryAddr,
|
|
459
|
+
orgId,
|
|
460
|
+
name: nameToSend,
|
|
461
|
+
metadataHash,
|
|
462
|
+
});
|
|
463
|
+
intent.meta.ipfs = { cid: metaCid, metadata };
|
|
464
|
+
if (logoCid)
|
|
465
|
+
intent.meta.summary.logoCid = logoCid;
|
|
466
|
+
return intent;
|
|
467
|
+
}
|
|
468
|
+
/** The exact proposal title `pop org set-metadata-admin` uses. */
|
|
469
|
+
function setMetadataAdminTitle(hatId) {
|
|
470
|
+
return `Set org metadata-admin hat to ${ethers_1.ethers.BigNumber.from(hatId).toString()}`;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Port of `pop org set-metadata-admin` — src/commands/org/set-metadata-admin.ts.
|
|
474
|
+
*
|
|
475
|
+
* setOrgMetadataAdminHat(bytes32 orgId, uint256 hatId) is executor-only after
|
|
476
|
+
* bootstrap (revert NotOrgExecutor), so it ships as a HybridVoting proposal
|
|
477
|
+
* whose option-0 execution batch calls the OrgRegistry via the executor.
|
|
478
|
+
* Option 1 is "keep current" (empty batch). hatId 0 is meaningful: it clears
|
|
479
|
+
* the override so updateOrgMetaAsAdmin falls back to the org topHat.
|
|
480
|
+
*/
|
|
481
|
+
function buildSetMetadataAdminProposal(a) {
|
|
482
|
+
const hatId = ethers_1.ethers.BigNumber.from(a.hatId);
|
|
483
|
+
// Same inline fragment the CLI encodes with — identical selector + calldata
|
|
484
|
+
// to the full OrgRegistry ABI.
|
|
485
|
+
const registryIface = new ethers_1.ethers.utils.Interface([
|
|
486
|
+
'function setOrgMetadataAdminHat(bytes32 orgId, uint256 hatId)',
|
|
487
|
+
]);
|
|
488
|
+
const setHatCall = registryIface.encodeFunctionData('setOrgMetadataAdminHat', [a.orgId, hatId]);
|
|
489
|
+
return (0, governance_1.buildGovernanceProposal)({
|
|
490
|
+
votingAddress: a.hybridVotingAddress,
|
|
491
|
+
votingAbiName: 'HybridVotingNew',
|
|
492
|
+
title: a.title ?? setMetadataAdminTitle(hatId),
|
|
493
|
+
descriptionHash: a.descriptionHash,
|
|
494
|
+
durationMinutes: a.durationMinutes,
|
|
495
|
+
numOptions: 2,
|
|
496
|
+
batches: [
|
|
497
|
+
[{ target: a.orgRegistryAddress, value: ethers_1.ethers.BigNumber.from(0), calldata: setHatCall }], // option 0: apply
|
|
498
|
+
[], // option 1: keep current
|
|
499
|
+
],
|
|
500
|
+
hatIds: [],
|
|
501
|
+
orgId: a.orgId,
|
|
502
|
+
domain: 'org',
|
|
503
|
+
action: 'set-metadata-admin',
|
|
504
|
+
summary: { hatId: hatId.toString(), orgRegistry: a.orgRegistryAddress },
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Resolved builder for `pop org set-metadata-admin` — src/commands/org/set-metadata-admin.ts.
|
|
509
|
+
* Resolves the org's HybridVoting module + the global OrgRegistry, pins the
|
|
510
|
+
* proposal metadata ({description, optionNames, createdAt} — key order is
|
|
511
|
+
* load-bearing), and delegates to buildSetMetadataAdminProposal.
|
|
512
|
+
*/
|
|
513
|
+
async function setMetadataAdminIntent(ctx, params) {
|
|
514
|
+
const hatId = ethers_1.ethers.BigNumber.from(params.hatId);
|
|
515
|
+
const durationMinutes = params.durationMinutes ?? 60;
|
|
516
|
+
const modules = await (0, resolve_1.resolveOrgModules)(ctx.client, params.org, ctx.chainId);
|
|
517
|
+
const hybridVotingAddress = modules.hybridVotingAddress;
|
|
518
|
+
if (!hybridVotingAddress) {
|
|
519
|
+
throw new errors_1.CliError('HybridVoting not deployed for this org — cannot create a governance proposal.', exit_codes_1.EXIT.PRECONDITION);
|
|
520
|
+
}
|
|
521
|
+
// OrgRegistry is global infrastructure (not an org module).
|
|
522
|
+
const orgRegistryAddr = await resolveOrgRegistryAddress(ctx);
|
|
523
|
+
const hatLabel = hatId.isZero() ? '0 (clear — topHat fallback)' : hatId.toString();
|
|
524
|
+
const title = setMetadataAdminTitle(hatId);
|
|
525
|
+
// Proposal metadata — key order (description, optionNames, createdAt) is a
|
|
526
|
+
// protocol contract (metadata/proposal.ts); text matches the CLI exactly.
|
|
527
|
+
const metadata = (0, proposal_1.buildProposalMetadata)({
|
|
528
|
+
description: `Set the org's metadata-admin hat to ${hatLabel} via OrgRegistry.setOrgMetadataAdminHat. Wearers of this hat can update the org name/metadata directly (pop org update-metadata) without a vote.${hatId.isZero() ? ' A value of 0 clears the override so the org topHat is required instead.' : ''}`,
|
|
529
|
+
optionNames: [title, 'Keep current metadata admin'],
|
|
530
|
+
});
|
|
531
|
+
const cid = await (0, ipfs_1.pinJson)((0, proposal_1.serializeProposalMetadata)(metadata), ctx.ipfs);
|
|
532
|
+
const descriptionHash = (0, encoding_1.ipfsCidToBytes32)(cid);
|
|
533
|
+
const intent = buildSetMetadataAdminProposal({
|
|
534
|
+
hybridVotingAddress,
|
|
535
|
+
orgRegistryAddress: orgRegistryAddr,
|
|
536
|
+
orgId: modules.orgId,
|
|
537
|
+
hatId,
|
|
538
|
+
durationMinutes,
|
|
539
|
+
title,
|
|
540
|
+
descriptionHash,
|
|
541
|
+
});
|
|
542
|
+
intent.meta.ipfs = { cid, metadata };
|
|
543
|
+
return intent;
|
|
544
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paymaster transaction builders — ports of `pop paymaster *` write paths
|
|
3
|
+
* (src/commands/paymaster/).
|
|
4
|
+
*
|
|
5
|
+
* buildDepositForOrg / depositForOrgIntent — pop paymaster deposit
|
|
6
|
+
* (PaymasterHub.depositForOrg(bytes32) payable — permissionless)
|
|
7
|
+
* buildRegisterCalldata / buildAdminCallRegister / registerOrgIntent —
|
|
8
|
+
* pop paymaster register (PoaManager.adminCall wrapping the
|
|
9
|
+
* registrar-gated registerOrg / registerAndConfigureOrg)
|
|
10
|
+
*
|
|
11
|
+
* Registration gate — VERIFIED against contracts origin/main
|
|
12
|
+
* src/PaymasterHub.sol: registerOrg / registerAndConfigureOrg are
|
|
13
|
+
* registrar-gated (_onlyRegistrar: msg.sender must be the PoaManager CONTRACT
|
|
14
|
+
* or the orgRegistrar, i.e. the OrgDeployer). No EOA can ever call them
|
|
15
|
+
* directly — the after-the-fact path is the PoaManager OWNER routing the call
|
|
16
|
+
* through PoaManager.adminCall(target, data). adminCall is NOT payable, so an
|
|
17
|
+
* initial deposit is always its own, separate depositForOrg transaction.
|
|
18
|
+
*/
|
|
19
|
+
import { ethers } from 'ethers';
|
|
20
|
+
import type { TxIntent } from './intent';
|
|
21
|
+
import type { PopContext } from '../context';
|
|
22
|
+
/** DeployConfig struct shape — verified against PaymasterHub.sol origin/main. */
|
|
23
|
+
export declare const DEPLOY_CONFIG_TUPLE: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional --config JSON for registerAndConfigureOrg. Fee caps are GWEI and
|
|
26
|
+
* budget caps are ETHER units, matching the `pop org deploy` paymaster block
|
|
27
|
+
* conventions.
|
|
28
|
+
*/
|
|
29
|
+
export interface PaymasterRegisterConfig {
|
|
30
|
+
operatorHatId?: string | number;
|
|
31
|
+
maxFeePerGas?: string | number;
|
|
32
|
+
maxPriorityFeePerGas?: string | number;
|
|
33
|
+
maxCallGas?: number;
|
|
34
|
+
maxVerificationGas?: number;
|
|
35
|
+
maxPreVerificationGas?: number;
|
|
36
|
+
rules?: Array<{
|
|
37
|
+
target: string;
|
|
38
|
+
selector: string;
|
|
39
|
+
allowed: boolean;
|
|
40
|
+
maxCallGasHint?: number;
|
|
41
|
+
}>;
|
|
42
|
+
budgets?: Array<{
|
|
43
|
+
subjectKey?: string;
|
|
44
|
+
hatId?: string | number;
|
|
45
|
+
capPerEpoch: string | number;
|
|
46
|
+
epochLen: number;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
/** Parse a hat ID flag — BigNumber, never parseInt (Hats IDs exceed 2^53). */
|
|
50
|
+
export declare function parsePaymasterHatId(input: string | number, flag: string): ethers.BigNumber;
|
|
51
|
+
/** Parse an amount in ether units of the native gas token — port of parseDepositAmount in src/commands/paymaster/deposit.ts. */
|
|
52
|
+
export declare function parseDepositAmount(input: string | number): ethers.BigNumber;
|
|
53
|
+
/**
|
|
54
|
+
* Build the registerOrg / registerAndConfigureOrg calldata from the hat IDs +
|
|
55
|
+
* the optional config (gwei fee caps + ether budget caps). Pure port of
|
|
56
|
+
* buildRegisterCalldata in src/commands/paymaster/register.ts — byte-for-byte
|
|
57
|
+
* identical encoding.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildRegisterCalldata(orgId: string, adminHatId: ethers.BigNumber, operatorHatId: ethers.BigNumber, config?: PaymasterRegisterConfig): {
|
|
60
|
+
method: string;
|
|
61
|
+
data: string;
|
|
62
|
+
};
|
|
63
|
+
export interface DepositForOrgArgs {
|
|
64
|
+
paymasterHubAddress: string;
|
|
65
|
+
/** bytes32 org id. */
|
|
66
|
+
orgId: string;
|
|
67
|
+
/** Native value in wei (the function is payable; reverts ZeroAmount on 0). */
|
|
68
|
+
amountWei: ethers.BigNumberish;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Port of `pop paymaster deposit` — src/commands/paymaster/deposit.ts:
|
|
72
|
+
* PaymasterHub.depositForOrg(bytes32 orgId) payable. PERMISSIONLESS ("anyone
|
|
73
|
+
* can deposit to any org"); reverts ZeroAmount on msg.value == 0 and
|
|
74
|
+
* OrgNotRegistered when the org has no paymaster config (adminHatId == 0).
|
|
75
|
+
* ONE-WAY on the deployed hub: no withdraw exists (PR #185 / audit M-04 not
|
|
76
|
+
* shipped) — funds draw down only as sponsored UserOperations consume them.
|
|
77
|
+
*/
|
|
78
|
+
export declare function buildDepositForOrg(a: DepositForOrgArgs): TxIntent;
|
|
79
|
+
export interface AdminCallRegisterArgs {
|
|
80
|
+
poaManagerAddress: string;
|
|
81
|
+
paymasterHubAddress: string;
|
|
82
|
+
/** bytes32 org id. */
|
|
83
|
+
orgId: string;
|
|
84
|
+
adminHatId: ethers.BigNumber;
|
|
85
|
+
/** 0 = none. */
|
|
86
|
+
operatorHatId: ethers.BigNumber;
|
|
87
|
+
/** Present → registerAndConfigureOrg; absent → registerOrg. */
|
|
88
|
+
config?: PaymasterRegisterConfig;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Port of the owner path of `pop paymaster register` —
|
|
92
|
+
* src/commands/paymaster/register.ts: PoaManager.adminCall(paymasterHub,
|
|
93
|
+
* registerCalldata). Only the PoaManager OWNER can send this (adminCall is
|
|
94
|
+
* onlyOwner); any other signer's transaction must revert — the CLI prints the
|
|
95
|
+
* calldata for the owner instead of broadcasting, which hosts can reproduce
|
|
96
|
+
* with buildRegisterCalldata + encodeIntent.
|
|
97
|
+
*/
|
|
98
|
+
export declare function buildAdminCallRegister(a: AdminCallRegisterArgs): TxIntent;
|
|
99
|
+
export interface DepositForOrgParams {
|
|
100
|
+
org: string;
|
|
101
|
+
/** Ether units of the native gas token (e.g. 0.05 = 0.05 xDAI on Gnosis). */
|
|
102
|
+
amount: string | number;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Resolved port of `pop paymaster deposit` — src/commands/paymaster/deposit.ts.
|
|
106
|
+
* Resolves the org id and the chain's singleton PaymasterHub proxy from the
|
|
107
|
+
* infrastructure subgraph (the hub is NOT in the org's module list). When
|
|
108
|
+
* ctx.provider is present, replicates the CLI's authoritative
|
|
109
|
+
* OrgNotRegistered gate (an eth_call on purpose — subgraph lag in a
|
|
110
|
+
* revert-predicting read means knowingly broadcasting a doomed tx).
|
|
111
|
+
*/
|
|
112
|
+
export declare function depositForOrgIntent(ctx: PopContext, params: DepositForOrgParams): Promise<TxIntent>;
|
|
113
|
+
export interface RegisterOrgParams {
|
|
114
|
+
org: string;
|
|
115
|
+
/** Hat ID that will administer the org's paymaster config (usually the org top hat). */
|
|
116
|
+
adminHat: string | number;
|
|
117
|
+
/** Optional hat ID allowed to manage budgets/rules (0 = none). */
|
|
118
|
+
operatorHat?: string | number;
|
|
119
|
+
/** Initial fee caps (gwei), rules, and budgets (ether caps) — uses registerAndConfigureOrg. */
|
|
120
|
+
config?: PaymasterRegisterConfig;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolved port of `pop paymaster register` —
|
|
124
|
+
* src/commands/paymaster/register.ts (the adminCall route). Resolves the org
|
|
125
|
+
* id + PaymasterHub + PoaManager from the infrastructure subgraph and returns
|
|
126
|
+
* the PoaManager.adminCall intent.
|
|
127
|
+
*
|
|
128
|
+
* Registration check is asymmetric like the CLI: a POSITIVE indexed
|
|
129
|
+
* "registered" answer is trusted; a negative is re-confirmed on chain when
|
|
130
|
+
* ctx.provider is available. An already-registered org throws
|
|
131
|
+
* PreconditionError (the CLI reports it as a no-op success — builder callers
|
|
132
|
+
* need the hard stop, since the intent would be a doomed re-registration).
|
|
133
|
+
*
|
|
134
|
+
* NOTE the on-chain gate the builder cannot check for you: adminCall is
|
|
135
|
+
* onlyOwner on the PoaManager, and PoaManager.owner() has no subgraph field
|
|
136
|
+
* in either live deployment — the CLI reads it via eth_call and prints the
|
|
137
|
+
* calldata instead of broadcasting when the signer is not the owner. Hosts
|
|
138
|
+
* should do the same before executing this intent.
|
|
139
|
+
*/
|
|
140
|
+
export declare function registerOrgIntent(ctx: PopContext, params: RegisterOrgParams): Promise<TxIntent>;
|