From b67e7ecbbb6470acc70c8c1fa4d005bf847875fd Mon Sep 17 00:00:00 2001 From: Daniel Gretzke Date: Tue, 2 Jul 2024 21:12:01 +0200 Subject: [PATCH] Add new functionality to template repo (#3) * Dev (#2) * Remove default MIT license * Remove libraries Co-authored-by: Madeveda * forge install: forge-gas-snapshot * add testing info * remove versioning * add gasmetering to tests (#1) * update solidity version * add issue templates * add coverage script * add formatting rules * add RPCs --------- Co-authored-by: Madeveda * Ignore coverage report, increase coverage, adjust CI (#4) * Ignore coverage report, increase coverage, adjust CI * remove test helper * Dev (#5) * Ignore coverage report, increase coverage, adjust CI * remove test helper * Add testing as option * Add requirements for merging --------- Co-authored-by: Madeveda --- .env.example | 4 +- .../Increment counter number.snap | 1 + .forge-snapshots/Set counter number.snap | 1 + .github/ISSUE_TEMPLATE/BUG_REPORT.yaml | 31 +++++++ .../ISSUE_TEMPLATE/FEATURE_IMPROVEMENT.yaml | 37 ++++++++ .github/PULL_REQUEST_TEMPLATE.md | 1 - .github/workflows/coverage.yaml | 89 +++++++++++++++++++ .github/workflows/test.yaml | 12 ++- .gitignore | 5 +- .gitmodules | 18 +--- CONTRIBUTING.md | 77 ++++++---------- LICENSE.md | 7 -- README.md | 2 - docs/autogen/src/README.md | 2 - docs/autogen/src/SUMMARY.md | 1 - .../src/src/Counter.sol/contract.Counter.md | 26 +----- .../ICounter.sol/interface.ICounter.md | 5 +- docs/autogen/src/src/interface/README.md | 1 - foundry.toml | 69 +++++++++++--- lib/deployer-kit | 1 - lib/forge-gas-snapshot | 1 + lib/forge-std | 2 +- lib/openzeppelin-contracts | 1 - lib/openzeppelin-contracts-upgradeable | 1 - lib/solady | 1 - lib/storage-delta | 1 - script/Deploy.s.sol | 16 ++-- script/deployers/CounterDeployer.s.sol | 43 --------- src/Counter.sol | 20 ++--- src/interface/ICounter.sol | 8 +- src/interface/IVersioned.sol | 7 -- test/Counter.t.sol | 63 ++++++------- test/util/TestHelpers.sol | 13 --- 33 files changed, 308 insertions(+), 259 deletions(-) create mode 100644 .forge-snapshots/Increment counter number.snap create mode 100644 .forge-snapshots/Set counter number.snap create mode 100644 .github/ISSUE_TEMPLATE/BUG_REPORT.yaml create mode 100644 .github/ISSUE_TEMPLATE/FEATURE_IMPROVEMENT.yaml create mode 100644 .github/workflows/coverage.yaml delete mode 100644 LICENSE.md delete mode 160000 lib/deployer-kit create mode 160000 lib/forge-gas-snapshot delete mode 160000 lib/openzeppelin-contracts delete mode 160000 lib/openzeppelin-contracts-upgradeable delete mode 160000 lib/solady delete mode 160000 lib/storage-delta delete mode 100644 script/deployers/CounterDeployer.s.sol delete mode 100644 src/interface/IVersioned.sol delete mode 100644 test/util/TestHelpers.sol diff --git a/.env.example b/.env.example index cacde6e..d25d24f 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,3 @@ PRIVATE_KEY= INFURA_KEY= -ETHERSCAN_API_KEY= -POLYGONSCAN_API_KEY= -POLYGONSCAN_ZKEVM_API_KEY= \ No newline at end of file +ETHERSCAN_API_KEY= \ No newline at end of file diff --git a/.forge-snapshots/Increment counter number.snap b/.forge-snapshots/Increment counter number.snap new file mode 100644 index 0000000..64cea38 --- /dev/null +++ b/.forge-snapshots/Increment counter number.snap @@ -0,0 +1 @@ +26304 \ No newline at end of file diff --git a/.forge-snapshots/Set counter number.snap b/.forge-snapshots/Set counter number.snap new file mode 100644 index 0000000..44d33ec --- /dev/null +++ b/.forge-snapshots/Set counter number.snap @@ -0,0 +1 @@ +26394 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml new file mode 100644 index 0000000..7979d0a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml @@ -0,0 +1,31 @@ +name: Bug report +description: File a bug report to help us improve the code +title: "[Bug]: " +labels: ["bug"] + +body: + - type: markdown + attributes: + value: | + Please check that the bug is not already being tracked. + - type: textarea + attributes: + label: Describe the bug + description: Provide a clear and concise description of what the bug is and which contracts it affects. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: Provide a clear and concise description of the desired fix. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + description: If you have written tests to showcase the bug, what can we run to reproduce the issue? + placeholder: "git checkout / forge test --isolate --mt " + - type: textarea + attributes: + label: Additional context + description: If there is any additional context needed like a dependency or integrating contract that is affected please describe it below. diff --git a/.github/ISSUE_TEMPLATE/FEATURE_IMPROVEMENT.yaml b/.github/ISSUE_TEMPLATE/FEATURE_IMPROVEMENT.yaml new file mode 100644 index 0000000..c942227 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE_IMPROVEMENT.yaml @@ -0,0 +1,37 @@ +name: Feature Improvement +description: Suggest an improvement. +labels: ["triage"] + +body: + - type: markdown + attributes: + value: | + Please ensure that the feature has not already been requested. + - type: dropdown + attributes: + label: Component + description: Which area of code does your idea improve? + multiple: true + options: + - Gas Optimization + - General design optimization (improving efficiency, cleanliness, or developer experience) + - Testing + - Documentation + - type: textarea + attributes: + label: Describe the suggested feature and problem it solves. + description: Provide a clear and concise description of what feature you would like to see, and what problems it solves. + validations: + required: true + - type: textarea + attributes: + label: Describe the desired implementation. + description: If possible, provide a suggested architecture change or implementation. + - type: textarea + attributes: + label: Describe alternatives. + description: If possible, describe the alternatives you've considered, or describe the current functionality and how it may be sub-optimal. + - type: textarea + attributes: + label: Additional context. + description: Please list any additional dependencies or integrating contacts that are affected. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9fbe179..3e32391 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -19,7 +19,6 @@ Before deployment - [ ] fuzz and invariant tests (when applicable) - [ ] formal verification (when applicable) - [ ] deployment or upgrade scripts ready -- [ ] version management agreed upon and implemented After deployment diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml new file mode 100644 index 0000000..98e1130 --- /dev/null +++ b/.github/workflows/coverage.yaml @@ -0,0 +1,89 @@ +name: code coverage + +on: + pull_request: + branches: [main, master, staging, dev] + +jobs: + comment-forge-coverage: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v3 + # with: + # token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + - name: Run forge coverage + id: coverage + run: | + { + echo 'COVERAGE<> "$GITHUB_OUTPUT" + echo $GITHUB_OUTPUT + env: + FOUNDRY_RPC_URL: "${{ secrets.RPC_URL }}" + + - name: Check coverage is updated + uses: actions/github-script@v5 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const file = "coverage.txt" + if(!fs.existsSync(file)) { + console.log("Nothing to check"); + return + } + const currentCoverage = fs.readFileSync(file, "utf8").trim(); + const newCoverage = (`${{ steps.coverage.outputs.COVERAGE }}`).trim(); + if (newCoverage != currentCoverage) { + core.setFailed(`Code coverage not updated. Run : forge coverage --no-match-coverage "(test)" | grep '^|' > coverage.txt`); + } + + - name: Comment on PR + id: comment + uses: actions/github-script@v5 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const {data: comments} = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }) + + const botComment = comments.find(comment => comment.user.id === 41898282) + + const output = `${{ steps.coverage.outputs.COVERAGE }}`; + const commentBody = `Forge code coverage:\n${output}\n`; + + if (botComment) { + github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: commentBody + }) + } else { + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentBody + }); + } diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 42c3cda..5eabf40 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -3,12 +3,14 @@ name: test on: pull_request: branches: [main, master, staging, dev, feat/**, fix/**] + push: + branches: [main, master, staging, dev] env: - FOUNDRY_PROFILE: ci + FOUNDRY_PROFILE: ${{ github.event_name == 'push' && 'ci' || 'pr' }} jobs: - check: + forge-test: strategy: fail-fast: true @@ -26,11 +28,13 @@ jobs: - name: Run Forge build run: | + echo Foundry profile: $FOUNDRY_PROFILE forge --version forge build --sizes id: build - name: Run Forge tests - run: | - forge test -vvv + run: forge test --isolate -vvv id: test + env: + FORGE_SNAPSHOT_CHECK: true diff --git a/.gitignore b/.gitignore index 95a845d..39541da 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /out /cache /coverage +/report lcov.info .DS_Store .env @@ -10,7 +11,3 @@ lcov.info broadcast/*/31337 deployments/**/31337.* - -# storage layout checker library -storage_check_cache -storage_check_report \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index beb9ef4..7dc401b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,21 +1,9 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "lib/openzeppelin-contracts"] - path = lib/openzeppelin-contracts - url = https://github.com/openzeppelin/openzeppelin-contracts -[submodule "lib/openzeppelin-contracts-upgradeable"] - path = lib/openzeppelin-contracts-upgradeable - url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable -[submodule "lib/solady"] - path = lib/solady - url = https://github.com/vectorized/solady -[submodule "lib/storage-delta"] - path = lib/storage-delta - url = https://github.com/0xPolygon/storage-delta -[submodule "lib/deployer-kit"] - path = lib/deployer-kit - url = https://github.com/0xPolygon/deployer-kit [submodule "lib/forge-chronicles"] path = lib/forge-chronicles url = https://github.com/0xPolygon/forge-chronicles +[submodule "lib/forge-gas-snapshot"] + path = lib/forge-gas-snapshot + url = https://github.com/marktoda/forge-gas-snapshot diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9d7e286..95b5244 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,7 @@ - [Install](#install) - [Pre-commit Hooks](#pre-commit-hooks) +- [Requirements for merge](#requirements-for-merge) - [Branching](#branching) - [Main](#main) - [Staging](#staging) @@ -12,14 +13,11 @@ - [Code Style](#code-style) - [Interfaces](#interfaces) - [NatSpec \& Comments](#natspec--comments) -- [Versioning](#versioning) - [Testing](#testing) - - [Deployer Template](#deployer-template) + - [Gas Metering](#gas-metering) - [Deployment](#deployment) - - [Deployer Template](#deployer-template-1) - [Deployment](#deployment-1) - [Deployment Info Generation](#deployment-info-generation) -- [Deployer Template Script](#deployer-template-script) - [Releases](#releases) ## Install @@ -41,21 +39,32 @@ This repo includes the following pre-commit hooks that are defined in the `.pre- - `doc`: This hook uses `forge doc` to automatically generate documentation for all Solidity files whenever the NatSpec documentation changes. The `script/util/doc_gen.sh` script is used to generate documentation. Forge updates the commit hash in the documentation automatically. To only generate new documentation when the documentation has actually changed, the script checks whether more than just the hash has changed in the documentation and discard all changes if only the hash has changed. - `prettier`: All remaining files are formatted using prettier. +## Requirements for merge + +In order for a PR to be merged, it must pass the following requirements: + +- All commits within the PR must be signed +- CI must pass (tests, linting, etc.) +- New features must be merged with associated tests +- Bug fixes must have a corresponding test that fails without the fix +- The PR must be approved by at least one maintainer + - The PR must be approved by 2+ maintainers if the PR is a new feature or > 100 LOC changed + ## Branching This section outlines the branching strategy of this repo. ### Main -The main branch is supposed to reflect the deployed state on all networks. Any pull requests into this branch MUST come from the staging branch. The main branch is protected and requires a separate code review by the security team. Whenever the main branch is updated, a new release is created with the latest version. For more information on versioning, check [here](#versioning). +The main branch is supposed to reflect the deployed state on all networks. Any pull requests into this branch MUST come from the staging branch. ### Staging -The staging branch reflects new code complete deployments or upgrades containing fixes and/or features. Any pull requests into this branch MUST come from the dev branch. The staging branch is used for security audits and deployments. Once the deployment is complete and deployment log files are generated, the branch can be merged into main. For more information on the deployment and log file generation check [here](#deployment--versioning). +The staging branch reflects new code complete deployments or upgrades containing fixes and/or features. Any pull requests into this branch MUST come from the dev branch. The staging branch is used for security audits and deployments. Once the deployment is complete and verified as well as deployment log files are generated, the branch can be merged into main. For more information on the deployment and log file generation check [here](#deployment). ### Dev -This is the active development branch. All pull requests into this branch MUST come from fix or feature branches. Upon code completion this branch is merged into staging for auditing and deployment. +This is the active development branch. All pull requests into this branch MUST come from fix or feature branches. Upon code completion this branch is merged into staging for auditing and deployment. PRs into this branch should squash all commits into a single commit. ### Feature @@ -106,7 +115,7 @@ The repo follows the official [Solidity Style Guide](https://docs.soliditylang.o abstract contract AccessControl is ..., { ``` -- Unchecked arithmetic blocks should contain comments explaining why overflow is guaranteed not to happen. If the reason is immediately apparent from the line above the unchecked block, the comment may be omitted. +- Unchecked arithmetic blocks should contain comments explaining why overflow is guaranteed not to happen or permissible. If the reason is immediately apparent from the line above the unchecked block, the comment may be omitted. ### Interfaces @@ -116,38 +125,30 @@ Every contract MUST implement their corresponding interface that includes all ex Interfaces should be the entrypoint for all contracts. When exploring the a contract within the repository, the interface MUST contain all relevant information to understand the functionality of the contract in the form of NatSpec comments. This includes all externally callable functions, errors and events. The NatSpec documentation MUST be added to the functions, errors and events within the interface. This allows a reader to understand the functionality of a function before moving on to the implementation. The implementing functions MUST point to the NatSpec documentation in the interface using `@inheritdoc`. Internal and private functions shouldn't have NatSpec documentation except for `@dev` comments, whenever more context is needed. Additional comments within a function should only be used to give more context to more complex operations, otherwise the code should be kept readable and self-explanatory. -## Versioning +## Testing -This repo utilizes [semantic versioning](https://semver.org/) for smart contracts. An `IVersioned` interface is included in the [interfaces directory](src/interface/IVersioned.sol) exposing a unified versioning interface for all contracts. This version MUST be included in all contracts, whether they are upgradeable or not, to be able to easily match deployed versions. For example, in the case of a non-upgradeable contract one version could be deployed to a network and later a new version might be deployed to another network. The exposed `version()` function is also used by the [Deployment Log Generator](https://github.com/0xPolygon/forge-chronicles#readme) to extract information about the version. +The following testing practices should be followed when writing unit tests for new code. All functions, lines and branches should be tested to result in 100% testing coverage. Fuzz parameters and conditions whenever possible. Extremes should be tested in dedicated edge case and corner case tests. Invariants should be tested in dedicated invariant tests. -Whenever contracts are modified, only the version of the changed contracts should be updated. Unmodified contracts should remain on the version of their last change. +Differential testing should be used to compare assembly implementations with implementations in Solidity or testing alternative implementations against existing Solidity or non-Solidity code using ffi. -## Testing +New features must be merged with associated tests. Bug fixes should have a corresponding test that fails without the bug fix. -### Deployer Template +### Gas Metering -This repo provides a deployer template library for consistency between scripts and unit tests. For more information on how to use the template, check [here](https://github.com/0xPolygon/deployer-kit#readme). +The [Forge Gas Snapshot](https://github.com/marktoda/forge-gas-snapshot) library is used to measure the gas cost of individual actions. To ensure that the measured gas is accurate, tests have to be run using the isolate argument to generate the correct snapshot and ensure that CI passes: -## Deployment +```sh +$ forge test --isolate +``` -This repo utilizes versioned deployments. Any changes to a contract should update the version of this specific contract. A script is provided that extracts deployment information from the `run-latest.json` file within the `broadcast` directory generated while the forge script runs. From this information a JSON and markdown file is generated containing various information about the deployment itself as well as past deployments. +When adding new functionality, a new gas snapshot should be added, preferably using `snapLastCall`. -### Deployer Template +## Deployment -This repo provides a deployer template library for consistency between scripts and unit tests. For more information on how to use the template, check [here](https://github.com/0xPolygon/deployer-kit#readme). +After deployments are executed a script is provided that extracts deployment information from the `run-latest.json` file within the `broadcast` directory generated while the forge script runs. From this information a JSON and markdown file is generated using the [Forge Chronicles](https://github.com/0xPolygon/forge-chronicles) library containing various information about the deployment itself as well as past deployments. ### Deployment -This repo set up the following RPCs in the `foundry.toml` file: - -- mainnet: Ethereum Mainnet -- goerli: Ethereum Goerli -- sepolia: Ethereum Sepolia -- polygon_pos: Polygon PoS -- mumbai: Polygon Mumbai -- polygon_zkevm: Polygon zkEVM -- polygon_zkevm_testnet: Polygon zkEVM Testnet - To deploy the contracts, provide the `--broadcast` flag to the forge script command. Should the etherscan verification time out, it can be picked up again by replacing the `--broadcast` flag with `--resume`. Deploy the contracts to one of the predefined networks by providing the according key with the `--rpc-url` flag. Most of the predefined networks require the `INFURA_KEY` environment variable to be set in the `.env` file. Including the `--verify` flag will verify deployed contracts on Etherscan. Define the appropriate environment variable for the Etherscan api key in the `.env` file. @@ -155,23 +156,3 @@ Including the `--verify` flag will verify deployed contracts on Etherscan. Defin ```shell forge script script/Deploy.s.sol --broadcast --rpc-url --verify ``` - -## Releases - -Releases should be created whenever the code on the main branch is updated to reflect a deployment or an upgrade on a network. The release should be named after the version of the contracts deployed or upgraded. -The release should include the following: - -- In case of a MAJOR version - - changelog - - summary of breaking changes - - summary of new features - - summary of fixes -- In case of a MINOR version - - changelog - - summary of new features - - summary of fixes -- In case of a PATCH version - - changelog - - summary of fixes -- Deployment information (can be copied from the generated log files) - - Addresses of the deployed contracts diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index f7f7438..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index b37b8a2..2f046dc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ## Template Repo (Foundry) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![CI Status](../../actions/workflows/test.yaml/badge.svg)](../../actions) This template repo is a quick and easy way to get started with a new Solidity project. It comes with a number of features that are useful for developing and deploying smart contracts. Such as: @@ -14,7 +13,6 @@ This template repo is a quick and easy way to get started with a new Solidity pr - [Deployment](#deployment) - [Docs](#docs) - [Contributing](#contributing) -- [License](#license) ## Setup diff --git a/docs/autogen/src/README.md b/docs/autogen/src/README.md index b37b8a2..2f046dc 100644 --- a/docs/autogen/src/README.md +++ b/docs/autogen/src/README.md @@ -1,6 +1,5 @@ ## Template Repo (Foundry) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![CI Status](../../actions/workflows/test.yaml/badge.svg)](../../actions) This template repo is a quick and easy way to get started with a new Solidity project. It comes with a number of features that are useful for developing and deploying smart contracts. Such as: @@ -14,7 +13,6 @@ This template repo is a quick and easy way to get started with a new Solidity pr - [Deployment](#deployment) - [Docs](#docs) - [Contributing](#contributing) -- [License](#license) ## Setup diff --git a/docs/autogen/src/SUMMARY.md b/docs/autogen/src/SUMMARY.md index 1c37b0f..332e1a1 100644 --- a/docs/autogen/src/SUMMARY.md +++ b/docs/autogen/src/SUMMARY.md @@ -3,5 +3,4 @@ # src - [❱ interface](src/interface/README.md) - [ICounter](src/interface/ICounter.sol/interface.ICounter.md) - - [IVersioned](src/interface/IVersioned.sol/interface.IVersioned.md) - [Counter](src/Counter.sol/contract.Counter.md) diff --git a/docs/autogen/src/src/Counter.sol/contract.Counter.md b/docs/autogen/src/src/Counter.sol/contract.Counter.md index 812857a..0366122 100644 --- a/docs/autogen/src/src/Counter.sol/contract.Counter.md +++ b/docs/autogen/src/src/Counter.sol/contract.Counter.md @@ -1,8 +1,8 @@ # Counter -[Git Source](https://github.com/gretzke/foundry-template/blob/952489c408f511dc764c05d3a2a21ded78da224f/src/Counter.sol) +[Git Source](https://github.com/Uniswap/foundry-template/blob/6ed2d53f10b4739f84426a12bef01482d7a2e669/src/Counter.sol) **Inherits:** -[ICounter](/src/interface/ICounter.sol/interface.ICounter.md), Initializable +[ICounter](/src/interface/ICounter.sol/interface.ICounter.md) ## State Variables @@ -18,14 +18,7 @@ uint256 public number; ```solidity -constructor(); -``` - -### initialize - - -```solidity -function initialize(uint256 initialNumber) public initializer; +constructor(uint256 initialNumber); ``` ### setNumber @@ -52,16 +45,3 @@ Increments the number by 1 function increment() public; ``` -### version - - -```solidity -function version() external pure returns (string memory); -``` -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`string`|The version of the contract| - - diff --git a/docs/autogen/src/src/interface/ICounter.sol/interface.ICounter.md b/docs/autogen/src/src/interface/ICounter.sol/interface.ICounter.md index c93d387..98a74ba 100644 --- a/docs/autogen/src/src/interface/ICounter.sol/interface.ICounter.md +++ b/docs/autogen/src/src/interface/ICounter.sol/interface.ICounter.md @@ -1,8 +1,5 @@ # ICounter -[Git Source](https://github.com/gretzke/foundry-template/blob/952489c408f511dc764c05d3a2a21ded78da224f/src/interface/ICounter.sol) - -**Inherits:** -[IVersioned](/src/interface/IVersioned.sol/interface.IVersioned.md) +[Git Source](https://github.com/Uniswap/foundry-template/blob/6ed2d53f10b4739f84426a12bef01482d7a2e669/src/interface/ICounter.sol) ## Functions diff --git a/docs/autogen/src/src/interface/README.md b/docs/autogen/src/src/interface/README.md index b060352..c06ad63 100644 --- a/docs/autogen/src/src/interface/README.md +++ b/docs/autogen/src/src/interface/README.md @@ -2,4 +2,3 @@ # Contents - [ICounter](ICounter.sol/interface.ICounter.md) -- [IVersioned](IVersioned.sol/interface.IVersioned.md) diff --git a/foundry.toml b/foundry.toml index baea3d3..3b27b64 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,10 +4,14 @@ out = "out" libs = ["lib"] optimizer = true optimizer_runs = 999999 -via_ir = true -solc = "0.8.23" +via_ir = false +solc = "0.8.26" verbosity = 2 ffi = true +fs_permissions = [ + { access = "read-write", path = ".forge-snapshots"}, + { access = "read", path = "script/" } +] remappings = [ "forge-std=lib/forge-std/src", @@ -15,31 +19,70 @@ remappings = [ "@openzeppelin/contracts-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts" ] -[profile.intense.fuzz] +[profile.default.fuzz] +runs = 1000 + +[profile.pr.fuzz] runs = 10000 -max_test_rejects = 999999 + +[profile.ci.fuzz] +runs = 100000 + +[profile.debug] +via_ir = false +optimizer_runs = 200 +fuzz.runs = 100 [fmt] -line_length = 160 +line_length = 120 +tab_width = 4 +quote_style = "single" +sort_imports = true number_underscore = "thousands" [rpc_endpoints] anvil = "http://127.0.0.1:8545" mainnet = "https://mainnet.infura.io/v3/${INFURA_KEY}" -goerli = "https://goerli.infura.io/v3/${INFURA_KEY}" sepolia = "https://sepolia.infura.io/v3/${INFURA_KEY}" +linea = "https://linea-mainnet.infura.io/v3/${INFURA_KEY}" +linea_sepolia = "https://linea-sepolia.infura.io/v3/${INFURA_KEY}" polygon_pos = "https://polygon-mainnet.infura.io/v3/${INFURA_KEY}" -mumbai = "https://polygon-mumbai.infura.io/v3/${INFURA_KEY}" +polygon_amoy = "https://polygon-amoy.infura.io/v3/${INFURA_KEY}" +blast = "https://blast-mainnet.infura.io/v3/${INFURA_KEY}" +blast_sepolia = "https://blast-sepolia.infura.io/v3/${INFURA_KEY}" +optimism = "https://optimism-mainnet.infura.io/v3/${INFURA_KEY}" +optimism_sepolia = "https://optimism-sepolia.infura.io/v3/${INFURA_KEY}" +arbitrum = "https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}" +arbitrum_sepolia = "https://arbitrum-sepolia.infura.io/v3/${INFURA_KEY}" +celo = "https://celo-mainnet.infura.io/v3/${INFURA_KEY}" +celo_alfajores = "https://celo-alfajores.infura.io/v3/${INFURA_KEY}" +zksync = "https://zksync-mainnet.infura.io/v3/${INFURA_KEY}" +zksync_sepolia = "https://zksync-sepolia.infura.io/v3/${INFURA_KEY}" +mantle = "https://mantle-mainnet.infura.io/v3/${INFURA_KEY}" +mantle_sepolia = "https://mantle-sepolia.infura.io/v3/${INFURA_KEY}" polygon_zkevm = "https://zkevm-rpc.com" polygon_zkevm_testnet = "https://rpc.public.zkevm-test.net" [etherscan] -mainnet = { key = "${ETHERSCAN_API_KEY}" } -goerli = { key = "${ETHERSCAN_API_KEY}" } sepolia = { key = "${ETHERSCAN_API_KEY}" } -polygon_pos = { key = "${POLYGONSCAN_API_KEY}" } -mumbai = { key = "${POLYGONSCAN_API_KEY}" } -polygon_zkevm = { key = "${POLYGONSCAN_ZKEVM_API_KEY}" } -polygon_zkevm_testnet = { key = "${POLYGONSCAN_ZKEVM_API_KEY}" } +mainnet = { key = "${ETHERSCAN_API_KEY}" } +linea = { key = "${ETHERSCAN_API_KEY}" } +linea_sepolia = { key = "${ETHERSCAN_API_KEY}" } +polygon_pos = { key = "${ETHERSCAN_API_KEY}" } +polygon_amoy = { key = "${ETHERSCAN_API_KEY}" } +blast = { key = "${ETHERSCAN_API_KEY}" } +blast_sepolia = { key = "${ETHERSCAN_API_KEY}" } +optimism = { key = "${ETHERSCAN_API_KEY}" } +optimism_sepolia = { key = "${ETHERSCAN_API_KEY}" } +arbitrum = { key = "${ETHERSCAN_API_KEY}" } +arbitrum_sepolia = { key = "${ETHERSCAN_API_KEY}" } +celo = { key = "${ETHERSCAN_API_KEY}" } +celo_alfajores = { key = "${ETHERSCAN_API_KEY}" } +zksync = { key = "${ETHERSCAN_API_KEY}" } +zksync_sepolia = { key = "${ETHERSCAN_API_KEY}" } +mantle = { key = "${ETHERSCAN_API_KEY}" } +mantle_sepolia = { key = "${ETHERSCAN_API_KEY}" } +polygon_zkevm = { key = "${ETHERSCAN_API_KEY}" } +polygon_zkevm_testnet = { key = "${ETHERSCAN_API_KEY}" } # See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/lib/deployer-kit b/lib/deployer-kit deleted file mode 160000 index 5ed82de..0000000 --- a/lib/deployer-kit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ed82de055ed1a96943d9958e9ba4301b41829d9 diff --git a/lib/forge-gas-snapshot b/lib/forge-gas-snapshot new file mode 160000 index 0000000..9161f7c --- /dev/null +++ b/lib/forge-gas-snapshot @@ -0,0 +1 @@ +Subproject commit 9161f7c0b6c6788a89081e2b3b9c67592b71e689 diff --git a/lib/forge-std b/lib/forge-std index 2f11269..5475f85 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 2f112697506eab12d433a65fdc31a639548fe365 +Subproject commit 5475f852e3f530d7e25dfb4596aa1f9baa8ffdfc diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts deleted file mode 160000 index 932fddf..0000000 --- a/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 932fddf69a699a9a80fd2396fd1a2ab91cdda123 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable deleted file mode 160000 index 625fb3c..0000000 --- a/lib/openzeppelin-contracts-upgradeable +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 625fb3c2b2696f1747ba2e72d1e1113066e6c177 diff --git a/lib/solady b/lib/solady deleted file mode 160000 index c565332..0000000 --- a/lib/solady +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c565332afef2d4f993fde402541487e4e331fddd diff --git a/lib/storage-delta b/lib/storage-delta deleted file mode 160000 index 117bf04..0000000 --- a/lib/storage-delta +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 117bf04f5c6be99c796e5483462d480bf949bc2c diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 39de5ed..216f766 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,15 +1,15 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.23; +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; -import "forge-std/Script.sol"; -import "script/deployers/CounterDeployer.s.sol"; +import 'forge-std/Script.sol'; -contract Deploy is Script, CounterDeployer { +import {Counter} from 'src/Counter.sol'; + +contract Deploy is Script { using stdJson for string; - function run() public { - address proxyAdmin = address(1); + function run() public returns (Counter) { uint256 initialNumber = 5; - deployCounterTransparent(proxyAdmin, initialNumber); + return new Counter(initialNumber); } } diff --git a/script/deployers/CounterDeployer.s.sol b/script/deployers/CounterDeployer.s.sol deleted file mode 100644 index a99f915..0000000 --- a/script/deployers/CounterDeployer.s.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -//////////////////////////////////////////////////// -// AUTOGENERATED - DO NOT EDIT THIS FILE DIRECTLY // -//////////////////////////////////////////////////// - -import "forge-std/Script.sol"; - -import "src/Counter.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -abstract contract CounterDeployer is Script { - Counter internal counter; - ProxyAdmin internal counterProxyAdmin; - address internal counterImplementation; - - function deployCounterTransparent(address proxyAdminOwner, uint256 initialNumber) - internal - returns (address implementation, address proxyAdmin, address proxy) - { - bytes memory initData = abi.encodeCall(Counter.initialize, (initialNumber)); - - vm.startBroadcast(vm.envUint("PRIVATE_KEY")); - - counterImplementation = address(new Counter()); - counter = Counter(address(new TransparentUpgradeableProxy(counterImplementation, proxyAdminOwner, initData))); - - vm.stopBroadcast(); - - counterProxyAdmin = - ProxyAdmin(address(uint160(uint256(vm.load(address(counter), hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"))))); - - return (counterImplementation, address(counterProxyAdmin), address(counter)); - } - - function deployCounterImplementation() internal returns (address implementation) { - vm.startBroadcast(vm.envUint("PRIVATE_KEY")); - implementation = address(new Counter()); - vm.stopBroadcast(); - } -} diff --git a/src/Counter.sol b/src/Counter.sol index 37782ec..3362e98 100644 --- a/src/Counter.sol +++ b/src/Counter.sol @@ -1,17 +1,12 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.23; +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; -import {ICounter, IVersioned} from "./interface/ICounter.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {ICounter} from './interface/ICounter.sol'; -contract Counter is ICounter, Initializable { +contract Counter is ICounter { uint256 public number; - constructor() { - _disableInitializers(); - } - - function initialize(uint256 initialNumber) public initializer { + constructor(uint256 initialNumber) { number = initialNumber; } @@ -24,9 +19,4 @@ contract Counter is ICounter, Initializable { function increment() public { number++; } - - /// @inheritdoc IVersioned - function version() external pure returns (string memory) { - return "1.0.0"; - } } diff --git a/src/interface/ICounter.sol b/src/interface/ICounter.sol index 57985b4..bee2abe 100644 --- a/src/interface/ICounter.sol +++ b/src/interface/ICounter.sol @@ -1,9 +1,7 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.23; +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; -import {IVersioned} from "./IVersioned.sol"; - -interface ICounter is IVersioned { +interface ICounter { /// @return The current number function number() external view returns (uint256); diff --git a/src/interface/IVersioned.sol b/src/interface/IVersioned.sol deleted file mode 100644 index 0897f48..0000000 --- a/src/interface/IVersioned.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.23; - -interface IVersioned { - /// @return The version of the contract - function version() external pure returns (string memory); -} diff --git a/test/Counter.t.sol b/test/Counter.t.sol index 04cfd67..084b23d 100644 --- a/test/Counter.t.sol +++ b/test/Counter.t.sol @@ -1,57 +1,52 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.23; +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; -import "forge-std/Test.sol"; -import "test/util/TestHelpers.sol"; +import {GasSnapshot} from 'forge-gas-snapshot/GasSnapshot.sol'; +import 'forge-std/Test.sol'; -import "script/deployers/CounterDeployer.s.sol"; +import {Deploy} from 'script/Deploy.s.sol'; +import {Counter} from 'src/Counter.sol'; -abstract contract BeforeScript is Test, TestHelpers, CounterDeployer { - function setUp() public virtual { - counter = Counter(deployCounterImplementation()); - } -} - -contract CounterTest_Zero is BeforeScript { - function test_InitialState() public { - assertEq(counter.number(), 0); - } +abstract contract Deployed is Test { + Counter counter; - function test_RevertsOnInitialization(uint256 number) public { - vm.expectRevert(Initializable.InvalidInitialization.selector); - counter.initialize(number); - } -} - -abstract contract AfterScript is Test, TestHelpers, CounterDeployer { function setUp() public virtual { - address proxyAdmin = makeAddr("alice"); uint256 initialNumber = 10; - deployCounterTransparent(proxyAdmin, initialNumber); + counter = new Counter(initialNumber); } } -contract CounterTest_Initialized is AfterScript { - function test_IsInitialized() public { +contract CounterTest_Deployed is Deployed, GasSnapshot { + function test_IsInitialized() public view { assertEq(counter.number(), 10); } - function test_RevertsIf_InitializedAgain() public { - vm.expectRevert(Initializable.InvalidInitialization.selector); - counter.initialize(1); - } - function test_IncrementsNumber() public { counter.increment(); + snapLastCall('Increment counter number'); assertEq(counter.number(), 11); } - function testFuzz_SetsNumber(uint256 x) public { + function test_fuzz_SetsNumber(uint256 x) public { counter.setNumber(x); assertEq(counter.number(), x); } - function test_ReturnsVersion() public { - assertEq(counter.version(), "1.0.0"); + function test_SetNumber_gas() public { + uint256 x = 100; + counter.setNumber(x); + snapLastCall('Set counter number'); + } +} + +contract DeploymentTest is Test { + Counter counter; + + function setUp() public virtual { + counter = new Deploy().run(); + } + + function test_IsDeployedCorrectly() public view { + assertEq(counter.number(), 5); } } diff --git a/test/util/TestHelpers.sol b/test/util/TestHelpers.sol deleted file mode 100644 index b524dc5..0000000 --- a/test/util/TestHelpers.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; - -abstract contract TestHelpers is Test { - Account internal DEPLOYER; - - constructor() { - DEPLOYER = makeAccount("DEPLOYER"); - vm.setEnv("PRIVATE_KEY", vm.toString(DEPLOYER.key)); - } -}