diff --git a/broadcast/DeployStuff.s.sol/31337/run-1700448371.json b/broadcast/DeployStuff.s.sol/31337/run-1700448371.json new file mode 100644 index 0000000..dd91aaa --- /dev/null +++ b/broadcast/DeployStuff.s.sol/31337/run-1700448371.json @@ -0,0 +1,48 @@ +{ + "transactions": [ + { + "hash": "0x642b1f5b3c0f5e2972d9e2e32d0af76d52017236fdccc4678c8b403245e15a3b", + "transactionType": "CREATE", + "contractName": "Stuff", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "gas": "0x191eb", + "value": "0x0", + "data": "0x6080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063cf33c3be14602d575b600080fd5b600760405190815260200160405180910390f3fea26469706673582212200382dc5886d11d466628cc59d88a4311d34375a8581173060a08b39ffe47d32464736f6c63430008150033", + "nonce": "0x0", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0x642b1f5b3c0f5e2972d9e2e32d0af76d52017236fdccc4678c8b403245e15a3b", + "transactionIndex": "0x0", + "blockHash": "0x73373ba2a0b941145f4b90354551b964a6fa7ace4a9517590a2caa952b9d2c75", + "blockNumber": "0x8", + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": null, + "cumulativeGasUsed": "0x13535", + "gasUsed": "0x13535", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xca386e61" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1700448371, + "chain": 31337, + "multi": false, + "commit": "742f1ca" +} \ No newline at end of file diff --git a/broadcast/DeployStuff.s.sol/31337/run-latest.json b/broadcast/DeployStuff.s.sol/31337/run-latest.json index 86d8053..dd91aaa 100644 --- a/broadcast/DeployStuff.s.sol/31337/run-latest.json +++ b/broadcast/DeployStuff.s.sol/31337/run-latest.json @@ -1,20 +1,19 @@ { "transactions": [ { - "hash": "0x707f599297156a5de34dee2fdc4d9266c3f3c63133130f1e2de605dc7e3e83d4", + "hash": "0x642b1f5b3c0f5e2972d9e2e32d0af76d52017236fdccc4678c8b403245e15a3b", "transactionType": "CREATE", "contractName": "Stuff", - "contractAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", "function": null, "arguments": null, - "rpc": "http://127.0.0.1:8545", "transaction": { "type": "0x02", "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "gas": "0x191eb", "value": "0x0", - "data": "0x6080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063cf33c3be14602d575b600080fd5b600760405190815260200160405180910390f3fea264697066735822122018510bdeac5343a57cccf367fda88793df7ffd26e6238a89c2521a68343c5b8e64736f6c63430008130033", - "nonce": "0x7", + "data": "0x6080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063cf33c3be14602d575b600080fd5b600760405190815260200160405180910390f3fea26469706673582212200382dc5886d11d466628cc59d88a4311d34375a8581173060a08b39ffe47d32464736f6c63430008150033", + "nonce": "0x0", "accessList": [] }, "additionalContracts": [], @@ -23,28 +22,27 @@ ], "receipts": [ { - "transactionHash": "0x707f599297156a5de34dee2fdc4d9266c3f3c63133130f1e2de605dc7e3e83d4", + "transactionHash": "0x642b1f5b3c0f5e2972d9e2e32d0af76d52017236fdccc4678c8b403245e15a3b", "transactionIndex": "0x0", - "blockHash": "0x5f5fc1671cfc65aa65134af53fd8b6238401a4d20c3f317a094527af56b077cf", + "blockHash": "0x73373ba2a0b941145f4b90354551b964a6fa7ace4a9517590a2caa952b9d2c75", "blockNumber": "0x8", "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "to": null, - "cumulativeGasUsed": "0x1352b", - "gasUsed": "0x1352b", - "contractAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "cumulativeGasUsed": "0x13535", + "gasUsed": "0x13535", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", "logs": [], "status": "0x1", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "effectiveGasPrice": "0xca6df430" + "effectiveGasPrice": "0xca386e61" } ], "libraries": [], "pending": [], - "path": "/Users/patrick/code/foundry-recent-deploy/broadcast/DeployStuff.s.sol/31337/run-latest.json", "returns": {}, - "timestamp": 1681340536, + "timestamp": 1700448371, "chain": 31337, "multi": false, - "commit": "0544b38" + "commit": "742f1ca" } \ No newline at end of file diff --git a/broadcast/NewDeployStuff.s.sol/31337/run-1700352554.json b/broadcast/NewDeployStuff.s.sol/31337/run-1700352554.json new file mode 100644 index 0000000..a9a739e --- /dev/null +++ b/broadcast/NewDeployStuff.s.sol/31337/run-1700352554.json @@ -0,0 +1,48 @@ +{ + "transactions": [ + { + "hash": "0xb9af5f14dd8208e7a5fa4315f2e23e915c56ecf8b37f11c1abe0086a3a3f0f30", + "transactionType": "CREATE", + "contractName": "NewStuff", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "gas": "0x191eb", + "value": "0x0", + "data": "0x6080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063cf33c3be14602d575b600080fd5b600760405190815260200160405180910390f3fea26469706673582212201d5632e18d7fe499584a6ee0e5aa0e7e68761b304d3018503db2bb4badc34e8264736f6c63430008150033", + "nonce": "0x0", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xb9af5f14dd8208e7a5fa4315f2e23e915c56ecf8b37f11c1abe0086a3a3f0f30", + "transactionIndex": "0x0", + "blockHash": "0x177a9f26fdfb32f329fbdac84132e343500da1f100eb3fd1d8c234b471338d41", + "blockNumber": "0x1", + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": null, + "cumulativeGasUsed": "0x13535", + "gasUsed": "0x13535", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xee6b2800" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1700352554, + "chain": 31337, + "multi": false, + "commit": "f421ebf" +} \ No newline at end of file diff --git a/broadcast/NewDeployStuff.s.sol/31337/run-latest.json b/broadcast/NewDeployStuff.s.sol/31337/run-latest.json new file mode 100644 index 0000000..a9a739e --- /dev/null +++ b/broadcast/NewDeployStuff.s.sol/31337/run-latest.json @@ -0,0 +1,48 @@ +{ + "transactions": [ + { + "hash": "0xb9af5f14dd8208e7a5fa4315f2e23e915c56ecf8b37f11c1abe0086a3a3f0f30", + "transactionType": "CREATE", + "contractName": "NewStuff", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "gas": "0x191eb", + "value": "0x0", + "data": "0x6080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063cf33c3be14602d575b600080fd5b600760405190815260200160405180910390f3fea26469706673582212201d5632e18d7fe499584a6ee0e5aa0e7e68761b304d3018503db2bb4badc34e8264736f6c63430008150033", + "nonce": "0x0", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xb9af5f14dd8208e7a5fa4315f2e23e915c56ecf8b37f11c1abe0086a3a3f0f30", + "transactionIndex": "0x0", + "blockHash": "0x177a9f26fdfb32f329fbdac84132e343500da1f100eb3fd1d8c234b471338d41", + "blockNumber": "0x1", + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": null, + "cumulativeGasUsed": "0x13535", + "gasUsed": "0x13535", + "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xee6b2800" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1700352554, + "chain": 31337, + "multi": false, + "commit": "f421ebf" +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index c3bfa10..a6dd9c0 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,6 +2,6 @@ src = "src" out = "out" libs = ["lib"] -ffi = true +fs_permissions = [{ access = "read", path = "./broadcast" }] -# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/lib/forge-std b/lib/forge-std index fc560fa..c22437a 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit fc560fa34fa12a335a50c35d92e55a6628ca467c +Subproject commit c22437a63d1c3418869bc35a7c55a5175a09b701 diff --git a/makefile b/makefile new file mode 100644 index 0000000..9ffe3f8 --- /dev/null +++ b/makefile @@ -0,0 +1,11 @@ +.PHONY: update anvil deploy + +DEFAULT_ANVIL_KEY := 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + +update:; forge update + +anvil:; anvil -m 'test test test test test test test test test test test junk' --steps-tracing --block-time 1 + +deploy:; forge script script/DeployStuff.s.sol:DeployStuff --broadcast --rpc-url http://localhost:8545 --private-key $(DEFAULT_ANVIL_KEY) -vvvv + +interact:; forge script script/InteractWithStuff.s.sol:InteractWithStuff --broadcast --rpc-url http://localhost:8545 --private-key $(DEFAULT_ANVIL_KEY) -vvvv \ No newline at end of file diff --git a/script/InteractWithStuff.s.sol b/script/InteractWithStuff.s.sol new file mode 100644 index 0000000..dadcb94 --- /dev/null +++ b/script/InteractWithStuff.s.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {Script} from "forge-std/Script.sol"; +import {DevOpsTools} from "../src/DevOpsTools.sol"; +import {Stuff} from "./DeployStuff.s.sol"; + +contract InteractWithStuff is Script { + function run() external { + address mostRecent = DevOpsTools.get_most_recent_deployment("DeployStuff", block.chainid); + uint256 value = Stuff(mostRecent).getSeven(); + assert(value == 7); + } +} diff --git a/script/NewDeployStuff.s.sol b/script/NewDeployStuff.s.sol new file mode 100644 index 0000000..6f32357 --- /dev/null +++ b/script/NewDeployStuff.s.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import {Script} from "forge-std/Script.sol"; + +contract NewStuff { + function getSeven() public pure returns (uint256) { + return 7; + } +} + +contract NewDeployStuff is Script { + function run() external { + vm.startBroadcast(); + new NewStuff(); + vm.stopBroadcast(); + } +} diff --git a/src/DevOpsTools.sol b/src/DevOpsTools.sol index 32864c0..89099da 100644 --- a/src/DevOpsTools.sol +++ b/src/DevOpsTools.sol @@ -3,103 +3,142 @@ pragma solidity >=0.8.13 <0.9.0; import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {StdCheatsSafe} from "forge-std/StdCheats.sol"; import {console} from "forge-std/console.sol"; +import {StringUtils} from "./StringUtils.sol"; + +/** + * Note: Though the arguments property is a string array, when there are no arguments, the json produced has a null value. The null value + * will not deserialize back into an empty string array. We could have used two different types and used a try / catch to use the correct + * type, but to keep things simple, we're deserializing arguments to a string, which allows abi.decode to not revert for a string array + * and a null value, but the the arguments value is not usable. Given the scope of the get_most_recent_deployment function, this seems an + * acceptable trade-off. Somewhat related: https://github.com/foundry-rs/foundry/issues/3731 + */ + +struct BroadcastTransaction { + // This is a guess that additionalContracts is a string array + string[] additionalContracts; + string arguments; + address contractAddress; + string contractName; + // json key = function + string functionSig; + bytes32 hash; + bool isFixedGasLimit; + BroadcastTransactionDetail transaction; + string transactionType; +} + +/** + * The broadcast files committed in April 2023 had reference to an rpc property, which seems to no longer be written. + */ +struct LegacyBroadcastTransaction { + // This is a guess that additionalContracts is a string array + string[] additionalContracts; + string arguments; + address contractAddress; + string contractName; + // json key = function + string functionSig; + bytes32 hash; + bool isFixedGasLimit; + string rpc; + BroadcastTransactionDetail transaction; + string transactionType; +} + +struct BroadcastTransactionDetail { + StdCheatsSafe.AccessList[] accessList; + bytes data; + address from; + uint256 gas; + uint256 nonce; + // json key = type + uint256 txType; + uint256 value; +} library DevOpsTools { - Vm public constant vm = - Vm(address(bytes20(uint160(uint256(keccak256("hevm cheat code")))))); + using stdJson for string; + using StringUtils for string; + + Vm public constant vm = Vm(address(bytes20(uint160(uint256(keccak256("hevm cheat code")))))); string public constant RELATIVE_BROADCAST_PATH = "./broadcast"; - string public constant RELATIVE_SCRIPT_PATH = - "./lib/foundry-devops/src/get_recent_deployment.sh"; - function get_most_recent_deployment( - string memory contractName, - uint256 chainId - ) public returns (address) { - return - get_most_recent_deployment( - contractName, - chainId, - RELATIVE_BROADCAST_PATH, - RELATIVE_SCRIPT_PATH - ); + function get_most_recent_deployment(string memory contractName, uint256 chainId) public view returns (address) { + return get_most_recent_deployment(contractName, chainId, RELATIVE_BROADCAST_PATH); } - function cleanStringPath( - string memory stringToClean - ) public pure returns (string memory) { - bytes memory inputBytes = bytes(stringToClean); - uint256 start = 0; - uint256 end = inputBytes.length; - - // Find the start of the non-whitespace characters - for (uint256 i = 0; i < inputBytes.length; i++) { - if (inputBytes[i] != " ") { - start = i; - break; + function get_most_recent_deployment( + string memory contractName, + uint256 chainId, + string memory relativeBroadcastPath + ) public view returns (address) { + address latestAddress = address(0); + uint256 lastTimestamp; + + bool runProcessed; + Vm.DirEntry[] memory entries = vm.readDir(relativeBroadcastPath, 3); + for (uint256 i = 0; i < entries.length; i++) { + Vm.DirEntry memory entry = entries[i]; + if (entry.path.contains(vm.toString(chainId)) && entry.path.contains("run-latest.json")) { + runProcessed = true; + string memory json = vm.readFile(entry.path); + + uint256 timestamp = vm.parseJsonUint(json, ".timestamp"); + + if (timestamp > lastTimestamp) { + // This broadcast is later than the last one we know about, process txns + console.log("Processing: ", entry.path); + + latestAddress = processRun(json, contractName, latestAddress); + } } } - // Find the end of the non-whitespace characters - for (uint256 i = inputBytes.length; i > 0; i--) { - if (inputBytes[i - 1] != " ") { - end = i; - break; - } + if (!runProcessed) { + revert("No run-latest.json file found for specified chain"); } - // Remove the leading '.' if it exists - if (inputBytes[start] == ".") { - start += 1; + if (latestAddress != address(0)) { + return latestAddress; + } else { + revert("No contract deployed"); } + } - // Create a new bytes array for the trimmed string - bytes memory trimmedBytes = new bytes(end - start); + function processRun(string memory json, string memory contractName, address latestAddress) + internal + view + returns (address) + { + bytes memory transactionsBytes = json.parseRaw("$.transactions"); - // Copy the trimmed characters to the new bytes array - for (uint256 i = 0; i < trimmedBytes.length; i++) { - trimmedBytes[i] = inputBytes[start + i]; - } + if (vm.keyExists(json, "$.transactions[0].rpc")) { + LegacyBroadcastTransaction[] memory transactions = + abi.decode(transactionsBytes, (LegacyBroadcastTransaction[])); - return string(trimmedBytes); - } + console.log("Inspecting %s transactions", transactions.length); - function get_most_recent_deployment( - string memory contractName, - uint256 chainId, - string memory relativeBroadcastPath, - string memory relativeScriptPath - ) public returns (address) { - relativeBroadcastPath = cleanStringPath(relativeBroadcastPath); - relativeScriptPath = cleanStringPath(relativeScriptPath); - - string[] memory pwd = new string[](1); - pwd[0] = "pwd"; - string memory absolutePath = string(vm.ffi(pwd)); - - string[] memory getRecentDeployment = new string[](5); - getRecentDeployment[0] = "bash"; - getRecentDeployment[1] = string.concat( - absolutePath, - relativeScriptPath - ); - getRecentDeployment[2] = contractName; - getRecentDeployment[3] = vm.toString(chainId); - getRecentDeployment[4] = string.concat( - absolutePath, - "/", - relativeBroadcastPath - ); - - bytes memory retData = vm.ffi(getRecentDeployment); - console.log("Return Data:"); - console.logBytes(retData); - address returnedAddress = address(uint160(bytes20(retData))); - if (returnedAddress != address(0)) { - return returnedAddress; + for (uint256 i = 0; i < transactions.length; i++) { + LegacyBroadcastTransaction memory transaction = transactions[i]; + if (transaction.contractName.isEqualTo(contractName)) { + latestAddress = transaction.contractAddress; + } + } } else { - revert("No contract deployed"); + BroadcastTransaction[] memory transactions = abi.decode(transactionsBytes, (BroadcastTransaction[])); + console.log("Inspecting %s transactions", transactions.length); + + for (uint256 i = 0; i < transactions.length; i++) { + BroadcastTransaction memory transaction = transactions[i]; + if (transaction.contractName.isEqualTo(contractName)) { + latestAddress = transaction.contractAddress; + } + } } + return latestAddress; } } diff --git a/src/StringUtils.sol b/src/StringUtils.sol new file mode 100644 index 0000000..22faa9b --- /dev/null +++ b/src/StringUtils.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.13 <0.9.0; + +library StringUtils { + function isEqualTo( + string memory str1, + string memory str2 + ) internal pure returns (bool) { + return + keccak256(abi.encodePacked(str1)) == + keccak256(abi.encodePacked(str2)); + } + + function contains( + string memory str, + string memory substr + ) internal pure returns (bool) { + bytes memory strBytes = bytes(str); + bytes memory substrBytes = bytes(substr); + if (strBytes.length < substrBytes.length || strBytes.length == 0) + return false; + + for (uint i = 0; i <= strBytes.length - substrBytes.length; i++) { + bool isEqual = true; + for (uint j = 0; j < substrBytes.length; j++) { + if (strBytes[i + j] != substrBytes[j]) { + isEqual = false; + break; + } + } + if (isEqual) return true; + } + return false; + } +} diff --git a/src/get_recent_deployment.sh b/src/get_recent_deployment.sh deleted file mode 100644 index 6fd1a2e..0000000 --- a/src/get_recent_deployment.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# Get the script's own directory -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - -# Move up two levels to get the project root directory -PROJECT_ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")" - -contractName=$1 -chainId=$2 - -if [ -z "$3" ]; then - path="./broadcast" -else - path="$3" -fi - -if [ -z "$contractName" ] || [ -z "$chainId" ] ; then - echo "Usage: $0 [path]" - exit 1 -fi - - -latestTimestamp=0 -latestContractAddress="" - -files=$(find $path -name "run-latest.json" -type f) - -for file in $files; do - if [[ $file == *"/$chainId/"* ]]; then - timestamp=$(jq '.timestamp' "$file") - - transactions_length=$(jq '.transactions | length' "$file") - - for ((i=0; i<$transactions_length; i++)); do - currentTransactionType=$(jq -r ".transactions[$i].transactionType" "$file") - currentContractName=$(jq -r ".transactions[$i].contractName" "$file") - - if [ "$currentTransactionType" == "CREATE" ] && [ "$currentContractName" == "$contractName" ] && [ $timestamp -gt $latestTimestamp ]; then - latestTimestamp=$timestamp - latestContractAddress=$(jq -r ".transactions[$i].contractAddress" "$file") - fi - done - fi -done - -if [ $latestTimestamp -ne 0 ]; then - echo "$latestContractAddress" -else - echo "0x0000000000000000000000000000000000000000" -fi diff --git a/test/DevOpsToolsTest.t.sol b/test/DevOpsToolsTest.t.sol index d715981..ddba38b 100644 --- a/test/DevOpsToolsTest.t.sol +++ b/test/DevOpsToolsTest.t.sol @@ -5,21 +5,18 @@ import {Test} from "forge-std/Test.sol"; import {DevOpsTools} from "../src/DevOpsTools.sol"; contract DevOpsToolsTest is Test { - address public constant EXPECTED_ADDRESS = - 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853; - string public constant CONTRACT_NAME = "Stuff"; - uint256 public constant CHAIN_ID = 31337; string public constant SEARCH_PATH = "broadcast"; - string public constant SCRIPT_PATH = "./src/get_recent_deployment.sh"; function testGetMostRecentlyDeployedContract() public { + string memory contractName = "Stuff"; + uint256 chainId = 31337; + address expectedAddress = 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853; address mostRecentDeployment = DevOpsTools.get_most_recent_deployment( - CONTRACT_NAME, - CHAIN_ID, - SEARCH_PATH, - SCRIPT_PATH + contractName, + chainId, + SEARCH_PATH ); - assertEq(mostRecentDeployment, EXPECTED_ADDRESS); + assertEq(mostRecentDeployment, expectedAddress); } function testGetMostRecentlyDeployedEvenWhenMultipleAreDeployed() public { @@ -29,8 +26,43 @@ contract DevOpsToolsTest is Test { address mostRecentDeployment = DevOpsTools.get_most_recent_deployment( contractName, chainId, - SEARCH_PATH, - SCRIPT_PATH + SEARCH_PATH + ); + assertEq(mostRecentDeployment, expectedAddress); + } + + function testExpectRevertIfNoRun() public { + string memory contractName = "FundMe"; + uint256 chainId = 9999; + vm.expectRevert("No run-latest.json file found for specified chain"); + DevOpsTools.get_most_recent_deployment( + contractName, + chainId, + SEARCH_PATH + ); + } + + function testExpectRevertIfNoDeployment() public { + string memory contractName = "MissingContract"; + uint256 chainId = 1234; + vm.expectRevert("No contract deployed"); + DevOpsTools.get_most_recent_deployment( + contractName, + chainId, + SEARCH_PATH + ); + } + + // All other tests use what appear to be legacy broadcast files + // This one uses the newer type with no rpc property + function testNonLegacyBroadcast() public { + string memory contractName = "NewStuff"; + uint256 chainId = 31337; + address expectedAddress = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address mostRecentDeployment = DevOpsTools.get_most_recent_deployment( + contractName, + chainId, + SEARCH_PATH ); assertEq(mostRecentDeployment, expectedAddress); }