From 0777956d861b2407fc4121fa4e23c98bd22dce60 Mon Sep 17 00:00:00 2001 From: joachimlebrun Date: Fri, 8 Dec 2023 18:00:30 +0700 Subject: [PATCH 1/2] :sparkles: () add contract deployer --- contracts/factory/ContractsDeployer.sol | 128 +++++++++++++++++++++ index.d.ts | 5 +- index.js | 4 + package-lock.json | 10 +- package.json | 2 +- test/fixtures/deploy-full-suite.fixture.ts | 72 ++++++++---- 6 files changed, 192 insertions(+), 29 deletions(-) create mode 100644 contracts/factory/ContractsDeployer.sol diff --git a/contracts/factory/ContractsDeployer.sol b/contracts/factory/ContractsDeployer.sol new file mode 100644 index 00000000..9724cd3b --- /dev/null +++ b/contracts/factory/ContractsDeployer.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +pragma solidity 0.8.17; + +import "../roles/AgentRole.sol"; + +/// @notice Error thrown when a contract with the same name has already been deployed. +/// @param addr The address of the previously deployed contract. +error ContractDeployedAlready(address addr); + +contract ContractsDeployer is AgentRole { + + /// @notice Maps a human-readable name to the address of a deployed contract. + /// @dev Used to retrieve contract addresses deployed by this deployer. + mapping(string => address) private _deployedContracts; + + /// @notice Emitted when a contract is deployed. + /// @param name The human-readable name of the deployed contract. + /// @param contractAddress The address of the deployed contract. + event ContractDeployed(string name, address contractAddress); + + + /** + * @dev Deploys a contract using the create2 opcode, ensuring deterministic address generation. + * @param name A human-readable name for the contract, used for referencing in the deployedContracts mapping. + * @param bytecode The bytecode of the contract to be deployed. + * @return addr The address of the deployed contract. + * @notice The function will revert with `ContractDeployedAlready` if a contract with the same name has been deployed. + */ + function deployContract(string memory name, bytes memory bytecode) external onlyAgent returns (address) { + bytes32 salt = keccak256(bytecode); + if (_deployedContracts[name] != address(0)) { + revert ContractDeployedAlready(_deployedContracts[name]); + } + + address addr; + // solhint-disable-next-line no-inline-assembly + assembly { + let encoded_data := add(0x20, bytecode) // Load initialization code. + let encoded_size := mload(bytecode) // Load init code's length. + addr := create2(0, encoded_data, encoded_size, salt) + if iszero(extcodesize(addr)) { + revert(0, 0) + } + } + _deployedContracts[name] = addr; + emit ContractDeployed(name, addr); + return addr; + } + + /** + * @dev Transfers the ownership of a contract to a new owner. + * @param _contract The address of the contract whose ownership is to be transferred. + * @param _owner The address of the new owner. + * @notice This function can only be called by an agent. + */ + function recoverContractOwnership(address _contract, address _owner) external onlyAgent { + Ownable(_contract).transferOwnership(_owner); + } + + /** + * @dev Retrieves the address of a deployed contract by its name. + * @param name The name of the contract. + * @return The address of the deployed contract. + */ + function getContract(string calldata name) external view returns (address) { + return _deployedContracts[name]; + } +} diff --git a/index.d.ts b/index.d.ts index 65d49032..190fedbf 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,7 +6,7 @@ type ContractJSON = { bytecode: string; deployedBytecode: string; linkReferences: any; -} +}; export namespace contracts { // Token @@ -48,6 +48,9 @@ export namespace contracts { export const TREXFactory: ContractJSON; // gateway export const TREXGateway: ContractJSON; + // contractsDeployer + + export const ContractsDeployer: ContractJSON; // DVD export const DVDTransferManager: ContractJSON; // DVA diff --git a/index.js b/index.js index 4a2d8819..fac7cd06 100644 --- a/index.js +++ b/index.js @@ -48,6 +48,8 @@ const TREXFactory = require('./artifacts/contracts/factory/TREXFactory.sol/TREXF // gateway const ITREXGateway = require('./artifacts/contracts/factory/ITREXGateway.sol/ITREXGateway.json'); const TREXGateway = require('./artifacts/contracts/factory/TREXGateway.sol/TREXGateway.json'); +// contractsDeployer +const ContractsDeployer = require('./artifacts/contracts/factory/ContractsDeployer.sol/ContractsDeployer.json'); // DVD const DVDTransferManager = require('./artifacts/contracts/DVD/DVDTransferManager.sol/DVDTransferManager.json'); // DVA @@ -112,6 +114,8 @@ module.exports = { TREXFactory, // gateway TREXGateway, + // contractsDeployer + ContractsDeployer, // DVD DVDTransferManager, // DVA diff --git a/package-lock.json b/package-lock.json index ba8037dd..bd7abd63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,13 +6,13 @@ "packages": { "": { "name": "@tokenysolutions/t-rex", - "version": "4.1.0-beta5", + "version": "4.1.1", "license": "SEE LICENSE IN LICENSE.md", "devDependencies": { "@commitlint/cli": "^17.6.1", "@nomicfoundation/hardhat-toolbox": "^2.0.2", "@nomiclabs/hardhat-solhint": "^3.0.1", - "@onchain-id/solidity": "^2.0.0", + "@onchain-id/solidity": "^2.2.0", "@openzeppelin/contracts": "^4.8.3", "@openzeppelin/contracts-upgradeable": "^4.8.3", "@primitivefi/hardhat-dodoc": "^0.2.3", @@ -2429,9 +2429,9 @@ } }, "node_modules/@onchain-id/solidity": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@onchain-id/solidity/-/solidity-2.1.0.tgz", - "integrity": "sha512-BBpBmUs8gg99N5NIKzANbs3R6DUhd90z2GEFvScVBhpHvpQhI5IzPNFRP6cXua1Lo0dWdNffxsteB3eUY1XMZw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@onchain-id/solidity/-/solidity-2.2.0.tgz", + "integrity": "sha512-rUE5kFToihuvRAhtZXihOrpsqPOxN66v9hmTzlZOQJ2B+yKruLg4/nU/sJ7VCnheW4IeEm8OlA2oUJsdm1j88w==", "dev": true }, "node_modules/@openzeppelin/contracts": { diff --git a/package.json b/package.json index 55686019..22aecea2 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@commitlint/cli": "^17.6.1", "@nomicfoundation/hardhat-toolbox": "^2.0.2", "@nomiclabs/hardhat-solhint": "^3.0.1", - "@onchain-id/solidity": "^2.0.0", + "@onchain-id/solidity": "^2.2.0", "@openzeppelin/contracts": "^4.8.3", "@openzeppelin/contracts-upgradeable": "^4.8.3", "@primitivefi/hardhat-dodoc": "^0.2.3", diff --git a/test/fixtures/deploy-full-suite.fixture.ts b/test/fixtures/deploy-full-suite.fixture.ts index eefa68ed..56198973 100644 --- a/test/fixtures/deploy-full-suite.fixture.ts +++ b/test/fixtures/deploy-full-suite.fixture.ts @@ -12,40 +12,68 @@ export async function deployIdentityProxy(implementationAuthority: Contract['add return ethers.getContractAt('Identity', identity.address, signer); } +// Function to deploy a single contract through ContractsDeployer +async function deployAndLoadContract(contractsDeployer: Contract, contractName: string): Promise { + // Fetch bytecode + const ContractFactory = await ethers.getContractFactory(contractName); + const bytecode = ContractFactory.bytecode; + + // Deploy contract using ContractsDeployer + await contractsDeployer.deployContract(contractName, bytecode); + + // Retrieve the deployed contract address + const address = await contractsDeployer.getContract(contractName); + + // Load the contract + return ethers.getContractAt(contractName, address); +} + +async function deployAndLoadContractWithArgs(contractsDeployer: Contract, contractName: string, constructorArgs: unknown[]): Promise { + // Create a ContractFactory with the ABI, bytecode, and constructor arguments + const ContractFactory = await ethers.getContractFactory(contractName); + const deployTx = ContractFactory.getDeployTransaction(...constructorArgs); + + // Deploy contract using ContractsDeployer with the combined bytecode and constructor args + await contractsDeployer.deployContract(contractName, deployTx.data); + + // Retrieve the deployed contract address + const address = await contractsDeployer.getContract(contractName); + + // Load the contract + return ethers.getContractAt(contractName, address); +} + export async function deployFullSuiteFixture() { const [deployer, tokenIssuer, tokenAgent, tokenAdmin, claimIssuer, aliceWallet, bobWallet, charlieWallet, davidWallet, anotherWallet] = await ethers.getSigners(); const claimIssuerSigningKey = ethers.Wallet.createRandom(); const aliceActionKey = ethers.Wallet.createRandom(); + const contractsDeployer = await ethers.deployContract('ContractsDeployer', deployer); + await contractsDeployer.addAgent(deployer.address); // Deploy implementations - const claimTopicsRegistryImplementation = await ethers.deployContract('ClaimTopicsRegistry', deployer); - const trustedIssuersRegistryImplementation = await ethers.deployContract('TrustedIssuersRegistry', deployer); - const identityRegistryStorageImplementation = await ethers.deployContract('IdentityRegistryStorage', deployer); - const identityRegistryImplementation = await ethers.deployContract('IdentityRegistry', deployer); - const modularComplianceImplementation = await ethers.deployContract('ModularCompliance', deployer); - const tokenImplementation = await ethers.deployContract('Token', deployer); - const identityImplementation = await new ethers.ContractFactory( - OnchainID.contracts.Identity.abi, - OnchainID.contracts.Identity.bytecode, - deployer, - ).deploy(deployer.address, true); - - const identityImplementationAuthority = await new ethers.ContractFactory( - OnchainID.contracts.ImplementationAuthority.abi, - OnchainID.contracts.ImplementationAuthority.bytecode, - deployer, - ).deploy(identityImplementation.address); + const claimTopicsRegistryImplementation = await deployAndLoadContract(contractsDeployer, 'ClaimTopicsRegistry'); + const trustedIssuersRegistryImplementation = await deployAndLoadContract(contractsDeployer, 'TrustedIssuersRegistry'); + const identityRegistryStorageImplementation = await deployAndLoadContract(contractsDeployer, 'IdentityRegistryStorage'); + const identityRegistryImplementation = await deployAndLoadContract(contractsDeployer, 'IdentityRegistry'); + const modularComplianceImplementation = await deployAndLoadContract(contractsDeployer, 'ModularCompliance'); + const tokenImplementation = await deployAndLoadContract(contractsDeployer, 'Token'); + const identityImplementation = await deployAndLoadContractWithArgs(contractsDeployer, 'Identity', [deployer.address, true]); + const identityImplementationAuthority = await deployAndLoadContractWithArgs(contractsDeployer, 'ImplementationAuthority', [ + identityImplementation.address, + ]); + await contractsDeployer.recoverContractOwnership(identityImplementationAuthority.address, deployer.address); const identityFactory = await new ethers.ContractFactory(OnchainID.contracts.Factory.abi, OnchainID.contracts.Factory.bytecode, deployer).deploy( identityImplementationAuthority.address, ); - const trexImplementationAuthority = await ethers.deployContract( - 'TREXImplementationAuthority', - [true, ethers.constants.AddressZero, ethers.constants.AddressZero], - deployer, - ); + const trexImplementationAuthority = await deployAndLoadContractWithArgs(contractsDeployer, 'TREXImplementationAuthority', [ + true, + ethers.constants.AddressZero, + ethers.constants.AddressZero, + ]); + await contractsDeployer.recoverContractOwnership(trexImplementationAuthority.address, deployer.address); const versionStruct = { major: 4, minor: 0, From b3893f37c7a5037921dcb234e5b4fca4063a766d Mon Sep 17 00:00:00 2001 From: joachimlebrun Date: Wed, 21 Feb 2024 19:43:16 +0800 Subject: [PATCH 2/2] :sparkles: () add deploy script --- contracts/_testContracts/OIDImports.sol | 2 + hardhat.config.ts | 21 +- package-lock.json | 36 +- package.json | 17 +- scripts/mumbaiDeploy.ts | 452 ++++++++++++++++++++++++ 5 files changed, 503 insertions(+), 25 deletions(-) create mode 100644 scripts/mumbaiDeploy.ts diff --git a/contracts/_testContracts/OIDImports.sol b/contracts/_testContracts/OIDImports.sol index f2e9b295..1426a07e 100644 --- a/contracts/_testContracts/OIDImports.sol +++ b/contracts/_testContracts/OIDImports.sol @@ -3,6 +3,8 @@ pragma solidity 0.8.17; import "@onchain-id/solidity/contracts/ClaimIssuer.sol"; import "@onchain-id/solidity/contracts/Identity.sol"; +import "@onchain-id/solidity/contracts/factory/IdFactory.sol"; +import "@onchain-id/solidity/contracts/gateway/Gateway.sol"; import "@onchain-id/solidity/contracts/proxy/ImplementationAuthority.sol"; contract OIDImports { diff --git a/hardhat.config.ts b/hardhat.config.ts index 688a158c..86aca0e7 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -4,6 +4,10 @@ import { HardhatUserConfig } from 'hardhat/config'; import 'solidity-coverage'; import '@nomiclabs/hardhat-solhint'; import '@primitivefi/hardhat-dodoc'; +import '@nomiclabs/hardhat-etherscan'; +import * as dotenv from 'dotenv'; + +dotenv.config(); const config: HardhatUserConfig = { solidity: { @@ -21,9 +25,24 @@ const config: HardhatUserConfig = { dodoc: { runOnCompile: false, debugMode: true, - outputDir: "./docgen", + outputDir: './docgen', freshOutput: true, }, + networks: { + mumbai: { + url: process.env.MUMBAI_RPC_URL, + accounts: [`0x${process.env.DEPLOYER_PRIVATE_KEY}`], + }, + polygon: { + url: process.env.POLYGON_RPC_URL, + accounts: [`0x${process.env.DEPLOYER_PRIVATE_KEY}`], + }, + }, + etherscan: { + apiKey: { + polygonMumbai: process.env.POLYGONSCAN_API_KEY, + }, + }, }; export default config; diff --git a/package-lock.json b/package-lock.json index bd7abd63..0c0a2e1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "devDependencies": { "@commitlint/cli": "^17.6.1", "@nomicfoundation/hardhat-toolbox": "^2.0.2", + "@nomiclabs/hardhat-etherscan": "^3.1.8", "@nomiclabs/hardhat-solhint": "^3.0.1", "@onchain-id/solidity": "^2.2.0", "@openzeppelin/contracts": "^4.8.3", @@ -19,6 +20,7 @@ "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "@xyrusworx/hardhat-solidity-json": "^1.0.2", + "dotenv": "^16.4.5", "eslint": "^8.39.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.8.0", @@ -2282,11 +2284,11 @@ } }, "node_modules/@nomiclabs/hardhat-etherscan": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.7.tgz", - "integrity": "sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz", + "integrity": "sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ==", + "deprecated": "The @nomiclabs/hardhat-etherscan package is deprecated, please use @nomicfoundation/hardhat-verify instead", "dev": true, - "peer": true, "dependencies": { "@ethersproject/abi": "^5.1.2", "@ethersproject/address": "^5.0.2", @@ -2308,7 +2310,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "peer": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -2321,7 +2322,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "peer": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2336,7 +2336,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "peer": true, "dependencies": { "color-name": "1.1.3" } @@ -2345,15 +2344,13 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@nomiclabs/hardhat-etherscan/node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, - "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -2368,7 +2365,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "peer": true, "engines": { "node": ">=4" } @@ -2378,7 +2374,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "peer": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -2388,7 +2383,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -2398,7 +2392,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "peer": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -2411,7 +2404,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "peer": true, "engines": { "node": ">= 4.0.0" } @@ -3843,7 +3835,6 @@ "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", "dev": true, - "peer": true, "dependencies": { "nofilter": "^3.1.0" }, @@ -4768,6 +4759,18 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -8994,7 +8997,6 @@ "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", "dev": true, - "peer": true, "engines": { "node": ">=12.19" } diff --git a/package.json b/package.json index 22aecea2..b18014dd 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ ], "scripts": { "build": "hardhat compile", + "deploy:mumbai": "hardhat run scripts/mumbaiDeploy.ts --network mumbai", "flatten": "node scripts/flatten.js", "coverage": "hardhat coverage", "test": "hardhat test", @@ -43,32 +44,34 @@ "devDependencies": { "@commitlint/cli": "^17.6.1", "@nomicfoundation/hardhat-toolbox": "^2.0.2", + "@nomiclabs/hardhat-etherscan": "^3.1.8", "@nomiclabs/hardhat-solhint": "^3.0.1", "@onchain-id/solidity": "^2.2.0", "@openzeppelin/contracts": "^4.8.3", "@openzeppelin/contracts-upgradeable": "^4.8.3", "@primitivefi/hardhat-dodoc": "^0.2.3", + "@typescript-eslint/eslint-plugin": "^6.7.4", + "@typescript-eslint/parser": "^6.7.4", "@xyrusworx/hardhat-solidity-json": "^1.0.2", + "dotenv": "^16.4.5", "eslint": "^8.39.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.8.0", + "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-chai-friendly": "^0.7.2", "eslint-plugin-import": "^2.27.5", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-security": "^1.7.1", + "eth-gas-reporter": "^0.2.27", + "fs-extra": "^11.1.1", + "glob": "^10.2.6", "hardhat": "^2.14.0", "husky": "^8.0.3", "lint-staged": "^13.2.2", "prettier": "^2.8.8", "prettier-plugin-solidity": "^1.1.3", "solhint": "^3.4.1", - "solhint-plugin-prettier": "^0.0.5", - "glob": "^10.2.6", - "fs-extra": "^11.1.1", - "@typescript-eslint/parser": "^6.7.4", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "eslint-import-resolver-typescript": "^3.6.1", - "eth-gas-reporter": "^0.2.27" + "solhint-plugin-prettier": "^0.0.5" }, "lint-staged": { "*.js": [ diff --git a/scripts/mumbaiDeploy.ts b/scripts/mumbaiDeploy.ts new file mode 100644 index 00000000..cc9703c2 --- /dev/null +++ b/scripts/mumbaiDeploy.ts @@ -0,0 +1,452 @@ +import { ethers, run } from 'hardhat'; +import * as dotenv from 'dotenv'; + +dotenv.config(); + +async function main() { + // Load private keys + const deployerPrivateKey = process.env.DEPLOYER_PRIVATE_KEY!; + const interactorPrivateKey = process.env.INTERACTOR_PRIVATE_KEY!; + + // Connect to the network + const deployerWallet = new ethers.Wallet(deployerPrivateKey, ethers.provider); + const interactorWallet = new ethers.Wallet(interactorPrivateKey, ethers.provider); + + // variable used for constructor arguments + let args; + + // Deploy ContractsDeployer contract + const ContractsDeployerFactory = await ethers.getContractFactory('ContractsDeployer', deployerWallet); + const contractsDeployer = await ContractsDeployerFactory.deploy(); + await contractsDeployer.deployed(); + console.log(`ContractsDeployer deployed to: ${contractsDeployer.address}`); + + // Verify ContractsDeployer contract + try { + await run('verify:verify', { + address: contractsDeployer.address, + constructorArguments: [], + network: 'mumbai', + }); + console.log('ContractsDeployer verification successful'); + } catch (error) { + console.error('ContractsDeployer verification failed:', error); + } + + // Add interactor as an agent + let tx = await contractsDeployer.connect(deployerWallet).addAgent(interactorWallet.address); + await tx.wait(); + console.log(`Interactor added as an agent.`); + + // Deploy Token contract via ContractsDeployer + const TokenFactory = await ethers.getContractFactory('Token'); + const tokenBytecode = TokenFactory.bytecode; + tx = await contractsDeployer.connect(interactorWallet).deployContract('Token_v4.1.3', tokenBytecode); + await tx.wait(); + + const deployedTokenAddress = await contractsDeployer.getContract('Token_v4.1.3'); + console.log(`Token contract deployed to: ${deployedTokenAddress}`); + + // Verify Token contract + try { + await run('verify:verify', { + address: deployedTokenAddress, + constructorArguments: [], + network: 'mumbai', + }); + console.log('Token contract verification successful'); + } catch (error) { + console.error('Token contract verification failed:', error); + } + + // Deploy Identity Registry contract via ContractsDeployer + const IRFactory = await ethers.getContractFactory('IdentityRegistry'); + const irBytecode = IRFactory.bytecode; + tx = await contractsDeployer.connect(interactorWallet).deployContract('IR_v4.1.3', irBytecode); + await tx.wait(); + + const deployedIRAddress = await contractsDeployer.getContract('IR_v4.1.3'); + console.log(`IdentityRegistry contract deployed to: ${deployedIRAddress}`); + + // Verify Identity Registry contract + try { + await run('verify:verify', { + address: deployedIRAddress, + constructorArguments: [], + network: 'mumbai', + }); + console.log('IdentityRegistry contract verification successful'); + } catch (error) { + console.error('IdentityRegistry contract verification failed:', error); + } + + // Deploy Identity Registry Storage contract via ContractsDeployer + const IRSFactory = await ethers.getContractFactory('IdentityRegistryStorage'); + const irsBytecode = IRSFactory.bytecode; + tx = await contractsDeployer.connect(interactorWallet).deployContract('IRS_v4.1.3', irsBytecode); + await tx.wait(); + + const deployedIRSAddress = await contractsDeployer.getContract('IRS_v4.1.3'); + console.log(`IdentityRegistryStorage contract deployed to: ${deployedIRSAddress}`); + + // Verify Identity Registry Storage contract + try { + await run('verify:verify', { + address: deployedIRSAddress, + constructorArguments: [], + network: 'mumbai', + }); + console.log('IdentityRegistryStorage contract verification successful'); + } catch (error) { + console.error('IdentityRegistryStorage contract verification failed:', error); + } + + // Deploy Trusted Issuers Registry contract via ContractsDeployer + const TIRFactory = await ethers.getContractFactory('TrustedIssuersRegistry'); + const tirBytecode = TIRFactory.bytecode; + tx = await contractsDeployer.connect(interactorWallet).deployContract('TIR_v4.1.3', tirBytecode); + await tx.wait(); + + const deployedTIRAddress = await contractsDeployer.getContract('TIR_v4.1.3'); + console.log(`IdentityRegistryStorage contract deployed to: ${deployedTIRAddress}`); + + // Verify Trusted Issuers Registry contract + try { + await run('verify:verify', { + address: deployedTIRAddress, + constructorArguments: [], + network: 'mumbai', + }); + console.log('TrustedIssuersRegistry contract verification successful'); + } catch (error) { + console.error('TrustedIssuersRegistry contract verification failed:', error); + } + + // Deploy Claim Topics Registry contract via ContractsDeployer + const CTRFactory = await ethers.getContractFactory('ClaimTopicsRegistry'); + const ctrBytecode = CTRFactory.bytecode; + tx = await contractsDeployer.connect(interactorWallet).deployContract('CTR_v4.1.3', ctrBytecode); + await tx.wait(); + + const deployedCTRAddress = await contractsDeployer.getContract('CTR_v4.1.3'); + console.log(`ClaimTopicsRegistry contract deployed to: ${deployedCTRAddress}`); + + // Verify Claim Topics Registry contract + try { + await run('verify:verify', { + address: deployedCTRAddress, + constructorArguments: [], + network: 'mumbai', + }); + console.log('ClaimTopicsRegistry contract verification successful'); + } catch (error) { + console.error('ClaimTopicsRegistry contract verification failed:', error); + } + + // Deploy Modular Compliance contract via ContractsDeployer + const MCFactory = await ethers.getContractFactory('ModularCompliance'); + const mcBytecode = MCFactory.bytecode; + tx = await contractsDeployer.connect(interactorWallet).deployContract('MC_v4.1.3', mcBytecode); + await tx.wait(); + + const deployedMCAddress = await contractsDeployer.getContract('MC_v4.1.3'); + console.log(`ModularCompliance contract deployed to: ${deployedMCAddress}`); + + // Verify Modular Compliance contract + try { + await run('verify:verify', { + address: deployedMCAddress, + constructorArguments: [], + network: 'mumbai', + }); + console.log('ModularCompliance contract verification successful'); + } catch (error) { + console.error('ModularCompliance contract verification failed:', error); + } + + // Deploy Identity contract via ContractsDeployer + const IdentityFactory = await ethers.getContractFactory('Identity'); + const idBytecode = IdentityFactory.bytecode; + args = ethers.utils.defaultAbiCoder.encode(['address', 'bool'], [contractsDeployer.address, true]); + const idBytecodeWithArgs = idBytecode + args.slice(2); + tx = await contractsDeployer.connect(interactorWallet).deployContract('OID_v2.2.0', idBytecodeWithArgs); + await tx.wait(); + + const deployedOIDAddress = await contractsDeployer.getContract('OID_v2.2.0'); + console.log(`Identity contract deployed to: ${deployedOIDAddress}`); + + // Verify Identity contract + try { + await run('verify:verify', { + address: deployedOIDAddress, + constructorArguments: [contractsDeployer.address, true], + network: 'mumbai', + }); + console.log('Identity contract verification successful'); + } catch (error) { + console.error('Identity contract verification failed:', error); + } + + // Deploy Implementation Authority (OID version) contract via ContractsDeployer + const IdIAFactory = await ethers.getContractFactory('ImplementationAuthority'); + const idiaBytecode = IdIAFactory.bytecode; + args = ethers.utils.defaultAbiCoder.encode(['address'], [deployedOIDAddress]); + const idiaBytecodeWithArgs = idiaBytecode + args.slice(2); + tx = await contractsDeployer.connect(interactorWallet).deployContract('OID_IA_v2.2.0', idiaBytecodeWithArgs); + await tx.wait(); + + const deployedIdIAAddress = await contractsDeployer.getContract('OID_IA_v2.2.0'); + console.log(`ImplementationAuthority contract deployed to: ${deployedIdIAAddress}`); + + // Verify Implementation Authority (OID version) contract + try { + await run('verify:verify', { + address: deployedIdIAAddress, + constructorArguments: [deployedOIDAddress], + network: 'mumbai', + }); + console.log('ImplementationAuthority verification successful'); + } catch (error) { + console.error('ImplementationAuthority verification failed:', error); + } + + // Recover ownership of ImplementationAuthority + tx = await contractsDeployer.connect(interactorWallet).recoverContractOwnership(deployedIdIAAddress, interactorWallet.address); + await tx.wait(); + console.log(`Ownership recovery transaction for ImplementationAuthority sent.`); + + // Create a contract instance for ImplementationAuthority + const implementationAuthorityInstance = await ethers.getContractAt('ImplementationAuthority', deployedIdIAAddress); + + // Check the current owner of the ImplementationAuthority contract + let currentOwner = await implementationAuthorityInstance.owner(); + + // Verify ownership has been transferred to interactorWallet + if (currentOwner.toLowerCase() === interactorWallet.address.toLowerCase()) { + console.log(`Ownership of ImplementationAuthority has been successfully transferred to: ${currentOwner}`); + } else { + console.error(`Ownership transfer of ImplementationAuthority failed. Current owner is: ${currentOwner}`); + } + + // Deploy OID Factory contract via ContractsDeployer + const OIDFactory = await ethers.getContractFactory('IdFactory'); + const oidFactoryBytecode = OIDFactory.bytecode; + args = ethers.utils.defaultAbiCoder.encode(['address'], [deployedIdIAAddress]); + const oidFactoryBytecodeWithArgs = oidFactoryBytecode + args.slice(2); + tx = await contractsDeployer.connect(interactorWallet).deployContract('OID_Factory_v2.2.0', oidFactoryBytecodeWithArgs); + await tx.wait(); + + const deployedOIDFactoryAddress = await contractsDeployer.getContract('OID_Factory_v2.2.0'); + console.log(`IdFactory contract deployed to: ${deployedOIDFactoryAddress}`); + + // Verify OID Factory contract + try { + await run('verify:verify', { + address: deployedOIDFactoryAddress, + constructorArguments: [deployedIdIAAddress], + network: 'mumbai', + }); + console.log('IdFactory verification successful'); + } catch (error) { + console.error('IdFactory verification failed:', error); + } + // Recover ownership of OID Factory + tx = await contractsDeployer.connect(interactorWallet).recoverContractOwnership(deployedOIDFactoryAddress, interactorWallet.address); + await tx.wait(); + console.log(`Ownership recovery transaction for IdFactory initiated.`); + + // Create a contract instance for IdFactory + const oidFactoryContract = await ethers.getContractAt('IdFactory', deployedOIDFactoryAddress, interactorWallet); + + // Check the current owner of the OID Factory contract + currentOwner = await oidFactoryContract.owner(); + if (currentOwner.toLowerCase() === interactorWallet.address.toLowerCase()) { + console.log(`Ownership of IdFactory successfully transferred to ${currentOwner}.`); + } else { + console.error(`Ownership transfer of IdFactory failed. Current owner is ${currentOwner}.`); + } + + // Deploy TREXImplementationAuthority contract via ContractsDeployer + const TREXIAFactory = await ethers.getContractFactory('TREXImplementationAuthority'); + const trexiaBytecode = TREXIAFactory.bytecode; + args = ethers.utils.defaultAbiCoder.encode(['bool', 'address', 'address'], [true, ethers.constants.AddressZero, ethers.constants.AddressZero]); + const trexiaBytecodeWithArgs = trexiaBytecode + args.slice(2); + tx = await contractsDeployer.connect(interactorWallet).deployContract('TREX_IA_v1.0.0', trexiaBytecodeWithArgs); + await tx.wait(); + + const deployedTrexIAAddress = await contractsDeployer.getContract('TREX_IA_v1.0.0'); + console.log(`TREXImplementationAuthority contract deployed to: ${deployedTrexIAAddress}`); + + // Verify TREXImplementationAuthority contract + try { + await run('verify:verify', { + address: deployedTrexIAAddress, + constructorArguments: [true, ethers.constants.AddressZero, ethers.constants.AddressZero], + network: 'mumbai', + }); + console.log('TREXImplementationAuthority verification successful'); + } catch (error) { + console.error('TREXImplementationAuthority verification failed:', error); + } + + // Recover ownership of TREXImplementationAuthority + tx = await contractsDeployer.connect(interactorWallet).recoverContractOwnership(deployedTrexIAAddress, interactorWallet.address); + await tx.wait(); + console.log(`Ownership recovery transaction for TREXImplementationAuthority initiated.`); + + // Create a contract instance for TREXImplementationAuthority + const trexIAContract = await ethers.getContractAt('TREXImplementationAuthority', deployedTrexIAAddress, interactorWallet); + + // Check the current owner of the TREXImplementationAuthority contract + const currentOwnerTrexIA = await trexIAContract.owner(); + if (currentOwnerTrexIA.toLowerCase() === interactorWallet.address.toLowerCase()) { + console.log(`Ownership of TREXImplementationAuthority successfully transferred to ${currentOwnerTrexIA}.`); + } else { + console.error(`Ownership transfer of TREXImplementationAuthority failed. Current owner is ${currentOwnerTrexIA}.`); + } + const version = { + major: 4, + minor: 1, + patch: 3, + }; + + const trexContracts = { + tokenImplementation: deployedTokenAddress, + ctrImplementation: deployedCTRAddress, + irImplementation: deployedIRAddress, + irsImplementation: deployedIRSAddress, + tirImplementation: deployedTIRAddress, + mcImplementation: deployedMCAddress, + }; + tx = await trexIAContract.connect(interactorWallet).addAndUseTREXVersion(version, trexContracts); + const receipt = await tx.wait(); + const trexVersionAddedEvent = receipt.events?.find((e) => e.event === 'TREXVersionAdded'); + const versionUpdatedEvent = receipt.events?.find((e) => e.event === 'VersionUpdated'); + + if (trexVersionAddedEvent && versionUpdatedEvent) { + console.log('TREXVersionAdded and VersionUpdated events were successfully emitted.'); + } else { + console.error('Failed to emit TREXVersionAdded and/or VersionUpdated events.'); + } + // Deploy TREXFactory contract via ContractsDeployer + const TREXFactory = await ethers.getContractFactory('TREXFactory'); + const trexFactoryBytecode = TREXFactory.bytecode; + args = ethers.utils.defaultAbiCoder.encode(['address', 'address'], [deployedTrexIAAddress, deployedOIDFactoryAddress]); + const trexFactoryBytecodeWithArgs = trexFactoryBytecode + args.slice(2); + tx = await contractsDeployer.connect(interactorWallet).deployContract('TREX_Factory_v4.1.3', trexFactoryBytecodeWithArgs); + await tx.wait(); + + const deployedTREXFactoryAddress = await contractsDeployer.getContract('TREX_Factory_v4.1.3'); + console.log(`TREXFactory contract deployed to: ${deployedTREXFactoryAddress}`); + + // Verify TREXFactory contract + try { + await run('verify:verify', { + address: deployedTREXFactoryAddress, + constructorArguments: [deployedTrexIAAddress, deployedOIDFactoryAddress], + network: 'mumbai', + }); + console.log('TREXFactory verification successful'); + } catch (error) { + console.error('TREXFactory verification failed:', error); + } + // Recover ownership of TREXFactory + tx = await contractsDeployer.connect(interactorWallet).recoverContractOwnership(deployedTREXFactoryAddress, interactorWallet.address); + await tx.wait(); + console.log(`Ownership recovery transaction for TREXFactory initiated.`); + + // Verify the ownership transfer + const trexFactoryContract = await ethers.getContractAt('TREXFactory', deployedTREXFactoryAddress, interactorWallet); + const currentOwnerTREXFactory = await trexFactoryContract.owner(); + if (currentOwnerTREXFactory.toLowerCase() === interactorWallet.address.toLowerCase()) { + console.log(`Ownership of TREXFactory successfully transferred to ${currentOwnerTREXFactory}.`); + } else { + console.error(`Ownership transfer of TREXFactory failed. Current owner is ${currentOwnerTREXFactory}.`); + } + // Deploy TREXGateway contract via ContractsDeployer + const TREXGatewayFactory = await ethers.getContractFactory('TREXGateway'); + const trexGatewayBytecode = TREXGatewayFactory.bytecode; + const argsTREXGateway = ethers.utils.defaultAbiCoder.encode( + ['address', 'bool'], + [deployedTREXFactoryAddress, false], // Using the deployed TREXFactory address and setting publicDeploymentStatus to false + ); + const trexGatewayBytecodeWithArgs = trexGatewayBytecode + argsTREXGateway.slice(2); + tx = await contractsDeployer.connect(interactorWallet).deployContract('TREX_Gateway', trexGatewayBytecodeWithArgs); + await tx.wait(); + + const deployedTREXGatewayAddress = await contractsDeployer.getContract('TREX_Gateway'); + console.log(`TREXGateway contract deployed to: ${deployedTREXGatewayAddress}`); + + try { + await run('verify:verify', { + address: deployedTREXGatewayAddress, + constructorArguments: [deployedTREXFactoryAddress, false], + network: 'mumbai', + }); + console.log('TREXGateway verification successful'); + } catch (error) { + console.error('TREXGateway verification failed:', error); + } + // Recover ownership of TREXGateway + tx = await contractsDeployer.connect(interactorWallet).recoverContractOwnership(deployedTREXGatewayAddress, interactorWallet.address); + await tx.wait(); + console.log(`Ownership recovery transaction for TREXGateway initiated.`); + + // Confirm the ownership transfer + const trexGatewayContract = await ethers.getContractAt('Ownable', deployedTREXGatewayAddress, interactorWallet); + const currentOwnerTREXGateway = await trexGatewayContract.owner(); + if (currentOwnerTREXGateway.toLowerCase() === interactorWallet.address.toLowerCase()) { + console.log(`Ownership of TREXGateway successfully transferred to ${currentOwnerTREXGateway}.`); + } else { + console.error(`Ownership transfer of TREXGateway failed. Current owner is ${currentOwnerTREXGateway}.`); + } + tx = await trexFactoryContract.connect(interactorWallet).transferOwnership(deployedTREXGatewayAddress); + await tx.wait(); + console.log(`Ownership of TREX Factory transferred to TREXGateway.`); + tx = await oidFactoryContract.connect(interactorWallet).addTokenFactory(deployedTREXFactoryAddress); + await tx.wait(); + console.log(`TREX Factory registered in IdFactory as a Token Factory`); + + // Deploy Gateway contract via ContractsDeployer + const GatewayFactory = await ethers.getContractFactory('Gateway'); + const gatewayBytecode = GatewayFactory.bytecode; + args = ethers.utils.defaultAbiCoder.encode(['address', 'address[]'], [deployedOIDFactoryAddress, []]); + const gatewayBytecodeWithArgs = gatewayBytecode + args.slice(2); + tx = await contractsDeployer.connect(interactorWallet).deployContract('Gateway', gatewayBytecodeWithArgs); + await tx.wait(); + + const deployedGatewayAddress = await contractsDeployer.getContract('Gateway'); + console.log(`Gateway contract deployed to: ${deployedGatewayAddress}`); + + try { + await run('verify:verify', { + address: deployedGatewayAddress, + constructorArguments: [deployedOIDFactoryAddress, []], + network: 'mumbai', + }); + console.log('Gateway verification successful'); + } catch (error) { + console.error('Gateway verification failed:', error); + } + // Recover ownership of Gateway + tx = await contractsDeployer.connect(interactorWallet).recoverContractOwnership(deployedGatewayAddress, interactorWallet.address); + await tx.wait(); + console.log(`Ownership recovery transaction for Gateway initiated.`); + + // Confirm the ownership transfer + const gatewayContract = await ethers.getContractAt('Gateway', deployedGatewayAddress, interactorWallet); + const currentOwnerGateway = await gatewayContract.owner(); + if (currentOwnerGateway.toLowerCase() === interactorWallet.address.toLowerCase()) { + console.log(`Ownership of Gateway successfully transferred to ${currentOwnerGateway}.`); + } else { + console.error(`Ownership transfer of Gateway failed. Current owner is ${currentOwnerGateway}.`); + } + tx = await oidFactoryContract.connect(interactorWallet).transferOwnership(deployedGatewayAddress); + await tx.wait(); + console.log(`Ownership of TREX Factory transferred to TREXGateway.`); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +});