Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions script/deploy/DeployInfrastructure.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {EligibilityModule} from "../../src/EligibilityModule.sol";
import {ToggleModule} from "../../src/ToggleModule.sol";
import {PasskeyAccount} from "../../src/PasskeyAccount.sol";
import {PasskeyAccountFactory} from "../../src/PasskeyAccountFactory.sol";
import {ZkEmailInvites} from "../../src/ZkEmailInvites.sol";

// Infrastructure
import {ImplementationRegistry} from "../../src/ImplementationRegistry.sol";
Expand Down Expand Up @@ -115,6 +116,7 @@ contract DeployInfrastructure is Script {
address toggleImpl = address(new ToggleModule());
address passkeyAccountImpl = address(new PasskeyAccount());
address passkeyAccountFactoryImpl = address(new PasskeyAccountFactory());
address zkEmailInvitesImpl = address(new ZkEmailInvites());
address implRegImpl = address(new ImplementationRegistry());
address orgRegImpl = address(new OrgRegistry());
address deployerImpl = address(new OrgDeployer());
Expand Down Expand Up @@ -215,6 +217,9 @@ contract DeployInfrastructure is Script {
pm.addContractType("ToggleModule", toggleImpl);
pm.addContractType("PasskeyAccount", passkeyAccountImpl);
pm.addContractType("PasskeyAccountFactory", passkeyAccountFactoryImpl);
// ZkEmailInvites beacon registered unconditionally; the per-org module stays dormant
// until OrgDeployer.setZkEmailInfrastructure wires the verifier + DKIM registry.
pm.addContractType("ZkEmailInvites", zkEmailInvitesImpl);

console.log("\n--- Contract Types Registered ---");

Expand Down
5 changes: 5 additions & 0 deletions script/deploy/DeploySatelliteInfrastructure.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {EligibilityModule} from "../../src/EligibilityModule.sol";
import {ToggleModule} from "../../src/ToggleModule.sol";
import {PasskeyAccount} from "../../src/PasskeyAccount.sol";
import {PasskeyAccountFactory} from "../../src/PasskeyAccountFactory.sol";
import {ZkEmailInvites} from "../../src/ZkEmailInvites.sol";

/**
* @title DeploySatelliteInfrastructure
Expand Down Expand Up @@ -107,6 +108,7 @@ contract DeploySatelliteInfrastructure is Script {
_deployIfNeeded(dd, "ToggleModule", "v1", type(ToggleModule).creationCode);
_deployIfNeeded(dd, "PasskeyAccount", "v1", type(PasskeyAccount).creationCode);
_deployIfNeeded(dd, "PasskeyAccountFactory", "v1", type(PasskeyAccountFactory).creationCode);
_deployIfNeeded(dd, "ZkEmailInvites", "v1", type(ZkEmailInvites).creationCode);
}

function _deployIfNeeded(DeterministicDeployer dd, string memory typeName, string memory version, bytes memory code)
Expand Down Expand Up @@ -138,6 +140,9 @@ contract DeploySatelliteInfrastructure is Script {
_registerType(pm, dd, "ToggleModule", "v1");
_registerType(pm, dd, "PasskeyAccount", "v1");
_registerType(pm, dd, "PasskeyAccountFactory", "v1");
// ZkEmailInvites beacon registered unconditionally; the per-org module stays dormant
// until OrgDeployer.setZkEmailInfrastructure wires the verifier + DKIM registry.
_registerType(pm, dd, "ZkEmailInvites", "v1");
}

function _registerType(PoaManager pm, DeterministicDeployer dd, string memory typeName, string memory version)
Expand Down
11 changes: 9 additions & 2 deletions script/helpers/DeployHelper.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {EligibilityModule} from "../../src/EligibilityModule.sol";
import {ToggleModule} from "../../src/ToggleModule.sol";
import {PasskeyAccount} from "../../src/PasskeyAccount.sol";
import {PasskeyAccountFactory} from "../../src/PasskeyAccountFactory.sol";
import {ZkEmailInvites} from "../../src/ZkEmailInvites.sol";
import {OrgRegistry} from "../../src/OrgRegistry.sol";
import {OrgDeployer} from "../../src/OrgDeployer.sol";
import {PaymasterHub} from "../../src/PaymasterHub.sol";
Expand Down Expand Up @@ -45,12 +46,17 @@ abstract contract DeployHelper is Script {
address public constant POA_GUARDIAN = address(0);
uint256 public constant INITIAL_SOLIDARITY_FUND = 0.005 ether;

/// @notice Canonical list of the 13 application contract types.
/// @notice Canonical list of the 14 application contract types.
/// Infrastructure types (ImplementationRegistry, OrgRegistry,
/// OrgDeployer, PaymasterHub) are handled separately because they
/// require special initialization (beacon proxies, ownership, etc.).
/// @dev ZkEmailInvites is registered here so its beacon exists on every chain.
/// The module only *activates* once OrgDeployer.setZkEmailInfrastructure wires the
/// verifier + DKIM registry; until then the per-org gate in ModulesFactory skips it.
/// Registering the beacon unconditionally costs one extra impl deploy but removes the
/// ordering hazard where enabling infra before the beacon would brick org deploys.
function _contractTypes() internal pure returns (ContractType[] memory types) {
types = new ContractType[](13);
types = new ContractType[](14);
types[0] = ContractType("HybridVoting", type(HybridVoting).creationCode);
types[1] = ContractType("DirectDemocracyVoting", type(DirectDemocracyVoting).creationCode);
types[2] = ContractType("Executor", type(Executor).creationCode);
Expand All @@ -64,6 +70,7 @@ abstract contract DeployHelper is Script {
types[10] = ContractType("ToggleModule", type(ToggleModule).creationCode);
types[11] = ContractType("PasskeyAccount", type(PasskeyAccount).creationCode);
types[12] = ContractType("PasskeyAccountFactory", type(PasskeyAccountFactory).creationCode);
types[13] = ContractType("ZkEmailInvites", type(ZkEmailInvites).creationCode);
}

/// @notice Infrastructure contract types that need beacon registration for cross-chain upgrades.
Expand Down
8 changes: 2 additions & 6 deletions script/upgrades/UpgradeEligibilitySuperAdminLockdown.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,7 @@ contract DryRun_GnosisUpgrade is Script {

vm.prank(KUBI_EXECUTOR);
(bool okExecOn,) = KUBI_ELIG_MODULE.call(
abi.encodeWithSignature(
"setWearerEligibility(address,uint256,bool,bool)", probe, MEMBER_HAT, true, true
)
abi.encodeWithSignature("setWearerEligibility(address,uint256,bool,bool)", probe, MEMBER_HAT, true, true)
);
require(okExecOn, "DryRun: superAdmin setWearerEligibility(true,true) reverted");
{
Expand All @@ -328,9 +326,7 @@ contract DryRun_GnosisUpgrade is Script {

vm.prank(KUBI_EXECUTOR);
(bool okExecOff,) = KUBI_ELIG_MODULE.call(
abi.encodeWithSignature(
"setWearerEligibility(address,uint256,bool,bool)", probe, MEMBER_HAT, false, false
)
abi.encodeWithSignature("setWearerEligibility(address,uint256,bool,bool)", probe, MEMBER_HAT, false, false)
);
require(okExecOff, "DryRun: superAdmin setWearerEligibility(false,false) reverted");
{
Expand Down
81 changes: 78 additions & 3 deletions src/OrgDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ contract OrgDeployer is Initializable {
address universalPasskeyFactory; // Universal PasskeyAccountFactory for all orgs
uint256 _status; // manual reentrancy guard
IHats hatsV2; // upgrade-safe hats reference (inside ERC-7201 namespace)
// ZK Email protocol infra (set once per chain via PoaManager). If unset, ZkEmailInvites
// module deployment is skipped per-org and the feature is gracefully unavailable.
address zkEmailVerifier;
address zkEmailDkimRegistry;
}

/// @dev Legacy slot-0 hats variable. Kept for ABI compatibility with existing proxies.
Expand Down Expand Up @@ -219,6 +223,17 @@ contract OrgDeployer is Initializable {
l.universalPasskeyFactory = _universalFactory;
}

/// @notice Wire the per-chain ZK Email protocol infra. Once both are non-zero, every new
/// org gets a ZkEmailInvites proxy alongside its other modules.
/// @dev Only callable by PoaManager. Passing address(0) for either field is a no-op for
/// that field; pass both non-zero to enable the feature.
function setZkEmailInfrastructure(address verifier, address dkimRegistry) external {
Layout storage l = _layout();
if (msg.sender != l.poaManager) revert InvalidAddress();
if (verifier != address(0)) l.zkEmailVerifier = verifier;
if (dkimRegistry != address(0)) l.zkEmailDkimRegistry = dkimRegistry;
}

/*════════════════ DEPLOYMENT STRUCTS ════════════════*/

struct DeploymentResult {
Expand All @@ -231,6 +246,9 @@ contract OrgDeployer is Initializable {
address educationHub;
address paymentManager;
address eligibilityModule;
// address(0) on chains where ZK Email protocol infra (verifier + DKIM registry)
// has not been wired into the OrgDeployer yet — the module is skipped.
address zkEmailInvites;
}

struct RoleAssignments {
Expand Down Expand Up @@ -450,13 +468,18 @@ contract OrgDeployer is Initializable {
roleHatIds: gov.roleHatIds,
autoUpgrade: params.autoUpgrade,
roleAssignments: moduleRoles,
educationHubConfig: params.educationHubConfig
educationHubConfig: params.educationHubConfig,
zkEmailVerifier: l.zkEmailVerifier,
zkEmailDkimRegistry: l.zkEmailDkimRegistry,
accountRegistry: params.registryAddr,
universalFactory: l.universalPasskeyFactory
});

modules = l.modulesFactory.deployModules(moduleParams);
result.taskManager = modules.taskManager;
result.educationHub = modules.educationHub;
result.paymentManager = modules.paymentManager;
result.zkEmailInvites = modules.zkEmailInvites;
}

/* 7. Deploy Voting Mechanisms (HybridVoting, DirectDemocracyVoting) */
Expand Down Expand Up @@ -487,6 +510,11 @@ contract OrgDeployer is Initializable {
/* 9. Authorize QuickJoin to mint hats */
IExecutorAdmin(result.executor).setHatMinterAuthorization(result.quickJoin, true);

/* 9b. Authorize ZkEmailInvites to mint hats (only if the module was deployed) */
if (result.zkEmailInvites != address(0)) {
IExecutorAdmin(result.executor).setHatMinterAuthorization(result.zkEmailInvites, true);
}

/* 10. Link executor to governor */
IExecutorAdmin(result.executor).setCaller(result.hybridVoting);

Expand Down Expand Up @@ -867,9 +895,11 @@ contract OrgDeployer is Initializable {
pure
returns (address[] memory targets, bytes4[] memory selectors, bool[] memory allowed, uint32[] memory gasHints)
{
// Count: QuickJoin(6) + TaskManager(14) + HybridVoting(3) + DDVoting(3) + PaymentManager(5) + EligibilityModule(5) + ParticipationToken(3) + Registry(2) + EducationHub(0 or 4)
// Count: QuickJoin(6) + TaskManager(14) + HybridVoting(3) + DDVoting(3) + PaymentManager(5) + EligibilityModule(5) + ParticipationToken(3) + Registry(2) + EducationHub(0 or 4) + ZkEmailInvites(0 or 4)
uint256 count = 41;
if (educationEnabled) count += 4;
bool zkEmailEnabled = result.zkEmailInvites != address(0);
if (zkEmailEnabled) count += 4;

targets = new address[](count);
selectors = new bytes4[](count);
Expand All @@ -896,12 +926,57 @@ contract OrgDeployer is Initializable {
i += 4;
}

// Set all rules to allowed with 0 gas hint (use default)
if (zkEmailEnabled) {
i = _appendZkEmailInvitesRules(targets, selectors, gasHints, result.zkEmailInvites, i);
}

// Set all rules to allowed (gas hints already populated where non-zero).
for (uint256 j = 0; j < count; j++) {
allowed[j] = true;
}
}

/// @dev EmailProof tuple: (string,bytes32,uint256,string,bytes32,bytes32,bool,bytes)
/// PasskeyEnrollment tuple: (bytes32,bytes32,bytes32,uint256)
/// WebAuthnAuth tuple: (bytes,bytes,uint256,uint256,bytes32,bytes32)
function _appendZkEmailInvitesRules(
address[] memory targets,
bytes4[] memory selectors,
uint32[] memory gasHints,
address zk,
uint256 i
) private pure returns (uint256) {
// Bare claim: Groth16 verify (~250k) + DKIM lookup + hat mint.
targets[i] = zk;
selectors[i] =
bytes4(keccak256("claimRoleByDomain((string,bytes32,uint256,string,bytes32,bytes32,bool,bytes),address)"));
gasHints[i] = 800_000;
i++;
targets[i] = zk;
selectors[i] =
bytes4(keccak256("claimRoleByEmail((string,bytes32,uint256,string,bytes32,bytes32,bool,bytes),address)"));
gasHints[i] = 800_000;
i++;
// Combined register + claim: passkey registration + account create + proof verify + hat mint.
targets[i] = zk;
selectors[i] = bytes4(
keccak256(
"registerAndClaimByDomainWithPasskey((bytes32,bytes32,bytes32,uint256),string,uint256,uint256,(bytes,bytes,uint256,uint256,bytes32,bytes32),(string,bytes32,uint256,string,bytes32,bytes32,bool,bytes))"
)
);
gasHints[i] = 1_200_000;
i++;
targets[i] = zk;
selectors[i] = bytes4(
keccak256(
"registerAndClaimByEmailWithPasskey((bytes32,bytes32,bytes32,uint256),string,uint256,uint256,(bytes,bytes,uint256,uint256,bytes32,bytes32),(string,bytes32,uint256,string,bytes32,bytes32,bool,bytes))"
)
);
gasHints[i] = 1_200_000;
i++;
return i;
}

function _appendQuickJoinRules(address[] memory targets, bytes4[] memory selectors, address qj, uint256 i)
private
pure
Expand Down
Loading