diff --git a/.gitmodules b/.gitmodules index d78ff238b..b15e197af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,6 @@ [submodule "packages/foundry/lib/openzeppelin-contracts"] path = packages/foundry/lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "packages/foundry/lib/solidity-bytes-utils"] + path = packages/foundry/lib/solidity-bytes-utils + url = https://github.com/gnsps/solidity-bytes-utils diff --git a/package.json b/package.json index fdd33aaa7..1e37943b0 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "scripts": { "account": "yarn workspace @se-2/foundry account", "fork": "yarn workspace @se-2/foundry fork", - "verify": "yarn workspace @se-2/hardhat verify", + "verify": "yarn workspace @se-2/foundry verify", "compile": "yarn workspace @se-2/foundry compile", "chain": "yarn workspace @se-2/foundry chain", "deploy": "yarn workspace @se-2/foundry deploy", diff --git a/packages/foundry/lib/solidity-bytes-utils b/packages/foundry/lib/solidity-bytes-utils new file mode 160000 index 000000000..6458fb278 --- /dev/null +++ b/packages/foundry/lib/solidity-bytes-utils @@ -0,0 +1 @@ +Subproject commit 6458fb2780a3092bc756e737f246be1de6d3d362 diff --git a/packages/foundry/package.json b/packages/foundry/package.json index 68ee14f1b..f6d403dc3 100644 --- a/packages/foundry/package.json +++ b/packages/foundry/package.json @@ -8,7 +8,8 @@ "compile": "forge compile", "generate": "node script/generateAccount.js", "deploy": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy && node script/generateTsAbis.js", - "deploy:verify": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy --verify && node script/generateTsAbis.js", + "deploy:verify": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy --verify ; node script/generateTsAbis.js", + "verify": "forge build --build-info --build-info-path out/build-info/ && forge script script/VerifyAll.s.sol --ffi --rpc-url ${1:-default_network}", "lint": "forge fmt", "test": "forge test" }, diff --git a/packages/foundry/script/VerifyAll.s.sol b/packages/foundry/script/VerifyAll.s.sol new file mode 100644 index 000000000..9a6ff02d3 --- /dev/null +++ b/packages/foundry/script/VerifyAll.s.sol @@ -0,0 +1,157 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "forge-std/Script.sol"; +import "forge-std/Vm.sol"; +import "solidity-bytes-utils/BytesLib.sol"; + +/** + * @dev Temp Vm implementation + * @notice calls the tryffi function on the Vm contract + * @notice will be deleted once the forge/std is updated + */ + +struct FfiResult { + int32 exit_code; + bytes stdout; + bytes stderr; +} + +interface tempVm { + function tryFfi(string[] calldata) external returns (FfiResult memory); +} + +contract VerifyAll is Script { + uint96 currTransactionIdx; + + function run() external { + string memory root = vm.projectRoot(); + string memory path = string.concat( + root, + "/broadcast/Deploy.s.sol/", + vm.toString(block.chainid), + "/run-latest.json" + ); + string memory content = vm.readFile(path); + + while (this.nextTransaction(content)) { + _verifyIfContractDeployment(content); + currTransactionIdx++; + } + } + + function _verifyIfContractDeployment(string memory content) internal { + string memory txType = abi.decode( + vm.parseJson( + content, + searchStr(currTransactionIdx, "transactionType") + ), + (string) + ); + if (keccak256(bytes(txType)) == keccak256(bytes("CREATE"))) { + _verifyContract(content); + } + } + + function _verifyContract(string memory content) internal { + string memory contractName = abi.decode( + vm.parseJson( + content, + searchStr(currTransactionIdx, "contractName") + ), + (string) + ); + address contractAddr = abi.decode( + vm.parseJson( + content, + searchStr(currTransactionIdx, "contractAddress") + ), + (address) + ); + bytes memory deployedBytecode = abi.decode( + vm.parseJson( + content, + searchStr(currTransactionIdx, "transaction.data") + ), + (bytes) + ); + bytes memory compiledBytecode = abi.decode( + vm.parseJson( + _getCompiledBytecode(contractName), + ".bytecode.object" + ), + (bytes) + ); + bytes memory constructorArgs = BytesLib.slice( + deployedBytecode, + compiledBytecode.length, + deployedBytecode.length - compiledBytecode.length + ); + + string[] memory inputs = new string[](9); + inputs[0] = "forge"; + inputs[1] = "verify-contract"; + inputs[2] = vm.toString(contractAddr); + inputs[3] = contractName; + inputs[4] = "--chain"; + inputs[5] = vm.toString(block.chainid); + inputs[6] = "--constructor-args"; + inputs[7] = vm.toString(constructorArgs); + inputs[8] = "--watch"; + + FfiResult memory f = tempVm(address(vm)).tryFfi(inputs); + + if (f.stderr.length != 0) { + console.logString( + string.concat( + "Submitting verification for contract: ", + vm.toString(contractAddr) + ) + ); + console.logString(string(f.stderr)); + } else { + console.logString(string(f.stdout)); + } + return; + } + + function nextTransaction( + string memory content + ) external view returns (bool) { + try this.getTransactionFromRaw(content, currTransactionIdx) { + return true; + } catch { + return false; + } + } + + function _getCompiledBytecode( + string memory contractName + ) internal view returns (string memory compiledBytecode) { + string memory root = vm.projectRoot(); + string memory path = string.concat( + root, + "/out/", + contractName, + ".sol/", + contractName, + ".json" + ); + compiledBytecode = vm.readFile(path); + } + + function getTransactionFromRaw( + string memory content, + uint96 idx + ) external pure { + abi.decode(vm.parseJson(content, searchStr(idx, "hash")), (bytes32)); + } + + function searchStr( + uint96 idx, + string memory searchKey + ) internal pure returns (string memory) { + return + string.concat(".transactions[", vm.toString(idx), "].", searchKey); + } +}