forked from OpenZeppelin/openzeppelin-contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create2 feature pending tasks (OpenZeppelin#2013)
* Add Create2 library (OpenZeppelin#1744) * feat(contracts): Add Create2 library to use create2 evm opcode * Upgrade sol-coverage * Add changelog entry * Update comments and code style * Remove create2 helper * Fix linter error * Fix truffle dependency * Fix linter error * refactor(Create2): Remove _deploy internal function * test(Create2): test Create2 with inline assembly code * fix(Create2): Check address returned form create2 instead of codesize of created contract * refactor(Create2):Add revert reason when Create2 deploy fails (OpenZeppelin#2062) * fix merge with master * fix test Co-authored-by: Augusto Lemble <me@augustol.com>
- Loading branch information
Showing
6 changed files
with
145 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
import "../utils/Create2.sol"; | ||
import "../token/ERC20/ERC20.sol"; | ||
|
||
contract Create2Impl { | ||
function deploy(bytes32 salt, bytes memory code) public { | ||
Create2.deploy(salt, code); | ||
} | ||
|
||
function deployERC20(bytes32 salt) public { | ||
// solhint-disable-next-line indent | ||
Create2.deploy(salt, type(ERC20).creationCode); | ||
} | ||
|
||
function computeAddress(bytes32 salt, bytes memory code) public view returns (address) { | ||
return Create2.computeAddress(salt, code); | ||
} | ||
|
||
function computeAddress(bytes32 salt, bytes memory code, address deployer) public pure returns (address) { | ||
return Create2.computeAddress(salt, code, deployer); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
/** | ||
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. | ||
* `CREATE2` can be used to compute in advance the address where a smart | ||
* contract will be deployed, which allows for interesting new mechanisms known | ||
* as 'counterfactual interactions'. | ||
* | ||
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more | ||
* information. | ||
*/ | ||
library Create2 { | ||
/** | ||
* @dev Deploys a contract using `CREATE2`. The address where the contract | ||
* will be deployed can be known in advance via {computeAddress}. Note that | ||
* a contract cannot be deployed twice using the same salt. | ||
*/ | ||
function deploy(bytes32 salt, bytes memory bytecode) internal returns (address) { | ||
address addr; | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly { | ||
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt) | ||
} | ||
require(addr != address(0), "Create2: Failed on deploy"); | ||
return addr; | ||
} | ||
|
||
/** | ||
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the `bytecode` | ||
* or `salt` will result in a new destination address. | ||
*/ | ||
function computeAddress(bytes32 salt, bytes memory bytecode) internal view returns (address) { | ||
return computeAddress(salt, bytecode, address(this)); | ||
} | ||
|
||
/** | ||
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at | ||
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. | ||
*/ | ||
function computeAddress(bytes32 salt, bytes memory bytecodeHash, address deployer) internal pure returns (address) { | ||
bytes32 bytecodeHashHash = keccak256(bytecodeHash); | ||
bytes32 _data = keccak256( | ||
abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHashHash) | ||
); | ||
return address(bytes20(_data << 96)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
const { contract, accounts, web3 } = require('@openzeppelin/test-environment'); | ||
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); | ||
|
||
const { expect } = require('chai'); | ||
|
||
const Create2Impl = contract.fromArtifact('Create2Impl'); | ||
const ERC20Mock = contract.fromArtifact('ERC20Mock'); | ||
const ERC20 = contract.fromArtifact('ERC20'); | ||
|
||
describe('Create2', function () { | ||
const [deployerAccount] = accounts; | ||
|
||
const salt = 'salt message'; | ||
const saltHex = web3.utils.soliditySha3(salt); | ||
const constructorByteCode = `${ERC20Mock.bytecode}${web3.eth.abi | ||
.encodeParameters(['address', 'uint256'], [deployerAccount, 100]).slice(2) | ||
}`; | ||
|
||
beforeEach(async function () { | ||
this.factory = await Create2Impl.new(); | ||
}); | ||
|
||
it('should compute the correct contract address', async function () { | ||
const onChainComputed = await this.factory | ||
.computeAddress(saltHex, constructorByteCode); | ||
const offChainComputed = | ||
computeCreate2Address(saltHex, constructorByteCode, this.factory.address); | ||
expect(onChainComputed).to.equal(offChainComputed); | ||
}); | ||
|
||
it('should compute the correct contract address with deployer', async function () { | ||
const onChainComputed = await this.factory | ||
.computeAddress(saltHex, constructorByteCode, deployerAccount); | ||
const offChainComputed = | ||
computeCreate2Address(saltHex, constructorByteCode, deployerAccount); | ||
expect(onChainComputed).to.equal(offChainComputed); | ||
}); | ||
|
||
it('should deploy a ERC20 from inline assembly code', async function () { | ||
const offChainComputed = | ||
computeCreate2Address(saltHex, ERC20.bytecode, this.factory.address); | ||
await this.factory | ||
.deploy(saltHex, ERC20.bytecode, { from: deployerAccount }); | ||
expect(ERC20.bytecode).to.include((await web3.eth.getCode(offChainComputed)).slice(2)); | ||
}); | ||
|
||
it('should deploy a ERC20Mock with correct balances', async function () { | ||
const offChainComputed = | ||
computeCreate2Address(saltHex, constructorByteCode, this.factory.address); | ||
await this.factory | ||
.deploy(saltHex, constructorByteCode, { from: deployerAccount }); | ||
const erc20 = await ERC20Mock.at(offChainComputed); | ||
expect(await erc20.balanceOf(deployerAccount)).to.be.bignumber.equal(new BN(100)); | ||
}); | ||
|
||
it('should failed deploying a contract in an existent address', async function () { | ||
await this.factory.deploy(saltHex, constructorByteCode, { from: deployerAccount }); | ||
await expectRevert( | ||
this.factory.deploy(saltHex, constructorByteCode, { from: deployerAccount }), 'Create2: Failed on deploy' | ||
); | ||
}); | ||
}); | ||
|
||
function computeCreate2Address (saltHex, bytecode, deployer) { | ||
return web3.utils.toChecksumAddress(`0x${web3.utils.sha3(`0x${[ | ||
'ff', | ||
deployer, | ||
saltHex, | ||
web3.utils.soliditySha3(bytecode), | ||
].map(x => x.replace(/0x/, '')).join('')}`).slice(-40)}`); | ||
} |