Skip to content

Commit 984233d

Browse files
dimitriospapathanasiouAmxxernestognw
authored
Bubble up returndata from reverted Create2 deployments (#5052)
Co-authored-by: Hadrien Croubois <[email protected]> Co-authored-by: ernestognw <[email protected]>
1 parent 52e0e3e commit 984233d

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

.changeset/nervous-eyes-teach.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Create2`: Bubbles up returndata from a deployed contract that reverted during construction.

contracts/mocks/ConstructorMock.sol

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
contract ConstructorMock {
6+
bool foo;
7+
8+
enum RevertType {
9+
None,
10+
RevertWithoutMessage,
11+
RevertWithMessage,
12+
RevertWithCustomError,
13+
Panic
14+
}
15+
16+
error CustomError();
17+
18+
constructor(RevertType error) {
19+
// After transpilation to upgradeable contract, the constructor will become an initializer
20+
// To silence the `... can be restricted to view` warning, we write to state
21+
foo = true;
22+
23+
if (error == RevertType.RevertWithoutMessage) {
24+
revert();
25+
} else if (error == RevertType.RevertWithMessage) {
26+
revert("ConstructorMock: reverting");
27+
} else if (error == RevertType.RevertWithCustomError) {
28+
revert CustomError();
29+
} else if (error == RevertType.Panic) {
30+
uint256 a = uint256(0) / uint256(0);
31+
a;
32+
}
33+
}
34+
}

contracts/utils/Create2.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ library Create2 {
4444
/// @solidity memory-safe-assembly
4545
assembly {
4646
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
47+
// if no address was created, and returndata is not empty, bubble revert
48+
if and(iszero(addr), not(iszero(returndatasize()))) {
49+
returndatacopy(0, 0, returndatasize())
50+
revert(0, returndatasize())
51+
}
4752
}
4853
if (addr == address(0)) {
4954
revert Errors.FailedDeployment();

test/utils/Create2.test.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const { ethers } = require('hardhat');
22
const { expect } = require('chai');
33
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
4+
const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
5+
6+
const { RevertType } = require('../helpers/enums');
47

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

22-
return { deployer, other, factory, constructorByteCode, constructorLessBytecode };
25+
const mockFactory = await ethers.getContractFactory('ConstructorMock');
26+
27+
return { deployer, other, factory, constructorByteCode, constructorLessBytecode, mockFactory };
2328
}
2429

2530
describe('Create2', function () {
@@ -130,5 +135,56 @@ describe('Create2', function () {
130135
.to.be.revertedWithCustomError(this.factory, 'InsufficientBalance')
131136
.withArgs(0n, 1n);
132137
});
138+
139+
describe('reverts error thrown during contract creation', function () {
140+
it('bubbles up without message', async function () {
141+
await expect(
142+
this.factory.$deploy(
143+
0n,
144+
saltHex,
145+
ethers.concat([
146+
this.mockFactory.bytecode,
147+
this.mockFactory.interface.encodeDeploy([RevertType.RevertWithoutMessage]),
148+
]),
149+
),
150+
).to.be.revertedWithCustomError(this.factory, 'FailedDeployment');
151+
});
152+
153+
it('bubbles up message', async function () {
154+
await expect(
155+
this.factory.$deploy(
156+
0n,
157+
saltHex,
158+
ethers.concat([
159+
this.mockFactory.bytecode,
160+
this.mockFactory.interface.encodeDeploy([RevertType.RevertWithMessage]),
161+
]),
162+
),
163+
).to.be.revertedWith('ConstructorMock: reverting');
164+
});
165+
166+
it('bubbles up custom error', async function () {
167+
await expect(
168+
this.factory.$deploy(
169+
0n,
170+
saltHex,
171+
ethers.concat([
172+
this.mockFactory.bytecode,
173+
this.mockFactory.interface.encodeDeploy([RevertType.RevertWithCustomError]),
174+
]),
175+
),
176+
).to.be.revertedWithCustomError({ interface: this.mockFactory.interface }, 'CustomError');
177+
});
178+
179+
it('bubbles up panic', async function () {
180+
await expect(
181+
this.factory.$deploy(
182+
0n,
183+
saltHex,
184+
ethers.concat([this.mockFactory.bytecode, this.mockFactory.interface.encodeDeploy([RevertType.Panic])]),
185+
),
186+
).to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO);
187+
});
188+
});
133189
});
134190
});

0 commit comments

Comments
 (0)