diff --git a/README.md b/README.md index 8d8bb9ae..9def81cd 100644 --- a/README.md +++ b/README.md @@ -65,16 +65,6 @@ Then, the test suite can be run with: pnpm run test ``` -#### Address Mismatch - -Many of the contracts (e.g. callbacks) require a specific address prefix or have a deterministic address. If tests are failing for this reason, the cause is usually one of: - -- The code of a callback contract has been changed - - This requires re-generating the salt for the contract. See the [test_salts.sh](/script/salts/test/test_salts.sh) script. -- There has been a change to the dependencies under `/lib` or `/dependencies`. The dependencies affect the build output, so any changes will affect the bytecode generated by the Solidity compiler. - - If the change was inadvertent, this can be fixed by running `pnpm install` to reset the changes. - - If the change to dependencies and invalidation of salts is expected, then new salts must be generated. In some cases (such as Uniswap V2 and V3 factories), the new addresses must be recorded in the `Constants.sol` file. - ### Format Combines `forge fmt` and `solhint` diff --git a/script/deploy/deploy.sh b/script/deploy/deploy.sh index 4ffa68a1..5a458390 100755 --- a/script/deploy/deploy.sh +++ b/script/deploy/deploy.sh @@ -9,6 +9,9 @@ # RPC_URL: URL for the RPC node. Should be specified in .env. # VERIFIER_URL: URL for the Etherscan API verifier. Should be specified when used on an unsupported chain. +# Exit on error +set -e + # Iterate through named arguments # Source: https://unix.stackexchange.com/a/388038 while [ $# -gt 0 ]; do diff --git a/script/deploy/write_deployment.sh b/script/deploy/write_deployment.sh index 341405f7..13edbe79 100755 --- a/script/deploy/write_deployment.sh +++ b/script/deploy/write_deployment.sh @@ -4,6 +4,9 @@ # ./write_deployment.sh # Updates the env.json file with the key-value pair +# Exit on error +set -e + # Get command-line arguments KEY=$1 VALUE=$2 diff --git a/script/install.sh b/script/install.sh index b9c9cae2..bf032c43 100755 --- a/script/install.sh +++ b/script/install.sh @@ -1,5 +1,8 @@ #!/bin/bash +# Exit on error +set -e + # echo "" # echo "*** Setting up submodules" # git submodule init diff --git a/script/publish.sh b/script/publish.sh index 8914d4cd..d69374a1 100755 --- a/script/publish.sh +++ b/script/publish.sh @@ -1,5 +1,8 @@ #!/bin/bash +# Exit on error +set -e + # This script is used to release a new version of the package. It will: # - Validate that the working directory is clean # - Validate the version number argument diff --git a/script/salts/README.md b/script/salts/README.md index 34531d5e..ac3eb2e3 100644 --- a/script/salts/README.md +++ b/script/salts/README.md @@ -4,26 +4,6 @@ This document provides instructions on how to generate and use salts for CREATE2 ## Tasks -### Generating Test Salts - -Many of the tests use salts to deploy callbacks at addresses with a specific prefix. Changes to the state variables in the test contract can require the re-generation of the salt. To do so, perform the following: - -```bash -./script/salts/test/test_salts.sh --saltKey -``` - -For example, if re-generating salts for the `MockCallback` contract, make the following call: - -```bash -./script/salts/test/test_salts.sh --saltKey MockCallback -``` - -The resulting salts will be written to the `./script/salts/salts.json` file, which the test cases will read from. - -Notes: - -- When re-generating salts, ensure that any git submodule changes are either reverted or committed. For example, if switching from a branch with a particular submodule to a branch in which that submodule is not present, the submodule changes will not be automatically reverted. This will cause changes in the generated bytecode and in turn, the salt. The submodule changes must be reverted and the salts generated afterwards. - ### Generating AuctionHouse Salts For aesthetic reasons, the AuctionHouse contracts may need to be deployed at deterministic addresses. diff --git a/script/salts/WithSalts.s.sol b/script/salts/WithSalts.s.sol index 2f5035f2..cc706cbf 100644 --- a/script/salts/WithSalts.s.sol +++ b/script/salts/WithSalts.s.sol @@ -17,21 +17,18 @@ contract WithSalts is Script { } function _getBytecodePath( - string memory name_ + string memory name_, + bytes32 bytecodeHash_ ) internal pure returns (string memory) { - return string.concat(_getBytecodeDirectory(), "/", name_, ".bin"); + return string.concat( + _getBytecodeDirectory(), "/", name_, "-", vm.toString(bytecodeHash_), ".bin" + ); } function _createBytecodeDirectory() internal { // Create the bytecode folder if it doesn't exist if (!vm.isDir(_getBytecodeDirectory())) { - console2.log("Creating bytecode directory"); - - string[] memory inputs = new string[](2); - inputs[0] = "mkdir"; - inputs[1] = _BYTECODE_DIR; - - vm.ffi(inputs); + vm.createDir(_getBytecodeDirectory(), true); } } @@ -44,12 +41,55 @@ contract WithSalts is Script { bytes memory bytecode = abi.encodePacked(creationCode_, contractArgs_); bytecodeHash = keccak256(bytecode); - bytecodePath = _getBytecodePath(contractName_); + bytecodePath = _getBytecodePath(contractName_, bytecodeHash); + + // Don't write the bytecode if it already exists + if (vm.isFile(bytecodePath)) { + console2.log("Skipping bytecode write for ", contractName_); + return (bytecodePath, bytecodeHash); + } + + _createBytecodeDirectory(); vm.writeFile(bytecodePath, vm.toString(bytecode)); return (bytecodePath, bytecodeHash); } + function _generateSalt( + string memory contractName_, + bytes memory creationCode_, + bytes memory contractArgs_, + string memory prefix_ + ) internal returns (bytes32) { + // Write the bytecode + (string memory bytecodePath, bytes32 bytecodeHash) = + _writeBytecode(contractName_, creationCode_, contractArgs_); + + // Call the salts script to generate and return the salt + string[] memory inputs = new string[](9); + inputs[0] = "./script/salts/generate_salt.sh"; + inputs[1] = "--bytecodeFile"; + inputs[2] = bytecodePath; + inputs[3] = "--bytecodeHash"; + inputs[4] = vm.toString(bytecodeHash); + inputs[5] = "--prefix"; + inputs[6] = prefix_; + + bytes memory output = vm.ffi(inputs); + console2.log("Salt generated for", contractName_, "with prefix", prefix_); + console2.logBytes32(bytes32(output)); + + return bytes32(output); + } + + function _getTestSalt( + string memory contractName_, + bytes memory creationCode_, + bytes memory contractArgs_ + ) internal returns (bytes32) { + return _getSalt(string.concat("Test_", contractName_), creationCode_, contractArgs_); + } + /// @notice Gets the salt for a given key /// @dev If the key is not found, the function will return `bytes32(0)`. /// diff --git a/script/salts/generate_salt.sh b/script/salts/generate_salt.sh new file mode 100755 index 00000000..e24edbde --- /dev/null +++ b/script/salts/generate_salt.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Usage: +# ./generate_salt.sh --bytecode --prefix --bytecodeHash [--deployer ] + +# Exit on error +set -e + +# Iterate through named arguments +# Source: https://unix.stackexchange.com/a/388038 +while [ $# -gt 0 ]; do + if [[ $1 == *"--"* ]]; then + v="${1/--/}" + declare $v="$2" + fi + + shift +done + +# Check if bytecodeFile is set +if [ -z "$bytecodeFile" ] +then + echo "No bytecode file specified. Provide the relative path after the command." + exit 1 +fi + +# Check if bytecodeFile exists +if [ ! -f "$bytecodeFile" ] +then + echo "Bytecode file ($bytecodeFile) not found. Provide the correct relative path after the command." + exit 1 +fi + +# Check if prefix is set +if [ -z "$prefix" ] +then + echo "No prefix specified. Provide the prefix after the bytecode file." + exit 1 +fi + +# Check if bytecodeHash is set +if [ -z "$bytecodeHash" ] +then + echo "No args hash specified. Provide the args hash after the salt key." + exit 1 +fi + +DEPLOYER_FLAG="" +if [ ! -z "$deployer" ] +then + DEPLOYER_FLAG="--deployer $deployer" +fi + +# Generate salt using cast create2 +output=$(cast create2 --case-sensitive --starts-with $prefix --init-code $(cat $bytecodeFile) $DEPLOYER_FLAG) + +# Get the first salt (as cast will often return the same salt multiple times) +salt=$(echo "$output" | grep 'Salt: ' | head -n 1 | awk -F' ' '{print $2}') + +echo "$salt" diff --git a/script/salts/salts.json b/script/salts/salts.json index f70a5031..87f74533 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -25,25 +25,5 @@ "0xcad29ef747f1a7ad12c1e5e715ac675f8534c0d3682dfddfa39287ae7b7d7f01": "0x761de87b1ea43dc2f84665b59b2ce8498c1a09458e3a89751578be7c72811114", "0xf7e1693ed561ec566275117c8ea944263f9f801df4d4c61e4a934b7152f36dcc": "0x0b3e7731b05ed1f6d2cbbc4e64b503b8582801ae807ee756b3472916ec29c6fb", "0xf877dc218ee7538ffb3ad6f8fd1b4694cbb6f38d9e0b7a4d9a19ace3c24830a2": "0xad2a1f39589d55fd4fb667bda19d168106901d33100bb9c50fc57b1735024bcd" - }, - "Test_MockCallback": { - "0x122b7366cd1ef2d5b4e5301e1905912ec0fe8b73e77aee436d38421a86d15313": "0x0a9151c7b46dc91425f2a34eb6b2fb33ee191550a9165830ae27877b3f4ca221", - "0x31839cd002be9d58c711006d802d11286e56601f846dda258f2e3a3d6312e9d5": "0x413ec56e5cda12c513a637af386dc614c6d8d4cd3332147aa3c1e8a384357a78", - "0x3906ecd77f3b4704ca6c6d5a06ecf7039c68312b200ade661f6d2a28098049e1": "0xe9927b53dc310098994a46e7f6c2cd5104b776f99b91115eece80ec5f8acebe8", - "0x525f9fb7cb93f53509d24ec2f82f9d832e21209c45978e2e315915d6ff8c6a7f": "0xe900d098e9a5d9bb11ba2a3667b6b1eeae83814fe553bb76e514cf5b3fa7a2ac", - "0x54a5ad7b0937b5a5876975ca86a146047356b10210457afbbe77c13e6f5dd954": "0x8c917d14c4e8991eb770603f76c7a2ee6ec9938465f1b8e3c4bc144066179dac", - "0x6205fbf29817ca90fa2498c5b1187af9359d49f9386e35ba328382bcaa131cc3": "0x360e652e5304ee51b6e45d13472e12154037096162a58739e9407aae45926f01", - "0x678385a33025444b4cc1bb4114e4f2b91518f7b62874b3dc9c42cc273c353cb9": "0x55c1bb8e1768f6374b0d715192f3a4623af7018e5c4ccfae641766ff58ef6f53", - "0x780f4d36b46e5c008598b23b91a1a33d3a6d9a009ab4e7d1ec6c4fd0b96ae27e": "0x2e795045262fbbed80e54de0ee9a59506de844b46ffdcbf2fbfe6d2cb7bf6247", - "0x7dceb7e223c0a4949e5e2af68c3896c5549e9d942a7c0a2f82de4b4f1048467f": "0x384c9eafa40ee73af86d2d4fafdfd1ee31e06696f2c23a0c1d18b0e8785b6ce1", - "0x7fdc727348e9937fdf4d1ce371e33669ccd01a66514298047926c8cf38059721": "0x67567fd2ac4f9e05bda2cd306f73bb75aec8f0ce6ea6a60f00665b3ba7440b62", - "0x804b4adedbf071a753a22b7f14d78b78c9417097e5826cae690132d4b0d1598b": "0x8bb2239f825595560b0a31bb2bf07b18cd578df9dfb500b00c386eac3fd8b609", - "0x97c630af696c3e707d37a9df6f374aa2de8cbad3b57d70fd716c420aff85ee21": "0x32dbea5832194b7fa3c189f428b3abd9be5dc93d536c7799e7dbe122f0b9da89", - "0xb7e994be576e566138244c31d8ed1a61a2c2326f7850970f250734441540ba47": "0x3c1a8efebca923e4a1cdc651c554d06a3548e7929d0c7971866bf59b7d6571b3", - "0xb8efc6c465a7b9a95be1d788850dc2fc5372dc35032c8128b1ffec136000ed33": "0xb31bd2f0dada0bf7ceae9b37708c9329967684b9b73b7cc4a99027477b46b132", - "0xbc04d3cb883befcd89fc74569e56a84bf7adb63ae3864cbd1c11314d21c1355a": "0x4783fc51f9862eb57357966692ae70dd540e68ea0848903b77500534194b7f1c", - "0xc0c056f15966bf95b1fe024d064fdf5dc1ff953912a563bc162ff480bb29829d": "0x2eaeb172f8893ca0778cc5470e73f871ffc45825607e7fc37e15db3c369898a8", - "0xc51d6c63ad7dee6c11da98be8f32ac5ca7df37533c82928856a61da22d2e15dd": "0x19ce56b3efb04248482e4368f2063c9ea6f0ca1cedaced0737fa06ece52c43e1", - "0xdf57220126314faa7501d1bf9080691a765b85dcd73ddbc413ad43a5981beb82": "0x4be72c05d6c702b486af22bf4f9a29e0c0c466b036fee37355e9e7fb7c67b66d" } } diff --git a/script/salts/test/TestSalts.s.sol b/script/salts/test/TestSalts.s.sol deleted file mode 100644 index 533296aa..00000000 --- a/script/salts/test/TestSalts.s.sol +++ /dev/null @@ -1,365 +0,0 @@ -/// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.19; - -// Scripting libraries -import {Script} from "@forge-std-1.9.1/Script.sol"; -import {WithEnvironment} from "../../deploy/WithEnvironment.s.sol"; -import {Permit2User} from "../../../test/lib/permit2/Permit2User.sol"; -import {WithSalts} from "../WithSalts.s.sol"; - -import {MockCallback} from "../../../test/callbacks/MockCallback.sol"; -import {Callbacks} from "../../../src/lib/Callbacks.sol"; - -import {TestConstants} from "../../../test/Constants.sol"; - -contract TestSalts is Script, WithEnvironment, Permit2User, WithSalts, TestConstants { - string internal constant _MOCK_CALLBACK = "MockCallback"; - - function _setUp( - string calldata chain_ - ) internal { - _loadEnv(chain_); - _createBytecodeDirectory(); - } - - function generate(string calldata chain_, string calldata saltKey_) public { - _setUp(chain_); - - // For the given salt key, call the appropriate selector - // e.g. a salt key named MockCallback would require the following function: generateMockCallback() - bytes4 selector = bytes4(keccak256(bytes(string.concat("generate", saltKey_, "()")))); - - // Call the generate function for the salt key - (bool success,) = address(this).call(abi.encodeWithSelector(selector)); - require(success, string.concat("Failed to generate ", saltKey_)); - } - - function generateMockCallback() public { - // Allowlist callback supports onCreate, onPurchase, and onBid callbacks - // 10011000 = 0x98 - // cast create2 -s 98 -i $(cat ./bytecode/MockCallback98.bin) - bytes memory args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: false, - onCurate: false, - onPurchase: true, - onBid: true, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - bytes memory contractCode = type(MockCallback).creationCode; - (string memory bytecodePath, bytes32 bytecodeHash) = - _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "98", _MOCK_CALLBACK, bytecodeHash); - - // 11111111 = 0xFF - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: true, - onCurate: true, - onPurchase: true, - onBid: true, - onSettle: true, - receiveQuoteTokens: true, - sendBaseTokens: true - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "FF", _MOCK_CALLBACK, bytecodeHash); - - // 11111101 = 0xFD - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: true, - onCurate: true, - onPurchase: true, - onBid: true, - onSettle: true, - receiveQuoteTokens: false, - sendBaseTokens: true - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "FD", _MOCK_CALLBACK, bytecodeHash); - - // 11111110 = 0xFE - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: true, - onCurate: true, - onPurchase: true, - onBid: true, - onSettle: true, - receiveQuoteTokens: true, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "FE", _MOCK_CALLBACK, bytecodeHash); - - // 11111100 = 0xFC - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: true, - onCurate: true, - onPurchase: true, - onBid: true, - onSettle: true, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "FC", _MOCK_CALLBACK, bytecodeHash); - - // 00000000 - 0x00 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "00", _MOCK_CALLBACK, bytecodeHash); - - // 00000010 - 0x02 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: true, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "02", _MOCK_CALLBACK, bytecodeHash); - - // 10000000 = 0x80 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "80", _MOCK_CALLBACK, bytecodeHash); - - // 01000000 = 0x40 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: true, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "40", _MOCK_CALLBACK, bytecodeHash); - - // 00100000 = 0x20 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: true, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "20", _MOCK_CALLBACK, bytecodeHash); - - // 00010000 = 0x10 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: true, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "10", _MOCK_CALLBACK, bytecodeHash); - - // 00001000 = 0x08 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: true, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "08", _MOCK_CALLBACK, bytecodeHash); - - // 00000100 = 0x04 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: true, - receiveQuoteTokens: false, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "04", _MOCK_CALLBACK, bytecodeHash); - - // 00000010 = 0x02 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: true, - sendBaseTokens: false - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "02", _MOCK_CALLBACK, bytecodeHash); - - // 00000001 = 0x01 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: true - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "01", _MOCK_CALLBACK, bytecodeHash); - - // 10000001 = 0x81 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: false, - onCurate: false, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: true - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "81", _MOCK_CALLBACK, bytecodeHash); - - // 00100001 = 0x21 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: true, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: true - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "21", _MOCK_CALLBACK, bytecodeHash); - - // 10100001 = 0xA1 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: true, - onCancel: false, - onCurate: true, - onPurchase: false, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: true - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "A1", _MOCK_CALLBACK, bytecodeHash); - - // 00010001 = 0x11 - args = abi.encode( - _AUCTION_HOUSE, - Callbacks.Permissions({ - onCreate: false, - onCancel: false, - onCurate: false, - onPurchase: true, - onBid: false, - onSettle: false, - receiveQuoteTokens: false, - sendBaseTokens: true - }) - ); - (bytecodePath, bytecodeHash) = _writeBytecode(_MOCK_CALLBACK, contractCode, args); - _setTestSalt(bytecodePath, "11", _MOCK_CALLBACK, bytecodeHash); - } -} diff --git a/script/salts/test/test_salts.sh b/script/salts/test/test_salts.sh deleted file mode 100755 index f1dacaca..00000000 --- a/script/salts/test/test_salts.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# Usage: -# ./test_salts.sh --saltKey --envFile <.env> - -# Iterate through named arguments -# Source: https://unix.stackexchange.com/a/388038 -while [ $# -gt 0 ]; do - if [[ $1 == *"--"* ]]; then - v="${1/--/}" - declare $v="$2" - fi - - shift -done - -# Get the name of the .env file or use the default -ENV_FILE=${envFile:-".env"} -echo "Sourcing environment variables from $ENV_FILE" - -# Load environment file -set -a # Automatically export all variables -source $ENV_FILE -set +a # Disable automatic export - -# Check that the CHAIN environment variable is set -if [ -z "$CHAIN" ] -then - echo "CHAIN environment variable is not set. Please set it in the .env file or provide it as an environment variable." - exit 1 -fi - -# Check if saltKey is specified -if [ -z "$saltKey" ] -then - echo "No salt key specified. Provide the salt key after the --saltKey flag." - exit 1 -fi - -echo "Using chain: $CHAIN" -echo "Using RPC at URL: $RPC_URL" -echo "Salt key: $saltKey" - -salt_file="./script/salts/salts.json" -salt_tmp_file="./script/salts/salts.json.tmp" - -# Clear the salts for the specified salt key -if [ -f $salt_file ]; then - echo "Clearing old values for salt key: $saltKey" - jq "del(.\"Test_$saltKey\")" $salt_file > $salt_tmp_file && mv $salt_tmp_file $salt_file -fi - -# Generate bytecode -forge script ./script/salts/test/TestSalts.s.sol:TestSalts --sig "generate(string,string)()" $CHAIN $saltKey diff --git a/script/salts/write_salt.sh b/script/salts/write_salt.sh index 1ca8551c..d9ad6f93 100755 --- a/script/salts/write_salt.sh +++ b/script/salts/write_salt.sh @@ -3,6 +3,9 @@ # Usage: # ./write_salt.sh --bytecode --prefix --saltKey --bytecodeHash [--deployer ] +# Exit on error +set -e + # Iterate through named arguments # Source: https://unix.stackexchange.com/a/388038 while [ $# -gt 0 ]; do diff --git a/test/AtomicAuctionHouse/AuctionHouseTest.sol b/test/AtomicAuctionHouse/AuctionHouseTest.sol index 73914b79..feeaac61 100644 --- a/test/AtomicAuctionHouse/AuctionHouseTest.sol +++ b/test/AtomicAuctionHouse/AuctionHouseTest.sol @@ -30,9 +30,10 @@ import {AuctionModule} from "../../src/modules/Auction.sol"; import {Veecode, toKeycode, keycodeFromVeecode, Keycode} from "../../src/modules/Keycode.sol"; -import {WithSalts} from "../lib/WithSalts.sol"; +import {WithSalts} from "../../script/salts/WithSalts.s.sol"; +import {TestConstants} from "../Constants.sol"; -abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts { +abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts, TestConstants { MockFeeOnTransferERC20 internal _baseToken; MockFeeOnTransferERC20 internal _quoteToken; @@ -51,7 +52,6 @@ abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts { uint256 internal constant _BASE_SCALE = 1e18; - address internal constant _OWNER = address(0x1); address internal constant _SELLER = address(0x2); address internal constant _PROTOCOL = address(0x3); address internal constant _CURATOR = address(0x4); @@ -106,7 +106,7 @@ abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts { // Create an AtomicAuctionHouse at a deterministic address, since it is used as input to callbacks AtomicAuctionHouse auctionHouse = new AtomicAuctionHouse(_OWNER, _PROTOCOL, _permit2Address); - _auctionHouse = AtomicAuctionHouse(address(0x000000000000000000000000000000000000000A)); + _auctionHouse = AtomicAuctionHouse(_AUCTION_HOUSE); vm.etch(address(_auctionHouse), address(auctionHouse).code); vm.store(address(_auctionHouse), bytes32(uint256(0)), bytes32(abi.encode(_OWNER))); // Owner vm.store(address(_auctionHouse), bytes32(uint256(6)), bytes32(abi.encode(1))); // Reentrancy @@ -272,6 +272,7 @@ abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts { modifier givenLotHasAllowlist() { // Get the salt + // 10011000 = 0x98 Callbacks.Permissions memory permissions = Callbacks.Permissions({ onCreate: true, onCancel: false, @@ -283,7 +284,7 @@ abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts { sendBaseTokens: false }); bytes memory args = abi.encode(address(_auctionHouse), permissions); - bytes32 salt = _getTestSalt("MockCallback", type(MockCallback).creationCode, args); + bytes32 salt = _generateSalt("MockCallback", type(MockCallback).creationCode, args, "98"); vm.startBroadcast(); // required for CREATE2 address to work correctly. doesn't do anything in a test _callback = new MockCallback{salt: salt}(address(_auctionHouse), permissions); @@ -367,6 +368,10 @@ abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts { modifier givenCallbackIsSet() { // Get the salt + // 11111100 = 0xFC + // 11111110 = 0xFE + // 11111111 = 0xFF + // 11111101 = 0xFD Callbacks.Permissions memory permissions = Callbacks.Permissions({ onCreate: true, onCancel: true, @@ -377,8 +382,16 @@ abstract contract AtomicAuctionHouseTest is Test, Permit2User, WithSalts { receiveQuoteTokens: _callbackReceiveQuoteTokens, sendBaseTokens: _callbackSendBaseTokens }); + string memory prefix = "FC"; + if (_callbackReceiveQuoteTokens && _callbackSendBaseTokens) { + prefix = "FF"; + } else if (_callbackReceiveQuoteTokens) { + prefix = "FE"; + } else if (_callbackSendBaseTokens) { + prefix = "FD"; + } bytes memory args = abi.encode(address(_auctionHouse), permissions); - bytes32 salt = _getTestSalt("MockCallback", type(MockCallback).creationCode, args); + bytes32 salt = _generateSalt("MockCallback", type(MockCallback).creationCode, args, prefix); // Required for CREATE2 address to work correctly. doesn't do anything in a test // Source: https://github.com/foundry-rs/foundry/issues/6402 diff --git a/test/AuctionHouse/sendPayment.t.sol b/test/AuctionHouse/sendPayment.t.sol index 4af2a14e..0ead95b7 100644 --- a/test/AuctionHouse/sendPayment.t.sol +++ b/test/AuctionHouse/sendPayment.t.sol @@ -9,12 +9,12 @@ import {MockFeeOnTransferERC20} from "../lib/mocks/MockFeeOnTransferERC20.sol"; import {Permit2User} from "../lib/permit2/Permit2User.sol"; import {Callbacks} from "../../src/lib/Callbacks.sol"; -import {WithSalts} from "../lib/WithSalts.sol"; +import {WithSalts} from "../../script/salts/WithSalts.s.sol"; +import {TestConstants} from "../Constants.sol"; -contract SendPaymentTest is Test, Permit2User, WithSalts { +contract SendPaymentTest is Test, Permit2User, WithSalts, TestConstants { MockAuctionHouse internal _auctionHouse; - address internal constant _OWNER = address(0x1); address internal constant _SELLER = address(0x2); address internal constant _PROTOCOL = address(0x3); address internal constant _USER = address(0x4); @@ -31,7 +31,7 @@ contract SendPaymentTest is Test, Permit2User, WithSalts { // Create an AuctionHouse at a deterministic address, since it is used as input to callbacks MockAuctionHouse mockAuctionHouse = new MockAuctionHouse(_OWNER, _PROTOCOL, _permit2Address); - _auctionHouse = MockAuctionHouse(address(0x000000000000000000000000000000000000000A)); + _auctionHouse = MockAuctionHouse(_AUCTION_HOUSE); vm.etch(address(_auctionHouse), address(mockAuctionHouse).code); vm.store(address(_auctionHouse), bytes32(uint256(0)), bytes32(abi.encode(_OWNER))); // Owner vm.store(address(_auctionHouse), bytes32(uint256(6)), bytes32(abi.encode(1))); // Reentrancy @@ -53,6 +53,8 @@ contract SendPaymentTest is Test, Permit2User, WithSalts { modifier givenAuctionHasCallback() { // Get the salt + // 00000000 = 0x00 + // 00000010 = 0x02 Callbacks.Permissions memory permissions = Callbacks.Permissions({ onCreate: false, onCancel: false, @@ -63,8 +65,12 @@ contract SendPaymentTest is Test, Permit2User, WithSalts { receiveQuoteTokens: _callbackReceiveQuoteTokens, sendBaseTokens: false }); + string memory prefix = "00"; + if (_callbackReceiveQuoteTokens) { + prefix = "02"; + } bytes memory args = abi.encode(address(_auctionHouse), permissions); - bytes32 salt = _getTestSalt("MockCallback", type(MockCallback).creationCode, args); + bytes32 salt = _generateSalt("MockCallback", type(MockCallback).creationCode, args, prefix); vm.broadcast(); // required for CREATE2 address to work correctly. doesn't do anything in a test _callback = new MockCallback{salt: salt}(address(_auctionHouse), permissions); diff --git a/test/BatchAuctionHouse/AuctionHouseTest.sol b/test/BatchAuctionHouse/AuctionHouseTest.sol index 001502bc..058c9490 100644 --- a/test/BatchAuctionHouse/AuctionHouseTest.sol +++ b/test/BatchAuctionHouse/AuctionHouseTest.sol @@ -30,9 +30,10 @@ import {AuctionModule} from "../../src/modules/Auction.sol"; import {Veecode, toKeycode, keycodeFromVeecode, Keycode} from "../../src/modules/Keycode.sol"; -import {WithSalts} from "../lib/WithSalts.sol"; +import {WithSalts} from "../../script/salts/WithSalts.s.sol"; +import {TestConstants} from "../Constants.sol"; -abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts { +abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts, TestConstants { MockFeeOnTransferERC20 internal _baseToken; MockFeeOnTransferERC20 internal _quoteToken; @@ -50,7 +51,6 @@ abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts { uint256 internal constant _BASE_SCALE = 1e18; - address internal constant _OWNER = address(0x1); address internal constant _SELLER = address(0x2); address internal constant _PROTOCOL = address(0x3); address internal constant _CURATOR = address(0x4); @@ -106,7 +106,7 @@ abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts { // Create a BatchAuctionHouse at a deterministic address, since it is used as input to callbacks BatchAuctionHouse auctionHouse = new BatchAuctionHouse(_OWNER, _PROTOCOL, _permit2Address); - _auctionHouse = BatchAuctionHouse(address(0x000000000000000000000000000000000000000A)); + _auctionHouse = BatchAuctionHouse(_AUCTION_HOUSE); vm.etch(address(_auctionHouse), address(auctionHouse).code); vm.store(address(_auctionHouse), bytes32(uint256(0)), bytes32(abi.encode(_OWNER))); // Owner vm.store(address(_auctionHouse), bytes32(uint256(6)), bytes32(abi.encode(1))); // Reentrancy @@ -325,6 +325,7 @@ abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts { modifier givenLotHasAllowlist() { // Get the salt + // 10011000 = 0x98 Callbacks.Permissions memory permissions = Callbacks.Permissions({ onCreate: true, onCancel: false, @@ -336,7 +337,7 @@ abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts { sendBaseTokens: false }); bytes memory args = abi.encode(address(_auctionHouse), permissions); - bytes32 salt = _getTestSalt("MockCallback", type(MockCallback).creationCode, args); + bytes32 salt = _generateSalt("MockCallback", type(MockCallback).creationCode, args, "98"); vm.startBroadcast(); // required for CREATE2 address to work correctly. doesn't do anything in a test _callback = new MockCallback{salt: salt}(address(_auctionHouse), permissions); @@ -420,6 +421,10 @@ abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts { modifier givenCallbackIsSet() { // Get the salt + // 11111100 = 0xFC + // 11111110 = 0xFE + // 11111111 = 0xFF + // 11111101 = 0xFD Callbacks.Permissions memory permissions = Callbacks.Permissions({ onCreate: true, onCancel: true, @@ -430,8 +435,16 @@ abstract contract BatchAuctionHouseTest is Test, Permit2User, WithSalts { receiveQuoteTokens: _callbackReceiveQuoteTokens, sendBaseTokens: _callbackSendBaseTokens }); + string memory prefix = "FC"; + if (_callbackReceiveQuoteTokens && _callbackSendBaseTokens) { + prefix = "FF"; + } else if (_callbackReceiveQuoteTokens) { + prefix = "FE"; + } else if (_callbackSendBaseTokens) { + prefix = "FD"; + } bytes memory args = abi.encode(address(_auctionHouse), permissions); - bytes32 salt = _getTestSalt("MockCallback", type(MockCallback).creationCode, args); + bytes32 salt = _generateSalt("MockCallback", type(MockCallback).creationCode, args, prefix); // Required for CREATE2 address to work correctly. doesn't do anything in a test // Source: https://github.com/foundry-rs/foundry/issues/6402 diff --git a/test/callbacks/Callbacks.t.sol b/test/callbacks/Callbacks.t.sol index d173f7b1..264fc4bc 100644 --- a/test/callbacks/Callbacks.t.sol +++ b/test/callbacks/Callbacks.t.sol @@ -7,18 +7,19 @@ import {ICallback} from "../../src/interfaces/ICallback.sol"; import {MockCallback} from "./MockCallback.sol"; import {Test} from "@forge-std-1.9.1/Test.sol"; -import {WithSalts} from "../lib/WithSalts.sol"; +import {WithSalts} from "../../script/salts/WithSalts.s.sol"; +import {TestConstants} from "../Constants.sol"; -contract CallbacksTest is Test, WithSalts { +contract CallbacksTest is Test, WithSalts, TestConstants { using Callbacks for ICallback; - address internal constant _AUCTION_HOUSE = address(0x000000000000000000000000000000000000000A); - function _getMockCallbackSalt( - Callbacks.Permissions memory permissions_ + Callbacks.Permissions memory permissions_, + string memory prefix_ ) internal returns (bytes32) { bytes memory args = abi.encode(_AUCTION_HOUSE, permissions_); - return _getTestSalt("MockCallback", type(MockCallback).creationCode, args); + + return _generateSalt("MockCallback", type(MockCallback).creationCode, args, prefix_); } function _allFalseSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -34,7 +35,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "00"), permissions); } function _onCreateSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -50,7 +51,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "80"), permissions); } function _onCancelSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -66,7 +67,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "40"), permissions); } function _onCurateSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -82,7 +83,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "20"), permissions); } function _onPurchaseSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -98,7 +99,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "10"), permissions); } function _onBidSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -114,10 +115,11 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "08"), permissions); } function _onSettleSalt() internal returns (bytes32, Callbacks.Permissions memory) { + // 00000100 = 0x04 Callbacks.Permissions memory permissions = Callbacks.Permissions({ onCreate: false, onCancel: false, @@ -129,7 +131,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "04"), permissions); } function _receiveQuoteTokensSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -145,7 +147,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: false }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "02"), permissions); } function _sendBaseTokensSalt() internal returns (bytes32, Callbacks.Permissions memory) { @@ -161,7 +163,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: true }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "01"), permissions); } function _sendBaseTokens_onCreateSalt() @@ -180,7 +182,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: true }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "81"), permissions); } function _sendBaseTokens_onCurateSalt() @@ -199,7 +201,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: true }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "21"), permissions); } function _sendBaseTokens_onCreate_onCurateSalt() @@ -218,7 +220,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: true }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "A1"), permissions); } function _sendBaseTokens_onPurchaseSalt() @@ -237,7 +239,7 @@ contract CallbacksTest is Test, WithSalts { sendBaseTokens: true }); - return (_getMockCallbackSalt(permissions), permissions); + return (_getMockCallbackSalt(permissions, "11"), permissions); } // validateCallbacksPermissions diff --git a/test/lib/WithSalts.sol b/test/lib/WithSalts.sol deleted file mode 100644 index d4eec62c..00000000 --- a/test/lib/WithSalts.sol +++ /dev/null @@ -1,44 +0,0 @@ -/// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.19; - -import {Test} from "@forge-std-1.9.1/Test.sol"; -import {stdJson} from "@forge-std-1.9.1/StdJson.sol"; - -contract WithSalts is Test { - using stdJson for string; - - string internal constant _SALTS_PATH = "./script/salts/salts.json"; - string internal _saltJson; - - /// @notice Gets the salt for a given key - /// @dev Test salts are read from underneath the ".test" prefix. - /// - /// If the key is not found, the function will return `bytes32(0)`. - /// - /// @param contractName_ The contract to get the salt for - /// @param contractCode_ The creation code of the contract - /// @param args_ The abi-encoded constructor arguments to the contract - /// @return The salt for the given key - function _getTestSalt( - string memory contractName_, - bytes memory contractCode_, - bytes memory args_ - ) internal returns (bytes32) { - // Load salt file if needed - if (bytes(_saltJson).length == 0) { - _saltJson = vm.readFile(_SALTS_PATH); - } - - // Generate the bytecode hash - bytes memory bytecode = abi.encodePacked(contractCode_, args_); - bytes32 bytecodeHash = keccak256(bytecode); - - bytes32 salt = bytes32( - vm.parseJson( - _saltJson, string.concat(".Test_", contractName_, ".", vm.toString(bytecodeHash)) - ) - ); - - return salt; - } -}