diff --git a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json index fd217f921f..5f033c3aad 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json @@ -36,6 +36,8 @@ "filePanel.addscriptetherscan": "Add Etherscan scripts", "filePanel.workspace.addscriptsindri": "Adds scripts for interacting with Sindri, a zk proof generation remote service", "filePanel.addscriptsindri": "Add Sindri ZK scripts", + "filePanel.workspace.addcreate2solidityfactory": "A contract which allows you to deploy a contract using CREATE2.", + "filePanel.addcreate2solidityfactory": "Add Create2 Solidity factory", "filePanel.workspace.addscriptdeployer": "Adds scripts which can be used to deploy contracts", "filePanel.addscriptdeployer": "Add contract deployer scripts", "filePanel.workspace.slitherghaction": "Adds a preset yml file to run slither analysis on github actions CI", diff --git a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx index 822490a06d..41d6076a2c 100644 --- a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx +++ b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx @@ -194,6 +194,16 @@ export function HamburgerMenu(props: HamburgerMenuProps) { }} platforms={[appPlatformTypes.web, appPlatformTypes.desktop]} > + { + props.addHelperScripts('contractCreate2Factory') + props.hideIconsMenu(!showIconsMenu) + }} + platforms={[appPlatformTypes.web, appPlatformTypes.desktop]} + > ) } diff --git a/libs/remix-ws-templates/src/index.ts b/libs/remix-ws-templates/src/index.ts index e0e1a4edef..0ccb356460 100644 --- a/libs/remix-ws-templates/src/index.ts +++ b/libs/remix-ws-templates/src/index.ts @@ -13,4 +13,5 @@ export { default as rln } from './templates/rln' export { contractDeployerScripts } from './script-templates/contract-deployer' export { etherscanScripts } from './script-templates/etherscan' export { sindriScripts } from './script-templates/sindri' +export { contractCreate2Factory } from './script-templates/create2-solidity-factory' diff --git a/libs/remix-ws-templates/src/script-templates/create2-solidity-factory/create2-factory.sol b/libs/remix-ws-templates/src/script-templates/create2-solidity-factory/create2-factory.sol new file mode 100644 index 0000000000..b4bd059de3 --- /dev/null +++ b/libs/remix-ws-templates/src/script-templates/create2-solidity-factory/create2-factory.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +// credits to https://solidity-by-example.org/app/create2/ + +contract Create2Factory { + // Returns the address of the newly deployed contract + function deploy(address _owner, uint256 _foo, bytes32 _salt) + public + payable + returns (address) + { + // This syntax is a newer way to invoke create2 without assembly, you just need to pass salt + // https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2 + return address(new TestContract{salt: _salt}(_owner, _foo)); + } +} + +// This is the older way of doing it using assembly +contract Create2FactoryAssembly { + event Deployed(address addr, uint256 salt); + + // 1. Get bytecode of contract to be deployed + // NOTE: _owner and _foo are arguments of the TestContract's constructor + function getBytecode(address _owner, uint256 _foo) + public + pure + returns (bytes memory) + { + bytes memory bytecode = type(TestContract).creationCode; + + return abi.encodePacked(bytecode, abi.encode(_owner, _foo)); + } + + // 2. Compute the address of the contract to be deployed + // NOTE: _salt is a random number used to create an address + function getAddress(bytes memory bytecode, uint256 _salt) + public + view + returns (address) + { + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0xff), address(this), _salt, keccak256(bytecode) + ) + ); + + // NOTE: cast last 20 bytes of hash to address + return address(uint160(uint256(hash))); + } + + // 3. Deploy the contract + // NOTE: + // Check the event log Deployed which contains the address of the deployed TestContract. + // The address in the log should equal the address computed from above. + function deploy(bytes memory bytecode, uint256 _salt) public payable { + address addr; + + /* + NOTE: How to call create2 + + create2(v, p, n, s) + create new contract with code at memory p to p + n + and send v wei + and return the new address + where new address = first 20 bytes of keccak256(0xff + address(this) + s + keccak256(mem[p…(p+n))) + s = big-endian 256-bit value + */ + assembly { + addr := + create2( + callvalue(), // wei sent with current call + // Actual code starts after skipping the first 32 bytes + add(bytecode, 0x20), + mload(bytecode), // Load the size of code contained in the first 32 bytes + _salt // Salt from function arguments + ) + + if iszero(extcodesize(addr)) { revert(0, 0) } + } + + emit Deployed(addr, _salt); + } +} + +contract TestContract { + address public owner; + uint256 public foo; + + constructor(address _owner, uint256 _foo) payable { + owner = _owner; + foo = _foo; + } + + function getBalance() public view returns (uint256) { + return address(this).balance; + } +} diff --git a/libs/remix-ws-templates/src/script-templates/create2-solidity-factory/index.ts b/libs/remix-ws-templates/src/script-templates/create2-solidity-factory/index.ts new file mode 100644 index 0000000000..c492142ea9 --- /dev/null +++ b/libs/remix-ws-templates/src/script-templates/create2-solidity-factory/index.ts @@ -0,0 +1,8 @@ +export const contractCreate2Factory = async (plugin) => { + await plugin.call('fileManager', 'writeFile', + 'contracts/libs/create2-factory.sol' , + // @ts-ignore + (await import('!!raw-loader!./create2-factory.sol')).default) + + await plugin.call('fileManager', 'open', 'contracts/libs/create2-factory.sol') + } \ No newline at end of file