diff --git a/foundry.toml b/foundry.toml index 8e8d78d9..fcacb1ff 100644 --- a/foundry.toml +++ b/foundry.toml @@ -34,6 +34,7 @@ # Test the optimized contracts without re-compiling them [profile.test-optimized] + ffi = true src = "test" [doc] diff --git a/script/Base.s.sol b/script/Base.s.sol index e9f18740..8e710a6a 100644 --- a/script/Base.s.sol +++ b/script/Base.s.sol @@ -1,9 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-or-later +// solhint-disable no-console pragma solidity >=0.8.19 <0.9.0; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +import { console2 } from "forge-std/src/console2.sol"; import { Script } from "forge-std/src/Script.sol"; -abstract contract BaseScript is Script { +contract BaseScript is Script { + using Strings for uint256; + /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; @@ -38,4 +44,26 @@ abstract contract BaseScript is Script { _; vm.stopBroadcast(); } + + /// @dev The presence of the salt instructs Forge to deploy contracts via this deterministic CREATE2 factory: + /// https://github.com/Arachnid/deterministic-deployment-proxy + /// + /// Notes: + /// - The salt format is "ChainID , Version ". + /// - The version is obtained from `package.json` using the `ffi` cheatcode: + /// https://book.getfoundry.sh/cheatcodes/ffi + /// - Requires `jq` CLI tool installed: https://jqlang.github.io/jq/ + function constructCreate2Salt() public returns (bytes32) { + string memory chainId = block.chainid.toString(); + string[] memory inputs = new string[](4); + inputs[0] = "jq"; + inputs[1] = "-r"; + inputs[2] = ".version"; + inputs[3] = "./package.json"; + bytes memory result = vm.ffi(inputs); + string memory version = string(result); + string memory create2Salt = string.concat("ChainID ", chainId, ", Version ", version); + console2.log("The CREATE2 salt is \"%s\"", create2Salt); + return bytes32(abi.encodePacked(create2Salt)); + } } diff --git a/script/DeployDeterministicBatch.s.sol b/script/DeployDeterministicBatch.s.sol index df6a9362..16d67e21 100644 --- a/script/DeployDeterministicBatch.s.sol +++ b/script/DeployDeterministicBatch.s.sol @@ -8,9 +8,8 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// @notice Deploys {SablierV2Batch} at a deterministic address across chains. /// @dev Reverts if the contract has already been deployed. contract DeployDeterministicBatch is BaseScript { - /// @dev The presence of the salt instructs Forge to deploy contracts via this deterministic CREATE2 factory: - /// https://github.com/Arachnid/deterministic-deployment-proxy function run(string memory create2Salt) public virtual broadcast returns (SablierV2Batch batch) { - batch = new SablierV2Batch{ salt: bytes32(abi.encodePacked(create2Salt)) }(); + bytes32 salt = bytes32(abi.encodePacked(create2Salt)); + batch = new SablierV2Batch{ salt: salt }(); } } diff --git a/script/DeployDeterministicPeriphery.s.sol b/script/DeployDeterministicPeriphery.s.sol index 01fdb7d3..d196d534 100644 --- a/script/DeployDeterministicPeriphery.s.sol +++ b/script/DeployDeterministicPeriphery.s.sol @@ -13,15 +13,14 @@ import { SablierV2MerkleStreamerFactory } from "../src/SablierV2MerkleStreamerFa /// /// @dev Reverts if any contract has already been deployed. contract DeployDeterministicPeriphery is BaseScript { - /// @dev The presence of the salt instructs Forge to deploy the contract via a deterministic CREATE2 factory. - /// https://github.com/Arachnid/deterministic-deployment-proxy function run(string memory create2Salt) public virtual broadcast returns (SablierV2Batch batch, SablierV2MerkleStreamerFactory merkleStreamerFactory) { - batch = new SablierV2Batch{ salt: bytes32(abi.encodePacked(create2Salt)) }(); + bytes32 salt = constructCreate2Salt(); + batch = new SablierV2Batch{ salt: salt }(); merkleStreamerFactory = new SablierV2MerkleStreamerFactory{ salt: bytes32(abi.encodePacked(create2Salt)) }(); } } diff --git a/test/utils/BaseScript.t.sol b/test/utils/BaseScript.t.sol new file mode 100644 index 00000000..28c15073 --- /dev/null +++ b/test/utils/BaseScript.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.19 <0.9.0; + +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { PRBTest } from "@prb/test/src/PRBTest.sol"; + +import { BaseScript } from "script/Base.s.sol"; + +contract BaseScript_Test is PRBTest { + using Strings for uint256; + + BaseScript internal baseScript = new BaseScript(); + + function test_ConstructCreate2Salt() public { + string memory chainId = block.chainid.toString(); + string memory version = "1.1.1"; + string memory salt = string.concat("ChainID ", chainId, ", Version ", version); + + bytes32 actualSalt = baseScript.constructCreate2Salt(); + bytes32 expectedSalt = bytes32(abi.encodePacked(salt)); + assertEq(actualSalt, expectedSalt, "CREATE2 salt mismatch"); + } +}