Skip to content

Commit

Permalink
Bubble up returndata from reverted Create2 deployments (OpenZeppeli…
Browse files Browse the repository at this point in the history
…n#5052)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: ernestognw <ernestognw@gmail.com>
  • Loading branch information
3 people authored May 27, 2024
1 parent 52e0e3e commit 984233d
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/nervous-eyes-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`Create2`: Bubbles up returndata from a deployed contract that reverted during construction.
34 changes: 34 additions & 0 deletions contracts/mocks/ConstructorMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

contract ConstructorMock {
bool foo;

enum RevertType {
None,
RevertWithoutMessage,
RevertWithMessage,
RevertWithCustomError,
Panic
}

error CustomError();

constructor(RevertType error) {
// After transpilation to upgradeable contract, the constructor will become an initializer
// To silence the `... can be restricted to view` warning, we write to state
foo = true;

if (error == RevertType.RevertWithoutMessage) {
revert();
} else if (error == RevertType.RevertWithMessage) {
revert("ConstructorMock: reverting");
} else if (error == RevertType.RevertWithCustomError) {
revert CustomError();
} else if (error == RevertType.Panic) {
uint256 a = uint256(0) / uint256(0);
a;
}
}
}
5 changes: 5 additions & 0 deletions contracts/utils/Create2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ library Create2 {
/// @solidity memory-safe-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
// if no address was created, and returndata is not empty, bubble revert
if and(iszero(addr), not(iszero(returndatasize()))) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
if (addr == address(0)) {
revert Errors.FailedDeployment();
Expand Down
58 changes: 57 additions & 1 deletion test/utils/Create2.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');

const { RevertType } = require('../helpers/enums');

async function fixture() {
const [deployer, other] = await ethers.getSigners();
Expand All @@ -19,7 +22,9 @@ async function fixture() {
.getContractFactory('$Create2')
.then(({ bytecode, interface }) => ethers.concat([bytecode, interface.encodeDeploy([])]));

return { deployer, other, factory, constructorByteCode, constructorLessBytecode };
const mockFactory = await ethers.getContractFactory('ConstructorMock');

return { deployer, other, factory, constructorByteCode, constructorLessBytecode, mockFactory };
}

describe('Create2', function () {
Expand Down Expand Up @@ -130,5 +135,56 @@ describe('Create2', function () {
.to.be.revertedWithCustomError(this.factory, 'InsufficientBalance')
.withArgs(0n, 1n);
});

describe('reverts error thrown during contract creation', function () {
it('bubbles up without message', async function () {
await expect(
this.factory.$deploy(
0n,
saltHex,
ethers.concat([
this.mockFactory.bytecode,
this.mockFactory.interface.encodeDeploy([RevertType.RevertWithoutMessage]),
]),
),
).to.be.revertedWithCustomError(this.factory, 'FailedDeployment');
});

it('bubbles up message', async function () {
await expect(
this.factory.$deploy(
0n,
saltHex,
ethers.concat([
this.mockFactory.bytecode,
this.mockFactory.interface.encodeDeploy([RevertType.RevertWithMessage]),
]),
),
).to.be.revertedWith('ConstructorMock: reverting');
});

it('bubbles up custom error', async function () {
await expect(
this.factory.$deploy(
0n,
saltHex,
ethers.concat([
this.mockFactory.bytecode,
this.mockFactory.interface.encodeDeploy([RevertType.RevertWithCustomError]),
]),
),
).to.be.revertedWithCustomError({ interface: this.mockFactory.interface }, 'CustomError');
});

it('bubbles up panic', async function () {
await expect(
this.factory.$deploy(
0n,
saltHex,
ethers.concat([this.mockFactory.bytecode, this.mockFactory.interface.encodeDeploy([RevertType.Panic])]),
),
).to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO);
});
});
});
});

0 comments on commit 984233d

Please sign in to comment.