Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/lib/LibRainDeploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import {console2} from "forge-std-1.16.1/src/console2.sol";
library LibRainDeploy {
/// Thrown when deployment via Zoltu factory fails. This could be either an
/// explicit revert that manifests as non success, or a silent failure that
/// results in the deployed address being empty somehow.
/// results in the deployed address being empty somehow. `deployedAddress`
/// is zero whenever `success` is false, as a failed factory call returns no
/// address.
error DeployFailed(bool success, address deployedAddress);

/// Thrown when a dependency is missing on a network before deployment.
Expand Down Expand Up @@ -161,7 +163,11 @@ library LibRainDeploy {
// Writing 20 bytes at offset 12 (= 32 - 20) right-aligns the address
// in scratch space so that mload(0) produces a correctly padded value.
success := call(gas(), zoltuFactory, 0, add(creationCode, 0x20), mload(creationCode), 12, 20)
deployedAddress := mload(0)
// The EVM copies revert data into the output region too, so only a
// successful call leaves an address there. A failed call leaves
// `deployedAddress` zero rather than reporting revert bytes as an
// address.
if success { deployedAddress := mload(0) }
}
if (!success || deployedAddress == address(0) || deployedAddress.code.length == 0) {
console2.log("Zoltu deployment failed. Success:", success, "Deployed Address:", deployedAddress);
Expand Down
17 changes: 17 additions & 0 deletions test/src/lib/LibRainDeploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.25;

import {Test} from "forge-std-1.16.1/src/Test.sol";
import {LibRainDeploy} from "../../../src/lib/LibRainDeploy.sol";
import {MockAddressRevertingFactory} from "./MockAddressRevertingFactory.sol";
import {MockDeployable} from "./MockDeployable.sol";
import {MockReverter} from "./MockReverter.sol";

Expand Down Expand Up @@ -297,6 +298,22 @@ contract LibRainDeployTest is Test {
this.externalDeployZoltu(type(MockReverter).creationCode);
}

/// `deployZoltu` MUST report the zero address when the factory call fails,
/// even when the factory reverts with data that reads as an address. The
/// call output buffer holds revert data on the failure path, so anything
/// read from it there is not an address the factory returned.
function testDeployZoltuFailedCallReportsZeroAddress() external {
vm.createSelectFork(LibRainDeploy.ARBITRUM_ONE);
vm.etch(LibRainDeploy.ZOLTU_FACTORY, address(new MockAddressRevertingFactory()).code);
// The factory reverts with its own address, which has code, so the
// reported address is only zero if the failure path never reads the
// output buffer.
assertGt(LibRainDeploy.ZOLTU_FACTORY.code.length, 0);

vm.expectRevert(abi.encodeWithSelector(LibRainDeploy.DeployFailed.selector, false, address(0)));
this.externalDeployZoltu(type(MockDeployable).creationCode);
}

/// `deployToNetworks` MUST revert with `UnexpectedDeployedAddress` when the
/// deployed address does not match the expected address.
function testUnexpectedDeployedAddressReverts() external {
Expand Down
18 changes: 18 additions & 0 deletions test/src/lib/MockAddressRevertingFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity ^0.8.25;

/// @title MockAddressRevertingFactory
/// Zoltu factory stand-in whose calls always fail, reverting with exactly the
/// twenty bytes of its own address. A caller that reads its call output buffer
/// without checking the call succeeded sees the address of a contract that has
/// code.
contract MockAddressRevertingFactory {
fallback() external {
bytes20 self = bytes20(address(this));
assembly ("memory-safe") {
mstore(0, self)
revert(0, 20)
}
}
}
Loading