diff --git a/.editorconfig b/.editorconfig index 5b24bbf..746ae31 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,11 +1,19 @@ +# EditorConfig http://EditorConfig.org + +# top-most EditorConfig file root = true +# All files [*] charset = utf-8 end_of_line = lf +indent_size = 2 +indent_style = space insert_final_newline = true +trim_trailing_whitespace = true -[*.{js,json,yml}] -charset = utf-8 -indent_style = space -indent_size = 2 +[*.sol] +indent_size = 4 + +[*.tree] +indent_size = 1 diff --git a/.env.example b/.env.example index dd02f31..98c1028 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,11 @@ -GOERLI_RPC_URL=goerli_rpc -LOCAL_RPC_URL=local_rpc -LOCAL_PRIVATE_KEY=private_key -PRIVATE_KEY=private_key -ETHERSCAN_API_KEY=etherscan_key -GANACHE_PRIVATE_KEY=ganache_private_key -DEPLOYMENT_PATH=client +export API_KEY_ALCHEMY="YOUR_API_KEY_ALCHEMY" +export API_KEY_ARBISCAN="YOUR_API_KEY_ARBISCAN" +export API_KEY_BSCSCAN="YOUR_API_KEY_BSCSCAN" +export API_KEY_ETHERSCAN="YOUR_API_KEY_ETHERSCAN" +export API_KEY_GNOSISSCAN="YOUR_API_KEY_GNOSISSCAN" +export API_KEY_INFURA="YOUR_API_KEY_INFURA" +export API_KEY_OPTIMISTIC_ETHERSCAN="YOUR_API_KEY_OPTIMISTIC_ETHERSCAN" +export API_KEY_POLYGONSCAN="YOUR_API_KEY_POLYGONSCAN" +export API_KEY_SNOWTRACE="YOUR_API_KEY_SNOWTRACE" +export MNEMONIC="YOUR_MNEMONIC" +export FOUNDRY_PROFILE="default" diff --git a/.gitignore b/.gitignore index 79ec8d3..e108b40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,20 @@ -# Compiler files -cache/ -out/ +# directories +cache +coverage +node_modules +out -# Ignores development broadcast logs -!/broadcast -/broadcast/*/31337/ -/broadcast/*/5/ -/broadcast/**/dry-run/ +# files +*.env +*.log +.DS_Store +.pnp.* +lcov.info +package-lock.json +pnpm-lock.yaml +yarn.lock -# Dotenv file -.env -node_modules -!.vscode +# broadcasts +!broadcast +broadcast/* +broadcast/*/31337/ diff --git a/.prettierignore b/.prettierignore index 47cab42..3996d20 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1,17 @@ -build +# directories +broadcast +cache coverage -out -lib -assets node_modules -.next \ No newline at end of file +out + +# files +*.env +*.log +.DS_Store +.pnp.* +bun.lockb +lcov.info +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/.solhint.json b/.solhint.json index 6adc36c..14f780e 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,12 +1,14 @@ { - "extends": ["solhint:recommended"], + "extends": "solhint:recommended", "rules": { - "prettier/prettier": "error", - "compiler-version": "off", - "avoid-throw": "off", - "avoid-suicide": "error", - "avoid-sha3": "warn", - "func-name-mixedcase": "off" - }, - "plugins": ["prettier"] + "code-complexity": ["error", 8], + "compiler-version": ["error", ">=0.8.25"], + "func-name-mixedcase": "off", + "func-visibility": ["error", { "ignoreConstructors": true }], + "max-line-length": ["error", 120], + "named-parameters-mapping": "warn", + "no-console": "off", + "not-rely-on-time": "off", + "one-contract-per-file": "off" + } } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 21f5666..313a3b3 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,11 +1,11 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - // List of extensions which should be recommended for users of this workspace. - "recommendations": [ - "NomicFoundation.hardhat-solidity", - "esbenp.prettier-vscode" - ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "NomicFoundation.hardhat-solidity", + "tamasfe.even-better-toml" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] } diff --git a/.vscode/settings.json b/.vscode/settings.json index a528e2c..77df49c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,9 @@ { "[solidity]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" }, - "solidity.formatter": "prettier", - "solidity.linter": "solhint" + "[toml]": { + "editor.defaultFormatter": "tamasfe.even-better-toml" + }, + "solidity.formatter": "forge", } diff --git a/Makefile b/Makefile index 1bc2fbf..65c2c47 100644 --- a/Makefile +++ b/Makefile @@ -1,49 +1,9 @@ -include .env -.PHONY: all test clean deploy-anvil - -all: clean remove install update build - -# Clean the repo -clean :; forge clean - -# Remappings -remap :; forge remappings > remappings.txt - -# Remove modules -remove :; rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib - -install :; yarn - -# Update Dependencies -update:; forge update - -build:; forge build - -test :; forge test - -snapshot :; forge snapshot - -slither :; slither ./src - -format :; prettier --write src/**/*.sol && prettier --write src/*.sol - -# solhint should be installed globally -lint :; solhint src/**/*.sol && solhint src/*.sol - -anvil :; anvil -m 'test test test test test test test test test test test junk' - fund :; cast send ${account} --value 1ether -f 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -# use the "@" to hide the command from your shell -deploy-sepolia :; @forge script scripts/${contract}.s.sol:${contract} --rpc-url ${SEPOLIA_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast --verify --etherscan-api-key ${ETHERSCAN_API_KEY} -vvvv - -deploy-base-sepolia :; - @forge build - @forge script scripts/${contract}.s.sol:${contract} --rpc-url ${BASE_RPC_URL} --private-key ${BASE_PRIVATE_KEY} --verifier-url https://api-sepolia.basescan.org/api --etherscan-api-key ${BLOCKSCOUT_API_KEY} --broadcast --verify -vvvv - -# This is the private key of account from the mnemonic from the "make anvil" command -deploy-anvil :; - @LOCAL_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 ETHERSCAN_API_KEY=test forge script scripts/${contract}.s.sol:${contract} --rpc-url http://localhost:8545 --broadcast - -deploy-ganache :; @forge script scripts/${contract}.s.sol:${contract} --rpc-url http://localhost:8545 --private-key ${GANACHE_PRIVATE_KEY} --broadcast +deploy-any :; + @echo "Running $(contract) on remote network" + @SAVE_DEPLOYMENTS=1 $(if $(context),DEPLOYMENT_CONTEXT=$(context)) \ + forge script scripts/${contract}.s.sol:${contract} \ + --rpc-url ${rpc} --private-key ${private_key} --broadcast diff --git a/client/deploy.json b/client/deploy.json deleted file mode 100644 index 5f00f1c..0000000 --- a/client/deploy.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "31337": { - "hello": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" - } -} \ No newline at end of file diff --git a/client/index.js b/client/index.js deleted file mode 100644 index 0c4188e..0000000 --- a/client/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import "dotenv/config"; -import { ethers } from "ethers"; -import { createRequire } from "module"; -const require = createRequire(import.meta.url); - -const provider = new ethers.JsonRpcProvider(process.env.LOCAL_RPC_URL); -const wallet = new ethers.Wallet(process.env.LOCAL_PRIVATE_KEY, provider); - -async function main() { - const { address } = require("./addresses/Hello.json"); - const { abi } = require("../out/Hello.sol/Hello.json"); - - // read-write you pass wallet as third argument - const contract = new ethers.Contract(address, abi, wallet); - console.log(await contract.sayHello()); - - contract.on("GreetingChanged", (oldGreeting, newGreeting, event) => { - console.log(oldGreeting, newGreeting); - event.removeListener(); - }); - - const tx = await contract.setGreeting("Hola, mundo!"); - await tx.wait(); - console.log(await contract.sayHello()); -} - -async function sign() { - const message = "sign into ethers.org"; - const signature = await wallet.signMessage(message); - const recovered = ethers.verifyMessage(message, signature); - console.log(recovered === wallet.address); -} - -// main(); -sign(); diff --git a/foundry.toml b/foundry.toml index 29f0de3..f123984 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,30 +1,55 @@ +# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config + [profile.default] -auto_detect_solc = false # Foundry will automatically try to resolve appropriate Solidity compiler versions -block_timestamp = 1_680_220_800 # The value of block.timestamp in tests. March 31, 2023 at 00:00 GMT -bytecode_hash = "none" # Determines the hash method for the metadata hash that is appended to the bytecode -cbor_metadata = false # Remove all metadata hashes from your contract's bytecode. -evm_version = "paris" # The EVM version to use during tests. -fuzz = { runs = 256 } # The amount of fuzz runs to perform for each fuzz test case. -gas_reports = ["*"] # The contracts to print gas reports for. -libs = ["node_modules", "lib"] -optimizer = true # Whether or not to enable the Solidity optimizer. -optimizer_runs = 200 # The amount of optimizer runs to perform. -out = "out" # The path to put contract artifacts in, relative to the root of the project. -script = "scripts" # The path to the contract scripts relative to the root of the project. -solc_version = "0.8.25" # Uses a single Solidity compiler version for the project -src = "src" # The path to the contract sources relative to the root of the project. -test = "test" # The path to the test contract sources relative to the root of the project. -verbosity = 3 # The verbosity level to use during tests. -fs_permissions = [ - { access = "read-write", path = "./client" }, -] # Configures permissions for cheatcodes that touch the filesystem -extra_output = ["metadata", "abi", "bin"] -extra_output_files = ["metadata", "abi", "bin"] +auto_detect_solc = false +block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT +bytecode_hash = "none" +evm_version = "shanghai" +fuzz = { runs = 1_000 } +gas_reports = ["*"] +optimizer = true +optimizer_runs = 10_000 +out = "out" +script = "script" +solc = "0.8.25" +src = "src" +test = "test" +fs_permissions = [{ access = "read-write", path = "./client" }] -[rpc_endpoints] -sepolia = "${SEPOLIA_RPC_URL}" +[profile.ci] +fuzz = { runs = 10_000 } +verbosity = 4 [etherscan] -sepolia = { key = "${ETHERSCAN_API_KEY}" } +arbitrum = { key = "${API_KEY_ARBISCAN}" } +avalanche = { key = "${API_KEY_SNOWTRACE}" } +base = { key = "${API_KEY_BASESCAN}" } +gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" } +goerli = { key = "${API_KEY_ETHERSCAN}" } +mainnet = { key = "${API_KEY_ETHERSCAN}" } +optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } +polygon = { key = "${API_KEY_POLYGONSCAN}" } +sepolia = { key = "${API_KEY_ETHERSCAN}" } -# See more config options https://github.com/foundry-rs/foundry/tree/master/config +[fmt] +bracket_spacing = true +int_types = "long" +line_length = 120 +multiline_func_header = "all" +number_underscore = "thousands" +quote_style = "double" +tab_width = 4 +wrap_comments = true + +[rpc_endpoints] +arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" +avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" +base = "https://mainnet.base.org" +bnb_smart_chain = "https://bsc-dataseed.binance.org" +gnosis_chain = "https://rpc.gnosischain.com" +goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}" +localhost = "http://localhost:8545" +mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" +optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}" +polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" +sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}" diff --git a/package.json b/package.json index aa31baf..a0c50eb 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,25 @@ { - "name": "template", + "name": "forge-template-base", "version": "1.0.0", - "main": "client/index.js", - "type": "module", "author": "g <5714678+giuseppecrj@users.noreply.github.com>", "license": "MIT", "scripts": { - "solhint": "solhint -f table src/**/*.sol", - "prettier:solidity": "prettier --write src/**/*.sol" + "clean": "rm -rf cache out coverage lcov.info", + "build": "forge build", + "lint": "yarn run lint:sol && yarn run prettier:check", + "lint:sol": "forge fmt --check && yarn solhint -f table {script,src,test}/**/*.sol", + "prettier:check": "prettier --check \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "prettier:write": "prettier --write \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "test": "forge test", + "test:coverage": "forge coverage --no-match-coverage '(test|scripts)'", + "test:coverage:report": "forge coverage --no-match-coverage '(test|scripts)' --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage --rc derive_function_end_line=0" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.0.2" }, "devDependencies": { - "@openzeppelin/contracts": "^5.0.2", - "dotenv": "^16.0.3", - "ds-test": "github:dapphub/ds-test", - "ethers": "^6.1.0", - "forge-std": "github:foundry-rs/forge-std#v1", - "husky": "^8.0.1", - "prettier": "^2.7.1", - "prettier-plugin-solidity": "^1.1.3", - "solhint": "^3.3.7", - "solhint-plugin-prettier": "^0.0.5" + "forge-std": "github:foundry-rs/forge-std#v1.8.1", + "prettier": "^3.0.0", + "solhint": "^3.6.2" } } diff --git a/readme.md b/readme.md index 8cef733..fc17edd 100644 --- a/readme.md +++ b/readme.md @@ -16,12 +16,12 @@ pragma solidity 0.8.10; import "forge-std/Test.sol"; contract ContractTest is Test { - function testExample() public { - vm.roll(100); - console.log(1); - emit log("hi"); - assertTrue(true); - } + function testExample() public { + vm.roll(100); + console.log(1); + emit log("hi"); + assertTrue(true); + } } ``` diff --git a/scripts/DeployHello.s.sol b/scripts/DeployHello.s.sol index f4d9892..592eff9 100644 --- a/scripts/DeployHello.s.sol +++ b/scripts/DeployHello.s.sol @@ -6,16 +6,16 @@ pragma solidity ^0.8.20; //libraries //contracts -import {Deployer} from "./utils/Deployer.s.sol"; -import {Hello} from "src/hello/Hello.sol"; +import { Deployer } from "./utils/Deployer.s.sol"; +import { Hello } from "src/hello/Hello.sol"; contract DeployHello is Deployer { - function versionName() public pure override returns (string memory) { - return "hello"; - } + function versionName() public pure override returns (string memory) { + return "hello"; + } - function __deploy(uint256 deployerPK) public override returns (address) { - vm.broadcast(deployerPK); - return address(new Hello()); - } + function __deploy(address deployer) public override returns (address) { + vm.broadcast(deployer); + return address(new Hello()); + } } diff --git a/scripts/utils/Deployer.s.sol b/scripts/utils/Deployer.s.sol index dc4d51f..852a278 100644 --- a/scripts/utils/Deployer.s.sol +++ b/scripts/utils/Deployer.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; //interfaces @@ -7,76 +7,89 @@ pragma solidity ^0.8.0; //contracts import "forge-std/Script.sol"; -import {DeployBase} from "./DeployBase.s.sol"; +import { DeployBase } from "./DeployBase.s.sol"; abstract contract Deployer is Script, DeployBase { - // override this with the name of the deployment version that this script deploys - function versionName() public view virtual returns (string memory); - - // override this with the actual deployment logic, no need to worry about: - // - existing deployments - // - loading private keys - // - saving deployments - // - logging - function __deploy( - uint256 deployerPrivateKey - ) public virtual returns (address); - - // will first try to load existing deployments from `deployments//.json` - // if OVERRIDE_DEPLOYMENTS is set or if no deployment is found: - // - read PRIVATE_KEY from env - // - invoke __deploy() with the private key - // - save the deployment to `deployments//.json` - function deploy() public virtual returns (address deployedAddr) { - bool overrideDeployment = vm.envOr("OVERRIDE_DEPLOYMENTS", uint256(0)) > 0; - - address existingAddr = isTesting() - ? address(0) - : getDeployment(versionName()); - - if (!overrideDeployment && existingAddr != address(0)) { - debug( - string.concat("found existing ", versionName(), " deployment at"), - existingAddr - ); - debug("(override with OVERRIDE_DEPLOYMENTS=1)"); - return existingAddr; + // override this with the name of the deployment version that this script deploys + function versionName() public view virtual returns (string memory); + + // override this with the actual deployment logic, no need to worry about: + // - existing deployments + // - loading private keys + // - saving deployments + // - logging + function __deploy(address deployer) public virtual returns (address); + + // will first try to load existing deployments from `deployments//.json` + // if OVERRIDE_DEPLOYMENTS is set to true or if no cached deployment is found: + // - read PRIVATE_KEY from env + // - invoke __deploy() with the private key + // - save the deployment to `deployments//.json` + function deploy() public virtual returns (address deployedAddr) { + return deploy(_msgSender()); } - uint256 pk = isAnvil() - ? vm.envUint("LOCAL_PRIVATE_KEY") - : vm.envUint("PRIVATE_KEY"); - - address deployer = vm.addr(pk); - - if (!isTesting()) - info( - string.concat( - unicode"deploying \n\t📜 ", - versionName(), - unicode"\n\t⚡️ on ", - chainAlias(), - unicode"\n\t📬 from deployer address" - ), - vm.toString(deployer) - ); - - deployedAddr = __deploy(pk); - - if (!isTesting()) { - info( - string.concat(unicode"✅ ", versionName(), " deployed at"), - vm.toString(deployedAddr) - ); - saveDeployment(versionName(), deployedAddr); - saveAddresses(versionName(), deployedAddr); - postDeploy(deployer, deployedAddr); + function deploy(address deployer) public virtual returns (address deployedAddr) { + bool overrideDeployment = vm.envOr("OVERRIDE_DEPLOYMENTS", uint256(0)) > 0; + + address existingAddr = isTesting() ? address(0) : getDeployment(versionName()); + + if (!overrideDeployment && existingAddr != address(0)) { + info( + string.concat(unicode"📝 using an existing address for ", versionName(), " at"), + vm.toString(existingAddr) + ); + return existingAddr; + } + + if (!isTesting()) { + info( + string.concat( + unicode"deploying \n\t📜 ", + versionName(), + unicode"\n\t⚡️ on ", + getChain(block.chainid).chainAlias, + unicode"\n\t📬 from deployer address" + ), + vm.toString(deployer) + ); + } + + // call __deploy hook + deployedAddr = __deploy(deployer); + + if (!isTesting()) { + info(string.concat(unicode"✅ ", versionName(), " deployed at"), vm.toString(deployedAddr)); + + if (deployedAddr != address(0)) { + saveDeployment(versionName(), deployedAddr); + } + } + + if (!isTesting()) postDeploy(deployer, deployedAddr); } - } - function postDeploy(address deployer, address deployment) public virtual {} + function postDeploy(address deployer, address deployment) public virtual { } + + function run() public virtual { + bytes memory data = abi.encodeWithSignature("deploy()"); - function run() public virtual { - deploy(); - } + // we use a dynamic call to call deploy as we do not want to prescribe a return type + (bool success, bytes memory returnData) = address(this).delegatecall(data); + if (!success) { + if (returnData.length > 0) { + /// @solidity memory-safe-assembly + assembly { + let returnDataSize := mload(returnData) + revert(add(32, returnData), returnDataSize) + } + } else { + revert("FAILED_TO_CALL: deploy()"); + } + } + } + + function _msgSender() internal view returns (address) { + return msg.sender; + } } diff --git a/src/hello/Hello.sol b/src/hello/Hello.sol index dea6481..3710bc2 100644 --- a/src/hello/Hello.sol +++ b/src/hello/Hello.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; +pragma solidity ^0.8.25; // interfaces -import {IHello} from "./IHello.sol"; +import { IHello } from "./IHello.sol"; contract Hello is IHello { - event GreetingChanged(string oldGreeting, string newGreeting); + event GreetingChanged(string oldGreeting, string newGreeting); - string public greeting = "Hello, Forge!"; + string public greeting = "Hello, Forge!"; - function sayHello() public view returns (string memory) { - return greeting; - } + function sayHello() public view returns (string memory) { + return greeting; + } - function setGreeting(string memory newGreeting) public { - string memory oldGreeting = greeting; - greeting = newGreeting; - emit GreetingChanged(oldGreeting, newGreeting); - } + function setGreeting(string memory newGreeting) public { + string memory oldGreeting = greeting; + greeting = newGreeting; + emit GreetingChanged(oldGreeting, newGreeting); + } } diff --git a/src/hello/IHello.sol b/src/hello/IHello.sol index d0f2d62..8c1bb0b 100644 --- a/src/hello/IHello.sol +++ b/src/hello/IHello.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.23; +pragma solidity ^0.8.25; // interfaces @@ -8,7 +8,7 @@ pragma solidity ^0.8.23; // contracts interface IHello { - function sayHello() external view returns (string memory); + function sayHello() external view returns (string memory); - function setGreeting(string memory newGreeting) external; + function setGreeting(string memory newGreeting) external; } diff --git a/test/Hello.t.sol b/test/Hello.t.sol index f88ef78..1a64b8e 100644 --- a/test/Hello.t.sol +++ b/test/Hello.t.sol @@ -2,23 +2,28 @@ pragma solidity ^0.8.0; //interfaces -import {IHello} from "src/hello/IHello.sol"; +import { IHello } from "src/hello/IHello.sol"; //libraries //contracts -import "./utils/TestUtils.sol"; -import {DeployHello} from "scripts/DeployHello.s.sol"; +import { TestUtils } from "./utils/TestUtils.t.sol"; +import { DeployHello } from "scripts/DeployHello.s.sol"; contract HelloTest is TestUtils { - IHello hello; + IHello hello; - function setUp() external { - DeployHello helloHelper = new DeployHello(); - hello = IHello(helloHelper.deploy()); - } + function setUp() external { + DeployHello helloHelper = new DeployHello(); + hello = IHello(helloHelper.deploy()); + } - function test_sayHello() external view { - assertEq(hello.sayHello(), "Hello, Forge!"); - } + function test_sayHello() external view { + assertEq(hello.sayHello(), "Hello, Forge!"); + } + + function test_setGreeting() external { + hello.setGreeting("Hello, World!"); + assertEq(hello.sayHello(), "Hello, World!"); + } } diff --git a/test/utils/TestUtils.sol b/test/utils/TestUtils.sol deleted file mode 100644 index 15b090c..0000000 --- a/test/utils/TestUtils.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "forge-std/Test.sol"; - -contract TestUtils is Test { - uint256 private immutable _NONCE; - - modifier onlyForked() { - if (block.number > 1e6) { - _; - } - } - - constructor() { - vm.setEnv("IS_TEST", "true"); - vm.setEnv( - "LOCAL_PRIVATE_KEY", - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" - ); - - _NONCE = uint256( - keccak256( - abi.encode( - tx.origin, - tx.origin.balance, - block.number, - block.timestamp, - block.coinbase, - gasleft() - ) - ) - ); - } - - function _bytes32ToString(bytes32 str) internal pure returns (string memory) { - return string(abi.encodePacked(str)); - } - - function _randomBytes32() internal view returns (bytes32) { - bytes memory seed = abi.encode(_NONCE, block.timestamp, gasleft()); - return keccak256(seed); - } - - function _randomUint256() internal view returns (uint256) { - return uint256(_randomBytes32()); - } - - function _randomAddress() internal view returns (address payable) { - return payable(address(uint160(_randomUint256()))); - } - - function _randomAddress( - uint256 amount - ) internal view returns (address[] memory) { - address[] memory accounts = new address[](amount); - for (uint256 i = 0; i < amount; i++) { - accounts[i] = _randomAddress(); - } - return accounts; - } - - function _randomRange( - uint256 lo, - uint256 hi - ) internal view returns (uint256) { - return lo + (_randomUint256() % (hi - lo)); - } - - function _toAddressArray( - address v - ) internal pure returns (address[] memory arr) { - arr = new address[](1); - arr[0] = v; - } - - function _toUint256Array( - uint256 v - ) internal pure returns (uint256[] memory arr) { - arr = new uint256[](1); - arr[0] = v; - } - - function _expectNonIndexedEmit() internal { - vm.expectEmit(false, false, false, true); - } - - function _isEqual( - string memory s1, - string memory s2 - ) public pure returns (bool) { - return keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)); - } - - function _isEqual(bytes32 s1, bytes32 s2) public pure returns (bool) { - return keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)); - } -} diff --git a/test/utils/TestUtils.t.sol b/test/utils/TestUtils.t.sol new file mode 100644 index 0000000..400f341 --- /dev/null +++ b/test/utils/TestUtils.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.25; + +import { Test } from "forge-std/Test.sol"; + +contract TestUtils is Test { + uint256 private immutable _NONCE; + + modifier onlyForked() { + if (block.number > 1e6) { + _; + } + } + + constructor() { + vm.setEnv("IS_TEST", "true"); + vm.setEnv("LOCAL_PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"); + + // + _NONCE = uint256( + keccak256( + abi.encode(tx.origin, tx.origin.balance, block.number, block.timestamp, block.coinbase, gasleft()) //solhint-disable-line + // avoid-tx-origin + ) + ); + } + + function _bytes32ToString(bytes32 str) internal pure returns (string memory) { + return string(abi.encodePacked(str)); + } + + function _randomBytes32() internal view returns (bytes32) { + bytes memory seed = abi.encode(_NONCE, block.timestamp, gasleft()); + return keccak256(seed); + } + + function _randomUint256() internal view returns (uint256) { + return uint256(_randomBytes32()); + } + + function _randomAddress() internal view returns (address payable) { + return payable(address(uint160(_randomUint256()))); + } + + function _randomAddress(uint256 amount) internal view returns (address[] memory) { + address[] memory accounts = new address[](amount); + for (uint256 i = 0; i < amount; i++) { + accounts[i] = _randomAddress(); + } + return accounts; + } + + function _randomRange(uint256 lo, uint256 hi) internal view returns (uint256) { + return lo + (_randomUint256() % (hi - lo)); + } + + function _toAddressArray(address v) internal pure returns (address[] memory arr) { + arr = new address[](1); + arr[0] = v; + } + + function _toUint256Array(uint256 v) internal pure returns (uint256[] memory arr) { + arr = new uint256[](1); + arr[0] = v; + } + + function _expectNonIndexedEmit() internal { + vm.expectEmit(false, false, false, true); + } + + function _isEqual(string memory s1, string memory s2) public pure returns (bool) { + return keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)); + } + + function _isEqual(bytes32 s1, bytes32 s2) public pure returns (bool) { + return keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)); + } +} diff --git a/yarn.lock b/yarn.lock index 4a35932..15d49be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@adraffy/ens-normalize@1.10.1": - version "1.10.1" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" - integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== - "@babel/code-frame@^7.0.0": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" @@ -30,18 +25,6 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@noble/curves@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" - integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== - dependencies: - "@noble/hashes" "1.3.2" - -"@noble/hashes@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== - "@openzeppelin/contracts@^5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210" @@ -54,21 +37,6 @@ dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.18.0": - version "0.18.0" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" - integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== - -"@types/node@18.15.13": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== - -aes-js@4.0.0-beta.5: - version "4.0.0-beta.5" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" - integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== - ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -206,15 +174,6 @@ cosmiconfig@^8.0.0: parse-json "^5.2.0" path-type "^4.0.0" -dotenv@^16.0.3: - version "16.4.5" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" - integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== - -"ds-test@github:dapphub/ds-test": - version "1.0.0" - resolved "https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0" - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -232,25 +191,12 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -ethers@^6.1.0: - version "6.13.2" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.2.tgz#4b67d4b49e69b59893931a032560999e5e4419fe" - integrity sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg== - dependencies: - "@adraffy/ens-normalize" "1.10.1" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@types/node" "18.15.13" - aes-js "4.0.0-beta.5" - tslib "2.4.0" - ws "8.17.1" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-diff@^1.1.2, fast-diff@^1.2.0: +fast-diff@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== @@ -265,9 +211,9 @@ fast-uri@^3.0.1: resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134" integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== -"forge-std@github:foundry-rs/forge-std#v1": - version "1.9.2" - resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/1714bee72e286e73f76e320d110e0eaf5c4e649d" +"forge-std@github:foundry-rs/forge-std#v1.8.1": + version "1.7.6" + resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef" fs.realpath@^1.0.0: version "1.0.0" @@ -295,11 +241,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -husky@^8.0.1: - version "8.0.3" - resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" - integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== - ignore@^5.2.4: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -424,26 +365,16 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier-plugin-solidity@^1.1.3: - version "1.4.1" - resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz#8060baf18853a9e34d2e09e47e87b4f19e15afe9" - integrity sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg== - dependencies: - "@solidity-parser/parser" "^0.18.0" - semver "^7.5.4" - -prettier@^2.7.1, prettier@^2.8.3: +prettier@^2.8.3: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" + integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -459,7 +390,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -semver@^7.5.2, semver@^7.5.4: +semver@^7.5.2: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -473,14 +404,7 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -solhint-plugin-prettier@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" - integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== - dependencies: - prettier-linter-helpers "^1.0.0" - -solhint@^3.3.7: +solhint@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.6.2.tgz#2b2acbec8fdc37b2c68206a71ba89c7f519943fe" integrity sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ== @@ -551,11 +475,6 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -tslib@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -567,8 +486,3 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -ws@8.17.1: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==