From 3f382f42ffe9c93d6ed92ba42548f269b7d39354 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 04:51:32 +0500 Subject: [PATCH 1/8] feat: deploy transparant proxy contracts --- examples/CRISP/Readme.md | 6 +- examples/CRISP/enclave.config.yaml | 6 +- examples/CRISP/server/.env.example | 4 +- .../enclave-contracts/contracts/Enclave.sol | 28 +---- .../contracts/registry/BondingRegistry.sol | 36 +----- .../registry/CiphernodeRegistryOwnable.sol | 10 +- .../enclave-contracts/deployed_contracts.json | 65 +++++----- packages/enclave-contracts/hardhat.config.ts | 6 +- packages/enclave-contracts/package.json | 3 + .../scripts/deployAndSave/bondingRegistry.ts | 111 +++++++++++++++--- .../ciphernodeRegistryOwnable.ts | 101 ++++++++++++++-- .../scripts/deployAndSave/enclave.ts | 92 +++++++++++++-- .../scripts/upgrade/bondingRegistry.ts | 59 ++++++++++ .../upgrade/ciphernodeRegistryOwnable.ts | 65 ++++++++++ .../scripts/upgrade/enclave.ts | 59 ++++++++++ packages/enclave-contracts/scripts/utils.ts | 1 + templates/default/enclave.config.yaml | 6 +- tests/integration/enclave.config.yaml | 6 +- 18 files changed, 522 insertions(+), 142 deletions(-) create mode 100644 packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts create mode 100644 packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts create mode 100644 packages/enclave-contracts/scripts/upgrade/enclave.ts diff --git a/examples/CRISP/Readme.md b/examples/CRISP/Readme.md index a943319c83..213bfa0220 100644 --- a/examples/CRISP/Readme.md +++ b/examples/CRISP/Readme.md @@ -99,7 +99,7 @@ Deployments: ---------------------------------------------------------------------- Enclave: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 Verifier: 0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0 -InputValidator: 0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6 +InputValidator: 0x610178dA211FEF7D417bC0e6FeD39F05609AD788 CRISPInputValidatorFactory: 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 HonkVerifier: 0x9A676e781A523b5d0C0e43731313A708CB607508 CRISPProgram: 0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1 @@ -259,8 +259,8 @@ ETHERSCAN_API_KEY="" CRON_API_KEY=1234567890 # Based on Default Anvil Deployments (Only for testing) -ENCLAVE_ADDRESS="0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" -CIPHERNODE_REGISTRY_ADDRESS="0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" +ENCLAVE_ADDRESS="0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" +CIPHERNODE_REGISTRY_ADDRESS="0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" E3_PROGRAM_ADDRESS="0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" # CRISPProgram Contract Address FEE_TOKEN_ADDRESS="0x5FbDB2315678afecb367f032d93F642f64180aa3" # Mock ERC20 Token Address diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 45bd0d7664..96b92a1ac3 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -6,13 +6,13 @@ chains: address: "0xc5a5C42992dECbae36851359345FE25997F5C42d" deploy_block: 1 # Set to actual deploy block enclave: - address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" deploy_block: 1 # Set to actual deploy block ciphernode_registry: - address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + address: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" deploy_block: 1 # Set to actual deploy block bonding_registry: - address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" deploy_block: 1 # Set to actual deploy block fee_token: address: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index cf83c576ad..201dce4ade 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -13,8 +13,8 @@ ETHERSCAN_API_KEY="" CRON_API_KEY=1234567890 # Based on Default Anvil Deployments (Only for testing) -ENCLAVE_ADDRESS="0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" -CIPHERNODE_REGISTRY_ADDRESS="0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" +ENCLAVE_ADDRESS="0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" +CIPHERNODE_REGISTRY_ADDRESS="0x610178dA211FEF7D417bC0e6FeD39F05609AD788" E3_PROGRAM_ADDRESS="0xc5a5C42992dECbae36851359345FE25997F5C42d" # CRISPProgram Contract Address FEE_TOKEN_ADDRESS="0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 7753b6190d..7c61627452 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -196,30 +196,10 @@ contract Enclave is IEnclave, OwnableUpgradeable { // // //////////////////////////////////////////////////////////// - /// @notice Constructs the Enclave contract. - /// @dev Calls initialize() to set up the contract state. Can be used for non-proxy deployments. - /// @param _owner The owner address of this contract. - /// @param _ciphernodeRegistry The address of the Ciphernode Registry contract. - /// @param _bondingRegistry The address of the Bonding Registry contract. - /// @param _feeToken The address of the ERC20 token used for E3 fees. - /// @param _maxDuration The maximum duration of a computation in seconds. - /// @param _e3ProgramsParams Array of ABI encoded E3 encryption scheme parameters sets (e.g., for BFV). - constructor( - address _owner, - ICiphernodeRegistry _ciphernodeRegistry, - IBondingRegistry _bondingRegistry, - IERC20 _feeToken, - uint256 _maxDuration, - bytes[] memory _e3ProgramsParams - ) { - initialize( - _owner, - _ciphernodeRegistry, - _bondingRegistry, - _feeToken, - _maxDuration, - _e3ProgramsParams - ); + /// @notice Constructor that disables initializers. + /// @dev This contract does not use initializers. + constructor() { + _disableInitializers(); } /// @notice Initializes the Enclave contract with initial configuration. diff --git a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol index 8dee1952ea..9d2d013dfb 100644 --- a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol +++ b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol @@ -139,38 +139,10 @@ contract BondingRegistry is IBondingRegistry, OwnableUpgradeable { // // //////////////////////////////////////////////////////////// - /// @notice Constructor that initializes the bonding registry - /// @param _owner Address that will own the contract - /// @param _ticketToken Ticket token contract for collateral - /// @param _licenseToken License token contract for bonding - /// @param _registry Ciphernode registry contract - /// @param _slashedFundsTreasury Address to receive slashed funds - /// @param _ticketPrice Initial price per ticket - /// @param _licenseRequiredBond Initial required license bond for registration - /// @param _minTicketBalance Initial minimum ticket balance for activation - /// @param _exitDelay Initial exit delay period in seconds - constructor( - address _owner, - EnclaveTicketToken _ticketToken, - IERC20 _licenseToken, - ICiphernodeRegistry _registry, - address _slashedFundsTreasury, - uint256 _ticketPrice, - uint256 _licenseRequiredBond, - uint256 _minTicketBalance, - uint64 _exitDelay - ) { - initialize( - _owner, - _ticketToken, - _licenseToken, - _registry, - _slashedFundsTreasury, - _ticketPrice, - _licenseRequiredBond, - _minTicketBalance, - _exitDelay - ); + /// @notice Constructor that disables initializers. + /// @dev This contract does not use initializers. + constructor() { + _disableInitializers(); } /// @notice Initializes the bonding registry contract diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index db827933bd..deefda0710 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -175,12 +175,10 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { // // //////////////////////////////////////////////////////////// - /// @notice Constructor that initializes the registry with owner and enclave - /// @param _owner Address that will own the contract - /// @param _enclave Address of the Enclave contract - /// @param _submissionWindow The submission window for the E3 sortition in seconds - constructor(address _owner, address _enclave, uint256 _submissionWindow) { - initialize(_owner, _enclave, _submissionWindow); + /// @notice Constructor that disables initializers. + /// @dev This contract does not use initializers. + constructor() { + _disableInitializers(); } /// @notice Initializes the registry contract diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index d7b83debd8..1c53a5066d 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -116,45 +116,45 @@ }, "localhost": { "PoseidonT3": { - "blockNumber": 1, + "blockNumber": 3, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 2, - "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" + "blockNumber": 4, + "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 3, - "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + "blockNumber": 5, + "address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" }, "EnclaveTicketToken": { "constructorArgs": { - "baseToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "baseToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 5, - "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" + "blockNumber": 7, + "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "bondingRegistry": "0x0000000000000000000000000000000000000001" }, - "blockNumber": 6, - "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" + "blockNumber": 8, + "address": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, "BondingRegistry": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - "ticketToken": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", - "licenseToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "ticketToken": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", + "licenseToken": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", "registry": "0x0000000000000000000000000000000000000001", "slashedFundsTreasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "ticketPrice": "10000000", @@ -162,8 +162,9 @@ "minTicketBalance": "1", "exitDelay": "604800" }, - "blockNumber": 7, - "address": "0x0165878A594ca255338adfa4d48449f69242Eb8F" + "blockNumber": 8, + "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", + "implementationAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "CiphernodeRegistryOwnable": { "constructorArgs": { @@ -171,41 +172,43 @@ "enclaveAddress": "0x0000000000000000000000000000000000000001", "submissionWindow": "10" }, - "blockNumber": 8, - "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + "blockNumber": 11, + "address": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", + "implementationAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", - "bondingRegistry": "0x0165878A594ca255338adfa4d48449f69242Eb8F", - "feeToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "registry": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", + "bondingRegistry": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", + "feeToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", "maxDuration": "2592000", "params": [ - "0x000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000fc0010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000800000022a000100000000000000000000000000000000000000000000000000800000021a000100000000000000000000000000000000000000000000000000800000021200010000000000000000000000000000000000000000000000000080000001f60001" + "0x000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000fc00100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000003fffffff000001" ] }, - "blockNumber": 9, - "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + "blockNumber": 13, + "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + "implementationAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "MockComputeProvider": { - "blockNumber": 18, - "address": "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE" + "blockNumber": 23, + "address": "0x59b670e9fA9D0A427751Af201D676719a970857b" }, "MockDecryptionVerifier": { - "blockNumber": 19, - "address": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed" + "blockNumber": 24, + "address": "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1" }, "MockInputValidator": { - "blockNumber": 20, - "address": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c" + "blockNumber": 25, + "address": "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44" }, "MockE3Program": { "constructorArgs": { - "mockInputValidator": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c" + "mockInputValidator": "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44" }, - "blockNumber": 21, - "address": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d" + "blockNumber": 26, + "address": "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f" } } } \ No newline at end of file diff --git a/packages/enclave-contracts/hardhat.config.ts b/packages/enclave-contracts/hardhat.config.ts index 186f504277..66509ea589 100644 --- a/packages/enclave-contracts/hardhat.config.ts +++ b/packages/enclave-contracts/hardhat.config.ts @@ -168,7 +168,11 @@ const config: HardhatUserConfig = { tsNocheck: false, }, solidity: { - npmFilesToBuild: ["poseidon-solidity/PoseidonT3.sol"], + npmFilesToBuild: [ + "poseidon-solidity/PoseidonT3.sol", + "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol", + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol", + ], compilers: [ { version: "0.8.28", diff --git a/packages/enclave-contracts/package.json b/packages/enclave-contracts/package.json index 84c4a48406..bb1dd69102 100644 --- a/packages/enclave-contracts/package.json +++ b/packages/enclave-contracts/package.json @@ -146,6 +146,9 @@ "coverage": "pnpm test --coverage", "deploy": "hardhat run scripts/run.ts", "deploy:mocks": "export DEPLOY_MOCKS=true && pnpm clean:deployments && pnpm run deploy", + "upgrade:enclave": "hardhat run scripts/upgrade/enclave.ts", + "upgrade:bondingRegistry": "hardhat run scripts/upgrade/bondingRegistry.ts", + "upgrade:ciphernodeRegistryOwnable": "hardhat run scripts/upgrade/ciphernodeRegistryOwnable.ts", "e3:activate": "hardhat e3:activate", "e3:enable": "hardhat e3:enable", "ciphernode:add": "hardhat ciphernode:add", diff --git a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts index 0a44f35ce6..fc302e140a 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts @@ -87,26 +87,36 @@ export const deployAndSaveBondingRegistry = async ({ return { bondingRegistry: bondingRegistryContract }; } + const blockNumber = await ethers.provider.getBlockNumber(); + const bondingRegistryFactory = await ethers.getContractFactory("BondingRegistry"); - const bondingRegistry = await bondingRegistryFactory.deploy( - owner, - ticketToken, - licenseToken, - registry, - slashedFundsTreasury, - ticketPrice, - licenseRequiredBond, - minTicketBalance, - exitDelay, - ); - + const bondingRegistry = await bondingRegistryFactory.deploy(); await bondingRegistry.waitForDeployment(); + const bondingRegistryAddress = await bondingRegistry.getAddress(); - const blockNumber = await ethers.provider.getBlockNumber(); + const initData = bondingRegistryFactory.interface.encodeFunctionData( + "initialize", + [ + owner, + ticketToken, + licenseToken, + registry, + slashedFundsTreasury, + ticketPrice, + licenseRequiredBond, + minTicketBalance, + exitDelay, + ], + ); - const bondingRegistryAddress = await bondingRegistry.getAddress(); + const ProxyCF = await ethers.getContractFactory( + "TransparentUpgradeableProxy", + ); + const proxy = await ProxyCF.deploy(bondingRegistryAddress, signer, initData); + await proxy.waitForDeployment(); + const proxyAddress = await proxy.getAddress(); storeDeploymentArgs( { @@ -122,16 +132,85 @@ export const deployAndSaveBondingRegistry = async ({ exitDelay: exitDelay.toString(), }, blockNumber, - address: bondingRegistryAddress, + address: proxyAddress, + implementationAddress: bondingRegistryAddress, }, "BondingRegistry", chain, ); const bondingRegistryContract = BondingRegistryFactory.connect( - bondingRegistryAddress, + proxyAddress, signer, ); return { bondingRegistry: bondingRegistryContract }; }; + +/** + * Upgrades the BondingRegistry implementation while keeping the same proxy address + * @param param0 - The upgrade arguments + * @returns The upgraded BondingRegistry contract (same proxy address) + */ +export const upgradeAndSaveBondingRegistry = async ({ + proxyAdminAddress, + hre, +}: { + proxyAdminAddress: string; + hre: HardhatRuntimeEnvironment; +}): Promise<{ + bondingRegistry: BondingRegistry; + implementationAddress: string; +}> => { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const chain = hre.globalOptions.network; + + const preDeployedArgs = readDeploymentArgs("BondingRegistry", chain); + if (!preDeployedArgs?.address) { + throw new Error( + "BondingRegistry proxy not found. Deploy first before upgrading.", + ); + } + + const proxyAddress = preDeployedArgs.address; + + const bondingRegistryFactory = await ethers.getContractFactory( + "BondingRegistry", + signer, + ); + + const newImplementation = await bondingRegistryFactory.deploy(); + await newImplementation.waitForDeployment(); + const newImplementationAddress = await newImplementation.getAddress(); + + const proxyAdmin = await ethers.getContractAt( + "ProxyAdmin", + proxyAdminAddress, + signer, + ); + const upgradeTx = await proxyAdmin.upgrade( + proxyAddress, + newImplementationAddress, + ); + await upgradeTx.wait(); + + storeDeploymentArgs( + { + ...preDeployedArgs, + implementationAddress: newImplementationAddress, + }, + "BondingRegistry", + chain, + ); + + const bondingRegistryContract = BondingRegistryFactory.connect( + proxyAddress, + signer, + ); + + return { + bondingRegistry: bondingRegistryContract, + implementationAddress: newImplementationAddress, + }; +}; diff --git a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts index eaebb43a7d..7c4bfff1ae 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts @@ -75,18 +75,27 @@ export const deployAndSaveCiphernodeRegistryOwnable = async ({ signer, ); - const ciphernodeRegistry = await ciphernodeRegistryFactory.deploy( - owner, - enclaveAddress, - submissionWindow, - ); - + const ciphernodeRegistry = await ciphernodeRegistryFactory.deploy(); await ciphernodeRegistry.waitForDeployment(); - const blockNumber = await ethers.provider.getBlockNumber(); - const ciphernodeRegistryAddress = await ciphernodeRegistry.getAddress(); + const initData = ciphernodeRegistryFactory.interface.encodeFunctionData( + "initialize", + [owner, enclaveAddress, submissionWindow], + ); + + const ProxyCF = await ethers.getContractFactory( + "TransparentUpgradeableProxy", + ); + const proxy = await ProxyCF.deploy( + ciphernodeRegistryAddress, + signer, + initData, + ); + await proxy.waitForDeployment(); + const proxyAddress = await proxy.getAddress(); + storeDeploymentArgs( { constructorArgs: { @@ -95,16 +104,88 @@ export const deployAndSaveCiphernodeRegistryOwnable = async ({ submissionWindow: submissionWindow.toString(), }, blockNumber, - address: ciphernodeRegistryAddress, + address: proxyAddress, + implementationAddress: ciphernodeRegistryAddress, }, "CiphernodeRegistryOwnable", chain, ); const ciphernodeRegistryContract = CiphernodeRegistryOwnableFactory.connect( - ciphernodeRegistryAddress, + proxyAddress, signer, ); return { ciphernodeRegistry: ciphernodeRegistryContract }; }; + +export const upgradeAndSaveCiphernodeRegistryOwnable = async ({ + poseidonT3Address, + proxyAdminAddress, + hre, +}: { + poseidonT3Address: string; + proxyAdminAddress: string; + hre: HardhatRuntimeEnvironment; +}): Promise<{ + ciphernodeRegistry: CiphernodeRegistryOwnable; + implementationAddress: string; +}> => { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const chain = hre.globalOptions.network; + + const preDeployedArgs = readDeploymentArgs( + "CiphernodeRegistryOwnable", + chain, + ); + if (!preDeployedArgs?.address) { + throw new Error( + "CiphernodeRegistryOwnable proxy not found. Deploy first before upgrading.", + ); + } + + const proxyAddress = preDeployedArgs.address; + + const ciphernodeRegistryFactory = await ethers.getContractFactory( + CiphernodeRegistryOwnableFactory.abi, + CiphernodeRegistryOwnableFactory.linkBytecode({ + "npm/poseidon-solidity@0.0.5/PoseidonT3.sol:PoseidonT3": + poseidonT3Address, + }), + signer, + ); + + const newImplementation = await ciphernodeRegistryFactory.deploy(); + await newImplementation.waitForDeployment(); + const newImplementationAddress = await newImplementation.getAddress(); + + const proxyAdmin = await ethers.getContractAt( + "ProxyAdmin", + proxyAdminAddress, + signer, + ); + const upgradeTx = await proxyAdmin.upgrade( + proxyAddress, + newImplementationAddress, + ); + await upgradeTx.wait(); + + storeDeploymentArgs( + { + ...preDeployedArgs, + implementationAddress: newImplementationAddress, + }, + "CiphernodeRegistryOwnable", + chain, + ); + + const ciphernodeRegistryContract = CiphernodeRegistryOwnableFactory.connect( + proxyAddress, + signer, + ); + return { + ciphernodeRegistry: ciphernodeRegistryContract, + implementationAddress: newImplementationAddress, + }; +}; diff --git a/packages/enclave-contracts/scripts/deployAndSave/enclave.ts b/packages/enclave-contracts/scripts/deployAndSave/enclave.ts index 1b814b9e93..f2b7908a39 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/enclave.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/enclave.ts @@ -84,19 +84,26 @@ export const deployAndSaveEnclave = async ({ signer, ); - const enclave = await enclaveFactory.deploy( + const enclave = await enclaveFactory.deploy(); + await enclave.waitForDeployment(); + const blockNumber = await ethers.provider.getBlockNumber(); + const enclaveAddress = await enclave.getAddress(); + + const initData = enclaveFactory.interface.encodeFunctionData("initialize", [ owner, registry, bondingRegistry, feeToken, maxDuration, params, - ); - - await enclave.waitForDeployment(); + ]); - const enclaveAddress = await enclave.getAddress(); - const blockNumber = await ethers.provider.getBlockNumber(); + const ProxyCF = await ethers.getContractFactory( + "TransparentUpgradeableProxy", + ); + const proxy = await ProxyCF.deploy(enclaveAddress, signer, initData); + await proxy.waitForDeployment(); + const proxyAddress = await proxy.getAddress(); storeDeploymentArgs( { @@ -109,13 +116,82 @@ export const deployAndSaveEnclave = async ({ params, }, blockNumber, - address: enclaveAddress, + address: proxyAddress, + implementationAddress: enclaveAddress, }, "Enclave", chain, ); - const enclaveContract = EnclaveFactory.connect(enclaveAddress, signer); + const enclaveContract = EnclaveFactory.connect(proxyAddress, signer); return { enclave: enclaveContract }; }; + +/** + * Upgrades the Enclave implementation while keeping the same proxy address + * @param param0 - The upgrade arguments + * @returns The upgraded Enclave contract (same proxy address) + */ +export const upgradeAndSaveEnclave = async ({ + poseidonT3Address, + proxyAdminAddress, + hre, +}: { + poseidonT3Address: string; + proxyAdminAddress: string; + hre: HardhatRuntimeEnvironment; +}): Promise<{ enclave: Enclave; implementationAddress: string }> => { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const chain = hre.globalOptions.network; + + const preDeployedArgs = readDeploymentArgs("Enclave", chain); + if (!preDeployedArgs?.address) { + throw new Error("Enclave proxy not found. Deploy first before upgrading."); + } + + const proxyAddress = preDeployedArgs.address; + + const enclaveFactory = await ethers.getContractFactory( + EnclaveFactory.abi, + EnclaveFactory.linkBytecode({ + "npm/poseidon-solidity@0.0.5/PoseidonT3.sol:PoseidonT3": + poseidonT3Address, + }), + signer, + ); + + const newImplementation = await enclaveFactory.deploy(); + await newImplementation.waitForDeployment(); + const newImplementationAddress = await newImplementation.getAddress(); + console.log("New Implementation Address:", newImplementationAddress); + + const proxyAdmin = await ethers.getContractAt( + "ProxyAdmin", + proxyAdminAddress, + signer, + ); + + const upgradeTx = await proxyAdmin.upgradeAndCall( + proxyAddress, + newImplementationAddress, + "0x", + ); + await upgradeTx.wait(); + + storeDeploymentArgs( + { + ...preDeployedArgs, + implementationAddress: newImplementationAddress, + }, + "Enclave", + chain, + ); + + const enclaveContract = EnclaveFactory.connect(proxyAddress, signer); + return { + enclave: enclaveContract, + implementationAddress: newImplementationAddress, + }; +}; diff --git a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts new file mode 100644 index 0000000000..c938a9746d --- /dev/null +++ b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-3.0-only +import hre from "hardhat"; + +import { upgradeAndSaveBondingRegistry } from "../deployAndSave/bondingRegistry"; +import { readDeploymentArgs } from "../utils"; + +/** + * Upgrades the Enclave contract implementation and saves the deployment arguments + * This keeps the same proxy address, only updates the implementation + */ +export const upgradeBondingRegistry = async () => { + const { ethers } = await hre.network.connect(); + const [owner] = await ethers.getSigners(); + const ownerAddress = await owner.getAddress(); + const chain = hre.globalOptions.network; + console.log("Owner:", ownerAddress); + + const preDeployedArgs = readDeploymentArgs("BondingRegistry", chain); + if (!preDeployedArgs?.address) { + throw new Error( + "BondingRegistry proxy not found. Deploy first before upgrading.", + ); + } + + console.log( + "BondingRegistry Proxy Address (from deployments):", + preDeployedArgs.address, + ); + + const code = await ethers.provider.getCode(preDeployedArgs.address); + if (code === "0x") { + throw new Error( + `No contract found at proxy address ${preDeployedArgs.address}`, + ); + } + console.log("Proxy contract exists on-chain"); + + const { bondingRegistry, implementationAddress } = + await upgradeAndSaveBondingRegistry({ + proxyAdminAddress: ownerAddress, + hre, + }); + + const bondingRegistryAddress = await bondingRegistry.getAddress(); + + console.log(` + ============================================ + Upgrade Complete! + ============================================ + Proxy Address: ${bondingRegistryAddress} + New Implementation: ${implementationAddress} + ============================================ + `); +}; + +upgradeBondingRegistry().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts new file mode 100644 index 0000000000..a61399b800 --- /dev/null +++ b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LGPL-3.0-only +import hre from "hardhat"; + +import { upgradeAndSaveCiphernodeRegistryOwnable } from "../deployAndSave/ciphernodeRegistryOwnable"; +import { deployAndSavePoseidonT3 } from "../deployAndSave/poseidonT3"; +import { readDeploymentArgs } from "../utils"; + +/** + * Upgrades the Enclave contract implementation and saves the deployment arguments + * This keeps the same proxy address, only updates the implementation + */ +export const upgradeCiphernodeRegistryOwnable = async () => { + const { ethers } = await hre.network.connect(); + const [owner] = await ethers.getSigners(); + const ownerAddress = await owner.getAddress(); + const chain = hre.globalOptions.network; + console.log("Owner:", ownerAddress); + + const poseidonT3 = await deployAndSavePoseidonT3({ hre }); + const preDeployedArgs = readDeploymentArgs( + "CiphernodeRegistryOwnable", + chain, + ); + if (!preDeployedArgs?.address) { + throw new Error( + "CiphernodeRegistryOwnable proxy not found. Deploy first before upgrading.", + ); + } + + console.log( + "CiphernodeRegistryOwnable Proxy Address (from deployments):", + preDeployedArgs.address, + ); + + const code = await ethers.provider.getCode(preDeployedArgs.address); + if (code === "0x") { + throw new Error( + `No contract found at proxy address ${preDeployedArgs.address}`, + ); + } + console.log("Proxy contract exists on-chain"); + + const { ciphernodeRegistry, implementationAddress } = + await upgradeAndSaveCiphernodeRegistryOwnable({ + poseidonT3Address: poseidonT3, + proxyAdminAddress: ownerAddress, + hre, + }); + + const ciphernodeRegistryAddress = await ciphernodeRegistry.getAddress(); + + console.log(` + ============================================ + Upgrade Complete! + ============================================ + Proxy Address: ${ciphernodeRegistryAddress} + New Implementation: ${implementationAddress} + ============================================ + `); +}; + +upgradeCiphernodeRegistryOwnable().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/packages/enclave-contracts/scripts/upgrade/enclave.ts b/packages/enclave-contracts/scripts/upgrade/enclave.ts new file mode 100644 index 0000000000..eed9ebcf30 --- /dev/null +++ b/packages/enclave-contracts/scripts/upgrade/enclave.ts @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-3.0-only +import hre from "hardhat"; + +import { upgradeAndSaveEnclave } from "../deployAndSave/enclave"; +import { deployAndSavePoseidonT3 } from "../deployAndSave/poseidonT3"; +import { readDeploymentArgs } from "../utils"; + +/** + * Upgrades the Enclave contract implementation and saves the deployment arguments + * This keeps the same proxy address, only updates the implementation + */ +export const upgradeEnclave = async () => { + const { ethers } = await hre.network.connect(); + const [owner] = await ethers.getSigners(); + const ownerAddress = await owner.getAddress(); + const chain = hre.globalOptions.network; + console.log("Owner:", ownerAddress); + + const poseidonT3 = await deployAndSavePoseidonT3({ hre }); + const preDeployedArgs = readDeploymentArgs("Enclave", chain); + if (!preDeployedArgs?.address) { + throw new Error("Enclave proxy not found. Deploy first before upgrading."); + } + + console.log( + "Enclave Proxy Address (from deployments):", + preDeployedArgs.address, + ); + + const code = await ethers.provider.getCode(preDeployedArgs.address); + if (code === "0x") { + throw new Error( + `No contract found at proxy address ${preDeployedArgs.address}`, + ); + } + console.log("Proxy contract exists on-chain"); + + const { enclave, implementationAddress } = await upgradeAndSaveEnclave({ + poseidonT3Address: poseidonT3, + proxyAdminAddress: ownerAddress, + hre, + }); + + const enclaveAddress = await enclave.getAddress(); + + console.log(` + ============================================ + Upgrade Complete! + ============================================ + Proxy Address: ${enclaveAddress} + New Implementation: ${implementationAddress} + ============================================ + `); +}; + +upgradeEnclave().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index 64fcc5dc96..206dfec664 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -13,6 +13,7 @@ export interface DeploymentArgs { address: string; constructorArgs?: Record; blockNumber?: number | null; + implementationAddress?: string | null; } // Type for chain-specific deployments diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 24274110bf..510b37acb9 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -6,13 +6,13 @@ chains: address: "0x09635f643e140090a9a8dcd712ed6285858cebef" deploy_block: 1 # Set to actual deploy block enclave: - address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" deploy_block: 1 # Set to actual deploy block ciphernode_registry: - address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + address: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" deploy_block: 1 # Set to actual deploy block bonding_registry: - address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" deploy_block: 1 # Set to actual deploy block fee_token: address: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index 81daff0ca0..e8641d134e 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -6,13 +6,13 @@ chains: address: "0x09635F643e140090A9A8Dcd712eD6285858ceBef" deploy_block: 1 # Set to actual deploy block enclave: - address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" deploy_block: 1 # Set to actual deploy block ciphernode_registry: - address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + address: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" deploy_block: 1 # Set to actual deploy block bonding_registry: - address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" deploy_block: 1 # Set to actual deploy block fee_token: address: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" From 00b6cee9f1f3ba40652bbf72b61b1ca86dfdf971 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 17:36:00 +0500 Subject: [PATCH 2/8] fix: test to deploy proxy --- .../crisp-contracts/deployed_contracts.json | 57 ++++++++++--------- .../crisp-contracts/hardhat.config.ts | 2 + .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 2 +- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../contracts/registry/BondingRegistry.sol | 3 +- .../enclave-contracts/deployed_contracts.json | 4 +- .../ignition/modules/bondingRegistry.ts | 10 +++- .../ignition/modules/ciphernodeRegistry.ts | 24 +++++--- .../ignition/modules/enclave.ts | 27 ++++++--- .../scripts/deployAndSave/bondingRegistry.ts | 21 +++++-- .../ciphernodeRegistryOwnable.ts | 22 +++++-- .../scripts/deployAndSave/enclave.ts | 18 ++++-- packages/enclave-contracts/scripts/proxy.ts | 54 ++++++++++++++++++ .../scripts/upgrade/bondingRegistry.ts | 2 +- .../upgrade/ciphernodeRegistryOwnable.ts | 2 +- .../scripts/upgrade/enclave.ts | 2 +- .../enclave-contracts/test/Enclave.spec.ts | 29 +++++----- .../CiphernodeRegistryOwnable.spec.ts | 42 ++++++++++++-- templates/default/hardhat.config.ts | 2 + 20 files changed, 243 insertions(+), 84 deletions(-) create mode 100644 packages/enclave-contracts/scripts/proxy.ts diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index 87609307eb..c0188da069 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -158,8 +158,9 @@ "minTicketBalance": "1", "exitDelay": "604800" }, - "blockNumber": 9, - "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + "blockNumber": 8, + "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", + "implementationAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "CiphernodeRegistryOwnable": { "constructorArgs": { @@ -167,64 +168,66 @@ "enclaveAddress": "0x0000000000000000000000000000000000000001", "submissionWindow": "10" }, - "blockNumber": 10, - "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + "blockNumber": 11, + "address": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", + "implementationAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - "registry": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", - "bondingRegistry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "registry": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", + "bondingRegistry": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", "feeToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", "maxDuration": "2592000", "params": [ "0x000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000fc00100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000003fffffff000001" ] }, - "blockNumber": 11, - "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + "blockNumber": 13, + "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + "implementationAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "MockComputeProvider": { - "blockNumber": 20, - "address": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed" + "blockNumber": 23, + "address": "0x59b670e9fA9D0A427751Af201D676719a970857b" }, "MockDecryptionVerifier": { - "blockNumber": 21, - "address": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c" + "blockNumber": 24, + "address": "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1" }, "MockInputValidator": { - "blockNumber": 22, - "address": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d" + "blockNumber": 25, + "address": "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44" }, "MockE3Program": { "constructorArgs": { - "mockInputValidator": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d" + "mockInputValidator": "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44" }, - "blockNumber": 23, - "address": "0x59b670e9fA9D0A427751Af201D676719a970857b" + "blockNumber": 26, + "address": "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f" }, "MockRISC0Verifier": { - "address": "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f" + "address": "0x09635F643e140090A9A8Dcd712eD6285858ceBef" }, "CRISPInputValidator": { - "address": "0x4A679253410272dd5232B3Ff7cF5dbB88f295319" + "address": "0xc5a5C42992dECbae36851359345FE25997F5C42d" }, "CRISPInputValidatorFactory": { - "address": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", + "address": "0x67d269191c92Caf3cD7723F116c85e6E9bf55933", "constructorArgs": { - "inputValidator": "0x4A679253410272dd5232B3Ff7cF5dbB88f295319" + "inputValidator": "0xc5a5C42992dECbae36851359345FE25997F5C42d" } }, "HonkVerifier": { - "address": "0x09635F643e140090A9A8Dcd712eD6285858ceBef" + "address": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E" }, "CRISPProgram": { - "address": "0xc5a5C42992dECbae36851359345FE25997F5C42d", + "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690", "constructorArgs": { - "enclave": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", - "verifierAddress": "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f", - "inputValidatorAddress": "0x4A679253410272dd5232B3Ff7cF5dbB88f295319", - "honkVerifierAddress": "0x09635F643e140090A9A8Dcd712eD6285858ceBef", + "enclave": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + "verifierAddress": "0x09635F643e140090A9A8Dcd712eD6285858ceBef", + "inputValidatorAddress": "0xc5a5C42992dECbae36851359345FE25997F5C42d", + "honkVerifierAddress": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E", "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" } } diff --git a/examples/CRISP/packages/crisp-contracts/hardhat.config.ts b/examples/CRISP/packages/crisp-contracts/hardhat.config.ts index bced15cd93..1875a830b1 100644 --- a/examples/CRISP/packages/crisp-contracts/hardhat.config.ts +++ b/examples/CRISP/packages/crisp-contracts/hardhat.config.ts @@ -143,6 +143,8 @@ const config: HardhatUserConfig = { version: "0.8.28", npmFilesToBuild: [ "poseidon-solidity/PoseidonT3.sol", + "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol", + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol", "@enclave-e3/contracts/contracts/Enclave.sol", "@enclave-e3/contracts/contracts/registry/CiphernodeRegistryOwnable.sol", "@enclave-e3/contracts/contracts/registry/BondingRegistry.sol", diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 2fd0b2b467..0340498dd2 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -851,5 +851,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-c084532c59ac53ee00b443bea013584d95d4e870" + "buildInfoId": "solc-0_8_28-c47ee779175143051b923f87373a821faabf4e01" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index ffdaa8d9d4..99f471a58b 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -535,5 +535,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-c084532c59ac53ee00b443bea013584d95d4e870" + "buildInfoId": "solc-0_8_28-c47ee779175143051b923f87373a821faabf4e01" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index adf68d7704..f7286213f5 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -977,5 +977,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-c084532c59ac53ee00b443bea013584d95d4e870" + "buildInfoId": "solc-0_8_28-c47ee779175143051b923f87373a821faabf4e01" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol index 9d2d013dfb..a4482eb1d1 100644 --- a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol +++ b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol @@ -82,7 +82,7 @@ contract BondingRegistry is IBondingRegistry, OwnableUpgradeable { /// @notice Percentage (in basis points) of license bond that must remain bonded to stay active /// @dev Default 8000 = 80%. Allows operators to unbond up to 20% while remaining active - uint256 public licenseActiveBps = 8_000; + uint256 public licenseActiveBps; /// @notice Operator state data structure /// @param licenseBond Amount of license tokens currently bonded @@ -176,6 +176,7 @@ contract BondingRegistry is IBondingRegistry, OwnableUpgradeable { setLicenseRequiredBond(_licenseRequiredBond); setMinTicketBalance(_minTicketBalance); setExitDelay(_exitDelay); + setLicenseActiveBps(8_000); if (_owner != owner()) transferOwnership(_owner); } diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index 1c53a5066d..733a4667af 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -116,7 +116,7 @@ }, "localhost": { "PoseidonT3": { - "blockNumber": 3, + "blockNumber": 29, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { @@ -189,7 +189,7 @@ }, "blockNumber": 13, "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", - "implementationAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + "implementationAddress": "0xc5a5C42992dECbae36851359345FE25997F5C42d" }, "MockComputeProvider": { "blockNumber": 23, diff --git a/packages/enclave-contracts/ignition/modules/bondingRegistry.ts b/packages/enclave-contracts/ignition/modules/bondingRegistry.ts index de79a9ad2d..7e1e2d50d6 100644 --- a/packages/enclave-contracts/ignition/modules/bondingRegistry.ts +++ b/packages/enclave-contracts/ignition/modules/bondingRegistry.ts @@ -18,7 +18,9 @@ export default buildModule("BondingRegistry", (m) => { const exitDelay = m.getParameter("exitDelay"); const owner = m.getParameter("owner"); - const bondingRegistry = m.contract("BondingRegistry", [ + const bondingRegistryImpl = m.contract("BondingRegistry", []); + + const initData = m.encodeFunctionCall(bondingRegistryImpl, "initialize", [ owner, ticketToken, licenseToken, @@ -30,5 +32,11 @@ export default buildModule("BondingRegistry", (m) => { exitDelay, ]); + const bondingRegistry = m.contract("TransparentUpgradeableProxy", [ + bondingRegistryImpl, + owner, + initData, + ]); + return { bondingRegistry }; }) as any; diff --git a/packages/enclave-contracts/ignition/modules/ciphernodeRegistry.ts b/packages/enclave-contracts/ignition/modules/ciphernodeRegistry.ts index 4819aa85d2..15e599b404 100644 --- a/packages/enclave-contracts/ignition/modules/ciphernodeRegistry.ts +++ b/packages/enclave-contracts/ignition/modules/ciphernodeRegistry.ts @@ -14,15 +14,23 @@ export default buildModule("CiphernodeRegistry", (m) => { const poseidonT3 = m.library("PoseidonT3"); - const cipherNodeRegistry = m.contract( - "CiphernodeRegistryOwnable", - [owner, enclaveAddress, submissionWindow], - { - libraries: { - PoseidonT3: poseidonT3, - }, + const cipherNodeRegistryImpl = m.contract("CiphernodeRegistryOwnable", [], { + libraries: { + PoseidonT3: poseidonT3, }, - ); + }); + + const initData = m.encodeFunctionCall(cipherNodeRegistryImpl, "initialize", [ + owner, + enclaveAddress, + submissionWindow, + ]); + + const cipherNodeRegistry = m.contract("TransparentUpgradeableProxy", [ + cipherNodeRegistryImpl, + owner, + initData, + ]); return { cipherNodeRegistry }; }) as any; diff --git a/packages/enclave-contracts/ignition/modules/enclave.ts b/packages/enclave-contracts/ignition/modules/enclave.ts index 03aa4f9777..eb7c250e1b 100644 --- a/packages/enclave-contracts/ignition/modules/enclave.ts +++ b/packages/enclave-contracts/ignition/modules/enclave.ts @@ -17,15 +17,26 @@ export default buildModule("Enclave", (m) => { const poseidonT3 = m.library("PoseidonT3"); - const enclave = m.contract( - "Enclave", - [owner, registry, bondingRegistry, feeToken, maxDuration, [params]], - { - libraries: { - PoseidonT3: poseidonT3, - }, + const enclaveImpl = m.contract("Enclave", [], { + libraries: { + PoseidonT3: poseidonT3, }, - ); + }); + + const initData = m.encodeFunctionCall(enclaveImpl, "initialize", [ + owner, + registry, + bondingRegistry, + feeToken, + maxDuration, + [params], + ]); + + const enclave = m.contract("TransparentUpgradeableProxy", [ + enclaveImpl, + owner, + initData, + ]); return { enclave }; }) as any; diff --git a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts index fc302e140a..a60c636013 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts @@ -9,6 +9,7 @@ import { BondingRegistry, BondingRegistry__factory as BondingRegistryFactory, } from "../../types"; +import { getProxyAdmin, verifyProxyAdminOwner } from "../proxy"; import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; /** @@ -153,10 +154,10 @@ export const deployAndSaveBondingRegistry = async ({ * @returns The upgraded BondingRegistry contract (same proxy address) */ export const upgradeAndSaveBondingRegistry = async ({ - proxyAdminAddress, + ownerAddress, hre, }: { - proxyAdminAddress: string; + ownerAddress: string; hre: HardhatRuntimeEnvironment; }): Promise<{ bondingRegistry: BondingRegistry; @@ -175,6 +176,12 @@ export const upgradeAndSaveBondingRegistry = async ({ const proxyAddress = preDeployedArgs.address; + const autoProxyAdminAddress = await getProxyAdmin( + ethers.provider, + proxyAddress, + ); + console.log("Auto-deployed ProxyAdmin address:", autoProxyAdminAddress); + const bondingRegistryFactory = await ethers.getContractFactory( "BondingRegistry", signer, @@ -183,15 +190,21 @@ export const upgradeAndSaveBondingRegistry = async ({ const newImplementation = await bondingRegistryFactory.deploy(); await newImplementation.waitForDeployment(); const newImplementationAddress = await newImplementation.getAddress(); + console.log("New Implementation Address:", newImplementationAddress); const proxyAdmin = await ethers.getContractAt( "ProxyAdmin", - proxyAdminAddress, + autoProxyAdminAddress, signer, ); - const upgradeTx = await proxyAdmin.upgrade( + await verifyProxyAdminOwner(proxyAdmin, ownerAddress); + + // TODO: Add init data if needed + const initData = "0x"; + const upgradeTx = await proxyAdmin.upgradeAndCall( proxyAddress, newImplementationAddress, + initData, ); await upgradeTx.wait(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts index 7c4bfff1ae..70ac69b03c 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts @@ -9,6 +9,7 @@ import { CiphernodeRegistryOwnable, CiphernodeRegistryOwnable__factory as CiphernodeRegistryOwnableFactory, } from "../../types"; +import { getProxyAdmin, verifyProxyAdminOwner } from "../proxy"; import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; /** @@ -121,11 +122,11 @@ export const deployAndSaveCiphernodeRegistryOwnable = async ({ export const upgradeAndSaveCiphernodeRegistryOwnable = async ({ poseidonT3Address, - proxyAdminAddress, + ownerAddress, hre, }: { poseidonT3Address: string; - proxyAdminAddress: string; + ownerAddress: string; hre: HardhatRuntimeEnvironment; }): Promise<{ ciphernodeRegistry: CiphernodeRegistryOwnable; @@ -147,6 +148,12 @@ export const upgradeAndSaveCiphernodeRegistryOwnable = async ({ const proxyAddress = preDeployedArgs.address; + const autoProxyAdminAddress = await getProxyAdmin( + ethers.provider, + proxyAddress, + ); + console.log("Auto-deployed ProxyAdmin address:", autoProxyAdminAddress); + const ciphernodeRegistryFactory = await ethers.getContractFactory( CiphernodeRegistryOwnableFactory.abi, CiphernodeRegistryOwnableFactory.linkBytecode({ @@ -159,15 +166,22 @@ export const upgradeAndSaveCiphernodeRegistryOwnable = async ({ const newImplementation = await ciphernodeRegistryFactory.deploy(); await newImplementation.waitForDeployment(); const newImplementationAddress = await newImplementation.getAddress(); + console.log("New Implementation Address:", newImplementationAddress); const proxyAdmin = await ethers.getContractAt( "ProxyAdmin", - proxyAdminAddress, + autoProxyAdminAddress, signer, ); - const upgradeTx = await proxyAdmin.upgrade( + + await verifyProxyAdminOwner(proxyAdmin, ownerAddress); + + // TODO: Add init data if needed + const initData = "0x"; + const upgradeTx = await proxyAdmin.upgradeAndCall( proxyAddress, newImplementationAddress, + initData, ); await upgradeTx.wait(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/enclave.ts b/packages/enclave-contracts/scripts/deployAndSave/enclave.ts index f2b7908a39..ddf22355f7 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/enclave.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/enclave.ts @@ -6,6 +6,7 @@ import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import { Enclave, Enclave__factory as EnclaveFactory } from "../../types"; +import { getProxyAdmin, verifyProxyAdminOwner } from "../proxy"; import { areArraysEqual, readDeploymentArgs, @@ -135,11 +136,11 @@ export const deployAndSaveEnclave = async ({ */ export const upgradeAndSaveEnclave = async ({ poseidonT3Address, - proxyAdminAddress, + ownerAddress, hre, }: { poseidonT3Address: string; - proxyAdminAddress: string; + ownerAddress: string; hre: HardhatRuntimeEnvironment; }): Promise<{ enclave: Enclave; implementationAddress: string }> => { const { ethers } = await hre.network.connect(); @@ -153,6 +154,12 @@ export const upgradeAndSaveEnclave = async ({ const proxyAddress = preDeployedArgs.address; + const autoProxyAdminAddress = await getProxyAdmin( + ethers.provider, + proxyAddress, + ); + console.log("Auto-deployed ProxyAdmin address:", autoProxyAdminAddress); + const enclaveFactory = await ethers.getContractFactory( EnclaveFactory.abi, EnclaveFactory.linkBytecode({ @@ -169,14 +176,17 @@ export const upgradeAndSaveEnclave = async ({ const proxyAdmin = await ethers.getContractAt( "ProxyAdmin", - proxyAdminAddress, + autoProxyAdminAddress, signer, ); + await verifyProxyAdminOwner(proxyAdmin, ownerAddress); + // TODO: Add init data if needed + const initData = "0x"; const upgradeTx = await proxyAdmin.upgradeAndCall( proxyAddress, newImplementationAddress, - "0x", + initData, ); await upgradeTx.wait(); diff --git a/packages/enclave-contracts/scripts/proxy.ts b/packages/enclave-contracts/scripts/proxy.ts new file mode 100644 index 0000000000..3eb2e32dcd --- /dev/null +++ b/packages/enclave-contracts/scripts/proxy.ts @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { type Provider, getAddress } from "ethers"; + +/** + * ERC-1967 admin slot: keccak256("eip1967.proxy.admin") - 1 + * This is where TransparentUpgradeableProxy stores the ProxyAdmin address + * https://docs.openzeppelin.com/contracts/5.x/api/proxy#ERC1967Utils-getAdmin-- + */ +export const ERC1967_ADMIN_SLOT = + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; + +/** + * Gets the ProxyAdmin address from a TransparentUpgradeableProxy + * @param provider The ethers provider + * @param proxyAddress The address of the proxy contract + * @returns The address of the auto-deployed ProxyAdmin + */ +export async function getProxyAdmin( + provider: Provider, + proxyAddress: string, +): Promise { + const adminSlotValue = await provider.getStorage( + proxyAddress, + ERC1967_ADMIN_SLOT, + ); + + // Extract the address from the storage slot (last 20 bytes) + const addressHex = "0x" + adminSlotValue.slice(-40); + + return getAddress(addressHex); +} + +/** + * Verifies that the ProxyAdmin is owned by the expected owner + * @param proxyAdmin The ProxyAdmin contract instance + * @param expectedOwner The expected owner address + * @throws Error if owner doesn't match + */ +export async function verifyProxyAdminOwner( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + proxyAdmin: any, + expectedOwner: string, +): Promise { + const actualOwner = await proxyAdmin.owner(); + if (actualOwner.toLowerCase() !== expectedOwner.toLowerCase()) { + throw new Error( + `ProxyAdmin owner mismatch. Expected ${expectedOwner}, got ${actualOwner}`, + ); + } +} diff --git a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts index c938a9746d..a8714dd6c8 100644 --- a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts @@ -37,7 +37,7 @@ export const upgradeBondingRegistry = async () => { const { bondingRegistry, implementationAddress } = await upgradeAndSaveBondingRegistry({ - proxyAdminAddress: ownerAddress, + ownerAddress: ownerAddress, hre, }); diff --git a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts index a61399b800..a36c3b058d 100644 --- a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts @@ -43,7 +43,7 @@ export const upgradeCiphernodeRegistryOwnable = async () => { const { ciphernodeRegistry, implementationAddress } = await upgradeAndSaveCiphernodeRegistryOwnable({ poseidonT3Address: poseidonT3, - proxyAdminAddress: ownerAddress, + ownerAddress: ownerAddress, hre, }); diff --git a/packages/enclave-contracts/scripts/upgrade/enclave.ts b/packages/enclave-contracts/scripts/upgrade/enclave.ts index eed9ebcf30..10cfad3d60 100644 --- a/packages/enclave-contracts/scripts/upgrade/enclave.ts +++ b/packages/enclave-contracts/scripts/upgrade/enclave.ts @@ -37,7 +37,7 @@ export const upgradeEnclave = async () => { const { enclave, implementationAddress } = await upgradeAndSaveEnclave({ poseidonT3Address: poseidonT3, - proxyAdminAddress: ownerAddress, + ownerAddress: ownerAddress, hre, }); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 35613cfee1..83efd94102 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -22,6 +22,7 @@ import MockInputValidatorModule from "../ignition/modules/mockInputValidator"; import MockStableTokenModule from "../ignition/modules/mockStableToken"; import SlashingManagerModule from "../ignition/modules/slashingManager"; import { + BondingRegistry__factory as BondingRegistryFactory, CiphernodeRegistryOwnable__factory as CiphernodeRegistryOwnableFactory, Enclave__factory as EnclaveFactory, MockUSDC__factory as MockUSDCFactory, @@ -237,6 +238,12 @@ describe("Enclave", function () { ciphernodeRegistryAddress, owner, ); + + const bondingRegistry = BondingRegistryFactory.connect( + await bondingRegistryContract.bondingRegistry.getAddress(), + owner, + ); + const registryAddress = await enclave.ciphernodeRegistry(); if (registryAddress !== ciphernodeRegistryAddress) { @@ -244,25 +251,21 @@ describe("Enclave", function () { } await ciphernodeRegistryContract.setBondingRegistry( - await bondingRegistryContract.bondingRegistry.getAddress(), + await bondingRegistry.getAddress(), ); await ticketTokenContract.enclaveTicketToken.setRegistry( - await bondingRegistryContract.bondingRegistry.getAddress(), - ); - await bondingRegistryContract.bondingRegistry.setRegistry( - ciphernodeRegistryAddress, + await bondingRegistry.getAddress(), ); - await bondingRegistryContract.bondingRegistry.setSlashingManager( + await bondingRegistry.setRegistry(ciphernodeRegistryAddress); + await bondingRegistry.setSlashingManager( await slashingManagerContract.slashingManager.getAddress(), ); await slashingManagerContract.slashingManager.setBondingRegistry( - await bondingRegistryContract.bondingRegistry.getAddress(), + await bondingRegistry.getAddress(), ); - await bondingRegistryContract.bondingRegistry.setRewardDistributor( - enclaveAddress, - ); + await bondingRegistry.setRewardDistributor(enclaveAddress); const tree = new LeanIMT(hash); @@ -273,7 +276,7 @@ describe("Enclave", function () { await setupOperatorForSortition( operator1, - bondingRegistryContract.bondingRegistry, + bondingRegistry, licenseToken, usdcToken, ticketToken, @@ -283,7 +286,7 @@ describe("Enclave", function () { await setupOperatorForSortition( operator2, - bondingRegistryContract.bondingRegistry, + bondingRegistry, licenseToken, usdcToken, ticketToken, @@ -347,7 +350,7 @@ describe("Enclave", function () { return { enclave, ciphernodeRegistryContract, - bondingRegistry: bondingRegistryContract.bondingRegistry, + bondingRegistry: bondingRegistry, ticketToken: ticketTokenContract.enclaveTicketToken, licenseToken: licenseToken, usdcToken, diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index c5f09606a7..7e0f790a47 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -15,7 +15,10 @@ import EnclaveTicketTokenModule from "../../ignition/modules/enclaveTicketToken" import EnclaveTokenModule from "../../ignition/modules/enclaveToken"; import MockStableTokenModule from "../../ignition/modules/mockStableToken"; import SlashingManagerModule from "../../ignition/modules/slashingManager"; -import { CiphernodeRegistryOwnable__factory as CiphernodeRegistryFactory } from "../../types"; +import { + BondingRegistry__factory as BondingRegistryFactory, + CiphernodeRegistryOwnable__factory as CiphernodeRegistryFactory, +} from "../../types"; const AddressOne = "0x0000000000000000000000000000000000000001"; const AddressTwo = "0x0000000000000000000000000000000000000002"; @@ -160,7 +163,11 @@ describe("CiphernodeRegistryOwnable", function () { await registryContract.cipherNodeRegistry.getAddress(); const registry = CiphernodeRegistryFactory.connect(registryAddress, owner); - const bondingRegistry = bondingRegistryContract.bondingRegistry; + + const bondingRegistry = BondingRegistryFactory.connect( + await bondingRegistryContract.bondingRegistry.getAddress(), + owner, + ); await ticketTokenContract.enclaveTicketToken.setRegistry( await bondingRegistry.getAddress(), @@ -224,21 +231,44 @@ describe("CiphernodeRegistryOwnable", function () { it("correctly sets `_owner` and `enclave` ", async function () { const poseidonFactory = await ethers.getContractFactory("PoseidonT3"); const poseidonDeployment = await poseidonFactory.deploy(); + await poseidonDeployment.waitForDeployment(); + const poseidonAddress = await poseidonDeployment.getAddress(); const [deployer] = await ethers.getSigners(); if (!deployer) throw new Error("Bad getSigners() output"); + const ciphernodeRegistryFactory = await ethers.getContractFactory( "CiphernodeRegistryOwnable", { libraries: { - PoseidonT3: await poseidonDeployment.getAddress(), + PoseidonT3: poseidonAddress, }, }, ); - const ciphernodeRegistry = await ciphernodeRegistryFactory.deploy( + const implementation = await ciphernodeRegistryFactory.deploy(); + await implementation.waitForDeployment(); + const implementationAddress = await implementation.getAddress(); + + const initData = ciphernodeRegistryFactory.interface.encodeFunctionData( + "initialize", + [deployer.address, AddressTwo, SORTITION_SUBMISSION_WINDOW], + ); + + const proxyFactory = await ethers.getContractFactory( + "TransparentUpgradeableProxy", + ); + const proxy = await proxyFactory.deploy( + implementationAddress, deployer.address, - AddressTwo, - SORTITION_SUBMISSION_WINDOW, + initData, ); + await proxy.waitForDeployment(); + const proxyAddress = await proxy.getAddress(); + + const ciphernodeRegistry = CiphernodeRegistryFactory.connect( + proxyAddress, + deployer, + ); + expect(await ciphernodeRegistry.owner()).to.equal(deployer.address); expect(await ciphernodeRegistry.enclave()).to.equal(AddressTwo); expect(await ciphernodeRegistry.sortitionSubmissionWindow()).to.equal( diff --git a/templates/default/hardhat.config.ts b/templates/default/hardhat.config.ts index 739e7f671f..4af36e8d96 100644 --- a/templates/default/hardhat.config.ts +++ b/templates/default/hardhat.config.ts @@ -118,6 +118,8 @@ const config: HardhatUserConfig = { solidity: { npmFilesToBuild: [ "poseidon-solidity/PoseidonT3.sol", + "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol", + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol", "@enclave-e3/contracts/contracts/Enclave.sol", "@enclave-e3/contracts/contracts/registry/CiphernodeRegistryOwnable.sol", "@enclave-e3/contracts/contracts/registry/BondingRegistry.sol", From 676d5fa7e49de06d82ef0ddb5a9ac2febd7957af Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 17:36:31 +0500 Subject: [PATCH 3/8] chore: update contract addresses --- examples/CRISP/enclave.config.yaml | 2 +- templates/default/enclave.config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 96b92a1ac3..43efad8587 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -3,7 +3,7 @@ chains: rpc_url: "ws://localhost:8545" contracts: e3_program: - address: "0xc5a5C42992dECbae36851359345FE25997F5C42d" + address: "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" deploy_block: 1 # Set to actual deploy block enclave: address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 510b37acb9..11e04fcedb 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -3,7 +3,7 @@ chains: rpc_url: "ws://localhost:8545" contracts: e3_program: - address: "0x09635f643e140090a9a8dcd712ed6285858cebef" + address: "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" deploy_block: 1 # Set to actual deploy block enclave: address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" From 191e0dacfd55f54a33808ac529c12697194e6fcc Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 17:40:12 +0500 Subject: [PATCH 4/8] chore: update contract addresses --- examples/CRISP/client/.env.example | 2 +- examples/CRISP/server/.env.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/CRISP/client/.env.example b/examples/CRISP/client/.env.example index 233b53f9d7..abc74fa235 100644 --- a/examples/CRISP/client/.env.example +++ b/examples/CRISP/client/.env.example @@ -1,4 +1,4 @@ VITE_ENCLAVE_API=http://127.0.0.1:4000 VITE_TWITTER_SERVERLESS_API= VITE_WALLETCONNECT_PROJECT_ID= -VITE_E3_PROGRAM_ADDRESS=0xc5a5C42992dECbae36851359345FE25997F5C42d # Default E3 program address from hardhat +VITE_E3_PROGRAM_ADDRESS=0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 # Default E3 program address from hardhat diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index 201dce4ade..9dc272e761 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -15,7 +15,7 @@ CRON_API_KEY=1234567890 # Based on Default Anvil Deployments (Only for testing) ENCLAVE_ADDRESS="0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" CIPHERNODE_REGISTRY_ADDRESS="0x610178dA211FEF7D417bC0e6FeD39F05609AD788" -E3_PROGRAM_ADDRESS="0xc5a5C42992dECbae36851359345FE25997F5C42d" # CRISPProgram Contract Address +E3_PROGRAM_ADDRESS="0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" # CRISPProgram Contract Address FEE_TOKEN_ADDRESS="0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" # E3 Config From 09eb4b46a0ccdfbfb27300d0717e4e35b556f50b Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 18:05:26 +0500 Subject: [PATCH 5/8] chore: update contract addresses --- templates/default/enclave.config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 11e04fcedb..c1d111e3c4 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -3,7 +3,7 @@ chains: rpc_url: "ws://localhost:8545" contracts: e3_program: - address: "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" + address: "0xe6e340d132b5f46d1e472debcd681b2abc16e57e" deploy_block: 1 # Set to actual deploy block enclave: address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" From 61cb42cead93f2b83736aad16bb3918d5183bda3 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 18:12:06 +0500 Subject: [PATCH 6/8] chore: fix headers --- .../CRISP/circuits/src/ciphertext_addition.nr | 4 + .../packages/crisp-contracts/deploy/crisp.ts | 348 +++++++++++------- .../scripts/upgrade/bondingRegistry.ts | 4 + .../upgrade/ciphernodeRegistryOwnable.ts | 4 + .../scripts/upgrade/enclave.ts | 4 + 5 files changed, 224 insertions(+), 140 deletions(-) diff --git a/examples/CRISP/circuits/src/ciphertext_addition.nr b/examples/CRISP/circuits/src/ciphertext_addition.nr index e1afecdcf0..4beb864fa4 100644 --- a/examples/CRISP/circuits/src/ciphertext_addition.nr +++ b/examples/CRISP/circuits/src/ciphertext_addition.nr @@ -1,5 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-only // +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + // This file contains ciphertext addition verification for BFV ciphertexts. use greco::CryptographicParams; diff --git a/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts b/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts index 7806ca0d7f..4284f8e8aa 100644 --- a/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts +++ b/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts @@ -1,85 +1,105 @@ - // SPDX-License-Identifier: LGPL-3.0-only // // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { readDeploymentArgs, storeDeploymentArgs } from "@enclave-e3/contracts/scripts"; +import { + readDeploymentArgs, + storeDeploymentArgs, +} from "@enclave-e3/contracts/scripts"; import { Enclave__factory as EnclaveFactory } from "@enclave-e3/contracts/types"; import { execSync } from "child_process"; import hre from "hardhat"; -const IMAGE_ID = "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" +const IMAGE_ID = + "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2"; export const deployCRISPContracts = async () => { - const { ethers } = await hre.network.connect(); - const [owner] = await ethers.getSigners(); - - const chain = hre.globalOptions.network; - - const useMockVerifier = Boolean(process.env.USE_MOCK_VERIFIER) ?? false; - const useMockInputValidator = Boolean(process.env.USE_MOCK_INPUT_VALIDATOR) ?? false; - - console.log("useMockVerifier", useMockVerifier) - - const verifier = await deployVerifier(useMockVerifier) - - const inputValidator = await deployInputValidator(useMockInputValidator) - - const enclaveAddress = readDeploymentArgs("Enclave", chain)?.address; - if (!enclaveAddress) { - throw new Error("Enclave address not found, it must be deployed first"); - } - const enclave = EnclaveFactory.connect(enclaveAddress, owner); - - const crispInputValidatorFactoryFactory = await ethers.getContractFactory("CRISPInputValidatorFactory") - const crispInputValidatorFactory = await crispInputValidatorFactoryFactory.deploy(inputValidator); - - const crispInputValidatorFactoryAddress = await crispInputValidatorFactory.getAddress(); - storeDeploymentArgs({ - address: crispInputValidatorFactoryAddress, - constructorArgs: { - inputValidator - } - }, "CRISPInputValidatorFactory", chain); - - const honkVerifierFactory = await ethers.getContractFactory("HonkVerifier"); - const honkVerifier = await honkVerifierFactory.deploy(); - const honkVerifierAddress = await honkVerifier.getAddress(); - - storeDeploymentArgs({ - address: honkVerifierAddress, - }, "HonkVerifier", chain); - - const crispFactory = await ethers.getContractFactory("CRISPProgram"); - const crisp = await crispFactory.deploy( - enclaveAddress, - verifier, - crispInputValidatorFactory.getAddress(), + const { ethers } = await hre.network.connect(); + const [owner] = await ethers.getSigners(); + + const chain = hre.globalOptions.network; + + const useMockVerifier = Boolean(process.env.USE_MOCK_VERIFIER) ?? false; + const useMockInputValidator = + Boolean(process.env.USE_MOCK_INPUT_VALIDATOR) ?? false; + + console.log("useMockVerifier", useMockVerifier); + + const verifier = await deployVerifier(useMockVerifier); + + const inputValidator = await deployInputValidator(useMockInputValidator); + + const enclaveAddress = readDeploymentArgs("Enclave", chain)?.address; + if (!enclaveAddress) { + throw new Error("Enclave address not found, it must be deployed first"); + } + const enclave = EnclaveFactory.connect(enclaveAddress, owner); + + const crispInputValidatorFactoryFactory = await ethers.getContractFactory( + "CRISPInputValidatorFactory" + ); + const crispInputValidatorFactory = + await crispInputValidatorFactoryFactory.deploy(inputValidator); + + const crispInputValidatorFactoryAddress = + await crispInputValidatorFactory.getAddress(); + storeDeploymentArgs( + { + address: crispInputValidatorFactoryAddress, + constructorArgs: { + inputValidator, + }, + }, + "CRISPInputValidatorFactory", + chain + ); + + const honkVerifierFactory = await ethers.getContractFactory("HonkVerifier"); + const honkVerifier = await honkVerifierFactory.deploy(); + const honkVerifierAddress = await honkVerifier.getAddress(); + + storeDeploymentArgs( + { + address: honkVerifierAddress, + }, + "HonkVerifier", + chain + ); + + const crispFactory = await ethers.getContractFactory("CRISPProgram"); + const crisp = await crispFactory.deploy( + enclaveAddress, + verifier, + crispInputValidatorFactory.getAddress(), + honkVerifierAddress, + IMAGE_ID + ); + + const crispAddress = await crisp.getAddress(); + + storeDeploymentArgs( + { + address: crispAddress, + constructorArgs: { + enclave: enclaveAddress, + verifierAddress: verifier, + inputValidatorAddress: inputValidator, honkVerifierAddress, - IMAGE_ID - ); - - const crispAddress = await crisp.getAddress(); - - storeDeploymentArgs({ - address: crispAddress, - constructorArgs: { - enclave: enclaveAddress, - verifierAddress: verifier, - inputValidatorAddress: inputValidator, - honkVerifierAddress, - imageId: IMAGE_ID - } - }, "CRISPProgram", chain); - - // enable the program on Enclave - const tx = await enclave.enableE3Program(crispAddress); - await tx.wait(); - - console.log(` + imageId: IMAGE_ID, + }, + }, + "CRISPProgram", + chain + ); + + // enable the program on Enclave + const tx = await enclave.enableE3Program(crispAddress); + await tx.wait(); + + console.log(` Deployments: ---------------------------------------------------------------------- Enclave: ${enclaveAddress} @@ -89,98 +109,146 @@ export const deployCRISPContracts = async () => { HonkVerifier: ${honkVerifierAddress} CRISPProgram: ${crispAddress} `); -} +}; /** * Deploys the verifier contract * @param useMockVerifier - whether to use a mock verifier * @returns The address of the verifier */ -export const deployVerifier = async (useMockVerifier: boolean): Promise => { - const { ethers } = await hre.network.connect(); - const chain = hre.globalOptions.network; - - if (!useMockVerifier) { - const existingVerifier = readDeploymentArgs("RiscZeroGroth16Verifier", chain); - if (existingVerifier?.address) { - console.log("RiscZeroGroth16Verifier already deployed at:", existingVerifier.address); - return existingVerifier.address; - } - - const verifierFactory = await ethers.getContractFactory("RiscZeroGroth16Verifier"); - const verifier = await verifierFactory.deploy(); - await verifier.waitForDeployment(); - - const address = await verifier.getAddress(); - - storeDeploymentArgs({ - address, - }, "RiscZeroGroth16Verifier", chain); - - return address; - } - - // Check if mock verifier already deployed - const existingMockVerifier = readDeploymentArgs("MockRISC0Verifier", chain); - if (existingMockVerifier?.address) { - console.log("MockRISC0Verifier already deployed at:", existingMockVerifier.address); - return existingMockVerifier.address; +export const deployVerifier = async ( + useMockVerifier: boolean +): Promise => { + const { ethers } = await hre.network.connect(); + const chain = hre.globalOptions.network; + + if (!useMockVerifier) { + const existingVerifier = readDeploymentArgs( + "RiscZeroGroth16Verifier", + chain + ); + if (existingVerifier?.address) { + console.log( + "RiscZeroGroth16Verifier already deployed at:", + existingVerifier.address + ); + return existingVerifier.address; } - const mockVerifierFactory = await ethers.getContractFactory("MockRISC0Verifier"); - const mockVerifier = await mockVerifierFactory.deploy(); + const verifierFactory = await ethers.getContractFactory( + "RiscZeroGroth16Verifier" + ); + const verifier = await verifierFactory.deploy(); + await verifier.waitForDeployment(); - const mockVerifierAddress = await mockVerifier.getAddress(); - storeDeploymentArgs({ - address: mockVerifierAddress, - }, "MockRISC0Verifier", hre.globalOptions.network); + const address = await verifier.getAddress(); - return mockVerifierAddress; -} + storeDeploymentArgs( + { + address, + }, + "RiscZeroGroth16Verifier", + chain + ); + return address; + } + + // Check if mock verifier already deployed + const existingMockVerifier = readDeploymentArgs("MockRISC0Verifier", chain); + if (existingMockVerifier?.address) { + console.log( + "MockRISC0Verifier already deployed at:", + existingMockVerifier.address + ); + return existingMockVerifier.address; + } + + const mockVerifierFactory = await ethers.getContractFactory( + "MockRISC0Verifier" + ); + const mockVerifier = await mockVerifierFactory.deploy(); + + const mockVerifierAddress = await mockVerifier.getAddress(); + storeDeploymentArgs( + { + address: mockVerifierAddress, + }, + "MockRISC0Verifier", + hre.globalOptions.network + ); + + return mockVerifierAddress; +}; /** * Deploys the input validator contract * @param useMockInputValidator - whether to use a mock input validator * @returns The address of the input validator */ -export const deployInputValidator = async (useMockInputValidator: boolean): Promise => { - const { ethers } = await hre.network.connect(); - const chain = hre.globalOptions.network; - - if (useMockInputValidator) { - // Check if mock input validator already deployed - const existingMockInputValidator = readDeploymentArgs("MockCRISPInputValidator", chain); - if (existingMockInputValidator?.address) { - console.log("MockInputValidator already deployed at:", existingMockInputValidator.address); - return existingMockInputValidator.address; - } - - const mockInputValidatorFactory = await ethers.getContractFactory("MockCRISPInputValidator"); - const mockInputValidator = await mockInputValidatorFactory.deploy(); - const address = await mockInputValidator.getAddress(); - - storeDeploymentArgs({ - address, - }, "MockCRISPInputValidator", hre.globalOptions.network); - - return address; - } - - // Check if input validator already deployed - const existingInputValidator = readDeploymentArgs("CRISPInputValidator", chain); - if (existingInputValidator?.address) { - console.log("CRISPInputValidator already deployed at:", existingInputValidator.address); - return existingInputValidator.address; +export const deployInputValidator = async ( + useMockInputValidator: boolean +): Promise => { + const { ethers } = await hre.network.connect(); + const chain = hre.globalOptions.network; + + if (useMockInputValidator) { + // Check if mock input validator already deployed + const existingMockInputValidator = readDeploymentArgs( + "MockCRISPInputValidator", + chain + ); + if (existingMockInputValidator?.address) { + console.log( + "MockInputValidator already deployed at:", + existingMockInputValidator.address + ); + return existingMockInputValidator.address; } - const inputValidatorFactory = await ethers.getContractFactory("CRISPInputValidator"); - const inputValidator = await inputValidatorFactory.deploy(); - const address = await inputValidator.getAddress(); + const mockInputValidatorFactory = await ethers.getContractFactory( + "MockCRISPInputValidator" + ); + const mockInputValidator = await mockInputValidatorFactory.deploy(); + const address = await mockInputValidator.getAddress(); - storeDeploymentArgs({ + storeDeploymentArgs( + { address, - }, "CRISPInputValidator", hre.globalOptions.network); + }, + "MockCRISPInputValidator", + hre.globalOptions.network + ); return address; -} + } + + // Check if input validator already deployed + const existingInputValidator = readDeploymentArgs( + "CRISPInputValidator", + chain + ); + if (existingInputValidator?.address) { + console.log( + "CRISPInputValidator already deployed at:", + existingInputValidator.address + ); + return existingInputValidator.address; + } + + const inputValidatorFactory = await ethers.getContractFactory( + "CRISPInputValidator" + ); + const inputValidator = await inputValidatorFactory.deploy(); + const address = await inputValidator.getAddress(); + + storeDeploymentArgs( + { + address, + }, + "CRISPInputValidator", + hre.globalOptions.network + ); + + return address; +}; diff --git a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts index a8714dd6c8..81ef08324e 100644 --- a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts @@ -1,4 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. import hre from "hardhat"; import { upgradeAndSaveBondingRegistry } from "../deployAndSave/bondingRegistry"; diff --git a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts index a36c3b058d..c228191ae1 100644 --- a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts @@ -1,4 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. import hre from "hardhat"; import { upgradeAndSaveCiphernodeRegistryOwnable } from "../deployAndSave/ciphernodeRegistryOwnable"; diff --git a/packages/enclave-contracts/scripts/upgrade/enclave.ts b/packages/enclave-contracts/scripts/upgrade/enclave.ts index 10cfad3d60..e6d4948ea7 100644 --- a/packages/enclave-contracts/scripts/upgrade/enclave.ts +++ b/packages/enclave-contracts/scripts/upgrade/enclave.ts @@ -1,4 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. import hre from "hardhat"; import { upgradeAndSaveEnclave } from "../deployAndSave/enclave"; From 6237ae3ed5af1c6f40be27ef9b00372463ec9024 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 18:20:58 +0500 Subject: [PATCH 7/8] chore: update comments --- .../enclave-contracts/contracts/Enclave.sol | 2 +- .../contracts/registry/BondingRegistry.sol | 2 +- .../registry/CiphernodeRegistryOwnable.sol | 2 +- .../enclave-contracts/deployed_contracts.json | 6 +++--- .../scripts/upgrade/bondingRegistry.ts | 18 ++++++++++++------ .../upgrade/ciphernodeRegistryOwnable.ts | 18 ++++++++++++------ .../scripts/upgrade/enclave.ts | 16 +++++++++++----- 7 files changed, 41 insertions(+), 23 deletions(-) diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 7c61627452..5a985bf5a2 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -197,7 +197,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { //////////////////////////////////////////////////////////// /// @notice Constructor that disables initializers. - /// @dev This contract does not use initializers. + /// @dev Prevents the implementation contract from being initialized. Initialization is performed via the initialize() function when deployed behind a proxy. constructor() { _disableInitializers(); } diff --git a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol index a4482eb1d1..745aa9cfc5 100644 --- a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol +++ b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol @@ -140,7 +140,7 @@ contract BondingRegistry is IBondingRegistry, OwnableUpgradeable { //////////////////////////////////////////////////////////// /// @notice Constructor that disables initializers. - /// @dev This contract does not use initializers. + /// @dev Prevents the implementation contract from being initialized. Initialization is performed via the initialize() function when deployed behind a proxy. constructor() { _disableInitializers(); } diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index deefda0710..3351402714 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -176,7 +176,7 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { //////////////////////////////////////////////////////////// /// @notice Constructor that disables initializers. - /// @dev This contract does not use initializers. + /// @dev Prevents the implementation contract from being initialized. Initialization is performed via the initialize() function when deployed behind a proxy. constructor() { _disableInitializers(); } diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index 733a4667af..3d4079522e 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -116,7 +116,7 @@ }, "localhost": { "PoseidonT3": { - "blockNumber": 29, + "blockNumber": 28, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { @@ -164,7 +164,7 @@ }, "blockNumber": 8, "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", - "implementationAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + "implementationAddress": "0x67d269191c92Caf3cD7723F116c85e6E9bf55933" }, "CiphernodeRegistryOwnable": { "constructorArgs": { @@ -189,7 +189,7 @@ }, "blockNumber": 13, "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", - "implementationAddress": "0xc5a5C42992dECbae36851359345FE25997F5C42d" + "implementationAddress": "0x09635F643e140090A9A8Dcd712eD6285858ceBef" }, "MockComputeProvider": { "blockNumber": 23, diff --git a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts index 81ef08324e..41d5e54cae 100644 --- a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts @@ -9,15 +9,15 @@ import { upgradeAndSaveBondingRegistry } from "../deployAndSave/bondingRegistry" import { readDeploymentArgs } from "../utils"; /** - * Upgrades the Enclave contract implementation and saves the deployment arguments + * Upgrades the BondingRegistry contract implementation and saves the deployment arguments * This keeps the same proxy address, only updates the implementation */ export const upgradeBondingRegistry = async () => { const { ethers } = await hre.network.connect(); - const [owner] = await ethers.getSigners(); - const ownerAddress = await owner.getAddress(); - const chain = hre.globalOptions.network; - console.log("Owner:", ownerAddress); + const [signer] = await ethers.getSigners(); + const signerAddress = await signer.getAddress(); + const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + console.log("Signer:", signerAddress); const preDeployedArgs = readDeploymentArgs("BondingRegistry", chain); if (!preDeployedArgs?.address) { @@ -26,6 +26,12 @@ export const upgradeBondingRegistry = async () => { ); } + if (!preDeployedArgs?.implementationAddress) { + throw new Error( + "Existing deployment is not proxy-based. Cannot upgrade non-proxy deployments.", + ); + } + console.log( "BondingRegistry Proxy Address (from deployments):", preDeployedArgs.address, @@ -41,7 +47,7 @@ export const upgradeBondingRegistry = async () => { const { bondingRegistry, implementationAddress } = await upgradeAndSaveBondingRegistry({ - ownerAddress: ownerAddress, + ownerAddress: signerAddress, hre, }); diff --git a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts index c228191ae1..9c21d726c3 100644 --- a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts @@ -10,15 +10,15 @@ import { deployAndSavePoseidonT3 } from "../deployAndSave/poseidonT3"; import { readDeploymentArgs } from "../utils"; /** - * Upgrades the Enclave contract implementation and saves the deployment arguments + * Upgrades the CiphernodeRegistryOwnable contract implementation and saves the deployment arguments * This keeps the same proxy address, only updates the implementation */ export const upgradeCiphernodeRegistryOwnable = async () => { const { ethers } = await hre.network.connect(); - const [owner] = await ethers.getSigners(); - const ownerAddress = await owner.getAddress(); - const chain = hre.globalOptions.network; - console.log("Owner:", ownerAddress); + const [signer] = await ethers.getSigners(); + const signerAddress = await signer.getAddress(); + const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + console.log("Signer:", signerAddress); const poseidonT3 = await deployAndSavePoseidonT3({ hre }); const preDeployedArgs = readDeploymentArgs( @@ -31,6 +31,12 @@ export const upgradeCiphernodeRegistryOwnable = async () => { ); } + if (!preDeployedArgs?.implementationAddress) { + throw new Error( + "Existing deployment is not proxy-based. Cannot upgrade non-proxy deployments.", + ); + } + console.log( "CiphernodeRegistryOwnable Proxy Address (from deployments):", preDeployedArgs.address, @@ -47,7 +53,7 @@ export const upgradeCiphernodeRegistryOwnable = async () => { const { ciphernodeRegistry, implementationAddress } = await upgradeAndSaveCiphernodeRegistryOwnable({ poseidonT3Address: poseidonT3, - ownerAddress: ownerAddress, + ownerAddress: signerAddress, hre, }); diff --git a/packages/enclave-contracts/scripts/upgrade/enclave.ts b/packages/enclave-contracts/scripts/upgrade/enclave.ts index e6d4948ea7..24bb832e0b 100644 --- a/packages/enclave-contracts/scripts/upgrade/enclave.ts +++ b/packages/enclave-contracts/scripts/upgrade/enclave.ts @@ -15,10 +15,10 @@ import { readDeploymentArgs } from "../utils"; */ export const upgradeEnclave = async () => { const { ethers } = await hre.network.connect(); - const [owner] = await ethers.getSigners(); - const ownerAddress = await owner.getAddress(); - const chain = hre.globalOptions.network; - console.log("Owner:", ownerAddress); + const [signer] = await ethers.getSigners(); + const signerAddress = await signer.getAddress(); + const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + console.log("Signer:", signerAddress); const poseidonT3 = await deployAndSavePoseidonT3({ hre }); const preDeployedArgs = readDeploymentArgs("Enclave", chain); @@ -26,6 +26,12 @@ export const upgradeEnclave = async () => { throw new Error("Enclave proxy not found. Deploy first before upgrading."); } + if (!preDeployedArgs?.implementationAddress) { + throw new Error( + "Existing deployment is not proxy-based. Cannot upgrade non-proxy deployments.", + ); + } + console.log( "Enclave Proxy Address (from deployments):", preDeployedArgs.address, @@ -41,7 +47,7 @@ export const upgradeEnclave = async () => { const { enclave, implementationAddress } = await upgradeAndSaveEnclave({ poseidonT3Address: poseidonT3, - ownerAddress: ownerAddress, + ownerAddress: signerAddress, hre, }); From 43864269edd4b2718bfa31fb14ddf0a18233b430 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 10 Nov 2025 18:22:01 +0500 Subject: [PATCH 8/8] chore: update comments --- packages/enclave-contracts/contracts/Enclave.sol | 3 ++- .../enclave-contracts/contracts/registry/BondingRegistry.sol | 3 ++- .../contracts/registry/CiphernodeRegistryOwnable.sol | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 5a985bf5a2..c10f194f51 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -197,7 +197,8 @@ contract Enclave is IEnclave, OwnableUpgradeable { //////////////////////////////////////////////////////////// /// @notice Constructor that disables initializers. - /// @dev Prevents the implementation contract from being initialized. Initialization is performed via the initialize() function when deployed behind a proxy. + /// @dev Prevents the implementation contract from being initialized. Initialization is performed + /// via the initialize() function when deployed behind a proxy. constructor() { _disableInitializers(); } diff --git a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol index 745aa9cfc5..c931ea7c32 100644 --- a/packages/enclave-contracts/contracts/registry/BondingRegistry.sol +++ b/packages/enclave-contracts/contracts/registry/BondingRegistry.sol @@ -140,7 +140,8 @@ contract BondingRegistry is IBondingRegistry, OwnableUpgradeable { //////////////////////////////////////////////////////////// /// @notice Constructor that disables initializers. - /// @dev Prevents the implementation contract from being initialized. Initialization is performed via the initialize() function when deployed behind a proxy. + /// @dev Prevents the implementation contract from being initialized. Initialization is performed + /// via the initialize() function when deployed behind a proxy. constructor() { _disableInitializers(); } diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 3351402714..25ade0f694 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -176,7 +176,8 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { //////////////////////////////////////////////////////////// /// @notice Constructor that disables initializers. - /// @dev Prevents the implementation contract from being initialized. Initialization is performed via the initialize() function when deployed behind a proxy. + /// @dev Prevents the implementation contract from being initialized. Initialization is performed + /// via the initialize() function when deployed behind a proxy. constructor() { _disableInitializers(); }