Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add revert if the bytecode length is zero #2117

Merged
merged 10 commits into from
Mar 28, 2020
8 changes: 4 additions & 4 deletions contracts/mocks/Create2Impl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import "../utils/Create2.sol";
import "../token/ERC20/ERC20.sol";

contract Create2Impl {
function deploy(bytes32 salt, bytes memory code) public {
Create2.deploy(salt, code);
function deploy(uint256 value, bytes32 salt, bytes memory code) public {
Create2.deploy(value, salt, code);
nventuro marked this conversation as resolved.
Show resolved Hide resolved
}

function deployERC20(bytes32 salt) public {
function deployERC20(uint256 value, bytes32 salt) public {
// solhint-disable-next-line indent
Create2.deploy(salt, type(ERC20).creationCode);
Create2.deploy(value, salt, type(ERC20).creationCode);
}

function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) {
Expand Down
6 changes: 4 additions & 2 deletions contracts/utils/Create2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ library Create2 {
* 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) {
function deploy(uint256 value, bytes32 salt, bytes memory bytecode) internal returns (address) {
address addr;
require(address(this).balance >= value, "Factory does not have enough funds to fufill deposit");
require(bytecode.length > 0, "Create2: bytecode length is zero");
// solhint-disable-next-line no-inline-assembly
assembly {
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
addr := create2(value, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
return addr;
Expand Down
29 changes: 24 additions & 5 deletions test/utils/Create2.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { contract, accounts, web3 } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { balance, BN, ether, expectRevert, send } = require('@openzeppelin/test-helpers');

const { expect } = require('chai');

Expand Down Expand Up @@ -40,23 +40,42 @@ describe('Create2', function () {
const offChainComputed =
computeCreate2Address(saltHex, ERC20.bytecode, this.factory.address);
await this.factory
.deploy(saltHex, ERC20.bytecode, { from: deployerAccount });
.deploy(0, 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 });
.deploy(0, saltHex, constructorByteCode, { from: deployerAccount });
const erc20 = await ERC20Mock.at(offChainComputed);
expect(await erc20.balanceOf(deployerAccount)).to.be.bignumber.equal(new BN(100));
});

it('should deploy a contract with funds deposited in the factory', async function () {
const deposit = ether('1');
await send.ether(deployerAccount, this.factory.address, deposit);
nventuro marked this conversation as resolved.
Show resolved Hide resolved
const generatedAddress =
await this.factory.deploy(deposit, saltHex, constructorByteCode, { from: deployerAccount });
expect(await balance.current(generatedAddress)).to.be.bignumber.equal(deposit);
});

it('should failed deploying a contract in an existent address', async function () {
await this.factory.deploy(saltHex, constructorByteCode, { from: deployerAccount });
await this.factory.deploy(0, saltHex, constructorByteCode, { from: deployerAccount });
await expectRevert(
this.factory.deploy(0, saltHex, constructorByteCode, { from: deployerAccount }), 'Create2: Failed on deploy'
);
});
it('should fail deploying a contract if the bytecode length is zero', async function () {
await expectRevert(
this.factory.deploy(0, saltHex, '0x', { from: deployerAccount }), 'Create2: bytecode length is zero'
);
});
it('should fail deploying a contract if factory contract does not have sufficient balance', async function () {
await expectRevert(
this.factory.deploy(saltHex, constructorByteCode, { from: deployerAccount }), 'Create2: Failed on deploy'
this.factory.deploy(1, saltHex, constructorByteCode, { from: deployerAccount }),
'Factory does not have enough funds to fufill deposit'
);
});
});
Expand Down