Skip to content

Commit

Permalink
✨ () add contract deployer
Browse files Browse the repository at this point in the history
  • Loading branch information
Joachim-Lebrun committed Dec 8, 2023
1 parent 8db61c9 commit 0777956
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 29 deletions.
128 changes: 128 additions & 0 deletions contracts/factory/ContractsDeployer.sol
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
*/
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];
}
}
5 changes: 4 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type ContractJSON = {
bytecode: string;
deployedBytecode: string;
linkReferences: any;
}
};

export namespace contracts {
// Token
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -112,6 +114,8 @@ module.exports = {
TREXFactory,
// gateway
TREXGateway,
// contractsDeployer
ContractsDeployer,
// DVD
DVDTransferManager,
// DVA
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
72 changes: 50 additions & 22 deletions test/fixtures/deploy-full-suite.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Contract> {
// 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<Contract> {
// 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,
Expand Down

0 comments on commit 0777956

Please sign in to comment.