├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── sync.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── foundry.toml ├── package.json ├── scripts └── vm.py ├── src ├── Base.sol ├── Script.sol ├── StdAssertions.sol ├── StdChains.sol ├── StdCheats.sol ├── StdConstants.sol ├── StdError.sol ├── StdInvariant.sol ├── StdJson.sol ├── StdMath.sol ├── StdStorage.sol ├── StdStyle.sol ├── StdToml.sol ├── StdUtils.sol ├── Test.sol ├── Vm.sol ├── console.sol ├── console2.sol ├── interfaces │ ├── IERC1155.sol │ ├── IERC165.sol │ ├── IERC20.sol │ ├── IERC4626.sol │ ├── IERC6909.sol │ ├── IERC721.sol │ ├── IERC7540.sol │ ├── IERC7575.sol │ └── IMulticall3.sol └── safeconsole.sol └── test ├── CommonBase.t.sol ├── StdAssertions.t.sol ├── StdChains.t.sol ├── StdCheats.t.sol ├── StdConstants.t.sol ├── StdError.t.sol ├── StdJson.t.sol ├── StdMath.t.sol ├── StdStorage.t.sol ├── StdStyle.t.sol ├── StdToml.t.sol ├── StdUtils.t.sol ├── Vm.t.sol ├── compilation ├── CompilationScript.sol ├── CompilationScriptBase.sol ├── CompilationTest.sol └── CompilationTestBase.sol └── fixtures ├── broadcast.log.json ├── test.json └── test.toml /.gitattributes: -------------------------------------------------------------------------------- 1 | src/Vm.sol linguist-generated 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Install Foundry 17 | uses: foundry-rs/foundry-toolchain@v1 18 | with: 19 | version: nightly 20 | 21 | - name: Print forge version 22 | run: forge --version 23 | 24 | # Backwards compatibility checks: 25 | # - the oldest and newest version of each supported minor version 26 | # - versions with specific issues 27 | - name: Check compatibility with latest 28 | if: always() 29 | run: | 30 | output=$(forge build --skip test) 31 | if echo "$output" | grep -q "Warning"; then 32 | echo "$output" 33 | exit 1 34 | fi 35 | 36 | - name: Check compatibility with 0.8.0 37 | if: always() 38 | run: | 39 | output=$(forge build --skip test --use solc:0.8.0) 40 | if echo "$output" | grep -q "Warning"; then 41 | echo "$output" 42 | exit 1 43 | fi 44 | 45 | - name: Check compatibility with 0.7.6 46 | if: always() 47 | run: | 48 | output=$(forge build --skip test --use solc:0.7.6) 49 | if echo "$output" | grep -q "Warning"; then 50 | echo "$output" 51 | exit 1 52 | fi 53 | 54 | - name: Check compatibility with 0.7.0 55 | if: always() 56 | run: | 57 | output=$(forge build --skip test --use solc:0.7.0) 58 | if echo "$output" | grep -q "Warning"; then 59 | echo "$output" 60 | exit 1 61 | fi 62 | 63 | - name: Check compatibility with 0.6.12 64 | if: always() 65 | run: | 66 | output=$(forge build --skip test --use solc:0.6.12) 67 | if echo "$output" | grep -q "Warning"; then 68 | echo "$output" 69 | exit 1 70 | fi 71 | 72 | - name: Check compatibility with 0.6.2 73 | if: always() 74 | run: | 75 | output=$(forge build --skip test --use solc:0.6.2) 76 | if echo "$output" | grep -q "Warning"; then 77 | echo "$output" 78 | exit 1 79 | fi 80 | 81 | # via-ir compilation time checks. 82 | - name: Measure compilation time of Test with 0.8.17 --via-ir 83 | if: always() 84 | run: forge build --skip test --contracts test/compilation/CompilationTest.sol --use solc:0.8.17 --via-ir 85 | 86 | - name: Measure compilation time of TestBase with 0.8.17 --via-ir 87 | if: always() 88 | run: forge build --skip test --contracts test/compilation/CompilationTestBase.sol --use solc:0.8.17 --via-ir 89 | 90 | - name: Measure compilation time of Script with 0.8.17 --via-ir 91 | if: always() 92 | run: forge build --skip test --contracts test/compilation/CompilationScript.sol --use solc:0.8.17 --via-ir 93 | 94 | - name: Measure compilation time of ScriptBase with 0.8.17 --via-ir 95 | if: always() 96 | run: forge build --skip test --contracts test/compilation/CompilationScriptBase.sol --use solc:0.8.17 --via-ir 97 | 98 | test: 99 | runs-on: ubuntu-latest 100 | steps: 101 | - uses: actions/checkout@v4 102 | 103 | - name: Install Foundry 104 | uses: foundry-rs/foundry-toolchain@v1 105 | with: 106 | version: nightly 107 | 108 | - name: Print forge version 109 | run: forge --version 110 | 111 | - name: Run tests 112 | run: forge test -vvv 113 | 114 | fmt: 115 | runs-on: ubuntu-latest 116 | steps: 117 | - uses: actions/checkout@v4 118 | 119 | - name: Install Foundry 120 | uses: foundry-rs/foundry-toolchain@v1 121 | with: 122 | version: nightly 123 | 124 | - name: Print forge version 125 | run: forge --version 126 | 127 | - name: Check formatting 128 | run: forge fmt --check 129 | -------------------------------------------------------------------------------- /.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync Release Branch 2 | 3 | on: 4 | release: 5 | types: 6 | - created 7 | 8 | jobs: 9 | sync-release-branch: 10 | runs-on: ubuntu-latest 11 | if: startsWith(github.event.release.tag_name, 'v1') 12 | steps: 13 | - name: Check out the repo 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | ref: v1 18 | 19 | # The email is derived from the bots user id, 20 | # found here: https://api.github.com/users/github-actions%5Bbot%5D 21 | - name: Configure Git 22 | run: | 23 | git config user.name github-actions[bot] 24 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 25 | 26 | - name: Sync Release Branch 27 | run: | 28 | git fetch --tags 29 | git checkout v1 30 | git reset --hard ${GITHUB_REF} 31 | git push --force 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ 3 | .vscode 4 | .idea 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Foundry 2 | 3 | Thanks for your interest in improving Foundry! 4 | 5 | There are multiple opportunities to contribute at any level. It doesn't matter if you are just getting started with Rust or are the most weathered expert, we can use your help. 6 | 7 | This document will help you get started. **Do not let the document intimidate you**. 8 | It should be considered as a guide to help you navigate the process. 9 | 10 | The [dev Telegram][dev-tg] is available for any concerns you may have that are not covered in this guide. 11 | 12 | ### Code of Conduct 13 | 14 | The Foundry project adheres to the [Rust Code of Conduct][rust-coc]. This code of conduct describes the _minimum_ behavior expected from all contributors. 15 | 16 | Instances of violations of the Code of Conduct can be reported by contacting the team at [me@gakonst.com](mailto:me@gakonst.com). 17 | 18 | ### Ways to contribute 19 | 20 | There are fundamentally four ways an individual can contribute: 21 | 22 | 1. **By opening an issue:** For example, if you believe that you have uncovered a bug 23 | in Foundry, creating a new issue in the issue tracker is the way to report it. 24 | 2. **By adding context:** Providing additional context to existing issues, 25 | such as screenshots and code snippets, which help resolve issues. 26 | 3. **By resolving issues:** Typically this is done in the form of either 27 | demonstrating that the issue reported is not a problem after all, or more often, 28 | by opening a pull request that fixes the underlying problem, in a concrete and 29 | reviewable manner. 30 | 31 | **Anybody can participate in any stage of contribution**. We urge you to participate in the discussion 32 | around bugs and participate in reviewing PRs. 33 | 34 | ### Contributions Related to Spelling and Grammar 35 | 36 | At this time, we will not be accepting contributions that only fix spelling or grammatical errors in documentation, code or 37 | elsewhere. 38 | 39 | ### Asking for help 40 | 41 | If you have reviewed existing documentation and still have questions, or you are having problems, you can get help in the following ways: 42 | 43 | - **Asking in the support Telegram:** The [Foundry Support Telegram][support-tg] is a fast and easy way to ask questions. 44 | - **Opening a discussion:** This repository comes with a discussions board where you can also ask for help. Click the "Discussions" tab at the top. 45 | 46 | As Foundry is still in heavy development, the documentation can be a bit scattered. 47 | The [Foundry Book][foundry-book] is our current best-effort attempt at keeping up-to-date information. 48 | 49 | ### Submitting a bug report 50 | 51 | When filing a new bug report in the issue tracker, you will be presented with a basic form to fill out. 52 | 53 | If you believe that you have uncovered a bug, please fill out the form to the best of your ability. Do not worry if you cannot answer every detail; just fill in what you can. Contributors will ask follow-up questions if something is unclear. 54 | 55 | The most important pieces of information we need in a bug report are: 56 | 57 | - The Foundry version you are on (and that it is up to date) 58 | - The platform you are on (Windows, macOS, an M1 Mac or Linux) 59 | - Code snippets if this is happening in relation to testing or building code 60 | - Concrete steps to reproduce the bug 61 | 62 | In order to rule out the possibility of the bug being in your project, the code snippets should be as minimal 63 | as possible. It is better if you can reproduce the bug with a small snippet as opposed to an entire project! 64 | 65 | See [this guide][mcve] on how to create a minimal, complete, and verifiable example. 66 | 67 | ### Submitting a feature request 68 | 69 | When adding a feature request in the issue tracker, you will be presented with a basic form to fill out. 70 | 71 | Please include as detailed of an explanation as possible of the feature you would like, adding additional context if necessary. 72 | 73 | If you have examples of other tools that have the feature you are requesting, please include them as well. 74 | 75 | ### Resolving an issue 76 | 77 | Pull requests are the way concrete changes are made to the code, documentation, and dependencies of Foundry. 78 | 79 | Even minor pull requests, such as those fixing wording, are greatly appreciated. Before making a large change, it is usually 80 | a good idea to first open an issue describing the change to solicit feedback and guidance. This will increase 81 | the likelihood of the PR getting merged. 82 | 83 | Please make sure that the following commands pass if you have changed the code: 84 | 85 | ```sh 86 | forge fmt --check 87 | forge test -vvv 88 | ``` 89 | 90 | To make sure your changes are compatible with all compiler version targets, run the following commands: 91 | 92 | ```sh 93 | forge build --skip test --use solc:0.6.2 94 | forge build --skip test --use solc:0.6.12 95 | forge build --skip test --use solc:0.7.0 96 | forge build --skip test --use solc:0.7.6 97 | forge build --skip test --use solc:0.8.0 98 | ``` 99 | 100 | The CI will also ensure that the code is formatted correctly and that the tests are passing across all compiler version targets. 101 | 102 | #### Adding cheatcodes 103 | 104 | Please follow the guide outlined in the [cheatcodes](https://github.com/foundry-rs/foundry/blob/master/docs/dev/cheatcodes.md#adding-a-new-cheatcode) documentation of Foundry. 105 | 106 | When making modifications to the native cheatcodes or adding new ones, please make sure to run [`./scripts/vm.py`](./scripts/vm.py) to update the cheatcodes in the [`src/Vm.sol`](./src/Vm.sol) file. 107 | 108 | By default the script will automatically generate the cheatcodes from the [`cheatcodes.json`](https://raw.githubusercontent.com/foundry-rs/foundry/master/crates/cheatcodes/assets/cheatcodes.json) file but alternatively you can provide a path to a JSON file containing the Vm interface, as generated by Foundry, with the `--from` flag. 109 | 110 | ```sh 111 | ./scripts/vm.py --from path/to/cheatcodes.json 112 | ``` 113 | 114 | It is possible that the resulting [`src/Vm.sol`](./src/Vm.sol) file will have some changes that are not directly related to your changes, this is not a problem. 115 | 116 | #### Commits 117 | 118 | It is a recommended best practice to keep your changes as logically grouped as possible within individual commits. There is no limit to the number of commits any single pull request may have, and many contributors find it easier to review changes that are split across multiple commits. 119 | 120 | That said, if you have a number of commits that are "checkpoints" and don't represent a single logical change, please squash those together. 121 | 122 | #### Opening the pull request 123 | 124 | From within GitHub, opening a new pull request will present you with a template that should be filled out. Please try your best at filling out the details, but feel free to skip parts if you're not sure what to put. 125 | 126 | #### Discuss and update 127 | 128 | You will probably get feedback or requests for changes to your pull request. 129 | This is a big part of the submission process, so don't be discouraged! Some contributors may sign off on the pull request right away, others may have more detailed comments or feedback. 130 | This is a necessary part of the process in order to evaluate whether the changes are correct and necessary. 131 | 132 | **Any community member can review a PR, so you might get conflicting feedback**. 133 | Keep an eye out for comments from code owners to provide guidance on conflicting feedback. 134 | 135 | #### Reviewing pull requests 136 | 137 | **Any Foundry community member is welcome to review any pull request**. 138 | 139 | All contributors who choose to review and provide feedback on pull requests have a responsibility to both the project and individual making the contribution. Reviews and feedback must be helpful, insightful, and geared towards improving the contribution as opposed to simply blocking it. If there are reasons why you feel the PR should not be merged, explain what those are. Do not expect to be able to block a PR from advancing simply because you say "no" without giving an explanation. Be open to having your mind changed. Be open to working _with_ the contributor to make the pull request better. 140 | 141 | Reviews that are dismissive or disrespectful of the contributor or any other reviewers are strictly counter to the Code of Conduct. 142 | 143 | When reviewing a pull request, the primary goals are for the codebase to improve and for the person submitting the request to succeed. **Even if a pull request is not merged, the submitter should come away from the experience feeling like their effort was not unappreciated**. Every PR from a new contributor is an opportunity to grow the community. 144 | 145 | ##### Review a bit at a time 146 | 147 | Do not overwhelm new contributors. 148 | 149 | It is tempting to micro-optimize and make everything about relative performance, perfect grammar, or exact style matches. Do not succumb to that temptation.. 150 | 151 | Focus first on the most significant aspects of the change: 152 | 153 | 1. Does this change make sense for Foundry? 154 | 2. Does this change make Foundry better, even if only incrementally? 155 | 3. Are there clear bugs or larger scale issues that need attending? 156 | 4. Are the commit messages readable and correct? If it contains a breaking change, is it clear enough? 157 | 158 | Note that only **incremental** improvement is needed to land a PR. This means that the PR does not need to be perfect, only better than the status quo. Follow-up PRs may be opened to continue iterating. 159 | 160 | When changes are necessary, _request_ them, do not _demand_ them, and **do not assume that the submitter already knows how to add a test or run a benchmark**. 161 | 162 | Specific performance optimization techniques, coding styles and conventions change over time. The first impression you give to a new contributor never does. 163 | 164 | Nits (requests for small changes that are not essential) are fine, but try to avoid stalling the pull request. Most nits can typically be fixed by the Foundry maintainers merging the pull request, but they can also be an opportunity for the contributor to learn a bit more about the project. 165 | 166 | It is always good to clearly indicate nits when you comment, e.g.: `Nit: change foo() to bar(). But this is not blocking`. 167 | 168 | If your comments were addressed but were not folded after new commits, or if they proved to be mistaken, please, [hide them][hiding-a-comment] with the appropriate reason to keep the conversation flow concise and relevant. 169 | 170 | ##### Be aware of the person behind the code 171 | 172 | Be aware that _how_ you communicate requests and reviews in your feedback can have a significant impact on the success of the pull request. Yes, we may merge a particular change that makes Foundry better, but the individual might just not want to have anything to do with Foundry ever again. The goal is not just having good code. 173 | 174 | ##### Abandoned or stale pull requests 175 | 176 | If a pull request appears to be abandoned or stalled, it is polite to first check with the contributor to see if they intend to continue the work before checking if they would mind if you took it over (especially if it just has nits left). When doing so, it is courteous to give the original contributor credit for the work they started, either by preserving their name and e-mail address in the commit log, or by using the `Author: ` or `Co-authored-by: ` metadata tag in the commits. 177 | 178 | _Adapted from the [ethers-rs contributing guide](https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md)_. 179 | 180 | ### Releasing 181 | 182 | Releases are automatically done by the release workflow when a tag is pushed, however, these steps still need to be taken: 183 | 184 | 1. Ensure that the versions in the relevant `Cargo.toml` files are up-to-date. 185 | 2. Update documentation links 186 | 3. Perform a final audit for breaking changes. 187 | 188 | [rust-coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md 189 | [dev-tg]: https://t.me/foundry_rs 190 | [foundry-book]: https://github.com/foundry-rs/foundry-book 191 | [support-tg]: https://t.me/foundry_support 192 | [mcve]: https://stackoverflow.com/help/mcve 193 | [hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright Contributors to Forge Standard Library 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright Contributors to Forge Standard Library 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE.R 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Forge Standard Library • [![CI status](https://github.com/foundry-rs/forge-std/actions/workflows/ci.yml/badge.svg)](https://github.com/foundry-rs/forge-std/actions/workflows/ci.yml) 2 | 3 | Forge Standard Library is a collection of helpful contracts and libraries for use with [Forge and Foundry](https://github.com/foundry-rs/foundry). It leverages Forge's cheatcodes to make writing tests easier and faster, while improving the UX of cheatcodes. 4 | 5 | **Learn how to use Forge-Std with the [📖 Foundry Book (Forge-Std Guide)](https://book.getfoundry.sh/forge/forge-std.html).** 6 | 7 | ## Install 8 | 9 | ```bash 10 | forge install foundry-rs/forge-std 11 | ``` 12 | 13 | ## Contracts 14 | ### stdError 15 | 16 | This is a helper contract for errors and reverts. In Forge, this contract is particularly helpful for the `expectRevert` cheatcode, as it provides all compiler built-in errors. 17 | 18 | See the contract itself for all error codes. 19 | 20 | #### Example usage 21 | 22 | ```solidity 23 | 24 | import "forge-std/Test.sol"; 25 | 26 | contract TestContract is Test { 27 | ErrorsTest test; 28 | 29 | function setUp() public { 30 | test = new ErrorsTest(); 31 | } 32 | 33 | function testExpectArithmetic() public { 34 | vm.expectRevert(stdError.arithmeticError); 35 | test.arithmeticError(10); 36 | } 37 | } 38 | 39 | contract ErrorsTest { 40 | function arithmeticError(uint256 a) public { 41 | a = a - 100; 42 | } 43 | } 44 | ``` 45 | 46 | ### stdStorage 47 | 48 | This is a rather large contract due to all of the overloading to make the UX decent. Primarily, it is a wrapper around the `record` and `accesses` cheatcodes. It can *always* find and write the storage slot(s) associated with a particular variable without knowing the storage layout. The one _major_ caveat to this is while a slot can be found for packed storage variables, we can't write to that variable safely. If a user tries to write to a packed slot, the execution throws an error, unless it is uninitialized (`bytes32(0)`). 49 | 50 | This works by recording all `SLOAD`s and `SSTORE`s during a function call. If there is a single slot read or written to, it immediately returns the slot. Otherwise, behind the scenes, we iterate through and check each one (assuming the user passed in a `depth` parameter). If the variable is a struct, you can pass in a `depth` parameter which is basically the field depth. 51 | 52 | I.e.: 53 | ```solidity 54 | struct T { 55 | // depth 0 56 | uint256 a; 57 | // depth 1 58 | uint256 b; 59 | } 60 | ``` 61 | 62 | #### Example usage 63 | 64 | ```solidity 65 | import "forge-std/Test.sol"; 66 | 67 | contract TestContract is Test { 68 | using stdStorage for StdStorage; 69 | 70 | Storage test; 71 | 72 | function setUp() public { 73 | test = new Storage(); 74 | } 75 | 76 | function testFindExists() public { 77 | // Lets say we want to find the slot for the public 78 | // variable `exists`. We just pass in the function selector 79 | // to the `find` command 80 | uint256 slot = stdstore.target(address(test)).sig("exists()").find(); 81 | assertEq(slot, 0); 82 | } 83 | 84 | function testWriteExists() public { 85 | // Lets say we want to write to the slot for the public 86 | // variable `exists`. We just pass in the function selector 87 | // to the `checked_write` command 88 | stdstore.target(address(test)).sig("exists()").checked_write(100); 89 | assertEq(test.exists(), 100); 90 | } 91 | 92 | // It supports arbitrary storage layouts, like assembly based storage locations 93 | function testFindHidden() public { 94 | // `hidden` is a random hash of a bytes, iteration through slots would 95 | // not find it. Our mechanism does 96 | // Also, you can use the selector instead of a string 97 | uint256 slot = stdstore.target(address(test)).sig(test.hidden.selector).find(); 98 | assertEq(slot, uint256(keccak256("my.random.var"))); 99 | } 100 | 101 | // If targeting a mapping, you have to pass in the keys necessary to perform the find 102 | // i.e.: 103 | function testFindMapping() public { 104 | uint256 slot = stdstore 105 | .target(address(test)) 106 | .sig(test.map_addr.selector) 107 | .with_key(address(this)) 108 | .find(); 109 | // in the `Storage` constructor, we wrote that this address' value was 1 in the map 110 | // so when we load the slot, we expect it to be 1 111 | assertEq(uint(vm.load(address(test), bytes32(slot))), 1); 112 | } 113 | 114 | // If the target is a struct, you can specify the field depth: 115 | function testFindStruct() public { 116 | // NOTE: see the depth parameter - 0 means 0th field, 1 means 1st field, etc. 117 | uint256 slot_for_a_field = stdstore 118 | .target(address(test)) 119 | .sig(test.basicStruct.selector) 120 | .depth(0) 121 | .find(); 122 | 123 | uint256 slot_for_b_field = stdstore 124 | .target(address(test)) 125 | .sig(test.basicStruct.selector) 126 | .depth(1) 127 | .find(); 128 | 129 | assertEq(uint(vm.load(address(test), bytes32(slot_for_a_field))), 1); 130 | assertEq(uint(vm.load(address(test), bytes32(slot_for_b_field))), 2); 131 | } 132 | } 133 | 134 | // A complex storage contract 135 | contract Storage { 136 | struct UnpackedStruct { 137 | uint256 a; 138 | uint256 b; 139 | } 140 | 141 | constructor() { 142 | map_addr[msg.sender] = 1; 143 | } 144 | 145 | uint256 public exists = 1; 146 | mapping(address => uint256) public map_addr; 147 | // mapping(address => Packed) public map_packed; 148 | mapping(address => UnpackedStruct) public map_struct; 149 | mapping(address => mapping(address => uint256)) public deep_map; 150 | mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; 151 | UnpackedStruct public basicStruct = UnpackedStruct({ 152 | a: 1, 153 | b: 2 154 | }); 155 | 156 | function hidden() public view returns (bytes32 t) { 157 | // an extremely hidden storage slot 158 | bytes32 slot = keccak256("my.random.var"); 159 | assembly { 160 | t := sload(slot) 161 | } 162 | } 163 | } 164 | ``` 165 | 166 | ### stdCheats 167 | 168 | This is a wrapper over miscellaneous cheatcodes that need wrappers to be more dev friendly. Currently there are only functions related to `prank`. In general, users may expect ETH to be put into an address on `prank`, but this is not the case for safety reasons. Explicitly this `hoax` function should only be used for addresses that have expected balances as it will get overwritten. If an address already has ETH, you should just use `prank`. If you want to change that balance explicitly, just use `deal`. If you want to do both, `hoax` is also right for you. 169 | 170 | 171 | #### Example usage: 172 | ```solidity 173 | 174 | // SPDX-License-Identifier: MIT 175 | pragma solidity ^0.8.0; 176 | 177 | import "forge-std/Test.sol"; 178 | 179 | // Inherit the stdCheats 180 | contract StdCheatsTest is Test { 181 | Bar test; 182 | function setUp() public { 183 | test = new Bar(); 184 | } 185 | 186 | function testHoax() public { 187 | // we call `hoax`, which gives the target address 188 | // eth and then calls `prank` 189 | hoax(address(1337)); 190 | test.bar{value: 100}(address(1337)); 191 | 192 | // overloaded to allow you to specify how much eth to 193 | // initialize the address with 194 | hoax(address(1337), 1); 195 | test.bar{value: 1}(address(1337)); 196 | } 197 | 198 | function testStartHoax() public { 199 | // we call `startHoax`, which gives the target address 200 | // eth and then calls `startPrank` 201 | // 202 | // it is also overloaded so that you can specify an eth amount 203 | startHoax(address(1337)); 204 | test.bar{value: 100}(address(1337)); 205 | test.bar{value: 100}(address(1337)); 206 | vm.stopPrank(); 207 | test.bar(address(this)); 208 | } 209 | } 210 | 211 | contract Bar { 212 | function bar(address expectedSender) public payable { 213 | require(msg.sender == expectedSender, "!prank"); 214 | } 215 | } 216 | ``` 217 | 218 | ### Std Assertions 219 | 220 | Contains various assertions. 221 | 222 | ### `console.log` 223 | 224 | Usage follows the same format as [Hardhat](https://hardhat.org/hardhat-network/reference/#console-log). 225 | It's recommended to use `console2.sol` as shown below, as this will show the decoded logs in Forge traces. 226 | 227 | ```solidity 228 | // import it indirectly via Test.sol 229 | import "forge-std/Test.sol"; 230 | // or directly import it 231 | import "forge-std/console2.sol"; 232 | ... 233 | console2.log(someValue); 234 | ``` 235 | 236 | If you need compatibility with Hardhat, you must use the standard `console.sol` instead. 237 | Due to a bug in `console.sol`, logs that use `uint256` or `int256` types will not be properly decoded in Forge traces. 238 | 239 | ```solidity 240 | // import it indirectly via Test.sol 241 | import "forge-std/Test.sol"; 242 | // or directly import it 243 | import "forge-std/console.sol"; 244 | ... 245 | console.log(someValue); 246 | ``` 247 | 248 | ## Contributing 249 | 250 | See our [contributing guidelines](./CONTRIBUTING.md). 251 | 252 | ## Getting Help 253 | 254 | First, see if the answer to your question can be found in [book](https://book.getfoundry.sh). 255 | 256 | If the answer is not there: 257 | 258 | - Join the [support Telegram](https://t.me/foundry_support) to get help, or 259 | - Open a [discussion](https://github.com/foundry-rs/foundry/discussions/new/choose) with your question, or 260 | - Open an issue with [the bug](https://github.com/foundry-rs/foundry/issues/new/choose) 261 | 262 | If you want to contribute, or follow along with contributor discussion, you can use our [main telegram](https://t.me/foundry_rs) to chat with us about the development of Foundry! 263 | 264 | ## License 265 | 266 | Forge Standard Library is offered under either [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE) license. 267 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | fs_permissions = [{ access = "read-write", path = "./"}] 3 | optimizer = true 4 | optimizer_runs = 200 5 | 6 | [rpc_endpoints] 7 | # The RPC URLs are modified versions of the default for testing initialization. 8 | mainnet = "https://eth.merkle.io" # Different API key. 9 | optimism_sepolia = "https://sepolia.optimism.io/" # Adds a trailing slash. 10 | arbitrum_one_sepolia = "https://sepolia-rollup.arbitrum.io/rpc/" # Adds a trailing slash. 11 | needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}" 12 | 13 | [fmt] 14 | # These are all the `forge fmt` defaults. 15 | line_length = 120 16 | tab_width = 4 17 | bracket_spacing = false 18 | int_types = 'long' 19 | multiline_func_header = 'attributes_first' 20 | quote_style = 'double' 21 | number_underscore = 'preserve' 22 | single_line_statement_blocks = 'preserve' 23 | ignore = ["src/console.sol", "src/console2.sol"] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forge-std", 3 | "version": "1.9.7", 4 | "description": "Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry.", 5 | "homepage": "https://book.getfoundry.sh/forge/forge-std", 6 | "bugs": "https://github.com/foundry-rs/forge-std/issues", 7 | "license": "(Apache-2.0 OR MIT)", 8 | "author": "Contributors to Forge Standard Library", 9 | "files": [ 10 | "src/**/*" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/foundry-rs/forge-std.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Base.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | import {StdStorage} from "./StdStorage.sol"; 5 | import {Vm, VmSafe} from "./Vm.sol"; 6 | 7 | abstract contract CommonBase { 8 | /// @dev Cheat code address. 9 | /// Calculated as `address(uint160(uint256(keccak256("hevm cheat code"))))`. 10 | address internal constant VM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; 11 | /// @dev console.sol and console2.sol work by executing a staticcall to this address. 12 | /// Calculated as `address(uint160(uint88(bytes11("console.log"))))`. 13 | address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; 14 | /// @dev Used when deploying with create2. 15 | /// Taken from https://github.com/Arachnid/deterministic-deployment-proxy. 16 | address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 17 | /// @dev The default address for tx.origin and msg.sender. 18 | /// Calculated as `address(uint160(uint256(keccak256("foundry default caller"))))`. 19 | address internal constant DEFAULT_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38; 20 | /// @dev The address of the first contract `CREATE`d by a running test contract. 21 | /// When running tests, each test contract is `CREATE`d by `DEFAULT_SENDER` with nonce 1. 22 | /// Calculated as `VM.computeCreateAddress(VM.computeCreateAddress(DEFAULT_SENDER, 1), 1)`. 23 | address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; 24 | /// @dev Deterministic deployment address of the Multicall3 contract. 25 | /// Taken from https://www.multicall3.com. 26 | address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11; 27 | /// @dev The order of the secp256k1 curve. 28 | uint256 internal constant SECP256K1_ORDER = 29 | 115792089237316195423570985008687907852837564279074904382605163141518161494337; 30 | 31 | uint256 internal constant UINT256_MAX = 32 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 33 | 34 | Vm internal constant vm = Vm(VM_ADDRESS); 35 | StdStorage internal stdstore; 36 | } 37 | 38 | abstract contract TestBase is CommonBase {} 39 | 40 | abstract contract ScriptBase is CommonBase { 41 | VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS); 42 | } 43 | -------------------------------------------------------------------------------- /src/Script.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | // 💬 ABOUT 5 | // Forge Std's default Script. 6 | 7 | // 🧩 MODULES 8 | import {console} from "./console.sol"; 9 | import {console2} from "./console2.sol"; 10 | import {safeconsole} from "./safeconsole.sol"; 11 | import {StdChains} from "./StdChains.sol"; 12 | import {StdCheatsSafe} from "./StdCheats.sol"; 13 | import {StdConstants} from "./StdConstants.sol"; 14 | import {stdJson} from "./StdJson.sol"; 15 | import {stdMath} from "./StdMath.sol"; 16 | import {StdStorage, stdStorageSafe} from "./StdStorage.sol"; 17 | import {StdStyle} from "./StdStyle.sol"; 18 | import {StdUtils} from "./StdUtils.sol"; 19 | import {VmSafe} from "./Vm.sol"; 20 | 21 | // 📦 BOILERPLATE 22 | import {ScriptBase} from "./Base.sol"; 23 | 24 | // ⭐️ SCRIPT 25 | abstract contract Script is ScriptBase, StdChains, StdCheatsSafe, StdUtils { 26 | // Note: IS_SCRIPT() must return true. 27 | bool public IS_SCRIPT = true; 28 | } 29 | -------------------------------------------------------------------------------- /src/StdChains.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | import {VmSafe} from "./Vm.sol"; 5 | 6 | /** 7 | * StdChains provides information about EVM compatible chains that can be used in scripts/tests. 8 | * For each chain, the chain's name, chain ID, and a default RPC URL are provided. Chains are 9 | * identified by their alias, which is the same as the alias in the `[rpc_endpoints]` section of 10 | * the `foundry.toml` file. For best UX, ensure the alias in the `foundry.toml` file match the 11 | * alias used in this contract, which can be found as the first argument to the 12 | * `setChainWithDefaultRpcUrl` call in the `initializeStdChains` function. 13 | * 14 | * There are two main ways to use this contract: 15 | * 1. Set a chain with `setChain(string memory chainAlias, ChainData memory chain)` or 16 | * `setChain(string memory chainAlias, Chain memory chain)` 17 | * 2. Get a chain with `getChain(string memory chainAlias)` or `getChain(uint256 chainId)`. 18 | * 19 | * The first time either of those are used, chains are initialized with the default set of RPC URLs. 20 | * This is done in `initializeStdChains`, which uses `setChainWithDefaultRpcUrl`. Defaults are recorded in 21 | * `defaultRpcUrls`. 22 | * 23 | * The `setChain` function is straightforward, and it simply saves off the given chain data. 24 | * 25 | * The `getChain` methods use `getChainWithUpdatedRpcUrl` to return a chain. For example, let's say 26 | * we want to retrieve the RPC URL for `mainnet`: 27 | * - If you have specified data with `setChain`, it will return that. 28 | * - If you have configured a mainnet RPC URL in `foundry.toml`, it will return the URL, provided it 29 | * is valid (e.g. a URL is specified, or an environment variable is given and exists). 30 | * - If neither of the above conditions is met, the default data is returned. 31 | * 32 | * Summarizing the above, the prioritization hierarchy is `setChain` -> `foundry.toml` -> environment variable -> defaults. 33 | */ 34 | abstract contract StdChains { 35 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); 36 | 37 | bool private stdChainsInitialized; 38 | 39 | struct ChainData { 40 | string name; 41 | uint256 chainId; 42 | string rpcUrl; 43 | } 44 | 45 | struct Chain { 46 | // The chain name. 47 | string name; 48 | // The chain's Chain ID. 49 | uint256 chainId; 50 | // The chain's alias. (i.e. what gets specified in `foundry.toml`). 51 | string chainAlias; 52 | // A default RPC endpoint for this chain. 53 | // NOTE: This default RPC URL is included for convenience to facilitate quick tests and 54 | // experimentation. Do not use this RPC URL for production test suites, CI, or other heavy 55 | // usage as you will be throttled and this is a disservice to others who need this endpoint. 56 | string rpcUrl; 57 | } 58 | 59 | // Maps from the chain's alias (matching the alias in the `foundry.toml` file) to chain data. 60 | mapping(string => Chain) private chains; 61 | // Maps from the chain's alias to it's default RPC URL. 62 | mapping(string => string) private defaultRpcUrls; 63 | // Maps from a chain ID to it's alias. 64 | mapping(uint256 => string) private idToAlias; 65 | 66 | bool private fallbackToDefaultRpcUrls = true; 67 | 68 | // The RPC URL will be fetched from config or defaultRpcUrls if possible. 69 | function getChain(string memory chainAlias) internal virtual returns (Chain memory chain) { 70 | require(bytes(chainAlias).length != 0, "StdChains getChain(string): Chain alias cannot be the empty string."); 71 | 72 | initializeStdChains(); 73 | chain = chains[chainAlias]; 74 | require( 75 | chain.chainId != 0, 76 | string(abi.encodePacked("StdChains getChain(string): Chain with alias \"", chainAlias, "\" not found.")) 77 | ); 78 | 79 | chain = getChainWithUpdatedRpcUrl(chainAlias, chain); 80 | } 81 | 82 | function getChain(uint256 chainId) internal virtual returns (Chain memory chain) { 83 | require(chainId != 0, "StdChains getChain(uint256): Chain ID cannot be 0."); 84 | initializeStdChains(); 85 | string memory chainAlias = idToAlias[chainId]; 86 | 87 | chain = chains[chainAlias]; 88 | 89 | require( 90 | chain.chainId != 0, 91 | string(abi.encodePacked("StdChains getChain(uint256): Chain with ID ", vm.toString(chainId), " not found.")) 92 | ); 93 | 94 | chain = getChainWithUpdatedRpcUrl(chainAlias, chain); 95 | } 96 | 97 | // set chain info, with priority to argument's rpcUrl field. 98 | function setChain(string memory chainAlias, ChainData memory chain) internal virtual { 99 | require( 100 | bytes(chainAlias).length != 0, 101 | "StdChains setChain(string,ChainData): Chain alias cannot be the empty string." 102 | ); 103 | 104 | require(chain.chainId != 0, "StdChains setChain(string,ChainData): Chain ID cannot be 0."); 105 | 106 | initializeStdChains(); 107 | string memory foundAlias = idToAlias[chain.chainId]; 108 | 109 | require( 110 | bytes(foundAlias).length == 0 || keccak256(bytes(foundAlias)) == keccak256(bytes(chainAlias)), 111 | string( 112 | abi.encodePacked( 113 | "StdChains setChain(string,ChainData): Chain ID ", 114 | vm.toString(chain.chainId), 115 | " already used by \"", 116 | foundAlias, 117 | "\"." 118 | ) 119 | ) 120 | ); 121 | 122 | uint256 oldChainId = chains[chainAlias].chainId; 123 | delete idToAlias[oldChainId]; 124 | 125 | chains[chainAlias] = 126 | Chain({name: chain.name, chainId: chain.chainId, chainAlias: chainAlias, rpcUrl: chain.rpcUrl}); 127 | idToAlias[chain.chainId] = chainAlias; 128 | } 129 | 130 | // set chain info, with priority to argument's rpcUrl field. 131 | function setChain(string memory chainAlias, Chain memory chain) internal virtual { 132 | setChain(chainAlias, ChainData({name: chain.name, chainId: chain.chainId, rpcUrl: chain.rpcUrl})); 133 | } 134 | 135 | function _toUpper(string memory str) private pure returns (string memory) { 136 | bytes memory strb = bytes(str); 137 | bytes memory copy = new bytes(strb.length); 138 | for (uint256 i = 0; i < strb.length; i++) { 139 | bytes1 b = strb[i]; 140 | if (b >= 0x61 && b <= 0x7A) { 141 | copy[i] = bytes1(uint8(b) - 32); 142 | } else { 143 | copy[i] = b; 144 | } 145 | } 146 | return string(copy); 147 | } 148 | 149 | // lookup rpcUrl, in descending order of priority: 150 | // current -> config (foundry.toml) -> environment variable -> default 151 | function getChainWithUpdatedRpcUrl(string memory chainAlias, Chain memory chain) 152 | private 153 | view 154 | returns (Chain memory) 155 | { 156 | if (bytes(chain.rpcUrl).length == 0) { 157 | try vm.rpcUrl(chainAlias) returns (string memory configRpcUrl) { 158 | chain.rpcUrl = configRpcUrl; 159 | } catch (bytes memory err) { 160 | string memory envName = string(abi.encodePacked(_toUpper(chainAlias), "_RPC_URL")); 161 | if (fallbackToDefaultRpcUrls) { 162 | chain.rpcUrl = vm.envOr(envName, defaultRpcUrls[chainAlias]); 163 | } else { 164 | chain.rpcUrl = vm.envString(envName); 165 | } 166 | // Distinguish 'not found' from 'cannot read' 167 | // The upstream error thrown by forge for failing cheats changed so we check both the old and new versions 168 | bytes memory oldNotFoundError = 169 | abi.encodeWithSignature("CheatCodeError", string(abi.encodePacked("invalid rpc url ", chainAlias))); 170 | bytes memory newNotFoundError = abi.encodeWithSignature( 171 | "CheatcodeError(string)", string(abi.encodePacked("invalid rpc url: ", chainAlias)) 172 | ); 173 | bytes32 errHash = keccak256(err); 174 | if ( 175 | (errHash != keccak256(oldNotFoundError) && errHash != keccak256(newNotFoundError)) 176 | || bytes(chain.rpcUrl).length == 0 177 | ) { 178 | /// @solidity memory-safe-assembly 179 | assembly { 180 | revert(add(32, err), mload(err)) 181 | } 182 | } 183 | } 184 | } 185 | return chain; 186 | } 187 | 188 | function setFallbackToDefaultRpcUrls(bool useDefault) internal { 189 | fallbackToDefaultRpcUrls = useDefault; 190 | } 191 | 192 | function initializeStdChains() private { 193 | if (stdChainsInitialized) return; 194 | 195 | stdChainsInitialized = true; 196 | 197 | // If adding an RPC here, make sure to test the default RPC URL in `test_Rpcs` in `StdChains.t.sol` 198 | setChainWithDefaultRpcUrl("anvil", ChainData("Anvil", 31337, "http://127.0.0.1:8545")); 199 | setChainWithDefaultRpcUrl("mainnet", ChainData("Mainnet", 1, "https://eth.llamarpc.com")); 200 | setChainWithDefaultRpcUrl( 201 | "sepolia", ChainData("Sepolia", 11155111, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001") 202 | ); 203 | setChainWithDefaultRpcUrl("holesky", ChainData("Holesky", 17000, "https://rpc.holesky.ethpandaops.io")); 204 | setChainWithDefaultRpcUrl("hoodi", ChainData("Hoodi", 560048, "https://rpc.hoodi.ethpandaops.io")); 205 | setChainWithDefaultRpcUrl("optimism", ChainData("Optimism", 10, "https://mainnet.optimism.io")); 206 | setChainWithDefaultRpcUrl( 207 | "optimism_sepolia", ChainData("Optimism Sepolia", 11155420, "https://sepolia.optimism.io") 208 | ); 209 | setChainWithDefaultRpcUrl("arbitrum_one", ChainData("Arbitrum One", 42161, "https://arb1.arbitrum.io/rpc")); 210 | setChainWithDefaultRpcUrl( 211 | "arbitrum_one_sepolia", ChainData("Arbitrum One Sepolia", 421614, "https://sepolia-rollup.arbitrum.io/rpc") 212 | ); 213 | setChainWithDefaultRpcUrl("arbitrum_nova", ChainData("Arbitrum Nova", 42170, "https://nova.arbitrum.io/rpc")); 214 | setChainWithDefaultRpcUrl("polygon", ChainData("Polygon", 137, "https://polygon-rpc.com")); 215 | setChainWithDefaultRpcUrl( 216 | "polygon_amoy", ChainData("Polygon Amoy", 80002, "https://rpc-amoy.polygon.technology") 217 | ); 218 | setChainWithDefaultRpcUrl("avalanche", ChainData("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc")); 219 | setChainWithDefaultRpcUrl( 220 | "avalanche_fuji", ChainData("Avalanche Fuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc") 221 | ); 222 | setChainWithDefaultRpcUrl( 223 | "bnb_smart_chain", ChainData("BNB Smart Chain", 56, "https://bsc-dataseed1.binance.org") 224 | ); 225 | setChainWithDefaultRpcUrl( 226 | "bnb_smart_chain_testnet", 227 | ChainData("BNB Smart Chain Testnet", 97, "https://rpc.ankr.com/bsc_testnet_chapel") 228 | ); 229 | setChainWithDefaultRpcUrl("gnosis_chain", ChainData("Gnosis Chain", 100, "https://rpc.gnosischain.com")); 230 | setChainWithDefaultRpcUrl("moonbeam", ChainData("Moonbeam", 1284, "https://rpc.api.moonbeam.network")); 231 | setChainWithDefaultRpcUrl( 232 | "moonriver", ChainData("Moonriver", 1285, "https://rpc.api.moonriver.moonbeam.network") 233 | ); 234 | setChainWithDefaultRpcUrl("moonbase", ChainData("Moonbase", 1287, "https://rpc.testnet.moonbeam.network")); 235 | setChainWithDefaultRpcUrl("base_sepolia", ChainData("Base Sepolia", 84532, "https://sepolia.base.org")); 236 | setChainWithDefaultRpcUrl("base", ChainData("Base", 8453, "https://mainnet.base.org")); 237 | setChainWithDefaultRpcUrl("blast_sepolia", ChainData("Blast Sepolia", 168587773, "https://sepolia.blast.io")); 238 | setChainWithDefaultRpcUrl("blast", ChainData("Blast", 81457, "https://rpc.blast.io")); 239 | setChainWithDefaultRpcUrl("fantom_opera", ChainData("Fantom Opera", 250, "https://rpc.ankr.com/fantom/")); 240 | setChainWithDefaultRpcUrl( 241 | "fantom_opera_testnet", ChainData("Fantom Opera Testnet", 4002, "https://rpc.ankr.com/fantom_testnet/") 242 | ); 243 | setChainWithDefaultRpcUrl("fraxtal", ChainData("Fraxtal", 252, "https://rpc.frax.com")); 244 | setChainWithDefaultRpcUrl("fraxtal_testnet", ChainData("Fraxtal Testnet", 2522, "https://rpc.testnet.frax.com")); 245 | setChainWithDefaultRpcUrl( 246 | "berachain_bartio_testnet", ChainData("Berachain bArtio Testnet", 80084, "https://bartio.rpc.berachain.com") 247 | ); 248 | setChainWithDefaultRpcUrl("flare", ChainData("Flare", 14, "https://flare-api.flare.network/ext/C/rpc")); 249 | setChainWithDefaultRpcUrl( 250 | "flare_coston2", ChainData("Flare Coston2", 114, "https://coston2-api.flare.network/ext/C/rpc") 251 | ); 252 | 253 | setChainWithDefaultRpcUrl("mode", ChainData("Mode", 34443, "https://mode.drpc.org")); 254 | setChainWithDefaultRpcUrl("mode_sepolia", ChainData("Mode Sepolia", 919, "https://sepolia.mode.network")); 255 | 256 | setChainWithDefaultRpcUrl("zora", ChainData("Zora", 7777777, "https://zora.drpc.org")); 257 | setChainWithDefaultRpcUrl( 258 | "zora_sepolia", ChainData("Zora Sepolia", 999999999, "https://sepolia.rpc.zora.energy") 259 | ); 260 | 261 | setChainWithDefaultRpcUrl("race", ChainData("Race", 6805, "https://racemainnet.io")); 262 | setChainWithDefaultRpcUrl("race_sepolia", ChainData("Race Sepolia", 6806, "https://racemainnet.io")); 263 | 264 | setChainWithDefaultRpcUrl("metal", ChainData("Metal", 1750, "https://metall2.drpc.org")); 265 | setChainWithDefaultRpcUrl("metal_sepolia", ChainData("Metal Sepolia", 1740, "https://testnet.rpc.metall2.com")); 266 | 267 | setChainWithDefaultRpcUrl("binary", ChainData("Binary", 624, "https://rpc.zero.thebinaryholdings.com")); 268 | setChainWithDefaultRpcUrl( 269 | "binary_sepolia", ChainData("Binary Sepolia", 625, "https://rpc.zero.thebinaryholdings.com") 270 | ); 271 | 272 | setChainWithDefaultRpcUrl("orderly", ChainData("Orderly", 291, "https://rpc.orderly.network")); 273 | setChainWithDefaultRpcUrl( 274 | "orderly_sepolia", ChainData("Orderly Sepolia", 4460, "https://testnet-rpc.orderly.org") 275 | ); 276 | } 277 | 278 | // set chain info, with priority to chainAlias' rpc url in foundry.toml 279 | function setChainWithDefaultRpcUrl(string memory chainAlias, ChainData memory chain) private { 280 | string memory rpcUrl = chain.rpcUrl; 281 | defaultRpcUrls[chainAlias] = rpcUrl; 282 | chain.rpcUrl = ""; 283 | setChain(chainAlias, chain); 284 | chain.rpcUrl = rpcUrl; // restore argument 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/StdConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | import {IMulticall3} from "./interfaces/IMulticall3.sol"; 5 | import {Vm} from "./Vm.sol"; 6 | 7 | library StdConstants { 8 | /// @dev Cheat code address. 9 | /// Calculated as `address(uint160(uint256(keccak256("hevm cheat code"))))`. 10 | Vm internal constant VM = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); 11 | /// @dev console.sol and console2.sol work by executing a staticcall to this address. 12 | /// Calculated as `address(uint160(uint88(bytes11("console.log"))))`. 13 | address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; 14 | /// @dev Used when deploying with create2. 15 | /// Taken from https://github.com/Arachnid/deterministic-deployment-proxy. 16 | address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 17 | /// @dev The default address for tx.origin and msg.sender. 18 | /// Calculated as `address(uint160(uint256(keccak256("foundry default caller"))))`. 19 | address internal constant DEFAULT_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38; 20 | /// @dev The address of the first contract `CREATE`d by a running test contract. 21 | /// When running tests, each test contract is `CREATE`d by `DEFAULT_SENDER` with nonce 1. 22 | /// Calculated as `VM.computeCreateAddress(VM.computeCreateAddress(DEFAULT_SENDER, 1), 1)`. 23 | address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; 24 | /// @dev Deterministic deployment address of the Multicall3 contract. 25 | /// Taken from https://www.multicall3.com. 26 | IMulticall3 internal constant MULTICALL3_ADDRESS = IMulticall3(0xcA11bde05977b3631167028862bE2a173976CA11); 27 | /// @dev The order of the secp256k1 curve. 28 | uint256 internal constant SECP256K1_ORDER = 29 | 115792089237316195423570985008687907852837564279074904382605163141518161494337; 30 | } 31 | -------------------------------------------------------------------------------- /src/StdError.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test 3 | pragma solidity >=0.6.2 <0.9.0; 4 | 5 | library stdError { 6 | bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); 7 | bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); 8 | bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); 9 | bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); 10 | bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); 11 | bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); 12 | bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); 13 | bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); 14 | bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); 15 | } 16 | -------------------------------------------------------------------------------- /src/StdInvariant.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | abstract contract StdInvariant { 7 | struct FuzzSelector { 8 | address addr; 9 | bytes4[] selectors; 10 | } 11 | 12 | struct FuzzArtifactSelector { 13 | string artifact; 14 | bytes4[] selectors; 15 | } 16 | 17 | struct FuzzInterface { 18 | address addr; 19 | string[] artifacts; 20 | } 21 | 22 | address[] private _excludedContracts; 23 | address[] private _excludedSenders; 24 | address[] private _targetedContracts; 25 | address[] private _targetedSenders; 26 | 27 | string[] private _excludedArtifacts; 28 | string[] private _targetedArtifacts; 29 | 30 | FuzzArtifactSelector[] private _targetedArtifactSelectors; 31 | 32 | FuzzSelector[] private _excludedSelectors; 33 | FuzzSelector[] private _targetedSelectors; 34 | 35 | FuzzInterface[] private _targetedInterfaces; 36 | 37 | // Functions for users: 38 | // These are intended to be called in tests. 39 | 40 | function excludeContract(address newExcludedContract_) internal { 41 | _excludedContracts.push(newExcludedContract_); 42 | } 43 | 44 | function excludeSelector(FuzzSelector memory newExcludedSelector_) internal { 45 | _excludedSelectors.push(newExcludedSelector_); 46 | } 47 | 48 | function excludeSender(address newExcludedSender_) internal { 49 | _excludedSenders.push(newExcludedSender_); 50 | } 51 | 52 | function excludeArtifact(string memory newExcludedArtifact_) internal { 53 | _excludedArtifacts.push(newExcludedArtifact_); 54 | } 55 | 56 | function targetArtifact(string memory newTargetedArtifact_) internal { 57 | _targetedArtifacts.push(newTargetedArtifact_); 58 | } 59 | 60 | function targetArtifactSelector(FuzzArtifactSelector memory newTargetedArtifactSelector_) internal { 61 | _targetedArtifactSelectors.push(newTargetedArtifactSelector_); 62 | } 63 | 64 | function targetContract(address newTargetedContract_) internal { 65 | _targetedContracts.push(newTargetedContract_); 66 | } 67 | 68 | function targetSelector(FuzzSelector memory newTargetedSelector_) internal { 69 | _targetedSelectors.push(newTargetedSelector_); 70 | } 71 | 72 | function targetSender(address newTargetedSender_) internal { 73 | _targetedSenders.push(newTargetedSender_); 74 | } 75 | 76 | function targetInterface(FuzzInterface memory newTargetedInterface_) internal { 77 | _targetedInterfaces.push(newTargetedInterface_); 78 | } 79 | 80 | // Functions for forge: 81 | // These are called by forge to run invariant tests and don't need to be called in tests. 82 | 83 | function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) { 84 | excludedArtifacts_ = _excludedArtifacts; 85 | } 86 | 87 | function excludeContracts() public view returns (address[] memory excludedContracts_) { 88 | excludedContracts_ = _excludedContracts; 89 | } 90 | 91 | function excludeSelectors() public view returns (FuzzSelector[] memory excludedSelectors_) { 92 | excludedSelectors_ = _excludedSelectors; 93 | } 94 | 95 | function excludeSenders() public view returns (address[] memory excludedSenders_) { 96 | excludedSenders_ = _excludedSenders; 97 | } 98 | 99 | function targetArtifacts() public view returns (string[] memory targetedArtifacts_) { 100 | targetedArtifacts_ = _targetedArtifacts; 101 | } 102 | 103 | function targetArtifactSelectors() public view returns (FuzzArtifactSelector[] memory targetedArtifactSelectors_) { 104 | targetedArtifactSelectors_ = _targetedArtifactSelectors; 105 | } 106 | 107 | function targetContracts() public view returns (address[] memory targetedContracts_) { 108 | targetedContracts_ = _targetedContracts; 109 | } 110 | 111 | function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) { 112 | targetedSelectors_ = _targetedSelectors; 113 | } 114 | 115 | function targetSenders() public view returns (address[] memory targetedSenders_) { 116 | targetedSenders_ = _targetedSenders; 117 | } 118 | 119 | function targetInterfaces() public view returns (FuzzInterface[] memory targetedInterfaces_) { 120 | targetedInterfaces_ = _targetedInterfaces; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/StdJson.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {VmSafe} from "./Vm.sol"; 7 | 8 | // Helpers for parsing and writing JSON files 9 | // To parse: 10 | // ``` 11 | // using stdJson for string; 12 | // string memory json = vm.readFile(""); 13 | // json.readUint(""); 14 | // ``` 15 | // To write: 16 | // ``` 17 | // using stdJson for string; 18 | // string memory json = "json"; 19 | // json.serialize("a", uint256(123)); 20 | // string memory semiFinal = json.serialize("b", string("test")); 21 | // string memory finalJson = json.serialize("c", semiFinal); 22 | // finalJson.write(""); 23 | // ``` 24 | 25 | library stdJson { 26 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); 27 | 28 | function keyExists(string memory json, string memory key) internal view returns (bool) { 29 | return vm.keyExistsJson(json, key); 30 | } 31 | 32 | function parseRaw(string memory json, string memory key) internal pure returns (bytes memory) { 33 | return vm.parseJson(json, key); 34 | } 35 | 36 | function readUint(string memory json, string memory key) internal pure returns (uint256) { 37 | return vm.parseJsonUint(json, key); 38 | } 39 | 40 | function readUintArray(string memory json, string memory key) internal pure returns (uint256[] memory) { 41 | return vm.parseJsonUintArray(json, key); 42 | } 43 | 44 | function readInt(string memory json, string memory key) internal pure returns (int256) { 45 | return vm.parseJsonInt(json, key); 46 | } 47 | 48 | function readIntArray(string memory json, string memory key) internal pure returns (int256[] memory) { 49 | return vm.parseJsonIntArray(json, key); 50 | } 51 | 52 | function readBytes32(string memory json, string memory key) internal pure returns (bytes32) { 53 | return vm.parseJsonBytes32(json, key); 54 | } 55 | 56 | function readBytes32Array(string memory json, string memory key) internal pure returns (bytes32[] memory) { 57 | return vm.parseJsonBytes32Array(json, key); 58 | } 59 | 60 | function readString(string memory json, string memory key) internal pure returns (string memory) { 61 | return vm.parseJsonString(json, key); 62 | } 63 | 64 | function readStringArray(string memory json, string memory key) internal pure returns (string[] memory) { 65 | return vm.parseJsonStringArray(json, key); 66 | } 67 | 68 | function readAddress(string memory json, string memory key) internal pure returns (address) { 69 | return vm.parseJsonAddress(json, key); 70 | } 71 | 72 | function readAddressArray(string memory json, string memory key) internal pure returns (address[] memory) { 73 | return vm.parseJsonAddressArray(json, key); 74 | } 75 | 76 | function readBool(string memory json, string memory key) internal pure returns (bool) { 77 | return vm.parseJsonBool(json, key); 78 | } 79 | 80 | function readBoolArray(string memory json, string memory key) internal pure returns (bool[] memory) { 81 | return vm.parseJsonBoolArray(json, key); 82 | } 83 | 84 | function readBytes(string memory json, string memory key) internal pure returns (bytes memory) { 85 | return vm.parseJsonBytes(json, key); 86 | } 87 | 88 | function readBytesArray(string memory json, string memory key) internal pure returns (bytes[] memory) { 89 | return vm.parseJsonBytesArray(json, key); 90 | } 91 | 92 | function readUintOr(string memory json, string memory key, uint256 defaultValue) internal view returns (uint256) { 93 | return keyExists(json, key) ? readUint(json, key) : defaultValue; 94 | } 95 | 96 | function readUintArrayOr(string memory json, string memory key, uint256[] memory defaultValue) 97 | internal 98 | view 99 | returns (uint256[] memory) 100 | { 101 | return keyExists(json, key) ? readUintArray(json, key) : defaultValue; 102 | } 103 | 104 | function readIntOr(string memory json, string memory key, int256 defaultValue) internal view returns (int256) { 105 | return keyExists(json, key) ? readInt(json, key) : defaultValue; 106 | } 107 | 108 | function readIntArrayOr(string memory json, string memory key, int256[] memory defaultValue) 109 | internal 110 | view 111 | returns (int256[] memory) 112 | { 113 | return keyExists(json, key) ? readIntArray(json, key) : defaultValue; 114 | } 115 | 116 | function readBytes32Or(string memory json, string memory key, bytes32 defaultValue) 117 | internal 118 | view 119 | returns (bytes32) 120 | { 121 | return keyExists(json, key) ? readBytes32(json, key) : defaultValue; 122 | } 123 | 124 | function readBytes32ArrayOr(string memory json, string memory key, bytes32[] memory defaultValue) 125 | internal 126 | view 127 | returns (bytes32[] memory) 128 | { 129 | return keyExists(json, key) ? readBytes32Array(json, key) : defaultValue; 130 | } 131 | 132 | function readStringOr(string memory json, string memory key, string memory defaultValue) 133 | internal 134 | view 135 | returns (string memory) 136 | { 137 | return keyExists(json, key) ? readString(json, key) : defaultValue; 138 | } 139 | 140 | function readStringArrayOr(string memory json, string memory key, string[] memory defaultValue) 141 | internal 142 | view 143 | returns (string[] memory) 144 | { 145 | return keyExists(json, key) ? readStringArray(json, key) : defaultValue; 146 | } 147 | 148 | function readAddressOr(string memory json, string memory key, address defaultValue) 149 | internal 150 | view 151 | returns (address) 152 | { 153 | return keyExists(json, key) ? readAddress(json, key) : defaultValue; 154 | } 155 | 156 | function readAddressArrayOr(string memory json, string memory key, address[] memory defaultValue) 157 | internal 158 | view 159 | returns (address[] memory) 160 | { 161 | return keyExists(json, key) ? readAddressArray(json, key) : defaultValue; 162 | } 163 | 164 | function readBoolOr(string memory json, string memory key, bool defaultValue) internal view returns (bool) { 165 | return keyExists(json, key) ? readBool(json, key) : defaultValue; 166 | } 167 | 168 | function readBoolArrayOr(string memory json, string memory key, bool[] memory defaultValue) 169 | internal 170 | view 171 | returns (bool[] memory) 172 | { 173 | return keyExists(json, key) ? readBoolArray(json, key) : defaultValue; 174 | } 175 | 176 | function readBytesOr(string memory json, string memory key, bytes memory defaultValue) 177 | internal 178 | view 179 | returns (bytes memory) 180 | { 181 | return keyExists(json, key) ? readBytes(json, key) : defaultValue; 182 | } 183 | 184 | function readBytesArrayOr(string memory json, string memory key, bytes[] memory defaultValue) 185 | internal 186 | view 187 | returns (bytes[] memory) 188 | { 189 | return keyExists(json, key) ? readBytesArray(json, key) : defaultValue; 190 | } 191 | 192 | function serialize(string memory jsonKey, string memory rootObject) internal returns (string memory) { 193 | return vm.serializeJson(jsonKey, rootObject); 194 | } 195 | 196 | function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) { 197 | return vm.serializeBool(jsonKey, key, value); 198 | } 199 | 200 | function serialize(string memory jsonKey, string memory key, bool[] memory value) 201 | internal 202 | returns (string memory) 203 | { 204 | return vm.serializeBool(jsonKey, key, value); 205 | } 206 | 207 | function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) { 208 | return vm.serializeUint(jsonKey, key, value); 209 | } 210 | 211 | function serialize(string memory jsonKey, string memory key, uint256[] memory value) 212 | internal 213 | returns (string memory) 214 | { 215 | return vm.serializeUint(jsonKey, key, value); 216 | } 217 | 218 | function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) { 219 | return vm.serializeInt(jsonKey, key, value); 220 | } 221 | 222 | function serialize(string memory jsonKey, string memory key, int256[] memory value) 223 | internal 224 | returns (string memory) 225 | { 226 | return vm.serializeInt(jsonKey, key, value); 227 | } 228 | 229 | function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) { 230 | return vm.serializeAddress(jsonKey, key, value); 231 | } 232 | 233 | function serialize(string memory jsonKey, string memory key, address[] memory value) 234 | internal 235 | returns (string memory) 236 | { 237 | return vm.serializeAddress(jsonKey, key, value); 238 | } 239 | 240 | function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) { 241 | return vm.serializeBytes32(jsonKey, key, value); 242 | } 243 | 244 | function serialize(string memory jsonKey, string memory key, bytes32[] memory value) 245 | internal 246 | returns (string memory) 247 | { 248 | return vm.serializeBytes32(jsonKey, key, value); 249 | } 250 | 251 | function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) { 252 | return vm.serializeBytes(jsonKey, key, value); 253 | } 254 | 255 | function serialize(string memory jsonKey, string memory key, bytes[] memory value) 256 | internal 257 | returns (string memory) 258 | { 259 | return vm.serializeBytes(jsonKey, key, value); 260 | } 261 | 262 | function serialize(string memory jsonKey, string memory key, string memory value) 263 | internal 264 | returns (string memory) 265 | { 266 | return vm.serializeString(jsonKey, key, value); 267 | } 268 | 269 | function serialize(string memory jsonKey, string memory key, string[] memory value) 270 | internal 271 | returns (string memory) 272 | { 273 | return vm.serializeString(jsonKey, key, value); 274 | } 275 | 276 | function write(string memory jsonKey, string memory path) internal { 277 | vm.writeJson(jsonKey, path); 278 | } 279 | 280 | function write(string memory jsonKey, string memory path, string memory valueKey) internal { 281 | vm.writeJson(jsonKey, path, valueKey); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/StdMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | library stdMath { 5 | int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; 6 | 7 | function abs(int256 a) internal pure returns (uint256) { 8 | // Required or it will fail when `a = type(int256).min` 9 | if (a == INT256_MIN) { 10 | return 57896044618658097711785492504343953926634992332820282019728792003956564819968; 11 | } 12 | 13 | return uint256(a > 0 ? a : -a); 14 | } 15 | 16 | function delta(uint256 a, uint256 b) internal pure returns (uint256) { 17 | return a > b ? a - b : b - a; 18 | } 19 | 20 | function delta(int256 a, int256 b) internal pure returns (uint256) { 21 | // a and b are of the same sign 22 | // this works thanks to two's complement, the left-most bit is the sign bit 23 | if ((a ^ b) > -1) { 24 | return delta(abs(a), abs(b)); 25 | } 26 | 27 | // a and b are of opposite signs 28 | return abs(a) + abs(b); 29 | } 30 | 31 | function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { 32 | uint256 absDelta = delta(a, b); 33 | 34 | return absDelta * 1e18 / b; 35 | } 36 | 37 | function percentDelta(int256 a, int256 b) internal pure returns (uint256) { 38 | uint256 absDelta = delta(a, b); 39 | uint256 absB = abs(b); 40 | 41 | return absDelta * 1e18 / absB; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/StdStyle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.9.0; 3 | 4 | import {VmSafe} from "./Vm.sol"; 5 | 6 | library StdStyle { 7 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); 8 | 9 | string constant RED = "\u001b[91m"; 10 | string constant GREEN = "\u001b[92m"; 11 | string constant YELLOW = "\u001b[93m"; 12 | string constant BLUE = "\u001b[94m"; 13 | string constant MAGENTA = "\u001b[95m"; 14 | string constant CYAN = "\u001b[96m"; 15 | string constant BOLD = "\u001b[1m"; 16 | string constant DIM = "\u001b[2m"; 17 | string constant ITALIC = "\u001b[3m"; 18 | string constant UNDERLINE = "\u001b[4m"; 19 | string constant INVERSE = "\u001b[7m"; 20 | string constant RESET = "\u001b[0m"; 21 | 22 | function styleConcat(string memory style, string memory self) private pure returns (string memory) { 23 | return string(abi.encodePacked(style, self, RESET)); 24 | } 25 | 26 | function red(string memory self) internal pure returns (string memory) { 27 | return styleConcat(RED, self); 28 | } 29 | 30 | function red(uint256 self) internal pure returns (string memory) { 31 | return red(vm.toString(self)); 32 | } 33 | 34 | function red(int256 self) internal pure returns (string memory) { 35 | return red(vm.toString(self)); 36 | } 37 | 38 | function red(address self) internal pure returns (string memory) { 39 | return red(vm.toString(self)); 40 | } 41 | 42 | function red(bool self) internal pure returns (string memory) { 43 | return red(vm.toString(self)); 44 | } 45 | 46 | function redBytes(bytes memory self) internal pure returns (string memory) { 47 | return red(vm.toString(self)); 48 | } 49 | 50 | function redBytes32(bytes32 self) internal pure returns (string memory) { 51 | return red(vm.toString(self)); 52 | } 53 | 54 | function green(string memory self) internal pure returns (string memory) { 55 | return styleConcat(GREEN, self); 56 | } 57 | 58 | function green(uint256 self) internal pure returns (string memory) { 59 | return green(vm.toString(self)); 60 | } 61 | 62 | function green(int256 self) internal pure returns (string memory) { 63 | return green(vm.toString(self)); 64 | } 65 | 66 | function green(address self) internal pure returns (string memory) { 67 | return green(vm.toString(self)); 68 | } 69 | 70 | function green(bool self) internal pure returns (string memory) { 71 | return green(vm.toString(self)); 72 | } 73 | 74 | function greenBytes(bytes memory self) internal pure returns (string memory) { 75 | return green(vm.toString(self)); 76 | } 77 | 78 | function greenBytes32(bytes32 self) internal pure returns (string memory) { 79 | return green(vm.toString(self)); 80 | } 81 | 82 | function yellow(string memory self) internal pure returns (string memory) { 83 | return styleConcat(YELLOW, self); 84 | } 85 | 86 | function yellow(uint256 self) internal pure returns (string memory) { 87 | return yellow(vm.toString(self)); 88 | } 89 | 90 | function yellow(int256 self) internal pure returns (string memory) { 91 | return yellow(vm.toString(self)); 92 | } 93 | 94 | function yellow(address self) internal pure returns (string memory) { 95 | return yellow(vm.toString(self)); 96 | } 97 | 98 | function yellow(bool self) internal pure returns (string memory) { 99 | return yellow(vm.toString(self)); 100 | } 101 | 102 | function yellowBytes(bytes memory self) internal pure returns (string memory) { 103 | return yellow(vm.toString(self)); 104 | } 105 | 106 | function yellowBytes32(bytes32 self) internal pure returns (string memory) { 107 | return yellow(vm.toString(self)); 108 | } 109 | 110 | function blue(string memory self) internal pure returns (string memory) { 111 | return styleConcat(BLUE, self); 112 | } 113 | 114 | function blue(uint256 self) internal pure returns (string memory) { 115 | return blue(vm.toString(self)); 116 | } 117 | 118 | function blue(int256 self) internal pure returns (string memory) { 119 | return blue(vm.toString(self)); 120 | } 121 | 122 | function blue(address self) internal pure returns (string memory) { 123 | return blue(vm.toString(self)); 124 | } 125 | 126 | function blue(bool self) internal pure returns (string memory) { 127 | return blue(vm.toString(self)); 128 | } 129 | 130 | function blueBytes(bytes memory self) internal pure returns (string memory) { 131 | return blue(vm.toString(self)); 132 | } 133 | 134 | function blueBytes32(bytes32 self) internal pure returns (string memory) { 135 | return blue(vm.toString(self)); 136 | } 137 | 138 | function magenta(string memory self) internal pure returns (string memory) { 139 | return styleConcat(MAGENTA, self); 140 | } 141 | 142 | function magenta(uint256 self) internal pure returns (string memory) { 143 | return magenta(vm.toString(self)); 144 | } 145 | 146 | function magenta(int256 self) internal pure returns (string memory) { 147 | return magenta(vm.toString(self)); 148 | } 149 | 150 | function magenta(address self) internal pure returns (string memory) { 151 | return magenta(vm.toString(self)); 152 | } 153 | 154 | function magenta(bool self) internal pure returns (string memory) { 155 | return magenta(vm.toString(self)); 156 | } 157 | 158 | function magentaBytes(bytes memory self) internal pure returns (string memory) { 159 | return magenta(vm.toString(self)); 160 | } 161 | 162 | function magentaBytes32(bytes32 self) internal pure returns (string memory) { 163 | return magenta(vm.toString(self)); 164 | } 165 | 166 | function cyan(string memory self) internal pure returns (string memory) { 167 | return styleConcat(CYAN, self); 168 | } 169 | 170 | function cyan(uint256 self) internal pure returns (string memory) { 171 | return cyan(vm.toString(self)); 172 | } 173 | 174 | function cyan(int256 self) internal pure returns (string memory) { 175 | return cyan(vm.toString(self)); 176 | } 177 | 178 | function cyan(address self) internal pure returns (string memory) { 179 | return cyan(vm.toString(self)); 180 | } 181 | 182 | function cyan(bool self) internal pure returns (string memory) { 183 | return cyan(vm.toString(self)); 184 | } 185 | 186 | function cyanBytes(bytes memory self) internal pure returns (string memory) { 187 | return cyan(vm.toString(self)); 188 | } 189 | 190 | function cyanBytes32(bytes32 self) internal pure returns (string memory) { 191 | return cyan(vm.toString(self)); 192 | } 193 | 194 | function bold(string memory self) internal pure returns (string memory) { 195 | return styleConcat(BOLD, self); 196 | } 197 | 198 | function bold(uint256 self) internal pure returns (string memory) { 199 | return bold(vm.toString(self)); 200 | } 201 | 202 | function bold(int256 self) internal pure returns (string memory) { 203 | return bold(vm.toString(self)); 204 | } 205 | 206 | function bold(address self) internal pure returns (string memory) { 207 | return bold(vm.toString(self)); 208 | } 209 | 210 | function bold(bool self) internal pure returns (string memory) { 211 | return bold(vm.toString(self)); 212 | } 213 | 214 | function boldBytes(bytes memory self) internal pure returns (string memory) { 215 | return bold(vm.toString(self)); 216 | } 217 | 218 | function boldBytes32(bytes32 self) internal pure returns (string memory) { 219 | return bold(vm.toString(self)); 220 | } 221 | 222 | function dim(string memory self) internal pure returns (string memory) { 223 | return styleConcat(DIM, self); 224 | } 225 | 226 | function dim(uint256 self) internal pure returns (string memory) { 227 | return dim(vm.toString(self)); 228 | } 229 | 230 | function dim(int256 self) internal pure returns (string memory) { 231 | return dim(vm.toString(self)); 232 | } 233 | 234 | function dim(address self) internal pure returns (string memory) { 235 | return dim(vm.toString(self)); 236 | } 237 | 238 | function dim(bool self) internal pure returns (string memory) { 239 | return dim(vm.toString(self)); 240 | } 241 | 242 | function dimBytes(bytes memory self) internal pure returns (string memory) { 243 | return dim(vm.toString(self)); 244 | } 245 | 246 | function dimBytes32(bytes32 self) internal pure returns (string memory) { 247 | return dim(vm.toString(self)); 248 | } 249 | 250 | function italic(string memory self) internal pure returns (string memory) { 251 | return styleConcat(ITALIC, self); 252 | } 253 | 254 | function italic(uint256 self) internal pure returns (string memory) { 255 | return italic(vm.toString(self)); 256 | } 257 | 258 | function italic(int256 self) internal pure returns (string memory) { 259 | return italic(vm.toString(self)); 260 | } 261 | 262 | function italic(address self) internal pure returns (string memory) { 263 | return italic(vm.toString(self)); 264 | } 265 | 266 | function italic(bool self) internal pure returns (string memory) { 267 | return italic(vm.toString(self)); 268 | } 269 | 270 | function italicBytes(bytes memory self) internal pure returns (string memory) { 271 | return italic(vm.toString(self)); 272 | } 273 | 274 | function italicBytes32(bytes32 self) internal pure returns (string memory) { 275 | return italic(vm.toString(self)); 276 | } 277 | 278 | function underline(string memory self) internal pure returns (string memory) { 279 | return styleConcat(UNDERLINE, self); 280 | } 281 | 282 | function underline(uint256 self) internal pure returns (string memory) { 283 | return underline(vm.toString(self)); 284 | } 285 | 286 | function underline(int256 self) internal pure returns (string memory) { 287 | return underline(vm.toString(self)); 288 | } 289 | 290 | function underline(address self) internal pure returns (string memory) { 291 | return underline(vm.toString(self)); 292 | } 293 | 294 | function underline(bool self) internal pure returns (string memory) { 295 | return underline(vm.toString(self)); 296 | } 297 | 298 | function underlineBytes(bytes memory self) internal pure returns (string memory) { 299 | return underline(vm.toString(self)); 300 | } 301 | 302 | function underlineBytes32(bytes32 self) internal pure returns (string memory) { 303 | return underline(vm.toString(self)); 304 | } 305 | 306 | function inverse(string memory self) internal pure returns (string memory) { 307 | return styleConcat(INVERSE, self); 308 | } 309 | 310 | function inverse(uint256 self) internal pure returns (string memory) { 311 | return inverse(vm.toString(self)); 312 | } 313 | 314 | function inverse(int256 self) internal pure returns (string memory) { 315 | return inverse(vm.toString(self)); 316 | } 317 | 318 | function inverse(address self) internal pure returns (string memory) { 319 | return inverse(vm.toString(self)); 320 | } 321 | 322 | function inverse(bool self) internal pure returns (string memory) { 323 | return inverse(vm.toString(self)); 324 | } 325 | 326 | function inverseBytes(bytes memory self) internal pure returns (string memory) { 327 | return inverse(vm.toString(self)); 328 | } 329 | 330 | function inverseBytes32(bytes32 self) internal pure returns (string memory) { 331 | return inverse(vm.toString(self)); 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/StdToml.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {VmSafe} from "./Vm.sol"; 7 | 8 | // Helpers for parsing and writing TOML files 9 | // To parse: 10 | // ``` 11 | // using stdToml for string; 12 | // string memory toml = vm.readFile(""); 13 | // toml.readUint(""); 14 | // ``` 15 | // To write: 16 | // ``` 17 | // using stdToml for string; 18 | // string memory json = "json"; 19 | // json.serialize("a", uint256(123)); 20 | // string memory semiFinal = json.serialize("b", string("test")); 21 | // string memory finalJson = json.serialize("c", semiFinal); 22 | // finalJson.write(""); 23 | // ``` 24 | 25 | library stdToml { 26 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); 27 | 28 | function keyExists(string memory toml, string memory key) internal view returns (bool) { 29 | return vm.keyExistsToml(toml, key); 30 | } 31 | 32 | function parseRaw(string memory toml, string memory key) internal pure returns (bytes memory) { 33 | return vm.parseToml(toml, key); 34 | } 35 | 36 | function readUint(string memory toml, string memory key) internal pure returns (uint256) { 37 | return vm.parseTomlUint(toml, key); 38 | } 39 | 40 | function readUintArray(string memory toml, string memory key) internal pure returns (uint256[] memory) { 41 | return vm.parseTomlUintArray(toml, key); 42 | } 43 | 44 | function readInt(string memory toml, string memory key) internal pure returns (int256) { 45 | return vm.parseTomlInt(toml, key); 46 | } 47 | 48 | function readIntArray(string memory toml, string memory key) internal pure returns (int256[] memory) { 49 | return vm.parseTomlIntArray(toml, key); 50 | } 51 | 52 | function readBytes32(string memory toml, string memory key) internal pure returns (bytes32) { 53 | return vm.parseTomlBytes32(toml, key); 54 | } 55 | 56 | function readBytes32Array(string memory toml, string memory key) internal pure returns (bytes32[] memory) { 57 | return vm.parseTomlBytes32Array(toml, key); 58 | } 59 | 60 | function readString(string memory toml, string memory key) internal pure returns (string memory) { 61 | return vm.parseTomlString(toml, key); 62 | } 63 | 64 | function readStringArray(string memory toml, string memory key) internal pure returns (string[] memory) { 65 | return vm.parseTomlStringArray(toml, key); 66 | } 67 | 68 | function readAddress(string memory toml, string memory key) internal pure returns (address) { 69 | return vm.parseTomlAddress(toml, key); 70 | } 71 | 72 | function readAddressArray(string memory toml, string memory key) internal pure returns (address[] memory) { 73 | return vm.parseTomlAddressArray(toml, key); 74 | } 75 | 76 | function readBool(string memory toml, string memory key) internal pure returns (bool) { 77 | return vm.parseTomlBool(toml, key); 78 | } 79 | 80 | function readBoolArray(string memory toml, string memory key) internal pure returns (bool[] memory) { 81 | return vm.parseTomlBoolArray(toml, key); 82 | } 83 | 84 | function readBytes(string memory toml, string memory key) internal pure returns (bytes memory) { 85 | return vm.parseTomlBytes(toml, key); 86 | } 87 | 88 | function readBytesArray(string memory toml, string memory key) internal pure returns (bytes[] memory) { 89 | return vm.parseTomlBytesArray(toml, key); 90 | } 91 | 92 | function readUintOr(string memory toml, string memory key, uint256 defaultValue) internal view returns (uint256) { 93 | return keyExists(toml, key) ? readUint(toml, key) : defaultValue; 94 | } 95 | 96 | function readUintArrayOr(string memory toml, string memory key, uint256[] memory defaultValue) 97 | internal 98 | view 99 | returns (uint256[] memory) 100 | { 101 | return keyExists(toml, key) ? readUintArray(toml, key) : defaultValue; 102 | } 103 | 104 | function readIntOr(string memory toml, string memory key, int256 defaultValue) internal view returns (int256) { 105 | return keyExists(toml, key) ? readInt(toml, key) : defaultValue; 106 | } 107 | 108 | function readIntArrayOr(string memory toml, string memory key, int256[] memory defaultValue) 109 | internal 110 | view 111 | returns (int256[] memory) 112 | { 113 | return keyExists(toml, key) ? readIntArray(toml, key) : defaultValue; 114 | } 115 | 116 | function readBytes32Or(string memory toml, string memory key, bytes32 defaultValue) 117 | internal 118 | view 119 | returns (bytes32) 120 | { 121 | return keyExists(toml, key) ? readBytes32(toml, key) : defaultValue; 122 | } 123 | 124 | function readBytes32ArrayOr(string memory toml, string memory key, bytes32[] memory defaultValue) 125 | internal 126 | view 127 | returns (bytes32[] memory) 128 | { 129 | return keyExists(toml, key) ? readBytes32Array(toml, key) : defaultValue; 130 | } 131 | 132 | function readStringOr(string memory toml, string memory key, string memory defaultValue) 133 | internal 134 | view 135 | returns (string memory) 136 | { 137 | return keyExists(toml, key) ? readString(toml, key) : defaultValue; 138 | } 139 | 140 | function readStringArrayOr(string memory toml, string memory key, string[] memory defaultValue) 141 | internal 142 | view 143 | returns (string[] memory) 144 | { 145 | return keyExists(toml, key) ? readStringArray(toml, key) : defaultValue; 146 | } 147 | 148 | function readAddressOr(string memory toml, string memory key, address defaultValue) 149 | internal 150 | view 151 | returns (address) 152 | { 153 | return keyExists(toml, key) ? readAddress(toml, key) : defaultValue; 154 | } 155 | 156 | function readAddressArrayOr(string memory toml, string memory key, address[] memory defaultValue) 157 | internal 158 | view 159 | returns (address[] memory) 160 | { 161 | return keyExists(toml, key) ? readAddressArray(toml, key) : defaultValue; 162 | } 163 | 164 | function readBoolOr(string memory toml, string memory key, bool defaultValue) internal view returns (bool) { 165 | return keyExists(toml, key) ? readBool(toml, key) : defaultValue; 166 | } 167 | 168 | function readBoolArrayOr(string memory toml, string memory key, bool[] memory defaultValue) 169 | internal 170 | view 171 | returns (bool[] memory) 172 | { 173 | return keyExists(toml, key) ? readBoolArray(toml, key) : defaultValue; 174 | } 175 | 176 | function readBytesOr(string memory toml, string memory key, bytes memory defaultValue) 177 | internal 178 | view 179 | returns (bytes memory) 180 | { 181 | return keyExists(toml, key) ? readBytes(toml, key) : defaultValue; 182 | } 183 | 184 | function readBytesArrayOr(string memory toml, string memory key, bytes[] memory defaultValue) 185 | internal 186 | view 187 | returns (bytes[] memory) 188 | { 189 | return keyExists(toml, key) ? readBytesArray(toml, key) : defaultValue; 190 | } 191 | 192 | function serialize(string memory jsonKey, string memory rootObject) internal returns (string memory) { 193 | return vm.serializeJson(jsonKey, rootObject); 194 | } 195 | 196 | function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) { 197 | return vm.serializeBool(jsonKey, key, value); 198 | } 199 | 200 | function serialize(string memory jsonKey, string memory key, bool[] memory value) 201 | internal 202 | returns (string memory) 203 | { 204 | return vm.serializeBool(jsonKey, key, value); 205 | } 206 | 207 | function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) { 208 | return vm.serializeUint(jsonKey, key, value); 209 | } 210 | 211 | function serialize(string memory jsonKey, string memory key, uint256[] memory value) 212 | internal 213 | returns (string memory) 214 | { 215 | return vm.serializeUint(jsonKey, key, value); 216 | } 217 | 218 | function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) { 219 | return vm.serializeInt(jsonKey, key, value); 220 | } 221 | 222 | function serialize(string memory jsonKey, string memory key, int256[] memory value) 223 | internal 224 | returns (string memory) 225 | { 226 | return vm.serializeInt(jsonKey, key, value); 227 | } 228 | 229 | function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) { 230 | return vm.serializeAddress(jsonKey, key, value); 231 | } 232 | 233 | function serialize(string memory jsonKey, string memory key, address[] memory value) 234 | internal 235 | returns (string memory) 236 | { 237 | return vm.serializeAddress(jsonKey, key, value); 238 | } 239 | 240 | function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) { 241 | return vm.serializeBytes32(jsonKey, key, value); 242 | } 243 | 244 | function serialize(string memory jsonKey, string memory key, bytes32[] memory value) 245 | internal 246 | returns (string memory) 247 | { 248 | return vm.serializeBytes32(jsonKey, key, value); 249 | } 250 | 251 | function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) { 252 | return vm.serializeBytes(jsonKey, key, value); 253 | } 254 | 255 | function serialize(string memory jsonKey, string memory key, bytes[] memory value) 256 | internal 257 | returns (string memory) 258 | { 259 | return vm.serializeBytes(jsonKey, key, value); 260 | } 261 | 262 | function serialize(string memory jsonKey, string memory key, string memory value) 263 | internal 264 | returns (string memory) 265 | { 266 | return vm.serializeString(jsonKey, key, value); 267 | } 268 | 269 | function serialize(string memory jsonKey, string memory key, string[] memory value) 270 | internal 271 | returns (string memory) 272 | { 273 | return vm.serializeString(jsonKey, key, value); 274 | } 275 | 276 | function write(string memory jsonKey, string memory path) internal { 277 | vm.writeToml(jsonKey, path); 278 | } 279 | 280 | function write(string memory jsonKey, string memory path, string memory valueKey) internal { 281 | vm.writeToml(jsonKey, path, valueKey); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/StdUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {IMulticall3} from "./interfaces/IMulticall3.sol"; 7 | import {VmSafe} from "./Vm.sol"; 8 | 9 | abstract contract StdUtils { 10 | /*////////////////////////////////////////////////////////////////////////// 11 | CONSTANTS 12 | //////////////////////////////////////////////////////////////////////////*/ 13 | 14 | IMulticall3 private constant multicall = IMulticall3(0xcA11bde05977b3631167028862bE2a173976CA11); 15 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); 16 | address private constant CONSOLE2_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67; 17 | uint256 private constant INT256_MIN_ABS = 18 | 57896044618658097711785492504343953926634992332820282019728792003956564819968; 19 | uint256 private constant SECP256K1_ORDER = 20 | 115792089237316195423570985008687907852837564279074904382605163141518161494337; 21 | uint256 private constant UINT256_MAX = 22 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 23 | 24 | // Used by default when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy. 25 | address private constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 26 | 27 | /*////////////////////////////////////////////////////////////////////////// 28 | INTERNAL FUNCTIONS 29 | //////////////////////////////////////////////////////////////////////////*/ 30 | 31 | function _bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) { 32 | require(min <= max, "StdUtils bound(uint256,uint256,uint256): Max is less than min."); 33 | // If x is between min and max, return x directly. This is to ensure that dictionary values 34 | // do not get shifted if the min is nonzero. More info: https://github.com/foundry-rs/forge-std/issues/188 35 | if (x >= min && x <= max) return x; 36 | 37 | uint256 size = max - min + 1; 38 | 39 | // If the value is 0, 1, 2, 3, wrap that to min, min+1, min+2, min+3. Similarly for the UINT256_MAX side. 40 | // This helps ensure coverage of the min/max values. 41 | if (x <= 3 && size > x) return min + x; 42 | if (x >= UINT256_MAX - 3 && size > UINT256_MAX - x) return max - (UINT256_MAX - x); 43 | 44 | // Otherwise, wrap x into the range [min, max], i.e. the range is inclusive. 45 | if (x > max) { 46 | uint256 diff = x - max; 47 | uint256 rem = diff % size; 48 | if (rem == 0) return max; 49 | result = min + rem - 1; 50 | } else if (x < min) { 51 | uint256 diff = min - x; 52 | uint256 rem = diff % size; 53 | if (rem == 0) return min; 54 | result = max - rem + 1; 55 | } 56 | } 57 | 58 | function bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) { 59 | result = _bound(x, min, max); 60 | console2_log_StdUtils("Bound result", result); 61 | } 62 | 63 | function _bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) { 64 | require(min <= max, "StdUtils bound(int256,int256,int256): Max is less than min."); 65 | 66 | // Shifting all int256 values to uint256 to use _bound function. The range of two types are: 67 | // int256 : -(2**255) ~ (2**255 - 1) 68 | // uint256: 0 ~ (2**256 - 1) 69 | // So, add 2**255, INT256_MIN_ABS to the integer values. 70 | // 71 | // If the given integer value is -2**255, we cannot use `-uint256(-x)` because of the overflow. 72 | // So, use `~uint256(x) + 1` instead. 73 | uint256 _x = x < 0 ? (INT256_MIN_ABS - ~uint256(x) - 1) : (uint256(x) + INT256_MIN_ABS); 74 | uint256 _min = min < 0 ? (INT256_MIN_ABS - ~uint256(min) - 1) : (uint256(min) + INT256_MIN_ABS); 75 | uint256 _max = max < 0 ? (INT256_MIN_ABS - ~uint256(max) - 1) : (uint256(max) + INT256_MIN_ABS); 76 | 77 | uint256 y = _bound(_x, _min, _max); 78 | 79 | // To move it back to int256 value, subtract INT256_MIN_ABS at here. 80 | result = y < INT256_MIN_ABS ? int256(~(INT256_MIN_ABS - y) + 1) : int256(y - INT256_MIN_ABS); 81 | } 82 | 83 | function bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) { 84 | result = _bound(x, min, max); 85 | console2_log_StdUtils("Bound result", vm.toString(result)); 86 | } 87 | 88 | function boundPrivateKey(uint256 privateKey) internal pure virtual returns (uint256 result) { 89 | result = _bound(privateKey, 1, SECP256K1_ORDER - 1); 90 | } 91 | 92 | function bytesToUint(bytes memory b) internal pure virtual returns (uint256) { 93 | require(b.length <= 32, "StdUtils bytesToUint(bytes): Bytes length exceeds 32."); 94 | return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256)); 95 | } 96 | 97 | /// @dev Compute the address a contract will be deployed at for a given deployer address and nonce 98 | /// @notice adapted from Solmate implementation (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibRLP.sol) 99 | function computeCreateAddress(address deployer, uint256 nonce) internal pure virtual returns (address) { 100 | console2_log_StdUtils("computeCreateAddress is deprecated. Please use vm.computeCreateAddress instead."); 101 | return vm.computeCreateAddress(deployer, nonce); 102 | } 103 | 104 | function computeCreate2Address(bytes32 salt, bytes32 initcodeHash, address deployer) 105 | internal 106 | pure 107 | virtual 108 | returns (address) 109 | { 110 | console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead."); 111 | return vm.computeCreate2Address(salt, initcodeHash, deployer); 112 | } 113 | 114 | /// @dev returns the address of a contract created with CREATE2 using the default CREATE2 deployer 115 | function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) internal pure returns (address) { 116 | console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead."); 117 | return vm.computeCreate2Address(salt, initCodeHash); 118 | } 119 | 120 | /// @dev returns the hash of the init code (creation code + no args) used in CREATE2 with no constructor arguments 121 | /// @param creationCode the creation code of a contract C, as returned by type(C).creationCode 122 | function hashInitCode(bytes memory creationCode) internal pure returns (bytes32) { 123 | return hashInitCode(creationCode, ""); 124 | } 125 | 126 | /// @dev returns the hash of the init code (creation code + ABI-encoded args) used in CREATE2 127 | /// @param creationCode the creation code of a contract C, as returned by type(C).creationCode 128 | /// @param args the ABI-encoded arguments to the constructor of C 129 | function hashInitCode(bytes memory creationCode, bytes memory args) internal pure returns (bytes32) { 130 | return keccak256(abi.encodePacked(creationCode, args)); 131 | } 132 | 133 | // Performs a single call with Multicall3 to query the ERC-20 token balances of the given addresses. 134 | function getTokenBalances(address token, address[] memory addresses) 135 | internal 136 | virtual 137 | returns (uint256[] memory balances) 138 | { 139 | uint256 tokenCodeSize; 140 | assembly { 141 | tokenCodeSize := extcodesize(token) 142 | } 143 | require(tokenCodeSize > 0, "StdUtils getTokenBalances(address,address[]): Token address is not a contract."); 144 | 145 | // ABI encode the aggregate call to Multicall3. 146 | uint256 length = addresses.length; 147 | IMulticall3.Call[] memory calls = new IMulticall3.Call[](length); 148 | for (uint256 i = 0; i < length; ++i) { 149 | // 0x70a08231 = bytes4("balanceOf(address)")) 150 | calls[i] = IMulticall3.Call({target: token, callData: abi.encodeWithSelector(0x70a08231, (addresses[i]))}); 151 | } 152 | 153 | // Make the aggregate call. 154 | (, bytes[] memory returnData) = multicall.aggregate(calls); 155 | 156 | // ABI decode the return data and return the balances. 157 | balances = new uint256[](length); 158 | for (uint256 i = 0; i < length; ++i) { 159 | balances[i] = abi.decode(returnData[i], (uint256)); 160 | } 161 | } 162 | 163 | /*////////////////////////////////////////////////////////////////////////// 164 | PRIVATE FUNCTIONS 165 | //////////////////////////////////////////////////////////////////////////*/ 166 | 167 | function addressFromLast20Bytes(bytes32 bytesValue) private pure returns (address) { 168 | return address(uint160(uint256(bytesValue))); 169 | } 170 | 171 | // This section is used to prevent the compilation of console, which shortens the compilation time when console is 172 | // not used elsewhere. We also trick the compiler into letting us make the console log methods as `pure` to avoid 173 | // any breaking changes to function signatures. 174 | function _castLogPayloadViewToPure(function(bytes memory) internal view fnIn) 175 | internal 176 | pure 177 | returns (function(bytes memory) internal pure fnOut) 178 | { 179 | assembly { 180 | fnOut := fnIn 181 | } 182 | } 183 | 184 | function _sendLogPayload(bytes memory payload) internal pure { 185 | _castLogPayloadViewToPure(_sendLogPayloadView)(payload); 186 | } 187 | 188 | function _sendLogPayloadView(bytes memory payload) private view { 189 | uint256 payloadLength = payload.length; 190 | address consoleAddress = CONSOLE2_ADDRESS; 191 | /// @solidity memory-safe-assembly 192 | assembly { 193 | let payloadStart := add(payload, 32) 194 | let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) 195 | } 196 | } 197 | 198 | function console2_log_StdUtils(string memory p0) private pure { 199 | _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); 200 | } 201 | 202 | function console2_log_StdUtils(string memory p0, uint256 p1) private pure { 203 | _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); 204 | } 205 | 206 | function console2_log_StdUtils(string memory p0, string memory p1) private pure { 207 | _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/Test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | // 💬 ABOUT 7 | // Forge Std's default Test. 8 | 9 | // 🧩 MODULES 10 | import {console} from "./console.sol"; 11 | import {console2} from "./console2.sol"; 12 | import {safeconsole} from "./safeconsole.sol"; 13 | import {StdAssertions} from "./StdAssertions.sol"; 14 | import {StdChains} from "./StdChains.sol"; 15 | import {StdCheats} from "./StdCheats.sol"; 16 | import {StdConstants} from "./StdConstants.sol"; 17 | import {stdError} from "./StdError.sol"; 18 | import {StdInvariant} from "./StdInvariant.sol"; 19 | import {stdJson} from "./StdJson.sol"; 20 | import {stdMath} from "./StdMath.sol"; 21 | import {StdStorage, stdStorage} from "./StdStorage.sol"; 22 | import {StdStyle} from "./StdStyle.sol"; 23 | import {stdToml} from "./StdToml.sol"; 24 | import {StdUtils} from "./StdUtils.sol"; 25 | import {Vm} from "./Vm.sol"; 26 | 27 | // 📦 BOILERPLATE 28 | import {TestBase} from "./Base.sol"; 29 | 30 | // ⭐️ TEST 31 | abstract contract Test is TestBase, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils { 32 | // Note: IS_TEST() must return true. 33 | bool public IS_TEST = true; 34 | } 35 | -------------------------------------------------------------------------------- /src/console2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.9.0; 3 | 4 | import {console as console2} from "./console.sol"; 5 | -------------------------------------------------------------------------------- /src/interfaces/IERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import {IERC165} from "./IERC165.sol"; 5 | 6 | /// @title ERC-1155 Multi Token Standard 7 | /// @dev See https://eips.ethereum.org/EIPS/eip-1155 8 | /// Note: The ERC-165 identifier for this interface is 0xd9b67a26. 9 | interface IERC1155 is IERC165 { 10 | /// @dev 11 | /// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). 12 | /// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). 13 | /// - The `_from` argument MUST be the address of the holder whose balance is decreased. 14 | /// - The `_to` argument MUST be the address of the recipient whose balance is increased. 15 | /// - The `_id` argument MUST be the token type being transferred. 16 | /// - The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. 17 | /// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). 18 | /// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). 19 | event TransferSingle( 20 | address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value 21 | ); 22 | 23 | /// @dev 24 | /// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). 25 | /// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). 26 | /// - The `_from` argument MUST be the address of the holder whose balance is decreased. 27 | /// - The `_to` argument MUST be the address of the recipient whose balance is increased. 28 | /// - The `_ids` argument MUST be the list of tokens being transferred. 29 | /// - The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by. 30 | /// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). 31 | /// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). 32 | event TransferBatch( 33 | address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values 34 | ); 35 | 36 | /// @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled). 37 | event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); 38 | 39 | /// @dev MUST emit when the URI is updated for a token ID. URIs are defined in RFC 3986. 40 | /// The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema". 41 | event URI(string _value, uint256 indexed _id); 42 | 43 | /// @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call). 44 | /// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). 45 | /// - MUST revert if `_to` is the zero address. 46 | /// - MUST revert if balance of holder for token `_id` is lower than the `_value` sent. 47 | /// - MUST revert on any other error. 48 | /// - MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard). 49 | /// - After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). 50 | /// @param _from Source address 51 | /// @param _to Target address 52 | /// @param _id ID of the token type 53 | /// @param _value Transfer amount 54 | /// @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to` 55 | function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external; 56 | 57 | /// @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call). 58 | /// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). 59 | /// - MUST revert if `_to` is the zero address. 60 | /// - MUST revert if length of `_ids` is not the same as length of `_values`. 61 | /// - MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. 62 | /// - MUST revert on any other error. 63 | /// - MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard). 64 | /// - Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc). 65 | /// - After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). 66 | /// @param _from Source address 67 | /// @param _to Target address 68 | /// @param _ids IDs of each token type (order and length must match _values array) 69 | /// @param _values Transfer amounts per token type (order and length must match _ids array) 70 | /// @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to` 71 | function safeBatchTransferFrom( 72 | address _from, 73 | address _to, 74 | uint256[] calldata _ids, 75 | uint256[] calldata _values, 76 | bytes calldata _data 77 | ) external; 78 | 79 | /// @notice Get the balance of an account's tokens. 80 | /// @param _owner The address of the token holder 81 | /// @param _id ID of the token 82 | /// @return The _owner's balance of the token type requested 83 | function balanceOf(address _owner, uint256 _id) external view returns (uint256); 84 | 85 | /// @notice Get the balance of multiple account/token pairs 86 | /// @param _owners The addresses of the token holders 87 | /// @param _ids ID of the tokens 88 | /// @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair) 89 | function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) 90 | external 91 | view 92 | returns (uint256[] memory); 93 | 94 | /// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. 95 | /// @dev MUST emit the ApprovalForAll event on success. 96 | /// @param _operator Address to add to the set of authorized operators 97 | /// @param _approved True if the operator is approved, false to revoke approval 98 | function setApprovalForAll(address _operator, bool _approved) external; 99 | 100 | /// @notice Queries the approval status of an operator for a given owner. 101 | /// @param _owner The owner of the tokens 102 | /// @param _operator Address of authorized operator 103 | /// @return True if the operator is approved, false if not 104 | function isApprovedForAll(address _owner, address _operator) external view returns (bool); 105 | } 106 | -------------------------------------------------------------------------------- /src/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | interface IERC165 { 5 | /// @notice Query if a contract implements an interface 6 | /// @param interfaceID The interface identifier, as specified in ERC-165 7 | /// @dev Interface identification is specified in ERC-165. This function 8 | /// uses less than 30,000 gas. 9 | /// @return `true` if the contract implements `interfaceID` and 10 | /// `interfaceID` is not 0xffffffff, `false` otherwise 11 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /src/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | /// @dev Interface of the ERC20 standard as defined in the EIP. 5 | /// @dev This includes the optional name, symbol, and decimals metadata. 6 | interface IERC20 { 7 | /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`). 8 | event Transfer(address indexed from, address indexed to, uint256 value); 9 | 10 | /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value` 11 | /// is the new allowance. 12 | event Approval(address indexed owner, address indexed spender, uint256 value); 13 | 14 | /// @notice Returns the amount of tokens in existence. 15 | function totalSupply() external view returns (uint256); 16 | 17 | /// @notice Returns the amount of tokens owned by `account`. 18 | function balanceOf(address account) external view returns (uint256); 19 | 20 | /// @notice Moves `amount` tokens from the caller's account to `to`. 21 | function transfer(address to, uint256 amount) external returns (bool); 22 | 23 | /// @notice Returns the remaining number of tokens that `spender` is allowed 24 | /// to spend on behalf of `owner` 25 | function allowance(address owner, address spender) external view returns (uint256); 26 | 27 | /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens. 28 | /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 29 | function approve(address spender, uint256 amount) external returns (bool); 30 | 31 | /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism. 32 | /// `amount` is then deducted from the caller's allowance. 33 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 34 | 35 | /// @notice Returns the name of the token. 36 | function name() external view returns (string memory); 37 | 38 | /// @notice Returns the symbol of the token. 39 | function symbol() external view returns (string memory); 40 | 41 | /// @notice Returns the decimals places of the token. 42 | function decimals() external view returns (uint8); 43 | } 44 | -------------------------------------------------------------------------------- /src/interfaces/IERC4626.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import {IERC20} from "./IERC20.sol"; 5 | 6 | /// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in 7 | /// https://eips.ethereum.org/EIPS/eip-4626 8 | interface IERC4626 is IERC20 { 9 | event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); 10 | 11 | event Withdraw( 12 | address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares 13 | ); 14 | 15 | /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. 16 | /// @dev 17 | /// - MUST be an ERC-20 token contract. 18 | /// - MUST NOT revert. 19 | function asset() external view returns (address assetTokenAddress); 20 | 21 | /// @notice Returns the total amount of the underlying asset that is “managed” by Vault. 22 | /// @dev 23 | /// - SHOULD include any compounding that occurs from yield. 24 | /// - MUST be inclusive of any fees that are charged against assets in the Vault. 25 | /// - MUST NOT revert. 26 | function totalAssets() external view returns (uint256 totalManagedAssets); 27 | 28 | /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal 29 | /// scenario where all the conditions are met. 30 | /// @dev 31 | /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault. 32 | /// - MUST NOT show any variations depending on the caller. 33 | /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. 34 | /// - MUST NOT revert. 35 | /// 36 | /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the 37 | /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and 38 | /// from. 39 | function convertToShares(uint256 assets) external view returns (uint256 shares); 40 | 41 | /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal 42 | /// scenario where all the conditions are met. 43 | /// @dev 44 | /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault. 45 | /// - MUST NOT show any variations depending on the caller. 46 | /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. 47 | /// - MUST NOT revert. 48 | /// 49 | /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the 50 | /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and 51 | /// from. 52 | function convertToAssets(uint256 shares) external view returns (uint256 assets); 53 | 54 | /// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, 55 | /// through a deposit call. 56 | /// @dev 57 | /// - MUST return a limited value if receiver is subject to some deposit limit. 58 | /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. 59 | /// - MUST NOT revert. 60 | function maxDeposit(address receiver) external view returns (uint256 maxAssets); 61 | 62 | /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given 63 | /// current on-chain conditions. 64 | /// @dev 65 | /// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit 66 | /// call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called 67 | /// in the same transaction. 68 | /// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the 69 | /// deposit would be accepted, regardless if the user has enough tokens approved, etc. 70 | /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. 71 | /// - MUST NOT revert. 72 | /// 73 | /// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in 74 | /// share price or some other type of condition, meaning the depositor will lose assets by depositing. 75 | function previewDeposit(uint256 assets) external view returns (uint256 shares); 76 | 77 | /// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. 78 | /// @dev 79 | /// - MUST emit the Deposit event. 80 | /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the 81 | /// deposit execution, and are accounted for during deposit. 82 | /// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not 83 | /// approving enough underlying tokens to the Vault contract, etc). 84 | /// 85 | /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. 86 | function deposit(uint256 assets, address receiver) external returns (uint256 shares); 87 | 88 | /// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. 89 | /// @dev 90 | /// - MUST return a limited value if receiver is subject to some mint limit. 91 | /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. 92 | /// - MUST NOT revert. 93 | function maxMint(address receiver) external view returns (uint256 maxShares); 94 | 95 | /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given 96 | /// current on-chain conditions. 97 | /// @dev 98 | /// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call 99 | /// in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the 100 | /// same transaction. 101 | /// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint 102 | /// would be accepted, regardless if the user has enough tokens approved, etc. 103 | /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. 104 | /// - MUST NOT revert. 105 | /// 106 | /// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in 107 | /// share price or some other type of condition, meaning the depositor will lose assets by minting. 108 | function previewMint(uint256 shares) external view returns (uint256 assets); 109 | 110 | /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. 111 | /// @dev 112 | /// - MUST emit the Deposit event. 113 | /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint 114 | /// execution, and are accounted for during mint. 115 | /// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not 116 | /// approving enough underlying tokens to the Vault contract, etc). 117 | /// 118 | /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. 119 | function mint(uint256 shares, address receiver) external returns (uint256 assets); 120 | 121 | /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the 122 | /// Vault, through a withdrawal call. 123 | /// @dev 124 | /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock. 125 | /// - MUST NOT revert. 126 | function maxWithdraw(address owner) external view returns (uint256 maxAssets); 127 | 128 | /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, 129 | /// given current on-chain conditions. 130 | /// @dev 131 | /// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw 132 | /// call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if 133 | /// called 134 | /// in the same transaction. 135 | /// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though 136 | /// the withdrawal would be accepted, regardless if the user has enough shares, etc. 137 | /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. 138 | /// - MUST NOT revert. 139 | /// 140 | /// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in 141 | /// share price or some other type of condition, meaning the depositor will lose assets by depositing. 142 | function previewWithdraw(uint256 assets) external view returns (uint256 shares); 143 | 144 | /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver. 145 | /// @dev 146 | /// - MUST emit the Withdraw event. 147 | /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the 148 | /// withdraw execution, and are accounted for during withdrawal. 149 | /// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner 150 | /// not having enough shares, etc). 151 | /// 152 | /// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. 153 | /// Those methods should be performed separately. 154 | function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); 155 | 156 | /// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, 157 | /// through a redeem call. 158 | /// @dev 159 | /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock. 160 | /// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. 161 | /// - MUST NOT revert. 162 | function maxRedeem(address owner) external view returns (uint256 maxShares); 163 | 164 | /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, 165 | /// given current on-chain conditions. 166 | /// @dev 167 | /// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call 168 | /// in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the 169 | /// same transaction. 170 | /// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the 171 | /// redemption would be accepted, regardless if the user has enough shares, etc. 172 | /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. 173 | /// - MUST NOT revert. 174 | /// 175 | /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in 176 | /// share price or some other type of condition, meaning the depositor will lose assets by redeeming. 177 | function previewRedeem(uint256 shares) external view returns (uint256 assets); 178 | 179 | /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver. 180 | /// @dev 181 | /// - MUST emit the Withdraw event. 182 | /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the 183 | /// redeem execution, and are accounted for during redeem. 184 | /// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner 185 | /// not having enough shares, etc). 186 | /// 187 | /// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. 188 | /// Those methods should be performed separately. 189 | function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); 190 | } 191 | -------------------------------------------------------------------------------- /src/interfaces/IERC6909.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import {IERC165} from "./IERC165.sol"; 5 | 6 | /// @dev Required interface of an ERC-6909 compliant contract, as defined in 7 | /// https://eips.ethereum.org/EIPS/eip-6909 8 | interface IERC6909 is IERC165 { 9 | /// @dev Emitted when the allowance of a `spender` for an `owner` is set for a token of type `id`. 10 | event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount); 11 | 12 | /// @dev Emitted when `owner` grants or revokes operator status for a `spender`. 13 | event OperatorSet(address indexed owner, address indexed spender, bool approved); 14 | 15 | /// @dev Emitted when `amount` tokens of type `id` are moved from `sender` to `receiver` initiated by `caller`. 16 | event Transfer( 17 | address caller, address indexed sender, address indexed receiver, uint256 indexed id, uint256 amount 18 | ); 19 | 20 | ///@dev Returns the amount of tokens of type `id` owned by `owner`. 21 | function balanceOf(address owner, uint256 id) external view returns (uint256); 22 | 23 | /// @dev Returns the amount of tokens of type `id` that `spender` is allowed to spend on behalf of `owner`. 24 | /// NOTE: Does not include operator allowances. 25 | function allowance(address owner, address spender, uint256 id) external view returns (uint256); 26 | 27 | /// @dev Returns true if `spender` is set as an operator for `owner`. 28 | function isOperator(address owner, address spender) external view returns (bool); 29 | 30 | /// @dev Sets an approval to `spender` for `amount` tokens of type `id` from the caller's tokens. 31 | /// Must return true. 32 | function approve(address spender, uint256 id, uint256 amount) external returns (bool); 33 | 34 | /// @dev Grants or revokes unlimited transfer permission of any token id to `spender` for the caller's tokens. 35 | /// Must return true. 36 | function setOperator(address spender, bool approved) external returns (bool); 37 | 38 | /// @dev Transfers `amount` of token type `id` from the caller's account to `receiver`. 39 | /// Must return true. 40 | function transfer(address receiver, uint256 id, uint256 amount) external returns (bool); 41 | 42 | /// @dev Transfers `amount` of token type `id` from `sender` to `receiver`. 43 | /// Must return true. 44 | function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool); 45 | } 46 | 47 | /// @dev Optional extension of {IERC6909} that adds metadata functions. 48 | interface IERC6909Metadata is IERC6909 { 49 | /// @dev Returns the name of the token of type `id`. 50 | function name(uint256 id) external view returns (string memory); 51 | 52 | /// @dev Returns the ticker symbol of the token of type `id`. 53 | function symbol(uint256 id) external view returns (string memory); 54 | 55 | /// @dev Returns the number of decimals for the token of type `id`. 56 | function decimals(uint256 id) external view returns (uint8); 57 | } 58 | 59 | /// @dev Optional extension of {IERC6909} that adds content URI functions. 60 | interface IERC6909ContentURI is IERC6909 { 61 | /// @dev Returns URI for the contract. 62 | function contractURI() external view returns (string memory); 63 | 64 | /// @dev Returns the URI for the token of type `id`. 65 | function tokenURI(uint256 id) external view returns (string memory); 66 | } 67 | 68 | /// @dev Optional extension of {IERC6909} that adds a token supply function. 69 | interface IERC6909TokenSupply is IERC6909 { 70 | /// @dev Returns the total supply of the token of type `id`. 71 | function totalSupply(uint256 id) external view returns (uint256); 72 | } 73 | -------------------------------------------------------------------------------- /src/interfaces/IERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import {IERC165} from "./IERC165.sol"; 5 | 6 | /// @title ERC-721 Non-Fungible Token Standard 7 | /// @dev See https://eips.ethereum.org/EIPS/eip-721 8 | /// Note: the ERC-165 identifier for this interface is 0x80ac58cd. 9 | interface IERC721 is IERC165 { 10 | /// @dev This emits when ownership of any NFT changes by any mechanism. 11 | /// This event emits when NFTs are created (`from` == 0) and destroyed 12 | /// (`to` == 0). Exception: during contract creation, any number of NFTs 13 | /// may be created and assigned without emitting Transfer. At the time of 14 | /// any transfer, the approved address for that NFT (if any) is reset to none. 15 | event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); 16 | 17 | /// @dev This emits when the approved address for an NFT is changed or 18 | /// reaffirmed. The zero address indicates there is no approved address. 19 | /// When a Transfer event emits, this also indicates that the approved 20 | /// address for that NFT (if any) is reset to none. 21 | event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); 22 | 23 | /// @dev This emits when an operator is enabled or disabled for an owner. 24 | /// The operator can manage all NFTs of the owner. 25 | event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); 26 | 27 | /// @notice Count all NFTs assigned to an owner 28 | /// @dev NFTs assigned to the zero address are considered invalid, and this 29 | /// function throws for queries about the zero address. 30 | /// @param _owner An address for whom to query the balance 31 | /// @return The number of NFTs owned by `_owner`, possibly zero 32 | function balanceOf(address _owner) external view returns (uint256); 33 | 34 | /// @notice Find the owner of an NFT 35 | /// @dev NFTs assigned to zero address are considered invalid, and queries 36 | /// about them do throw. 37 | /// @param _tokenId The identifier for an NFT 38 | /// @return The address of the owner of the NFT 39 | function ownerOf(uint256 _tokenId) external view returns (address); 40 | 41 | /// @notice Transfers the ownership of an NFT from one address to another address 42 | /// @dev Throws unless `msg.sender` is the current owner, an authorized 43 | /// operator, or the approved address for this NFT. Throws if `_from` is 44 | /// not the current owner. Throws if `_to` is the zero address. Throws if 45 | /// `_tokenId` is not a valid NFT. When transfer is complete, this function 46 | /// checks if `_to` is a smart contract (code size > 0). If so, it calls 47 | /// `onERC721Received` on `_to` and throws if the return value is not 48 | /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. 49 | /// @param _from The current owner of the NFT 50 | /// @param _to The new owner 51 | /// @param _tokenId The NFT to transfer 52 | /// @param data Additional data with no specified format, sent in call to `_to` 53 | function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable; 54 | 55 | /// @notice Transfers the ownership of an NFT from one address to another address 56 | /// @dev This works identically to the other function with an extra data parameter, 57 | /// except this function just sets data to "". 58 | /// @param _from The current owner of the NFT 59 | /// @param _to The new owner 60 | /// @param _tokenId The NFT to transfer 61 | function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable; 62 | 63 | /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE 64 | /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE 65 | /// THEY MAY BE PERMANENTLY LOST 66 | /// @dev Throws unless `msg.sender` is the current owner, an authorized 67 | /// operator, or the approved address for this NFT. Throws if `_from` is 68 | /// not the current owner. Throws if `_to` is the zero address. Throws if 69 | /// `_tokenId` is not a valid NFT. 70 | /// @param _from The current owner of the NFT 71 | /// @param _to The new owner 72 | /// @param _tokenId The NFT to transfer 73 | function transferFrom(address _from, address _to, uint256 _tokenId) external payable; 74 | 75 | /// @notice Change or reaffirm the approved address for an NFT 76 | /// @dev The zero address indicates there is no approved address. 77 | /// Throws unless `msg.sender` is the current NFT owner, or an authorized 78 | /// operator of the current owner. 79 | /// @param _approved The new approved NFT controller 80 | /// @param _tokenId The NFT to approve 81 | function approve(address _approved, uint256 _tokenId) external payable; 82 | 83 | /// @notice Enable or disable approval for a third party ("operator") to manage 84 | /// all of `msg.sender`'s assets 85 | /// @dev Emits the ApprovalForAll event. The contract MUST allow 86 | /// multiple operators per owner. 87 | /// @param _operator Address to add to the set of authorized operators 88 | /// @param _approved True if the operator is approved, false to revoke approval 89 | function setApprovalForAll(address _operator, bool _approved) external; 90 | 91 | /// @notice Get the approved address for a single NFT 92 | /// @dev Throws if `_tokenId` is not a valid NFT. 93 | /// @param _tokenId The NFT to find the approved address for 94 | /// @return The approved address for this NFT, or the zero address if there is none 95 | function getApproved(uint256 _tokenId) external view returns (address); 96 | 97 | /// @notice Query if an address is an authorized operator for another address 98 | /// @param _owner The address that owns the NFTs 99 | /// @param _operator The address that acts on behalf of the owner 100 | /// @return True if `_operator` is an approved operator for `_owner`, false otherwise 101 | function isApprovedForAll(address _owner, address _operator) external view returns (bool); 102 | } 103 | 104 | /// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. 105 | interface IERC721TokenReceiver { 106 | /// @notice Handle the receipt of an NFT 107 | /// @dev The ERC721 smart contract calls this function on the recipient 108 | /// after a `transfer`. This function MAY throw to revert and reject the 109 | /// transfer. Return of other than the magic value MUST result in the 110 | /// transaction being reverted. 111 | /// Note: the contract address is always the message sender. 112 | /// @param _operator The address which called `safeTransferFrom` function 113 | /// @param _from The address which previously owned the token 114 | /// @param _tokenId The NFT identifier which is being transferred 115 | /// @param _data Additional data with no specified format 116 | /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 117 | /// unless throwing 118 | function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) 119 | external 120 | returns (bytes4); 121 | } 122 | 123 | /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension 124 | /// @dev See https://eips.ethereum.org/EIPS/eip-721 125 | /// Note: the ERC-165 identifier for this interface is 0x5b5e139f. 126 | interface IERC721Metadata is IERC721 { 127 | /// @notice A descriptive name for a collection of NFTs in this contract 128 | function name() external view returns (string memory _name); 129 | 130 | /// @notice An abbreviated name for NFTs in this contract 131 | function symbol() external view returns (string memory _symbol); 132 | 133 | /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. 134 | /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC 135 | /// 3986. The URI may point to a JSON file that conforms to the "ERC721 136 | /// Metadata JSON Schema". 137 | function tokenURI(uint256 _tokenId) external view returns (string memory); 138 | } 139 | 140 | /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension 141 | /// @dev See https://eips.ethereum.org/EIPS/eip-721 142 | /// Note: the ERC-165 identifier for this interface is 0x780e9d63. 143 | interface IERC721Enumerable is IERC721 { 144 | /// @notice Count NFTs tracked by this contract 145 | /// @return A count of valid NFTs tracked by this contract, where each one of 146 | /// them has an assigned and queryable owner not equal to the zero address 147 | function totalSupply() external view returns (uint256); 148 | 149 | /// @notice Enumerate valid NFTs 150 | /// @dev Throws if `_index` >= `totalSupply()`. 151 | /// @param _index A counter less than `totalSupply()` 152 | /// @return The token identifier for the `_index`th NFT, 153 | /// (sort order not specified) 154 | function tokenByIndex(uint256 _index) external view returns (uint256); 155 | 156 | /// @notice Enumerate NFTs assigned to an owner 157 | /// @dev Throws if `_index` >= `balanceOf(_owner)` or if 158 | /// `_owner` is the zero address, representing invalid NFTs. 159 | /// @param _owner An address where we are interested in NFTs owned by them 160 | /// @param _index A counter less than `balanceOf(_owner)` 161 | /// @return The token identifier for the `_index`th NFT assigned to `_owner`, 162 | /// (sort order not specified) 163 | function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256); 164 | } 165 | -------------------------------------------------------------------------------- /src/interfaces/IERC7540.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import {IERC7575} from "./IERC7575.sol"; 5 | 6 | /// @dev Interface of the base operator logic of ERC7540, as defined in 7 | /// https://eips.ethereum.org/EIPS/eip-7540 8 | interface IERC7540Operator { 9 | /** 10 | * @dev The event emitted when an operator is set. 11 | * 12 | * @param controller The address of the controller. 13 | * @param operator The address of the operator. 14 | * @param approved The approval status. 15 | */ 16 | event OperatorSet(address indexed controller, address indexed operator, bool approved); 17 | 18 | /** 19 | * @dev Sets or removes an operator for the caller. 20 | * 21 | * @param operator The address of the operator. 22 | * @param approved The approval status. 23 | * @return Whether the call was executed successfully or not 24 | */ 25 | function setOperator(address operator, bool approved) external returns (bool); 26 | 27 | /** 28 | * @dev Returns `true` if the `operator` is approved as an operator for an `controller`. 29 | * 30 | * @param controller The address of the controller. 31 | * @param operator The address of the operator. 32 | * @return status The approval status 33 | */ 34 | function isOperator(address controller, address operator) external view returns (bool status); 35 | } 36 | 37 | /// @dev Interface of the asynchronous deposit Vault interface of ERC7540, as defined in 38 | /// https://eips.ethereum.org/EIPS/eip-7540 39 | interface IERC7540Deposit is IERC7540Operator { 40 | event DepositRequest( 41 | address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets 42 | ); 43 | /** 44 | * @dev Transfers assets from sender into the Vault and submits a Request for asynchronous deposit. 45 | * 46 | * - MUST support ERC-20 approve / transferFrom on asset as a deposit Request flow. 47 | * - MUST revert if all of assets cannot be requested for deposit. 48 | * - owner MUST be msg.sender unless some unspecified explicit approval is given by the caller, 49 | * approval of ERC-20 tokens from owner to sender is NOT enough. 50 | * 51 | * @param assets the amount of deposit assets to transfer from owner 52 | * @param controller the controller of the request who will be able to operate the request 53 | * @param owner the source of the deposit assets 54 | * 55 | * NOTE: most implementations will require pre-approval of the Vault with the Vault's underlying asset token. 56 | */ 57 | 58 | function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId); 59 | 60 | /** 61 | * @dev Returns the amount of requested assets in Pending state. 62 | * 63 | * - MUST NOT include any assets in Claimable state for deposit or mint. 64 | * - MUST NOT show any variations depending on the caller. 65 | * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. 66 | */ 67 | function pendingDepositRequest(uint256 requestId, address controller) 68 | external 69 | view 70 | returns (uint256 pendingAssets); 71 | 72 | /** 73 | * @dev Returns the amount of requested assets in Claimable state for the controller to deposit or mint. 74 | * 75 | * - MUST NOT include any assets in Pending state. 76 | * - MUST NOT show any variations depending on the caller. 77 | * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. 78 | */ 79 | function claimableDepositRequest(uint256 requestId, address controller) 80 | external 81 | view 82 | returns (uint256 claimableAssets); 83 | 84 | /** 85 | * @dev Mints shares Vault shares to receiver by claiming the Request of the controller. 86 | * 87 | * - MUST emit the Deposit event. 88 | * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator. 89 | */ 90 | function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares); 91 | 92 | /** 93 | * @dev Mints exactly shares Vault shares to receiver by claiming the Request of the controller. 94 | * 95 | * - MUST emit the Deposit event. 96 | * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator. 97 | */ 98 | function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets); 99 | } 100 | 101 | /// @dev Interface of the asynchronous deposit Vault interface of ERC7540, as defined in 102 | /// https://eips.ethereum.org/EIPS/eip-7540 103 | interface IERC7540Redeem is IERC7540Operator { 104 | event RedeemRequest( 105 | address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets 106 | ); 107 | 108 | /** 109 | * @dev Assumes control of shares from sender into the Vault and submits a Request for asynchronous redeem. 110 | * 111 | * - MUST support a redeem Request flow where the control of shares is taken from sender directly 112 | * where msg.sender has ERC-20 approval over the shares of owner. 113 | * - MUST revert if all of shares cannot be requested for redeem. 114 | * 115 | * @param shares the amount of shares to be redeemed to transfer from owner 116 | * @param controller the controller of the request who will be able to operate the request 117 | * @param owner the source of the shares to be redeemed 118 | * 119 | * NOTE: most implementations will require pre-approval of the Vault with the Vault's share token. 120 | */ 121 | function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId); 122 | 123 | /** 124 | * @dev Returns the amount of requested shares in Pending state. 125 | * 126 | * - MUST NOT include any shares in Claimable state for redeem or withdraw. 127 | * - MUST NOT show any variations depending on the caller. 128 | * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. 129 | */ 130 | function pendingRedeemRequest(uint256 requestId, address controller) 131 | external 132 | view 133 | returns (uint256 pendingShares); 134 | 135 | /** 136 | * @dev Returns the amount of requested shares in Claimable state for the controller to redeem or withdraw. 137 | * 138 | * - MUST NOT include any shares in Pending state for redeem or withdraw. 139 | * - MUST NOT show any variations depending on the caller. 140 | * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. 141 | */ 142 | function claimableRedeemRequest(uint256 requestId, address controller) 143 | external 144 | view 145 | returns (uint256 claimableShares); 146 | } 147 | 148 | /// @dev Interface of the fully asynchronous Vault interface of ERC7540, as defined in 149 | /// https://eips.ethereum.org/EIPS/eip-7540 150 | interface IERC7540 is IERC7540Deposit, IERC7540Redeem, IERC7575 {} 151 | -------------------------------------------------------------------------------- /src/interfaces/IERC7575.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import {IERC165} from "./IERC165.sol"; 5 | 6 | /// @dev Interface of the ERC7575 "Multi-Asset ERC-4626 Vaults", as defined in 7 | /// https://eips.ethereum.org/EIPS/eip-7575 8 | interface IERC7575 is IERC165 { 9 | event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); 10 | event Withdraw( 11 | address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares 12 | ); 13 | 14 | /** 15 | * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. 16 | * 17 | * - MUST be an ERC-20 token contract. 18 | * - MUST NOT revert. 19 | */ 20 | function asset() external view returns (address assetTokenAddress); 21 | 22 | /** 23 | * @dev Returns the address of the share token 24 | * 25 | * - MUST be an ERC-20 token contract. 26 | * - MUST NOT revert. 27 | */ 28 | function share() external view returns (address shareTokenAddress); 29 | 30 | /** 31 | * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal 32 | * scenario where all the conditions are met. 33 | * 34 | * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. 35 | * - MUST NOT show any variations depending on the caller. 36 | * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. 37 | * - MUST NOT revert. 38 | * 39 | * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the 40 | * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and 41 | * from. 42 | */ 43 | function convertToShares(uint256 assets) external view returns (uint256 shares); 44 | 45 | /** 46 | * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal 47 | * scenario where all the conditions are met. 48 | * 49 | * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. 50 | * - MUST NOT show any variations depending on the caller. 51 | * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. 52 | * - MUST NOT revert. 53 | * 54 | * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the 55 | * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and 56 | * from. 57 | */ 58 | function convertToAssets(uint256 shares) external view returns (uint256 assets); 59 | 60 | /** 61 | * @dev Returns the total amount of the underlying asset that is “managed” by Vault. 62 | * 63 | * - SHOULD include any compounding that occurs from yield. 64 | * - MUST be inclusive of any fees that are charged against assets in the Vault. 65 | * - MUST NOT revert. 66 | */ 67 | function totalAssets() external view returns (uint256 totalManagedAssets); 68 | 69 | /** 70 | * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, 71 | * through a deposit call. 72 | * 73 | * - MUST return a limited value if receiver is subject to some deposit limit. 74 | * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. 75 | * - MUST NOT revert. 76 | */ 77 | function maxDeposit(address receiver) external view returns (uint256 maxAssets); 78 | 79 | /** 80 | * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given 81 | * current on-chain conditions. 82 | * 83 | * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit 84 | * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called 85 | * in the same transaction. 86 | * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the 87 | * deposit would be accepted, regardless if the user has enough tokens approved, etc. 88 | * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. 89 | * - MUST NOT revert. 90 | * 91 | * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in 92 | * share price or some other type of condition, meaning the depositor will lose assets by depositing. 93 | */ 94 | function previewDeposit(uint256 assets) external view returns (uint256 shares); 95 | 96 | /** 97 | * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. 98 | * 99 | * - MUST emit the Deposit event. 100 | * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the 101 | * deposit execution, and are accounted for during deposit. 102 | * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not 103 | * approving enough underlying tokens to the Vault contract, etc). 104 | * 105 | * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. 106 | */ 107 | function deposit(uint256 assets, address receiver) external returns (uint256 shares); 108 | 109 | /** 110 | * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. 111 | * - MUST return a limited value if receiver is subject to some mint limit. 112 | * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. 113 | * - MUST NOT revert. 114 | */ 115 | function maxMint(address receiver) external view returns (uint256 maxShares); 116 | 117 | /** 118 | * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given 119 | * current on-chain conditions. 120 | * 121 | * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call 122 | * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the 123 | * same transaction. 124 | * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint 125 | * would be accepted, regardless if the user has enough tokens approved, etc. 126 | * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. 127 | * - MUST NOT revert. 128 | * 129 | * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in 130 | * share price or some other type of condition, meaning the depositor will lose assets by minting. 131 | */ 132 | function previewMint(uint256 shares) external view returns (uint256 assets); 133 | 134 | /** 135 | * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. 136 | * 137 | * - MUST emit the Deposit event. 138 | * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint 139 | * execution, and are accounted for during mint. 140 | * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not 141 | * approving enough underlying tokens to the Vault contract, etc). 142 | * 143 | * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. 144 | */ 145 | function mint(uint256 shares, address receiver) external returns (uint256 assets); 146 | 147 | /** 148 | * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the 149 | * Vault, through a withdraw call. 150 | * 151 | * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. 152 | * - MUST NOT revert. 153 | */ 154 | function maxWithdraw(address owner) external view returns (uint256 maxAssets); 155 | 156 | /** 157 | * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, 158 | * given current on-chain conditions. 159 | * 160 | * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw 161 | * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if 162 | * called 163 | * in the same transaction. 164 | * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though 165 | * the withdrawal would be accepted, regardless if the user has enough shares, etc. 166 | * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. 167 | * - MUST NOT revert. 168 | * 169 | * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in 170 | * share price or some other type of condition, meaning the depositor will lose assets by depositing. 171 | */ 172 | function previewWithdraw(uint256 assets) external view returns (uint256 shares); 173 | 174 | /** 175 | * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. 176 | * 177 | * - MUST emit the Withdraw event. 178 | * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the 179 | * withdraw execution, and are accounted for during withdraw. 180 | * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner 181 | * not having enough shares, etc). 182 | * 183 | * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. 184 | * Those methods should be performed separately. 185 | */ 186 | function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); 187 | 188 | /** 189 | * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, 190 | * through a redeem call. 191 | * 192 | * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. 193 | * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. 194 | * - MUST NOT revert. 195 | */ 196 | function maxRedeem(address owner) external view returns (uint256 maxShares); 197 | 198 | /** 199 | * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, 200 | * given current on-chain conditions. 201 | * 202 | * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call 203 | * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the 204 | * same transaction. 205 | * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the 206 | * redemption would be accepted, regardless if the user has enough shares, etc. 207 | * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. 208 | * - MUST NOT revert. 209 | * 210 | * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in 211 | * share price or some other type of condition, meaning the depositor will lose assets by redeeming. 212 | */ 213 | function previewRedeem(uint256 shares) external view returns (uint256 assets); 214 | 215 | /** 216 | * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. 217 | * 218 | * - MUST emit the Withdraw event. 219 | * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the 220 | * redeem execution, and are accounted for during redeem. 221 | * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner 222 | * not having enough shares, etc). 223 | * 224 | * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. 225 | * Those methods should be performed separately. 226 | */ 227 | function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); 228 | } 229 | 230 | /// @dev Interface of the ERC20 share token, as defined in 231 | /// https://eips.ethereum.org/EIPS/eip-7575 232 | interface IERC7575Share is IERC165 { 233 | event VaultUpdate(address indexed asset, address vault); 234 | 235 | /** 236 | * @dev Returns the address of the Vault for the given asset. 237 | * 238 | * @param asset the ERC-20 token to deposit with into the Vault 239 | */ 240 | function vault(address asset) external view returns (address); 241 | } 242 | -------------------------------------------------------------------------------- /src/interfaces/IMulticall3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IMulticall3 { 7 | struct Call { 8 | address target; 9 | bytes callData; 10 | } 11 | 12 | struct Call3 { 13 | address target; 14 | bool allowFailure; 15 | bytes callData; 16 | } 17 | 18 | struct Call3Value { 19 | address target; 20 | bool allowFailure; 21 | uint256 value; 22 | bytes callData; 23 | } 24 | 25 | struct Result { 26 | bool success; 27 | bytes returnData; 28 | } 29 | 30 | function aggregate(Call[] calldata calls) 31 | external 32 | payable 33 | returns (uint256 blockNumber, bytes[] memory returnData); 34 | 35 | function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData); 36 | 37 | function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData); 38 | 39 | function blockAndAggregate(Call[] calldata calls) 40 | external 41 | payable 42 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 43 | 44 | function getBasefee() external view returns (uint256 basefee); 45 | 46 | function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash); 47 | 48 | function getBlockNumber() external view returns (uint256 blockNumber); 49 | 50 | function getChainId() external view returns (uint256 chainid); 51 | 52 | function getCurrentBlockCoinbase() external view returns (address coinbase); 53 | 54 | function getCurrentBlockDifficulty() external view returns (uint256 difficulty); 55 | 56 | function getCurrentBlockGasLimit() external view returns (uint256 gaslimit); 57 | 58 | function getCurrentBlockTimestamp() external view returns (uint256 timestamp); 59 | 60 | function getEthBalance(address addr) external view returns (uint256 balance); 61 | 62 | function getLastBlockHash() external view returns (bytes32 blockHash); 63 | 64 | function tryAggregate(bool requireSuccess, Call[] calldata calls) 65 | external 66 | payable 67 | returns (Result[] memory returnData); 68 | 69 | function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) 70 | external 71 | payable 72 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 73 | } 74 | -------------------------------------------------------------------------------- /test/CommonBase.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {CommonBase} from "../src/Base.sol"; 5 | import {StdConstants} from "../src/StdConstants.sol"; 6 | import {Test} from "../src/Test.sol"; 7 | 8 | contract CommonBaseTest is Test { 9 | function testVmAddressValue() public pure { 10 | assertEq(VM_ADDRESS, address(StdConstants.VM)); 11 | } 12 | 13 | function testConsoleValue() public pure { 14 | assertEq(CONSOLE, StdConstants.CONSOLE); 15 | } 16 | 17 | function testCreate2FactoryValue() public pure { 18 | assertEq(CREATE2_FACTORY, StdConstants.CREATE2_FACTORY); 19 | } 20 | 21 | function testDefaultSenderValue() public pure { 22 | assertEq(DEFAULT_SENDER, StdConstants.DEFAULT_SENDER); 23 | } 24 | 25 | function testDefaultTestContractValue() public pure { 26 | assertEq(DEFAULT_TEST_CONTRACT, StdConstants.DEFAULT_TEST_CONTRACT); 27 | } 28 | 29 | function testMulticall3AddressValue() public pure { 30 | assertEq(MULTICALL3_ADDRESS, address(StdConstants.MULTICALL3_ADDRESS)); 31 | } 32 | 33 | function testSecp256k1OrderValue() public pure { 34 | assertEq(SECP256K1_ORDER, StdConstants.SECP256K1_ORDER); 35 | } 36 | 37 | function testUint256MaxValue() public pure { 38 | assertEq(UINT256_MAX, type(uint256).max); 39 | } 40 | 41 | function testVmValue() public pure { 42 | assertEq(address(vm), address(StdConstants.VM)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/StdAssertions.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {StdAssertions} from "../src/StdAssertions.sol"; 5 | import {Vm} from "../src/Vm.sol"; 6 | 7 | interface VmInternal is Vm { 8 | function _expectCheatcodeRevert(bytes memory message) external; 9 | } 10 | 11 | contract StdAssertionsTest is StdAssertions { 12 | string constant errorMessage = "User provided message"; 13 | uint256 constant maxDecimals = 77; 14 | 15 | bool constant SHOULD_REVERT = true; 16 | bool constant SHOULD_RETURN = false; 17 | 18 | bool constant STRICT_REVERT_DATA = true; 19 | bool constant NON_STRICT_REVERT_DATA = false; 20 | 21 | VmInternal constant vm = VmInternal(address(uint160(uint256(keccak256("hevm cheat code"))))); 22 | 23 | function testFuzz_AssertEqCall_Return_Pass( 24 | bytes memory callDataA, 25 | bytes memory callDataB, 26 | bytes memory returnData, 27 | bool strictRevertData 28 | ) external { 29 | address targetA = address(new TestMockCall(returnData, SHOULD_RETURN)); 30 | address targetB = address(new TestMockCall(returnData, SHOULD_RETURN)); 31 | 32 | assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData); 33 | } 34 | 35 | function testFuzz_RevertWhenCalled_AssertEqCall_Return_Fail( 36 | bytes memory callDataA, 37 | bytes memory callDataB, 38 | bytes memory returnDataA, 39 | bytes memory returnDataB, 40 | bool strictRevertData 41 | ) external { 42 | vm.assume(keccak256(returnDataA) != keccak256(returnDataB)); 43 | 44 | address targetA = address(new TestMockCall(returnDataA, SHOULD_RETURN)); 45 | address targetB = address(new TestMockCall(returnDataB, SHOULD_RETURN)); 46 | 47 | vm._expectCheatcodeRevert( 48 | bytes( 49 | string.concat( 50 | "Call return data does not match: ", vm.toString(returnDataA), " != ", vm.toString(returnDataB) 51 | ) 52 | ) 53 | ); 54 | assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData); 55 | } 56 | 57 | function testFuzz_AssertEqCall_Revert_Pass( 58 | bytes memory callDataA, 59 | bytes memory callDataB, 60 | bytes memory revertDataA, 61 | bytes memory revertDataB 62 | ) external { 63 | address targetA = address(new TestMockCall(revertDataA, SHOULD_REVERT)); 64 | address targetB = address(new TestMockCall(revertDataB, SHOULD_REVERT)); 65 | 66 | assertEqCall(targetA, callDataA, targetB, callDataB, NON_STRICT_REVERT_DATA); 67 | } 68 | 69 | function testFuzz_RevertWhenCalled_AssertEqCall_Revert_Fail( 70 | bytes memory callDataA, 71 | bytes memory callDataB, 72 | bytes memory revertDataA, 73 | bytes memory revertDataB 74 | ) external { 75 | vm.assume(keccak256(revertDataA) != keccak256(revertDataB)); 76 | 77 | address targetA = address(new TestMockCall(revertDataA, SHOULD_REVERT)); 78 | address targetB = address(new TestMockCall(revertDataB, SHOULD_REVERT)); 79 | 80 | vm._expectCheatcodeRevert( 81 | bytes( 82 | string.concat( 83 | "Call revert data does not match: ", vm.toString(revertDataA), " != ", vm.toString(revertDataB) 84 | ) 85 | ) 86 | ); 87 | assertEqCall(targetA, callDataA, targetB, callDataB, STRICT_REVERT_DATA); 88 | } 89 | 90 | function testFuzz_RevertWhenCalled_AssertEqCall_Fail( 91 | bytes memory callDataA, 92 | bytes memory callDataB, 93 | bytes memory returnDataA, 94 | bytes memory returnDataB, 95 | bool strictRevertData 96 | ) external { 97 | address targetA = address(new TestMockCall(returnDataA, SHOULD_RETURN)); 98 | address targetB = address(new TestMockCall(returnDataB, SHOULD_REVERT)); 99 | 100 | vm.expectRevert(bytes("assertion failed")); 101 | this.assertEqCallExternal(targetA, callDataA, targetB, callDataB, strictRevertData); 102 | 103 | vm.expectRevert(bytes("assertion failed")); 104 | this.assertEqCallExternal(targetB, callDataB, targetA, callDataA, strictRevertData); 105 | } 106 | 107 | // Helper function to test outcome of assertEqCall via `expect` cheatcodes 108 | function assertEqCallExternal( 109 | address targetA, 110 | bytes memory callDataA, 111 | address targetB, 112 | bytes memory callDataB, 113 | bool strictRevertData 114 | ) public { 115 | assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData); 116 | } 117 | } 118 | 119 | contract TestMockCall { 120 | bytes returnData; 121 | bool shouldRevert; 122 | 123 | constructor(bytes memory returnData_, bool shouldRevert_) { 124 | returnData = returnData_; 125 | shouldRevert = shouldRevert_; 126 | } 127 | 128 | fallback() external payable { 129 | bytes memory returnData_ = returnData; 130 | 131 | if (shouldRevert) { 132 | assembly { 133 | revert(add(returnData_, 0x20), mload(returnData_)) 134 | } 135 | } else { 136 | assembly { 137 | return(add(returnData_, 0x20), mload(returnData_)) 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /test/StdChains.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {Test} from "../src/Test.sol"; 5 | 6 | contract StdChainsMock is Test { 7 | function exposed_getChain(string memory chainAlias) public returns (Chain memory) { 8 | return getChain(chainAlias); 9 | } 10 | 11 | function exposed_getChain(uint256 chainId) public returns (Chain memory) { 12 | return getChain(chainId); 13 | } 14 | 15 | function exposed_setChain(string memory chainAlias, ChainData memory chainData) public { 16 | setChain(chainAlias, chainData); 17 | } 18 | 19 | function exposed_setFallbackToDefaultRpcUrls(bool useDefault) public { 20 | setFallbackToDefaultRpcUrls(useDefault); 21 | } 22 | } 23 | 24 | contract StdChainsTest is Test { 25 | function test_ChainRpcInitialization() public { 26 | // RPCs specified in `foundry.toml` should be updated. 27 | assertEq(getChain(1).rpcUrl, "https://eth.merkle.io"); 28 | assertEq(getChain("optimism_sepolia").rpcUrl, "https://sepolia.optimism.io/"); 29 | assertEq(getChain("arbitrum_one_sepolia").rpcUrl, "https://sepolia-rollup.arbitrum.io/rpc/"); 30 | 31 | // Environment variables should be the next fallback 32 | assertEq(getChain("arbitrum_nova").rpcUrl, "https://nova.arbitrum.io/rpc"); 33 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "myoverride"); 34 | assertEq(getChain("arbitrum_nova").rpcUrl, "myoverride"); 35 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "https://nova.arbitrum.io/rpc"); 36 | 37 | // Cannot override RPCs defined in `foundry.toml` 38 | vm.setEnv("MAINNET_RPC_URL", "myoverride2"); 39 | assertEq(getChain("mainnet").rpcUrl, "https://eth.merkle.io"); 40 | 41 | // Other RPCs should remain unchanged. 42 | assertEq(getChain(31337).rpcUrl, "http://127.0.0.1:8545"); 43 | assertEq(getChain("sepolia").rpcUrl, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001"); 44 | } 45 | 46 | // Named with a leading underscore to clarify this is not intended to be run as a normal test, 47 | // and is intended to be used in the below `test_Rpcs` test. 48 | function _testRpc(string memory rpcAlias) internal { 49 | string memory rpcUrl = getChain(rpcAlias).rpcUrl; 50 | vm.createSelectFork(rpcUrl); 51 | } 52 | 53 | // Ensure we can connect to the default RPC URL for each chain. 54 | // Currently commented out since this is slow and public RPCs are flaky, often resulting in failing CI. 55 | // function test_Rpcs() public { 56 | // _testRpc("mainnet"); 57 | // _testRpc("sepolia"); 58 | // _testRpc("holesky"); 59 | // _testRpc("optimism"); 60 | // _testRpc("optimism_sepolia"); 61 | // _testRpc("arbitrum_one"); 62 | // _testRpc("arbitrum_one_sepolia"); 63 | // _testRpc("arbitrum_nova"); 64 | // _testRpc("polygon"); 65 | // _testRpc("polygon_amoy"); 66 | // _testRpc("avalanche"); 67 | // _testRpc("avalanche_fuji"); 68 | // _testRpc("bnb_smart_chain"); 69 | // _testRpc("bnb_smart_chain_testnet"); 70 | // _testRpc("gnosis_chain"); 71 | // _testRpc("moonbeam"); 72 | // _testRpc("moonriver"); 73 | // _testRpc("moonbase"); 74 | // _testRpc("base_sepolia"); 75 | // _testRpc("base"); 76 | // _testRpc("blast_sepolia"); 77 | // _testRpc("blast"); 78 | // _testRpc("fantom_opera"); 79 | // _testRpc("fantom_opera_testnet"); 80 | // _testRpc("fraxtal"); 81 | // _testRpc("fraxtal_testnet"); 82 | // _testRpc("berachain_bartio_testnet"); 83 | // _testRpc("flare"); 84 | // _testRpc("flare_coston2"); 85 | // } 86 | 87 | function test_RevertIf_ChainNotFound() public { 88 | // We deploy a mock to properly test the revert. 89 | StdChainsMock stdChainsMock = new StdChainsMock(); 90 | 91 | vm.expectRevert("StdChains getChain(string): Chain with alias \"does_not_exist\" not found."); 92 | stdChainsMock.exposed_getChain("does_not_exist"); 93 | } 94 | 95 | function test_RevertIf_SetChain_ChainIdExist_FirstTest() public { 96 | // We deploy a mock to properly test the revert. 97 | StdChainsMock stdChainsMock = new StdChainsMock(); 98 | 99 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID 31337 already used by \"anvil\"."); 100 | stdChainsMock.exposed_setChain("anvil2", ChainData("Anvil", 31337, "URL")); 101 | } 102 | 103 | function test_RevertIf_ChainBubbleUp() public { 104 | // We deploy a mock to properly test the revert. 105 | StdChainsMock stdChainsMock = new StdChainsMock(); 106 | 107 | stdChainsMock.exposed_setChain("needs_undefined_env_var", ChainData("", 123456789, "")); 108 | // Forge environment variable error. 109 | vm.expectRevert(); 110 | stdChainsMock.exposed_getChain("needs_undefined_env_var"); 111 | } 112 | 113 | function test_RevertIf_SetChain_ChainIdExists_SecondTest() public { 114 | // We deploy a mock to properly test the revert. 115 | StdChainsMock stdChainsMock = new StdChainsMock(); 116 | 117 | stdChainsMock.exposed_setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); 118 | 119 | vm.expectRevert('StdChains setChain(string,ChainData): Chain ID 123456789 already used by "custom_chain".'); 120 | 121 | stdChainsMock.exposed_setChain("another_custom_chain", ChainData("", 123456789, "")); 122 | } 123 | 124 | function test_SetChain() public { 125 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); 126 | Chain memory customChain = getChain("custom_chain"); 127 | assertEq(customChain.name, "Custom Chain"); 128 | assertEq(customChain.chainId, 123456789); 129 | assertEq(customChain.chainAlias, "custom_chain"); 130 | assertEq(customChain.rpcUrl, "https://custom.chain/"); 131 | Chain memory chainById = getChain(123456789); 132 | assertEq(chainById.name, customChain.name); 133 | assertEq(chainById.chainId, customChain.chainId); 134 | assertEq(chainById.chainAlias, customChain.chainAlias); 135 | assertEq(chainById.rpcUrl, customChain.rpcUrl); 136 | customChain.name = "Another Custom Chain"; 137 | customChain.chainId = 987654321; 138 | setChain("another_custom_chain", customChain); 139 | Chain memory anotherCustomChain = getChain("another_custom_chain"); 140 | assertEq(anotherCustomChain.name, "Another Custom Chain"); 141 | assertEq(anotherCustomChain.chainId, 987654321); 142 | assertEq(anotherCustomChain.chainAlias, "another_custom_chain"); 143 | assertEq(anotherCustomChain.rpcUrl, "https://custom.chain/"); 144 | // Verify the first chain data was not overwritten 145 | chainById = getChain(123456789); 146 | assertEq(chainById.name, "Custom Chain"); 147 | assertEq(chainById.chainId, 123456789); 148 | } 149 | 150 | function test_RevertIf_SetEmptyAlias() public { 151 | // We deploy a mock to properly test the revert. 152 | StdChainsMock stdChainsMock = new StdChainsMock(); 153 | 154 | vm.expectRevert("StdChains setChain(string,ChainData): Chain alias cannot be the empty string."); 155 | stdChainsMock.exposed_setChain("", ChainData("", 123456789, "")); 156 | } 157 | 158 | function test_RevertIf_SetNoChainId0() public { 159 | // We deploy a mock to properly test the revert. 160 | StdChainsMock stdChainsMock = new StdChainsMock(); 161 | 162 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID cannot be 0."); 163 | stdChainsMock.exposed_setChain("alias", ChainData("", 0, "")); 164 | } 165 | 166 | function test_RevertIf_GetNoChainId0() public { 167 | // We deploy a mock to properly test the revert. 168 | StdChainsMock stdChainsMock = new StdChainsMock(); 169 | 170 | vm.expectRevert("StdChains getChain(uint256): Chain ID cannot be 0."); 171 | stdChainsMock.exposed_getChain(0); 172 | } 173 | 174 | function test_RevertIf_GetNoEmptyAlias() public { 175 | // We deploy a mock to properly test the revert. 176 | StdChainsMock stdChainsMock = new StdChainsMock(); 177 | 178 | vm.expectRevert("StdChains getChain(string): Chain alias cannot be the empty string."); 179 | stdChainsMock.exposed_getChain(""); 180 | } 181 | 182 | function test_RevertIf_ChainIdNotFound() public { 183 | // We deploy a mock to properly test the revert. 184 | StdChainsMock stdChainsMock = new StdChainsMock(); 185 | 186 | vm.expectRevert("StdChains getChain(string): Chain with alias \"no_such_alias\" not found."); 187 | stdChainsMock.exposed_getChain("no_such_alias"); 188 | } 189 | 190 | function test_RevertIf_ChainAliasNotFound() public { 191 | // We deploy a mock to properly test the revert. 192 | StdChainsMock stdChainsMock = new StdChainsMock(); 193 | 194 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 321 not found."); 195 | 196 | stdChainsMock.exposed_getChain(321); 197 | } 198 | 199 | function test_SetChain_ExistingOne() public { 200 | // We deploy a mock to properly test the revert. 201 | StdChainsMock stdChainsMock = new StdChainsMock(); 202 | 203 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); 204 | assertEq(getChain(123456789).chainId, 123456789); 205 | 206 | setChain("custom_chain", ChainData("Modified Chain", 9999999999999999999, "https://modified.chain/")); 207 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 123456789 not found."); 208 | stdChainsMock.exposed_getChain(123456789); 209 | 210 | Chain memory modifiedChain = getChain(9999999999999999999); 211 | assertEq(modifiedChain.name, "Modified Chain"); 212 | assertEq(modifiedChain.chainId, 9999999999999999999); 213 | assertEq(modifiedChain.rpcUrl, "https://modified.chain/"); 214 | } 215 | 216 | function test_RevertIf_DontUseDefaultRpcUrl() public { 217 | // We deploy a mock to properly test the revert. 218 | StdChainsMock stdChainsMock = new StdChainsMock(); 219 | 220 | // Should error if default RPCs flag is set to false. 221 | stdChainsMock.exposed_setFallbackToDefaultRpcUrls(false); 222 | vm.expectRevert(); 223 | stdChainsMock.exposed_getChain(31337); 224 | vm.expectRevert(); 225 | stdChainsMock.exposed_getChain("sepolia"); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /test/StdConstants.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {StdConstants} from "../src/StdConstants.sol"; 5 | import {Test} from "../src/Test.sol"; 6 | 7 | contract StdConstantsTest is Test { 8 | function testVm() public view { 9 | assertEq(StdConstants.VM.getBlockNumber(), 1); 10 | } 11 | 12 | function testVmDerivation() public pure { 13 | assertEq(address(StdConstants.VM), address(uint160(uint256(keccak256("hevm cheat code"))))); 14 | } 15 | 16 | function testConsoleDerivation() public pure { 17 | assertEq(StdConstants.CONSOLE, address(uint160(uint88(bytes11("console.log"))))); 18 | } 19 | 20 | function testDefaultSender() public view { 21 | assertEq(StdConstants.DEFAULT_SENDER, msg.sender); 22 | } 23 | 24 | function testDefaultSenderDerivation() public pure { 25 | assertEq(StdConstants.DEFAULT_SENDER, address(uint160(uint256(keccak256("foundry default caller"))))); 26 | } 27 | 28 | function testDefaultTestContract() public { 29 | assertEq(StdConstants.DEFAULT_TEST_CONTRACT, address(new Dummy())); 30 | } 31 | 32 | function testDefaultTestContractDerivation() public view { 33 | assertEq(address(this), StdConstants.VM.computeCreateAddress(StdConstants.DEFAULT_SENDER, 1)); 34 | assertEq(StdConstants.DEFAULT_TEST_CONTRACT, StdConstants.VM.computeCreateAddress(address(this), 1)); 35 | } 36 | } 37 | 38 | contract Dummy {} 39 | -------------------------------------------------------------------------------- /test/StdError.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import {stdError} from "../src/StdError.sol"; 5 | import {Test} from "../src/Test.sol"; 6 | 7 | contract StdErrorsTest is Test { 8 | ErrorsTest test; 9 | 10 | function setUp() public { 11 | test = new ErrorsTest(); 12 | } 13 | 14 | function test_RevertIf_AssertionError() public { 15 | vm.expectRevert(stdError.assertionError); 16 | test.assertionError(); 17 | } 18 | 19 | function test_RevertIf_ArithmeticError() public { 20 | vm.expectRevert(stdError.arithmeticError); 21 | test.arithmeticError(10); 22 | } 23 | 24 | function test_RevertIf_DivisionError() public { 25 | vm.expectRevert(stdError.divisionError); 26 | test.divError(0); 27 | } 28 | 29 | function test_RevertIf_ModError() public { 30 | vm.expectRevert(stdError.divisionError); 31 | test.modError(0); 32 | } 33 | 34 | function test_RevertIf_EnumConversionError() public { 35 | vm.expectRevert(stdError.enumConversionError); 36 | test.enumConversion(1); 37 | } 38 | 39 | function test_RevertIf_EncodeStgError() public { 40 | vm.expectRevert(stdError.encodeStorageError); 41 | test.encodeStgError(); 42 | } 43 | 44 | function test_RevertIf_PopError() public { 45 | vm.expectRevert(stdError.popError); 46 | test.pop(); 47 | } 48 | 49 | function test_RevertIf_IndexOOBError() public { 50 | vm.expectRevert(stdError.indexOOBError); 51 | test.indexOOBError(1); 52 | } 53 | 54 | function test_RevertIf_MemOverflowError() public { 55 | vm.expectRevert(stdError.memOverflowError); 56 | test.mem(); 57 | } 58 | 59 | function test_RevertIf_InternError() public { 60 | vm.expectRevert(stdError.zeroVarError); 61 | test.intern(); 62 | } 63 | } 64 | 65 | contract ErrorsTest { 66 | enum T { 67 | T1 68 | } 69 | 70 | uint256[] public someArr; 71 | bytes someBytes; 72 | 73 | function assertionError() public pure { 74 | assert(false); 75 | } 76 | 77 | function arithmeticError(uint256 a) public pure { 78 | a -= 100; 79 | } 80 | 81 | function divError(uint256 a) public pure { 82 | 100 / a; 83 | } 84 | 85 | function modError(uint256 a) public pure { 86 | 100 % a; 87 | } 88 | 89 | function enumConversion(uint256 a) public pure { 90 | T(a); 91 | } 92 | 93 | function encodeStgError() public { 94 | /// @solidity memory-safe-assembly 95 | assembly { 96 | sstore(someBytes.slot, 1) 97 | } 98 | keccak256(someBytes); 99 | } 100 | 101 | function pop() public { 102 | someArr.pop(); 103 | } 104 | 105 | function indexOOBError(uint256 a) public pure { 106 | uint256[] memory t = new uint256[](0); 107 | t[a]; 108 | } 109 | 110 | function mem() public pure { 111 | uint256 l = 2 ** 256 / 32; 112 | new uint256[](l); 113 | } 114 | 115 | function intern() public returns (uint256) { 116 | function(uint256) internal returns (uint256) x; 117 | x(2); 118 | return 7; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /test/StdJson.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {Test, stdJson} from "../src/Test.sol"; 5 | 6 | contract StdJsonTest is Test { 7 | using stdJson for string; 8 | 9 | string root; 10 | string path; 11 | 12 | function setUp() public { 13 | root = vm.projectRoot(); 14 | path = string.concat(root, "/test/fixtures/test.json"); 15 | } 16 | 17 | struct SimpleJson { 18 | uint256 a; 19 | string b; 20 | } 21 | 22 | struct NestedJson { 23 | uint256 a; 24 | string b; 25 | SimpleJson c; 26 | } 27 | 28 | function test_readJson() public view { 29 | string memory json = vm.readFile(path); 30 | assertEq(json.readUint(".a"), 123); 31 | } 32 | 33 | function test_writeJson() public { 34 | string memory json = "json"; 35 | json.serialize("a", uint256(123)); 36 | string memory semiFinal = json.serialize("b", string("test")); 37 | string memory finalJson = json.serialize("c", semiFinal); 38 | finalJson.write(path); 39 | 40 | string memory json_ = vm.readFile(path); 41 | bytes memory data = json_.parseRaw("$"); 42 | NestedJson memory decodedData = abi.decode(data, (NestedJson)); 43 | 44 | assertEq(decodedData.a, 123); 45 | assertEq(decodedData.b, "test"); 46 | assertEq(decodedData.c.a, 123); 47 | assertEq(decodedData.c.b, "test"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/StdMath.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import {stdMath} from "../src/StdMath.sol"; 5 | import {Test, stdError} from "../src/Test.sol"; 6 | 7 | contract StdMathMock is Test { 8 | function exposed_percentDelta(uint256 a, uint256 b) public pure returns (uint256) { 9 | return stdMath.percentDelta(a, b); 10 | } 11 | 12 | function exposed_percentDelta(int256 a, int256 b) public pure returns (uint256) { 13 | return stdMath.percentDelta(a, b); 14 | } 15 | } 16 | 17 | contract StdMathTest is Test { 18 | function test_GetAbs() external pure { 19 | assertEq(stdMath.abs(-50), 50); 20 | assertEq(stdMath.abs(50), 50); 21 | assertEq(stdMath.abs(-1337), 1337); 22 | assertEq(stdMath.abs(0), 0); 23 | 24 | assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1); 25 | assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1)); 26 | } 27 | 28 | function testFuzz_GetAbs(int256 a) external pure { 29 | uint256 manualAbs = getAbs(a); 30 | 31 | uint256 abs = stdMath.abs(a); 32 | 33 | assertEq(abs, manualAbs); 34 | } 35 | 36 | function test_GetDelta_Uint() external pure { 37 | assertEq(stdMath.delta(uint256(0), uint256(0)), 0); 38 | assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337); 39 | assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max); 40 | assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max); 41 | assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max); 42 | 43 | assertEq(stdMath.delta(0, uint256(0)), 0); 44 | assertEq(stdMath.delta(1337, uint256(0)), 1337); 45 | assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max); 46 | assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max); 47 | assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max); 48 | 49 | assertEq(stdMath.delta(1337, uint256(1337)), 0); 50 | assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0); 51 | assertEq(stdMath.delta(5000, uint256(1250)), 3750); 52 | } 53 | 54 | function testFuzz_GetDelta_Uint(uint256 a, uint256 b) external pure { 55 | uint256 manualDelta = a > b ? a - b : b - a; 56 | 57 | uint256 delta = stdMath.delta(a, b); 58 | 59 | assertEq(delta, manualDelta); 60 | } 61 | 62 | function test_GetDelta_Int() external pure { 63 | assertEq(stdMath.delta(int256(0), int256(0)), 0); 64 | assertEq(stdMath.delta(int256(0), int256(1337)), 1337); 65 | assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1); 66 | assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1); 67 | assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1); 68 | 69 | assertEq(stdMath.delta(0, int256(0)), 0); 70 | assertEq(stdMath.delta(1337, int256(0)), 1337); 71 | assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1); 72 | assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1); 73 | assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1); 74 | 75 | assertEq(stdMath.delta(-0, int256(0)), 0); 76 | assertEq(stdMath.delta(-1337, int256(0)), 1337); 77 | assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1); 78 | assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1); 79 | assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1); 80 | 81 | assertEq(stdMath.delta(int256(0), -0), 0); 82 | assertEq(stdMath.delta(int256(0), -1337), 1337); 83 | assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1); 84 | assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1); 85 | assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1); 86 | 87 | assertEq(stdMath.delta(1337, int256(1337)), 0); 88 | assertEq(stdMath.delta(type(int256).max, type(int256).max), 0); 89 | assertEq(stdMath.delta(type(int256).min, type(int256).min), 0); 90 | assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max); 91 | assertEq(stdMath.delta(5000, int256(1250)), 3750); 92 | } 93 | 94 | function testFuzz_GetDelta_Int(int256 a, int256 b) external pure { 95 | uint256 absA = getAbs(a); 96 | uint256 absB = getAbs(b); 97 | uint256 absDelta = absA > absB ? absA - absB : absB - absA; 98 | 99 | uint256 manualDelta; 100 | if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { 101 | manualDelta = absDelta; 102 | } 103 | // (a < 0 && b >= 0) || (a >= 0 && b < 0) 104 | else { 105 | manualDelta = absA + absB; 106 | } 107 | 108 | uint256 delta = stdMath.delta(a, b); 109 | 110 | assertEq(delta, manualDelta); 111 | } 112 | 113 | function test_GetPercentDelta_Uint() external { 114 | StdMathMock stdMathMock = new StdMathMock(); 115 | 116 | assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18); 117 | assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18); 118 | assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18); 119 | assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18); 120 | 121 | assertEq(stdMath.percentDelta(1337, uint256(1337)), 0); 122 | assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0); 123 | assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18); 124 | assertEq(stdMath.percentDelta(2500, uint256(2500)), 0); 125 | assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18); 126 | assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18); 127 | 128 | vm.expectRevert(stdError.divisionError); 129 | stdMathMock.exposed_percentDelta(uint256(1), 0); 130 | } 131 | 132 | function testFuzz_GetPercentDelta_Uint(uint192 a, uint192 b) external pure { 133 | vm.assume(b != 0); 134 | uint256 manualDelta = a > b ? a - b : b - a; 135 | 136 | uint256 manualPercentDelta = manualDelta * 1e18 / b; 137 | uint256 percentDelta = stdMath.percentDelta(a, b); 138 | 139 | assertEq(percentDelta, manualPercentDelta); 140 | } 141 | 142 | function test_GetPercentDelta_Int() external { 143 | // We deploy a mock version so we can properly test the revert. 144 | StdMathMock stdMathMock = new StdMathMock(); 145 | 146 | assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18); 147 | assertEq(stdMath.percentDelta(int256(0), -1337), 1e18); 148 | assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18); 149 | assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18); 150 | assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18); 151 | assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18); 152 | assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18); 153 | assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18); 154 | 155 | assertEq(stdMath.percentDelta(1337, int256(1337)), 0); 156 | assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0); 157 | assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0); 158 | 159 | assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down 160 | assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down 161 | assertEq(stdMath.percentDelta(0, int256(2500)), 1e18); 162 | assertEq(stdMath.percentDelta(2500, int256(2500)), 0); 163 | assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18); 164 | assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18); 165 | 166 | vm.expectRevert(stdError.divisionError); 167 | stdMathMock.exposed_percentDelta(int256(1), 0); 168 | } 169 | 170 | function testFuzz_GetPercentDelta_Int(int192 a, int192 b) external pure { 171 | vm.assume(b != 0); 172 | uint256 absA = getAbs(a); 173 | uint256 absB = getAbs(b); 174 | uint256 absDelta = absA > absB ? absA - absB : absB - absA; 175 | 176 | uint256 manualDelta; 177 | if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { 178 | manualDelta = absDelta; 179 | } 180 | // (a < 0 && b >= 0) || (a >= 0 && b < 0) 181 | else { 182 | manualDelta = absA + absB; 183 | } 184 | 185 | uint256 manualPercentDelta = manualDelta * 1e18 / absB; 186 | uint256 percentDelta = stdMath.percentDelta(a, b); 187 | 188 | assertEq(percentDelta, manualPercentDelta); 189 | } 190 | 191 | /*////////////////////////////////////////////////////////////////////////// 192 | HELPERS 193 | //////////////////////////////////////////////////////////////////////////*/ 194 | 195 | function getAbs(int256 a) private pure returns (uint256) { 196 | if (a < 0) { 197 | return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a); 198 | } 199 | 200 | return uint256(a); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /test/StdStyle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {Test, console2, StdStyle} from "../src/Test.sol"; 5 | 6 | contract StdStyleTest is Test { 7 | function test_StyleColor() public pure { 8 | console2.log(StdStyle.red("StdStyle.red String Test")); 9 | console2.log(StdStyle.red(uint256(10e18))); 10 | console2.log(StdStyle.red(int256(-10e18))); 11 | console2.log(StdStyle.red(true)); 12 | console2.log(StdStyle.red(address(0))); 13 | console2.log(StdStyle.redBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 14 | console2.log(StdStyle.redBytes32("StdStyle.redBytes32")); 15 | console2.log(StdStyle.green("StdStyle.green String Test")); 16 | console2.log(StdStyle.green(uint256(10e18))); 17 | console2.log(StdStyle.green(int256(-10e18))); 18 | console2.log(StdStyle.green(true)); 19 | console2.log(StdStyle.green(address(0))); 20 | console2.log(StdStyle.greenBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 21 | console2.log(StdStyle.greenBytes32("StdStyle.greenBytes32")); 22 | console2.log(StdStyle.yellow("StdStyle.yellow String Test")); 23 | console2.log(StdStyle.yellow(uint256(10e18))); 24 | console2.log(StdStyle.yellow(int256(-10e18))); 25 | console2.log(StdStyle.yellow(true)); 26 | console2.log(StdStyle.yellow(address(0))); 27 | console2.log(StdStyle.yellowBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 28 | console2.log(StdStyle.yellowBytes32("StdStyle.yellowBytes32")); 29 | console2.log(StdStyle.blue("StdStyle.blue String Test")); 30 | console2.log(StdStyle.blue(uint256(10e18))); 31 | console2.log(StdStyle.blue(int256(-10e18))); 32 | console2.log(StdStyle.blue(true)); 33 | console2.log(StdStyle.blue(address(0))); 34 | console2.log(StdStyle.blueBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 35 | console2.log(StdStyle.blueBytes32("StdStyle.blueBytes32")); 36 | console2.log(StdStyle.magenta("StdStyle.magenta String Test")); 37 | console2.log(StdStyle.magenta(uint256(10e18))); 38 | console2.log(StdStyle.magenta(int256(-10e18))); 39 | console2.log(StdStyle.magenta(true)); 40 | console2.log(StdStyle.magenta(address(0))); 41 | console2.log(StdStyle.magentaBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 42 | console2.log(StdStyle.magentaBytes32("StdStyle.magentaBytes32")); 43 | console2.log(StdStyle.cyan("StdStyle.cyan String Test")); 44 | console2.log(StdStyle.cyan(uint256(10e18))); 45 | console2.log(StdStyle.cyan(int256(-10e18))); 46 | console2.log(StdStyle.cyan(true)); 47 | console2.log(StdStyle.cyan(address(0))); 48 | console2.log(StdStyle.cyanBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 49 | console2.log(StdStyle.cyanBytes32("StdStyle.cyanBytes32")); 50 | } 51 | 52 | function test_StyleFontWeight() public pure { 53 | console2.log(StdStyle.bold("StdStyle.bold String Test")); 54 | console2.log(StdStyle.bold(uint256(10e18))); 55 | console2.log(StdStyle.bold(int256(-10e18))); 56 | console2.log(StdStyle.bold(address(0))); 57 | console2.log(StdStyle.bold(true)); 58 | console2.log(StdStyle.boldBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 59 | console2.log(StdStyle.boldBytes32("StdStyle.boldBytes32")); 60 | console2.log(StdStyle.dim("StdStyle.dim String Test")); 61 | console2.log(StdStyle.dim(uint256(10e18))); 62 | console2.log(StdStyle.dim(int256(-10e18))); 63 | console2.log(StdStyle.dim(address(0))); 64 | console2.log(StdStyle.dim(true)); 65 | console2.log(StdStyle.dimBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 66 | console2.log(StdStyle.dimBytes32("StdStyle.dimBytes32")); 67 | console2.log(StdStyle.italic("StdStyle.italic String Test")); 68 | console2.log(StdStyle.italic(uint256(10e18))); 69 | console2.log(StdStyle.italic(int256(-10e18))); 70 | console2.log(StdStyle.italic(address(0))); 71 | console2.log(StdStyle.italic(true)); 72 | console2.log(StdStyle.italicBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 73 | console2.log(StdStyle.italicBytes32("StdStyle.italicBytes32")); 74 | console2.log(StdStyle.underline("StdStyle.underline String Test")); 75 | console2.log(StdStyle.underline(uint256(10e18))); 76 | console2.log(StdStyle.underline(int256(-10e18))); 77 | console2.log(StdStyle.underline(address(0))); 78 | console2.log(StdStyle.underline(true)); 79 | console2.log(StdStyle.underlineBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 80 | console2.log(StdStyle.underlineBytes32("StdStyle.underlineBytes32")); 81 | console2.log(StdStyle.inverse("StdStyle.inverse String Test")); 82 | console2.log(StdStyle.inverse(uint256(10e18))); 83 | console2.log(StdStyle.inverse(int256(-10e18))); 84 | console2.log(StdStyle.inverse(address(0))); 85 | console2.log(StdStyle.inverse(true)); 86 | console2.log(StdStyle.inverseBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 87 | console2.log(StdStyle.inverseBytes32("StdStyle.inverseBytes32")); 88 | } 89 | 90 | function test_StyleCombined() public pure { 91 | console2.log(StdStyle.red(StdStyle.bold("Red Bold String Test"))); 92 | console2.log(StdStyle.green(StdStyle.dim(uint256(10e18)))); 93 | console2.log(StdStyle.yellow(StdStyle.italic(int256(-10e18)))); 94 | console2.log(StdStyle.blue(StdStyle.underline(address(0)))); 95 | console2.log(StdStyle.magenta(StdStyle.inverse(true))); 96 | } 97 | 98 | function test_StyleCustom() public pure { 99 | console2.log(h1("Custom Style 1")); 100 | console2.log(h2("Custom Style 2")); 101 | } 102 | 103 | function h1(string memory a) private pure returns (string memory) { 104 | return StdStyle.cyan(StdStyle.inverse(StdStyle.bold(a))); 105 | } 106 | 107 | function h2(string memory a) private pure returns (string memory) { 108 | return StdStyle.magenta(StdStyle.bold(StdStyle.underline(a))); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/StdToml.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {Test, stdToml} from "../src/Test.sol"; 5 | 6 | contract StdTomlTest is Test { 7 | using stdToml for string; 8 | 9 | string root; 10 | string path; 11 | 12 | function setUp() public { 13 | root = vm.projectRoot(); 14 | path = string.concat(root, "/test/fixtures/test.toml"); 15 | } 16 | 17 | struct SimpleToml { 18 | uint256 a; 19 | string b; 20 | } 21 | 22 | struct NestedToml { 23 | uint256 a; 24 | string b; 25 | SimpleToml c; 26 | } 27 | 28 | function test_readToml() public view { 29 | string memory json = vm.readFile(path); 30 | assertEq(json.readUint(".a"), 123); 31 | } 32 | 33 | function test_writeToml() public { 34 | string memory json = "json"; 35 | json.serialize("a", uint256(123)); 36 | string memory semiFinal = json.serialize("b", string("test")); 37 | string memory finalJson = json.serialize("c", semiFinal); 38 | finalJson.write(path); 39 | 40 | string memory toml = vm.readFile(path); 41 | bytes memory data = toml.parseRaw("$"); 42 | NestedToml memory decodedData = abi.decode(data, (NestedToml)); 43 | 44 | assertEq(decodedData.a, 123); 45 | assertEq(decodedData.b, "test"); 46 | assertEq(decodedData.c.a, 123); 47 | assertEq(decodedData.c.b, "test"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/Vm.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import {Test} from "../src/Test.sol"; 5 | import {Vm, VmSafe} from "../src/Vm.sol"; 6 | 7 | // These tests ensure that functions are never accidentally removed from a Vm interface, or 8 | // inadvertently moved between Vm and VmSafe. These tests must be updated each time a function is 9 | // added to or removed from Vm or VmSafe. 10 | contract VmTest is Test { 11 | function test_VmInterfaceId() public pure { 12 | assertEq(type(Vm).interfaceId, bytes4(0xe835828d), "Vm"); 13 | } 14 | 15 | function test_VmSafeInterfaceId() public pure { 16 | assertEq(type(VmSafe).interfaceId, bytes4(0x4d98a4ca), "VmSafe"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/compilation/CompilationScript.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {Script} from "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScript is Script {} 11 | -------------------------------------------------------------------------------- /test/compilation/CompilationScriptBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {ScriptBase} from "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScriptBase is ScriptBase {} 11 | -------------------------------------------------------------------------------- /test/compilation/CompilationTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {Test} from "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTest is Test {} 11 | -------------------------------------------------------------------------------- /test/compilation/CompilationTestBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {TestBase} from "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTestBase is TestBase {} 11 | -------------------------------------------------------------------------------- /test/fixtures/broadcast.log.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactions": [ 3 | { 4 | "hash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", 5 | "type": "CALL", 6 | "contractName": "Test", 7 | "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", 8 | "function": "multiple_arguments(uint256,address,uint256[]):(uint256)", 9 | "arguments": ["1", "0000000000000000000000000000000000001337", "[3,4]"], 10 | "tx": { 11 | "type": "0x02", 12 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 13 | "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", 14 | "gas": "0x73b9", 15 | "value": "0x0", 16 | "data": "0x23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004", 17 | "nonce": "0x3", 18 | "accessList": [] 19 | } 20 | }, 21 | { 22 | "hash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", 23 | "type": "CALL", 24 | "contractName": "Test", 25 | "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", 26 | "function": "inc():(uint256)", 27 | "arguments": [], 28 | "tx": { 29 | "type": "0x02", 30 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 31 | "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", 32 | "gas": "0xdcb2", 33 | "value": "0x0", 34 | "data": "0x371303c0", 35 | "nonce": "0x4", 36 | "accessList": [] 37 | } 38 | }, 39 | { 40 | "hash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", 41 | "type": "CALL", 42 | "contractName": "Test", 43 | "contractAddress": "0x7c6b4bbe207d642d98d5c537142d85209e585087", 44 | "function": "t(uint256):(uint256)", 45 | "arguments": ["1"], 46 | "tx": { 47 | "type": "0x02", 48 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 49 | "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", 50 | "gas": "0x8599", 51 | "value": "0x0", 52 | "data": "0xafe29f710000000000000000000000000000000000000000000000000000000000000001", 53 | "nonce": "0x5", 54 | "accessList": [] 55 | } 56 | } 57 | ], 58 | "receipts": [ 59 | { 60 | "transactionHash": "0x481dc86e40bba90403c76f8e144aa9ff04c1da2164299d0298573835f0991181", 61 | "transactionIndex": "0x0", 62 | "blockHash": "0xef0730448490304e5403be0fa8f8ce64f118e9adcca60c07a2ae1ab921d748af", 63 | "blockNumber": "0x1", 64 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 65 | "to": null, 66 | "cumulativeGasUsed": "0x13f3a", 67 | "gasUsed": "0x13f3a", 68 | "contractAddress": "0x5fbdb2315678afecb367f032d93f642f64180aa3", 69 | "logs": [], 70 | "status": "0x1", 71 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 72 | "effectiveGasPrice": "0xee6b2800" 73 | }, 74 | { 75 | "transactionHash": "0x6a187183545b8a9e7f1790e847139379bf5622baff2cb43acf3f5c79470af782", 76 | "transactionIndex": "0x0", 77 | "blockHash": "0xf3acb96a90071640c2a8c067ae4e16aad87e634ea8d8bbbb5b352fba86ba0148", 78 | "blockNumber": "0x2", 79 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 80 | "to": null, 81 | "cumulativeGasUsed": "0x45d80", 82 | "gasUsed": "0x45d80", 83 | "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", 84 | "logs": [], 85 | "status": "0x1", 86 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 87 | "effectiveGasPrice": "0xee6b2800" 88 | }, 89 | { 90 | "transactionHash": "0x064ad173b4867bdef2fb60060bbdaf01735fbf10414541ea857772974e74ea9d", 91 | "transactionIndex": "0x0", 92 | "blockHash": "0x8373d02109d3ee06a0225f23da4c161c656ccc48fe0fcee931d325508ae73e58", 93 | "blockNumber": "0x3", 94 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 95 | "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", 96 | "cumulativeGasUsed": "0x45feb", 97 | "gasUsed": "0x45feb", 98 | "contractAddress": null, 99 | "logs": [], 100 | "status": "0x1", 101 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 102 | "effectiveGasPrice": "0xee6b2800" 103 | }, 104 | { 105 | "transactionHash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", 106 | "transactionIndex": "0x0", 107 | "blockHash": "0x16712fae5c0e18f75045f84363fb6b4d9a9fe25e660c4ce286833a533c97f629", 108 | "blockNumber": "0x4", 109 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 110 | "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", 111 | "cumulativeGasUsed": "0x5905", 112 | "gasUsed": "0x5905", 113 | "contractAddress": null, 114 | "logs": [], 115 | "status": "0x1", 116 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 117 | "effectiveGasPrice": "0xee6b2800" 118 | }, 119 | { 120 | "transactionHash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", 121 | "transactionIndex": "0x0", 122 | "blockHash": "0x156b88c3eb9a1244ba00a1834f3f70de735b39e3e59006dd03af4fe7d5480c11", 123 | "blockNumber": "0x5", 124 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 125 | "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", 126 | "cumulativeGasUsed": "0xa9c4", 127 | "gasUsed": "0xa9c4", 128 | "contractAddress": null, 129 | "logs": [], 130 | "status": "0x1", 131 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 132 | "effectiveGasPrice": "0xee6b2800" 133 | }, 134 | { 135 | "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", 136 | "transactionIndex": "0x0", 137 | "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", 138 | "blockNumber": "0x6", 139 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 140 | "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", 141 | "cumulativeGasUsed": "0x66c5", 142 | "gasUsed": "0x66c5", 143 | "contractAddress": null, 144 | "logs": [ 145 | { 146 | "address": "0x7c6b4bbe207d642d98d5c537142d85209e585087", 147 | "topics": [ 148 | "0x0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b" 149 | ], 150 | "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000", 151 | "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", 152 | "blockNumber": "0x6", 153 | "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", 154 | "transactionIndex": "0x1", 155 | "logIndex": "0x0", 156 | "transactionLogIndex": "0x0", 157 | "removed": false 158 | } 159 | ], 160 | "status": "0x1", 161 | "logsBloom": "0x00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100", 162 | "effectiveGasPrice": "0xee6b2800" 163 | }, 164 | { 165 | "transactionHash": "0x11fbb10230c168ca1e36a7e5c69a6dbcd04fd9e64ede39d10a83e36ee8065c16", 166 | "transactionIndex": "0x0", 167 | "blockHash": "0xf1e0ed2eda4e923626ec74621006ed50b3fc27580dc7b4cf68a07ca77420e29c", 168 | "blockNumber": "0x7", 169 | "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 170 | "to": "0x0000000000000000000000000000000000001337", 171 | "cumulativeGasUsed": "0x5208", 172 | "gasUsed": "0x5208", 173 | "contractAddress": null, 174 | "logs": [], 175 | "status": "0x1", 176 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 177 | "effectiveGasPrice": "0xee6b2800" 178 | } 179 | ], 180 | "libraries": [ 181 | "src/Broadcast.t.sol:F:0x5fbdb2315678afecb367f032d93f642f64180aa3" 182 | ], 183 | "pending": [], 184 | "path": "broadcast/Broadcast.t.sol/31337/run-latest.json", 185 | "returns": {}, 186 | "timestamp": 1655140035 187 | } 188 | -------------------------------------------------------------------------------- /test/fixtures/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": 123, 3 | "b": "test", 4 | "c": { 5 | "a": 123, 6 | "b": "test" 7 | } 8 | } -------------------------------------------------------------------------------- /test/fixtures/test.toml: -------------------------------------------------------------------------------- 1 | a = 123 2 | b = "test" 3 | 4 | [c] 5 | a = 123 6 | b = "test" 7 | --------------------------------------------------------------------------------