├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── LEGAL.txt ├── LICENSE ├── README.md ├── foundry.toml ├── lib └── forge-std │ ├── .github │ └── workflows │ │ ├── ci.yml │ │ └── sync.yml │ ├── .gitignore │ ├── .gitmodules │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── foundry.toml │ ├── lib │ └── ds-test │ │ ├── .github │ │ └── workflows │ │ │ └── build.yml │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── default.nix │ │ ├── demo │ │ └── demo.sol │ │ ├── package.json │ │ └── src │ │ ├── test.sol │ │ └── test.t.sol │ ├── package.json │ ├── src │ ├── Base.sol │ ├── Script.sol │ ├── StdAssertions.sol │ ├── StdChains.sol │ ├── StdCheats.sol │ ├── StdError.sol │ ├── StdInvariant.sol │ ├── StdJson.sol │ ├── StdMath.sol │ ├── StdStorage.sol │ ├── StdStyle.sol │ ├── StdUtils.sol │ ├── Test.sol │ ├── Vm.sol │ ├── console.sol │ ├── console2.sol │ ├── interfaces │ │ ├── IERC1155.sol │ │ ├── IERC165.sol │ │ ├── IERC20.sol │ │ ├── IERC4626.sol │ │ ├── IERC721.sol │ │ └── IMulticall3.sol │ └── safeconsole.sol │ └── test │ ├── StdAssertions.t.sol │ ├── StdChains.t.sol │ ├── StdCheats.t.sol │ ├── StdError.t.sol │ ├── StdMath.t.sol │ ├── StdStorage.t.sol │ ├── StdStyle.t.sol │ ├── StdUtils.t.sol │ ├── compilation │ ├── CompilationScript.sol │ ├── CompilationScriptBase.sol │ ├── CompilationTest.sol │ └── CompilationTestBase.sol │ └── fixtures │ └── broadcast.log.json ├── package-lock.json ├── package.json ├── remappings.txt ├── src ├── HelloToken.sol └── example-extensions │ ├── HelloMultipleTokens.sol │ ├── HelloTokenNative.sol │ └── HelloTokenWithoutSDK.sol ├── test ├── HelloMultipleTokens.t.sol ├── HelloToken.t.sol └── HelloTokenNative.t.sol └── ts-scripts ├── deploy-mock-tokens.ts ├── deploy.ts ├── getStatus.ts ├── hello_token.test.ts ├── jest.config.js ├── main.ts ├── testnet ├── config.json ├── contracts.json └── deployedAddresses.json └── utils.ts /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: {} 8 | 9 | env: 10 | FOUNDRY_PROFILE: ci 11 | 12 | jobs: 13 | check: 14 | strategy: 15 | fail-fast: true 16 | 17 | name: Foundry project 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: recursive 23 | 24 | - name: Install Foundry 25 | uses: foundry-rs/foundry-toolchain@v1 26 | with: 27 | version: nightly 28 | 29 | - name: Run Forge build 30 | run: | 31 | forge --version 32 | forge build --sizes 33 | id: build 34 | 35 | - name: Run Forge tests 36 | run: | 37 | forge test -vvv 38 | id: test 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env 15 | node_modules 16 | 17 | # Ethers contracts 18 | ts-scripts/ethers-contracts/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/wormhole-solidity-sdk"] 5 | path = lib/wormhole-solidity-sdk 6 | url = https://github.com/wormhole-foundation/wormhole-solidity-sdk 7 | -------------------------------------------------------------------------------- /LEGAL.txt: -------------------------------------------------------------------------------- 1 | The information and materials available through this repo (collectively, “Materials”) are available on an “AI IS” basis without warranties of any kind, either express or implied, including, but not limited to, warranties of merchantability, title, fitness for a particular purpose and non-infringement. You assume all risks associated with using the Materials, and digital assets and decentralized systems generally, including but not limited to, that: (a) digital assets are highly volatile; (b) using digital assets is inherently risky due to both features of such assets and the potential unauthorized acts of third parties; (c) you may not have ready access to digital assets; and (d) there is a risk of losing your digital assets or those owned by a third party. You agree that you will have no recourse against anyone else for any losses due to the use of the Materials. For example, these losses may arise from or relate to: (i) incorrect information; (ii) software or network failures; (iii) corrupted files; (iv) unauthorized access; (v) errors, mistakes, or inaccuracies; or (vi) third-party activities. 2 | 3 | You are solely responsible for using the Materials in compliance with applicable laws and regulations, including but not limited to, export control or sanctions laws of any applicable jurisdiction. You should be aware that U.S. export control and sanctions laws prohibit U.S. persons (and other persons that are subject to such laws) from transacting with persons in certain countries and territories or that are on the Specially Designated Nationals list. Accordingly, you must avoid providing the Materials to sanctioned persons because such activity could be a violation of U.S. export controls and sanctions law. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Wormhole Project Contributors 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | solc_version = "0.8.13" 3 | src = "src" 4 | out = "out" 5 | libs = ["lib"] 6 | 7 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 8 | [fmt] 9 | line_length = 100 10 | -------------------------------------------------------------------------------- /lib/forge-std/.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@v3 15 | 16 | - name: Install Foundry 17 | uses: onbjerg/foundry-toolchain@v1 18 | with: 19 | version: nightly 20 | 21 | - name: Print forge version 22 | run: forge --version 23 | 24 | # Backwards compatibility checks. 25 | - name: Check compatibility with 0.8.0 26 | if: always() 27 | run: forge build --skip test --use solc:0.8.0 28 | 29 | - name: Check compatibility with 0.7.6 30 | if: always() 31 | run: forge build --skip test --use solc:0.7.6 32 | 33 | - name: Check compatibility with 0.7.0 34 | if: always() 35 | run: forge build --skip test --use solc:0.7.0 36 | 37 | - name: Check compatibility with 0.6.12 38 | if: always() 39 | run: forge build --skip test --use solc:0.6.12 40 | 41 | - name: Check compatibility with 0.6.2 42 | if: always() 43 | run: forge build --skip test --use solc:0.6.2 44 | 45 | # via-ir compilation time checks. 46 | - name: Measure compilation time of Test with 0.8.17 --via-ir 47 | if: always() 48 | run: forge build --skip test --contracts test/compilation/CompilationTest.sol --use solc:0.8.17 --via-ir 49 | 50 | - name: Measure compilation time of TestBase with 0.8.17 --via-ir 51 | if: always() 52 | run: forge build --skip test --contracts test/compilation/CompilationTestBase.sol --use solc:0.8.17 --via-ir 53 | 54 | - name: Measure compilation time of Script with 0.8.17 --via-ir 55 | if: always() 56 | run: forge build --skip test --contracts test/compilation/CompilationScript.sol --use solc:0.8.17 --via-ir 57 | 58 | - name: Measure compilation time of ScriptBase with 0.8.17 --via-ir 59 | if: always() 60 | run: forge build --skip test --contracts test/compilation/CompilationScriptBase.sol --use solc:0.8.17 --via-ir 61 | 62 | test: 63 | runs-on: ubuntu-latest 64 | steps: 65 | - uses: actions/checkout@v3 66 | 67 | - name: Install Foundry 68 | uses: onbjerg/foundry-toolchain@v1 69 | with: 70 | version: nightly 71 | 72 | - name: Print forge version 73 | run: forge --version 74 | 75 | - name: Run tests 76 | run: forge test -vvv 77 | 78 | fmt: 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v3 82 | 83 | - name: Install Foundry 84 | uses: onbjerg/foundry-toolchain@v1 85 | with: 86 | version: nightly 87 | 88 | - name: Print forge version 89 | run: forge --version 90 | 91 | - name: Check formatting 92 | run: forge fmt --check 93 | -------------------------------------------------------------------------------- /lib/forge-std/.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@v3 15 | with: 16 | fetch-depth: 0 17 | ref: v1 18 | 19 | - name: Configure Git 20 | run: | 21 | git config user.name github-actions[bot] 22 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 23 | 24 | - name: Sync Release Branch 25 | run: | 26 | git fetch --tags 27 | git checkout v1 28 | git reset --hard ${GITHUB_REF} 29 | git push --force 30 | -------------------------------------------------------------------------------- /lib/forge-std/.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ 3 | .vscode 4 | .idea 5 | -------------------------------------------------------------------------------- /lib/forge-std/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/ds-test"] 2 | path = lib/ds-test 3 | url = https://github.com/dapphub/ds-test 4 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /lib/forge-std/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 builtin 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 | uint256 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 address 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 | Expand upon the assertion functions from the `DSTest` library. 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 | ## License 249 | 250 | Forge Standard Library is offered under either [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE) license. 251 | -------------------------------------------------------------------------------- /lib/forge-std/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | fs_permissions = [{ access = "read-write", path = "./"}] 3 | 4 | [rpc_endpoints] 5 | # The RPC URLs are modified versions of the default for testing initialization. 6 | mainnet = "https://mainnet.infura.io/v3/b1d3925804e74152b316ca7da97060d3" # Different API key. 7 | optimism_goerli = "https://goerli.optimism.io/" # Adds a trailing slash. 8 | arbitrum_one_goerli = "https://goerli-rollup.arbitrum.io/rpc/" # Adds a trailing slash. 9 | needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}" 10 | 11 | [fmt] 12 | # These are all the `forge fmt` defaults. 13 | line_length = 120 14 | tab_width = 4 15 | bracket_spacing = false 16 | int_types = 'long' 17 | multiline_func_header = 'attributes_first' 18 | quote_style = 'double' 19 | number_underscore = 'preserve' 20 | single_line_statement_blocks = 'preserve' 21 | ignore = ["src/console.sol", "src/console2.sol"] -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: cachix/install-nix-action@v20 11 | with: 12 | nix_path: nixpkgs=channel:nixos-unstable 13 | extra_nix_config: | 14 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 15 | 16 | - name: setup dapp binary cache 17 | uses: cachix/cachix-action@v12 18 | with: 19 | name: dapp 20 | 21 | - name: install dapptools 22 | run: nix profile install github:dapphub/dapptools#dapp --accept-flake-config 23 | 24 | - name: install foundry 25 | uses: foundry-rs/foundry-toolchain@v1 26 | 27 | - name: test with solc-0.5.17 28 | run: dapp --use solc-0.5.17 test -v 29 | 30 | - name: test with solc-0.6.11 31 | run: dapp --use solc-0.6.11 test -v 32 | 33 | - name: test with solc-0.7.6 34 | run: dapp --use solc-0.7.6 test -v 35 | 36 | - name: test with solc-0.8.18 37 | run: dapp --use solc-0.8.18 test -v 38 | 39 | - name: Run tests with foundry 40 | run: forge test -vvv 41 | 42 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/.gitignore: -------------------------------------------------------------------------------- 1 | /.dapple 2 | /build 3 | /out 4 | /cache/ 5 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/Makefile: -------------------------------------------------------------------------------- 1 | all:; dapp build 2 | 3 | test: 4 | -dapp --use solc:0.4.23 build 5 | -dapp --use solc:0.4.26 build 6 | -dapp --use solc:0.5.17 build 7 | -dapp --use solc:0.6.12 build 8 | -dapp --use solc:0.7.5 build 9 | 10 | demo: 11 | DAPP_SRC=demo dapp --use solc:0.7.5 build 12 | -hevm dapp-test --verbose 3 13 | 14 | .PHONY: test demo 15 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/default.nix: -------------------------------------------------------------------------------- 1 | { solidityPackage, dappsys }: solidityPackage { 2 | name = "ds-test"; 3 | src = ./src; 4 | } 5 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/demo/demo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | import "../src/test.sol"; 5 | 6 | contract DemoTest is DSTest { 7 | function test_this() public pure { 8 | require(true); 9 | } 10 | function test_logs() public { 11 | emit log("-- log(string)"); 12 | emit log("a string"); 13 | 14 | emit log("-- log_named_uint(string, uint)"); 15 | emit log_named_uint("uint", 512); 16 | 17 | emit log("-- log_named_int(string, int)"); 18 | emit log_named_int("int", -512); 19 | 20 | emit log("-- log_named_address(string, address)"); 21 | emit log_named_address("address", address(this)); 22 | 23 | emit log("-- log_named_bytes32(string, bytes32)"); 24 | emit log_named_bytes32("bytes32", "a string"); 25 | 26 | emit log("-- log_named_bytes(string, bytes)"); 27 | emit log_named_bytes("bytes", hex"cafefe"); 28 | 29 | emit log("-- log_named_string(string, string)"); 30 | emit log_named_string("string", "a string"); 31 | 32 | emit log("-- log_named_decimal_uint(string, uint, uint)"); 33 | emit log_named_decimal_uint("decimal uint", 1.0e18, 18); 34 | 35 | emit log("-- log_named_decimal_int(string, int, uint)"); 36 | emit log_named_decimal_int("decimal int", -1.0e18, 18); 37 | } 38 | event log_old_named_uint(bytes32,uint); 39 | function test_old_logs() public { 40 | emit log_old_named_uint("key", 500); 41 | emit log_named_bytes32("bkey", "val"); 42 | } 43 | function test_trace() public view { 44 | this.echo("string 1", "string 2"); 45 | } 46 | function test_multiline() public { 47 | emit log("a multiline\\nstring"); 48 | emit log("a multiline string"); 49 | emit log_bytes("a string"); 50 | emit log_bytes("a multiline\nstring"); 51 | emit log_bytes("a multiline\\nstring"); 52 | emit logs(hex"0000"); 53 | emit log_named_bytes("0x0000", hex"0000"); 54 | emit logs(hex"ff"); 55 | } 56 | function echo(string memory s1, string memory s2) public pure 57 | returns (string memory, string memory) 58 | { 59 | return (s1, s2); 60 | } 61 | 62 | function prove_this(uint x) public { 63 | emit log_named_uint("sym x", x); 64 | assertGt(x + 1, 0); 65 | } 66 | 67 | function test_logn() public { 68 | assembly { 69 | log0(0x01, 0x02) 70 | log1(0x01, 0x02, 0x03) 71 | log2(0x01, 0x02, 0x03, 0x04) 72 | log3(0x01, 0x02, 0x03, 0x04, 0x05) 73 | } 74 | } 75 | 76 | event MyEvent(uint, uint indexed, uint, uint indexed); 77 | function test_events() public { 78 | emit MyEvent(1, 2, 3, 4); 79 | } 80 | 81 | function test_asserts() public { 82 | string memory err = "this test has failed!"; 83 | emit log("## assertTrue(bool)\n"); 84 | assertTrue(false); 85 | emit log("\n"); 86 | assertTrue(false, err); 87 | 88 | emit log("\n## assertEq(address,address)\n"); 89 | assertEq(address(this), msg.sender); 90 | emit log("\n"); 91 | assertEq(address(this), msg.sender, err); 92 | 93 | emit log("\n## assertEq32(bytes32,bytes32)\n"); 94 | assertEq32("bytes 1", "bytes 2"); 95 | emit log("\n"); 96 | assertEq32("bytes 1", "bytes 2", err); 97 | 98 | emit log("\n## assertEq(bytes32,bytes32)\n"); 99 | assertEq32("bytes 1", "bytes 2"); 100 | emit log("\n"); 101 | assertEq32("bytes 1", "bytes 2", err); 102 | 103 | emit log("\n## assertEq(uint,uint)\n"); 104 | assertEq(uint(0), 1); 105 | emit log("\n"); 106 | assertEq(uint(0), 1, err); 107 | 108 | emit log("\n## assertEq(int,int)\n"); 109 | assertEq(-1, -2); 110 | emit log("\n"); 111 | assertEq(-1, -2, err); 112 | 113 | emit log("\n## assertEqDecimal(int,int,uint)\n"); 114 | assertEqDecimal(-1.0e18, -1.1e18, 18); 115 | emit log("\n"); 116 | assertEqDecimal(-1.0e18, -1.1e18, 18, err); 117 | 118 | emit log("\n## assertEqDecimal(uint,uint,uint)\n"); 119 | assertEqDecimal(uint(1.0e18), 1.1e18, 18); 120 | emit log("\n"); 121 | assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); 122 | 123 | emit log("\n## assertGt(uint,uint)\n"); 124 | assertGt(uint(0), 0); 125 | emit log("\n"); 126 | assertGt(uint(0), 0, err); 127 | 128 | emit log("\n## assertGt(int,int)\n"); 129 | assertGt(-1, -1); 130 | emit log("\n"); 131 | assertGt(-1, -1, err); 132 | 133 | emit log("\n## assertGtDecimal(int,int,uint)\n"); 134 | assertGtDecimal(-2.0e18, -1.1e18, 18); 135 | emit log("\n"); 136 | assertGtDecimal(-2.0e18, -1.1e18, 18, err); 137 | 138 | emit log("\n## assertGtDecimal(uint,uint,uint)\n"); 139 | assertGtDecimal(uint(1.0e18), 1.1e18, 18); 140 | emit log("\n"); 141 | assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); 142 | 143 | emit log("\n## assertGe(uint,uint)\n"); 144 | assertGe(uint(0), 1); 145 | emit log("\n"); 146 | assertGe(uint(0), 1, err); 147 | 148 | emit log("\n## assertGe(int,int)\n"); 149 | assertGe(-1, 0); 150 | emit log("\n"); 151 | assertGe(-1, 0, err); 152 | 153 | emit log("\n## assertGeDecimal(int,int,uint)\n"); 154 | assertGeDecimal(-2.0e18, -1.1e18, 18); 155 | emit log("\n"); 156 | assertGeDecimal(-2.0e18, -1.1e18, 18, err); 157 | 158 | emit log("\n## assertGeDecimal(uint,uint,uint)\n"); 159 | assertGeDecimal(uint(1.0e18), 1.1e18, 18); 160 | emit log("\n"); 161 | assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); 162 | 163 | emit log("\n## assertLt(uint,uint)\n"); 164 | assertLt(uint(0), 0); 165 | emit log("\n"); 166 | assertLt(uint(0), 0, err); 167 | 168 | emit log("\n## assertLt(int,int)\n"); 169 | assertLt(-1, -1); 170 | emit log("\n"); 171 | assertLt(-1, -1, err); 172 | 173 | emit log("\n## assertLtDecimal(int,int,uint)\n"); 174 | assertLtDecimal(-1.0e18, -1.1e18, 18); 175 | emit log("\n"); 176 | assertLtDecimal(-1.0e18, -1.1e18, 18, err); 177 | 178 | emit log("\n## assertLtDecimal(uint,uint,uint)\n"); 179 | assertLtDecimal(uint(2.0e18), 1.1e18, 18); 180 | emit log("\n"); 181 | assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); 182 | 183 | emit log("\n## assertLe(uint,uint)\n"); 184 | assertLe(uint(1), 0); 185 | emit log("\n"); 186 | assertLe(uint(1), 0, err); 187 | 188 | emit log("\n## assertLe(int,int)\n"); 189 | assertLe(0, -1); 190 | emit log("\n"); 191 | assertLe(0, -1, err); 192 | 193 | emit log("\n## assertLeDecimal(int,int,uint)\n"); 194 | assertLeDecimal(-1.0e18, -1.1e18, 18); 195 | emit log("\n"); 196 | assertLeDecimal(-1.0e18, -1.1e18, 18, err); 197 | 198 | emit log("\n## assertLeDecimal(uint,uint,uint)\n"); 199 | assertLeDecimal(uint(2.0e18), 1.1e18, 18); 200 | emit log("\n"); 201 | assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); 202 | 203 | emit log("\n## assertEq(string,string)\n"); 204 | string memory s1 = "string 1"; 205 | string memory s2 = "string 2"; 206 | assertEq(s1, s2); 207 | emit log("\n"); 208 | assertEq(s1, s2, err); 209 | 210 | emit log("\n## assertEq0(bytes,bytes)\n"); 211 | assertEq0(hex"abcdef01", hex"abcdef02"); 212 | emit log("\n"); 213 | assertEq0(hex"abcdef01", hex"abcdef02", err); 214 | } 215 | } 216 | 217 | contract DemoTestWithSetUp { 218 | function setUp() public { 219 | } 220 | function test_pass() public pure { 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ds-test", 3 | "version": "1.0.0", 4 | "description": "Assertions, equality checks and other test helpers ", 5 | "bugs": "https://github.com/dapphub/ds-test/issues", 6 | "license": "GPL-3.0", 7 | "author": "Contributors to ds-test", 8 | "files": [ 9 | "src/*" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/dapphub/ds-test.git" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/forge-std/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forge-std", 3 | "version": "1.5.6", 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 | -------------------------------------------------------------------------------- /lib/forge-std/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 | // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. 9 | address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); 10 | // console.sol and console2.sol work by executing a staticcall to this address. 11 | address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; 12 | // Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy. 13 | address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 14 | // Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38. 15 | address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller")))); 16 | // Address of the test contract, deployed by the DEFAULT_SENDER. 17 | address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; 18 | // Deterministic deployment address of the Multicall3 contract. 19 | address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11; 20 | // The order of the secp256k1 curve. 21 | uint256 internal constant SECP256K1_ORDER = 22 | 115792089237316195423570985008687907852837564279074904382605163141518161494337; 23 | 24 | uint256 internal constant UINT256_MAX = 25 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 26 | 27 | Vm internal constant vm = Vm(VM_ADDRESS); 28 | StdStorage internal stdstore; 29 | } 30 | 31 | abstract contract TestBase is CommonBase {} 32 | 33 | abstract contract ScriptBase is CommonBase { 34 | VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS); 35 | } 36 | -------------------------------------------------------------------------------- /lib/forge-std/src/Script.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | // 💬 ABOUT 5 | // Standard Library's default Script. 6 | 7 | // 🧩 MODULES 8 | import {ScriptBase} from "./Base.sol"; 9 | import {console} from "./console.sol"; 10 | import {console2} from "./console2.sol"; 11 | import {StdChains} from "./StdChains.sol"; 12 | import {StdCheatsSafe} from "./StdCheats.sol"; 13 | import {stdJson} from "./StdJson.sol"; 14 | import {stdMath} from "./StdMath.sol"; 15 | import {StdStorage, stdStorageSafe} from "./StdStorage.sol"; 16 | import {StdUtils} from "./StdUtils.sol"; 17 | import {VmSafe} from "./Vm.sol"; 18 | 19 | // 📦 BOILERPLATE 20 | import {ScriptBase} from "./Base.sol"; 21 | 22 | // ⭐️ SCRIPT 23 | abstract contract Script is StdChains, StdCheatsSafe, StdUtils, ScriptBase { 24 | // Note: IS_SCRIPT() must return true. 25 | bool public IS_SCRIPT = true; 26 | } 27 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdChains.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import {VmSafe} from "./Vm.sol"; 7 | 8 | /** 9 | * StdChains provides information about EVM compatible chains that can be used in scripts/tests. 10 | * For each chain, the chain's name, chain ID, and a default RPC URL are provided. Chains are 11 | * identified by their alias, which is the same as the alias in the `[rpc_endpoints]` section of 12 | * the `foundry.toml` file. For best UX, ensure the alias in the `foundry.toml` file match the 13 | * alias used in this contract, which can be found as the first argument to the 14 | * `setChainWithDefaultRpcUrl` call in the `initializeStdChains` function. 15 | * 16 | * There are two main ways to use this contract: 17 | * 1. Set a chain with `setChain(string memory chainAlias, ChainData memory chain)` or 18 | * `setChain(string memory chainAlias, Chain memory chain)` 19 | * 2. Get a chain with `getChain(string memory chainAlias)` or `getChain(uint256 chainId)`. 20 | * 21 | * The first time either of those are used, chains are initialized with the default set of RPC URLs. 22 | * This is done in `initializeStdChains`, which uses `setChainWithDefaultRpcUrl`. Defaults are recorded in 23 | * `defaultRpcUrls`. 24 | * 25 | * The `setChain` function is straightforward, and it simply saves off the given chain data. 26 | * 27 | * The `getChain` methods use `getChainWithUpdatedRpcUrl` to return a chain. For example, let's say 28 | * we want to retrieve the RPC URL for `mainnet`: 29 | * - If you have specified data with `setChain`, it will return that. 30 | * - If you have configured a mainnet RPC URL in `foundry.toml`, it will return the URL, provided it 31 | * is valid (e.g. a URL is specified, or an environment variable is given and exists). 32 | * - If neither of the above conditions is met, the default data is returned. 33 | * 34 | * Summarizing the above, the prioritization hierarchy is `setChain` -> `foundry.toml` -> environment variable -> defaults. 35 | */ 36 | abstract contract StdChains { 37 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); 38 | 39 | bool private stdChainsInitialized; 40 | 41 | struct ChainData { 42 | string name; 43 | uint256 chainId; 44 | string rpcUrl; 45 | } 46 | 47 | struct Chain { 48 | // The chain name. 49 | string name; 50 | // The chain's Chain ID. 51 | uint256 chainId; 52 | // The chain's alias. (i.e. what gets specified in `foundry.toml`). 53 | string chainAlias; 54 | // A default RPC endpoint for this chain. 55 | // NOTE: This default RPC URL is included for convenience to facilitate quick tests and 56 | // experimentation. Do not use this RPC URL for production test suites, CI, or other heavy 57 | // usage as you will be throttled and this is a disservice to others who need this endpoint. 58 | string rpcUrl; 59 | } 60 | 61 | // Maps from the chain's alias (matching the alias in the `foundry.toml` file) to chain data. 62 | mapping(string => Chain) private chains; 63 | // Maps from the chain's alias to it's default RPC URL. 64 | mapping(string => string) private defaultRpcUrls; 65 | // Maps from a chain ID to it's alias. 66 | mapping(uint256 => string) private idToAlias; 67 | 68 | bool private fallbackToDefaultRpcUrls = true; 69 | 70 | // The RPC URL will be fetched from config or defaultRpcUrls if possible. 71 | function getChain(string memory chainAlias) internal virtual returns (Chain memory chain) { 72 | require(bytes(chainAlias).length != 0, "StdChains getChain(string): Chain alias cannot be the empty string."); 73 | 74 | initializeStdChains(); 75 | chain = chains[chainAlias]; 76 | require( 77 | chain.chainId != 0, 78 | string(abi.encodePacked("StdChains getChain(string): Chain with alias \"", chainAlias, "\" not found.")) 79 | ); 80 | 81 | chain = getChainWithUpdatedRpcUrl(chainAlias, chain); 82 | } 83 | 84 | function getChain(uint256 chainId) internal virtual returns (Chain memory chain) { 85 | require(chainId != 0, "StdChains getChain(uint256): Chain ID cannot be 0."); 86 | initializeStdChains(); 87 | string memory chainAlias = idToAlias[chainId]; 88 | 89 | chain = chains[chainAlias]; 90 | 91 | require( 92 | chain.chainId != 0, 93 | string(abi.encodePacked("StdChains getChain(uint256): Chain with ID ", vm.toString(chainId), " not found.")) 94 | ); 95 | 96 | chain = getChainWithUpdatedRpcUrl(chainAlias, chain); 97 | } 98 | 99 | // set chain info, with priority to argument's rpcUrl field. 100 | function setChain(string memory chainAlias, ChainData memory chain) internal virtual { 101 | require( 102 | bytes(chainAlias).length != 0, 103 | "StdChains setChain(string,ChainData): Chain alias cannot be the empty string." 104 | ); 105 | 106 | require(chain.chainId != 0, "StdChains setChain(string,ChainData): Chain ID cannot be 0."); 107 | 108 | initializeStdChains(); 109 | string memory foundAlias = idToAlias[chain.chainId]; 110 | 111 | require( 112 | bytes(foundAlias).length == 0 || keccak256(bytes(foundAlias)) == keccak256(bytes(chainAlias)), 113 | string( 114 | abi.encodePacked( 115 | "StdChains setChain(string,ChainData): Chain ID ", 116 | vm.toString(chain.chainId), 117 | " already used by \"", 118 | foundAlias, 119 | "\"." 120 | ) 121 | ) 122 | ); 123 | 124 | uint256 oldChainId = chains[chainAlias].chainId; 125 | delete idToAlias[oldChainId]; 126 | 127 | chains[chainAlias] = 128 | Chain({name: chain.name, chainId: chain.chainId, chainAlias: chainAlias, rpcUrl: chain.rpcUrl}); 129 | idToAlias[chain.chainId] = chainAlias; 130 | } 131 | 132 | // set chain info, with priority to argument's rpcUrl field. 133 | function setChain(string memory chainAlias, Chain memory chain) internal virtual { 134 | setChain(chainAlias, ChainData({name: chain.name, chainId: chain.chainId, rpcUrl: chain.rpcUrl})); 135 | } 136 | 137 | function _toUpper(string memory str) private pure returns (string memory) { 138 | bytes memory strb = bytes(str); 139 | bytes memory copy = new bytes(strb.length); 140 | for (uint256 i = 0; i < strb.length; i++) { 141 | bytes1 b = strb[i]; 142 | if (b >= 0x61 && b <= 0x7A) { 143 | copy[i] = bytes1(uint8(b) - 32); 144 | } else { 145 | copy[i] = b; 146 | } 147 | } 148 | return string(copy); 149 | } 150 | 151 | // lookup rpcUrl, in descending order of priority: 152 | // current -> config (foundry.toml) -> environment variable -> default 153 | function getChainWithUpdatedRpcUrl(string memory chainAlias, Chain memory chain) private returns (Chain memory) { 154 | if (bytes(chain.rpcUrl).length == 0) { 155 | try vm.rpcUrl(chainAlias) returns (string memory configRpcUrl) { 156 | chain.rpcUrl = configRpcUrl; 157 | } catch (bytes memory err) { 158 | string memory envName = string(abi.encodePacked(_toUpper(chainAlias), "_RPC_URL")); 159 | if (fallbackToDefaultRpcUrls) { 160 | chain.rpcUrl = vm.envOr(envName, defaultRpcUrls[chainAlias]); 161 | } else { 162 | chain.rpcUrl = vm.envString(envName); 163 | } 164 | // distinguish 'not found' from 'cannot read' 165 | bytes memory notFoundError = 166 | abi.encodeWithSignature("CheatCodeError", string(abi.encodePacked("invalid rpc url ", chainAlias))); 167 | if (keccak256(notFoundError) != keccak256(err) || bytes(chain.rpcUrl).length == 0) { 168 | /// @solidity memory-safe-assembly 169 | assembly { 170 | revert(add(32, err), mload(err)) 171 | } 172 | } 173 | } 174 | } 175 | return chain; 176 | } 177 | 178 | function setFallbackToDefaultRpcUrls(bool useDefault) internal { 179 | fallbackToDefaultRpcUrls = useDefault; 180 | } 181 | 182 | function initializeStdChains() private { 183 | if (stdChainsInitialized) return; 184 | 185 | stdChainsInitialized = true; 186 | 187 | // If adding an RPC here, make sure to test the default RPC URL in `testRpcs` 188 | setChainWithDefaultRpcUrl("anvil", ChainData("Anvil", 31337, "http://127.0.0.1:8545")); 189 | setChainWithDefaultRpcUrl( 190 | "mainnet", ChainData("Mainnet", 1, "https://mainnet.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001") 191 | ); 192 | setChainWithDefaultRpcUrl( 193 | "goerli", ChainData("Goerli", 5, "https://goerli.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001") 194 | ); 195 | setChainWithDefaultRpcUrl( 196 | "sepolia", ChainData("Sepolia", 11155111, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001") 197 | ); 198 | setChainWithDefaultRpcUrl("optimism", ChainData("Optimism", 10, "https://mainnet.optimism.io")); 199 | setChainWithDefaultRpcUrl("optimism_goerli", ChainData("Optimism Goerli", 420, "https://goerli.optimism.io")); 200 | setChainWithDefaultRpcUrl("arbitrum_one", ChainData("Arbitrum One", 42161, "https://arb1.arbitrum.io/rpc")); 201 | setChainWithDefaultRpcUrl( 202 | "arbitrum_one_goerli", ChainData("Arbitrum One Goerli", 421613, "https://goerli-rollup.arbitrum.io/rpc") 203 | ); 204 | setChainWithDefaultRpcUrl("arbitrum_nova", ChainData("Arbitrum Nova", 42170, "https://nova.arbitrum.io/rpc")); 205 | setChainWithDefaultRpcUrl("polygon", ChainData("Polygon", 137, "https://polygon-rpc.com")); 206 | setChainWithDefaultRpcUrl( 207 | "polygon_mumbai", ChainData("Polygon Mumbai", 80001, "https://rpc-mumbai.maticvigil.com") 208 | ); 209 | setChainWithDefaultRpcUrl("avalanche", ChainData("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc")); 210 | setChainWithDefaultRpcUrl( 211 | "avalanche_fuji", ChainData("Avalanche Fuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc") 212 | ); 213 | setChainWithDefaultRpcUrl( 214 | "bnb_smart_chain", ChainData("BNB Smart Chain", 56, "https://bsc-dataseed1.binance.org") 215 | ); 216 | setChainWithDefaultRpcUrl( 217 | "bnb_smart_chain_testnet", 218 | ChainData("BNB Smart Chain Testnet", 97, "https://rpc.ankr.com/bsc_testnet_chapel") 219 | ); 220 | setChainWithDefaultRpcUrl("gnosis_chain", ChainData("Gnosis Chain", 100, "https://rpc.gnosischain.com")); 221 | } 222 | 223 | // set chain info, with priority to chainAlias' rpc url in foundry.toml 224 | function setChainWithDefaultRpcUrl(string memory chainAlias, ChainData memory chain) private { 225 | string memory rpcUrl = chain.rpcUrl; 226 | defaultRpcUrls[chainAlias] = rpcUrl; 227 | chain.rpcUrl = ""; 228 | setChain(chainAlias, chain); 229 | chain.rpcUrl = rpcUrl; // restore argument 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdInvariant.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | contract StdInvariant { 7 | struct FuzzSelector { 8 | address addr; 9 | bytes4[] selectors; 10 | } 11 | 12 | address[] private _excludedContracts; 13 | address[] private _excludedSenders; 14 | address[] private _targetedContracts; 15 | address[] private _targetedSenders; 16 | 17 | string[] private _excludedArtifacts; 18 | string[] private _targetedArtifacts; 19 | 20 | FuzzSelector[] private _targetedArtifactSelectors; 21 | FuzzSelector[] private _targetedSelectors; 22 | 23 | // Functions for users: 24 | // These are intended to be called in tests. 25 | 26 | function excludeContract(address newExcludedContract_) internal { 27 | _excludedContracts.push(newExcludedContract_); 28 | } 29 | 30 | function excludeSender(address newExcludedSender_) internal { 31 | _excludedSenders.push(newExcludedSender_); 32 | } 33 | 34 | function excludeArtifact(string memory newExcludedArtifact_) internal { 35 | _excludedArtifacts.push(newExcludedArtifact_); 36 | } 37 | 38 | function targetArtifact(string memory newTargetedArtifact_) internal { 39 | _targetedArtifacts.push(newTargetedArtifact_); 40 | } 41 | 42 | function targetArtifactSelector(FuzzSelector memory newTargetedArtifactSelector_) internal { 43 | _targetedArtifactSelectors.push(newTargetedArtifactSelector_); 44 | } 45 | 46 | function targetContract(address newTargetedContract_) internal { 47 | _targetedContracts.push(newTargetedContract_); 48 | } 49 | 50 | function targetSelector(FuzzSelector memory newTargetedSelector_) internal { 51 | _targetedSelectors.push(newTargetedSelector_); 52 | } 53 | 54 | function targetSender(address newTargetedSender_) internal { 55 | _targetedSenders.push(newTargetedSender_); 56 | } 57 | 58 | // Functions for forge: 59 | // These are called by forge to run invariant tests and don't need to be called in tests. 60 | 61 | function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) { 62 | excludedArtifacts_ = _excludedArtifacts; 63 | } 64 | 65 | function excludeContracts() public view returns (address[] memory excludedContracts_) { 66 | excludedContracts_ = _excludedContracts; 67 | } 68 | 69 | function excludeSenders() public view returns (address[] memory excludedSenders_) { 70 | excludedSenders_ = _excludedSenders; 71 | } 72 | 73 | function targetArtifacts() public view returns (string[] memory targetedArtifacts_) { 74 | targetedArtifacts_ = _targetedArtifacts; 75 | } 76 | 77 | function targetArtifactSelectors() public view returns (FuzzSelector[] memory targetedArtifactSelectors_) { 78 | targetedArtifactSelectors_ = _targetedArtifactSelectors; 79 | } 80 | 81 | function targetContracts() public view returns (address[] memory targetedContracts_) { 82 | targetedContracts_ = _targetedContracts; 83 | } 84 | 85 | function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) { 86 | targetedSelectors_ = _targetedSelectors; 87 | } 88 | 89 | function targetSenders() public view returns (address[] memory targetedSenders_) { 90 | targetedSenders_ = _targetedSenders; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/forge-std/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("some_peth"); 13 | // json.parseUint(""); 14 | // ``` 15 | // To write: 16 | // ``` 17 | // using stdJson for string; 18 | // string memory json = "deploymentArtifact"; 19 | // Contract contract = new Contract(); 20 | // json.serialize("contractAddress", address(contract)); 21 | // json = json.serialize("deploymentTimes", uint(1)); 22 | // // store the stringified JSON to the 'json' variable we have been using as a key 23 | // // as we won't need it any longer 24 | // string memory json2 = "finalArtifact"; 25 | // string memory final = json2.serialize("depArtifact", json); 26 | // final.write(""); 27 | // ``` 28 | 29 | library stdJson { 30 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); 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 returns (uint256) { 37 | return vm.parseJsonUint(json, key); 38 | } 39 | 40 | function readUintArray(string memory json, string memory key) internal returns (uint256[] memory) { 41 | return vm.parseJsonUintArray(json, key); 42 | } 43 | 44 | function readInt(string memory json, string memory key) internal returns (int256) { 45 | return vm.parseJsonInt(json, key); 46 | } 47 | 48 | function readIntArray(string memory json, string memory key) internal returns (int256[] memory) { 49 | return vm.parseJsonIntArray(json, key); 50 | } 51 | 52 | function readBytes32(string memory json, string memory key) internal returns (bytes32) { 53 | return vm.parseJsonBytes32(json, key); 54 | } 55 | 56 | function readBytes32Array(string memory json, string memory key) internal returns (bytes32[] memory) { 57 | return vm.parseJsonBytes32Array(json, key); 58 | } 59 | 60 | function readString(string memory json, string memory key) internal returns (string memory) { 61 | return vm.parseJsonString(json, key); 62 | } 63 | 64 | function readStringArray(string memory json, string memory key) internal returns (string[] memory) { 65 | return vm.parseJsonStringArray(json, key); 66 | } 67 | 68 | function readAddress(string memory json, string memory key) internal returns (address) { 69 | return vm.parseJsonAddress(json, key); 70 | } 71 | 72 | function readAddressArray(string memory json, string memory key) internal returns (address[] memory) { 73 | return vm.parseJsonAddressArray(json, key); 74 | } 75 | 76 | function readBool(string memory json, string memory key) internal returns (bool) { 77 | return vm.parseJsonBool(json, key); 78 | } 79 | 80 | function readBoolArray(string memory json, string memory key) internal returns (bool[] memory) { 81 | return vm.parseJsonBoolArray(json, key); 82 | } 83 | 84 | function readBytes(string memory json, string memory key) internal returns (bytes memory) { 85 | return vm.parseJsonBytes(json, key); 86 | } 87 | 88 | function readBytesArray(string memory json, string memory key) internal returns (bytes[] memory) { 89 | return vm.parseJsonBytesArray(json, key); 90 | } 91 | 92 | function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) { 93 | return vm.serializeBool(jsonKey, key, value); 94 | } 95 | 96 | function serialize(string memory jsonKey, string memory key, bool[] memory value) 97 | internal 98 | returns (string memory) 99 | { 100 | return vm.serializeBool(jsonKey, key, value); 101 | } 102 | 103 | function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) { 104 | return vm.serializeUint(jsonKey, key, value); 105 | } 106 | 107 | function serialize(string memory jsonKey, string memory key, uint256[] memory value) 108 | internal 109 | returns (string memory) 110 | { 111 | return vm.serializeUint(jsonKey, key, value); 112 | } 113 | 114 | function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) { 115 | return vm.serializeInt(jsonKey, key, value); 116 | } 117 | 118 | function serialize(string memory jsonKey, string memory key, int256[] memory value) 119 | internal 120 | returns (string memory) 121 | { 122 | return vm.serializeInt(jsonKey, key, value); 123 | } 124 | 125 | function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) { 126 | return vm.serializeAddress(jsonKey, key, value); 127 | } 128 | 129 | function serialize(string memory jsonKey, string memory key, address[] memory value) 130 | internal 131 | returns (string memory) 132 | { 133 | return vm.serializeAddress(jsonKey, key, value); 134 | } 135 | 136 | function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) { 137 | return vm.serializeBytes32(jsonKey, key, value); 138 | } 139 | 140 | function serialize(string memory jsonKey, string memory key, bytes32[] memory value) 141 | internal 142 | returns (string memory) 143 | { 144 | return vm.serializeBytes32(jsonKey, key, value); 145 | } 146 | 147 | function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) { 148 | return vm.serializeBytes(jsonKey, key, value); 149 | } 150 | 151 | function serialize(string memory jsonKey, string memory key, bytes[] memory value) 152 | internal 153 | returns (string memory) 154 | { 155 | return vm.serializeBytes(jsonKey, key, value); 156 | } 157 | 158 | function serialize(string memory jsonKey, string memory key, string memory value) 159 | internal 160 | returns (string memory) 161 | { 162 | return vm.serializeString(jsonKey, key, value); 163 | } 164 | 165 | function serialize(string memory jsonKey, string memory key, string[] memory value) 166 | internal 167 | returns (string memory) 168 | { 169 | return vm.serializeString(jsonKey, key, value); 170 | } 171 | 172 | function write(string memory jsonKey, string memory path) internal { 173 | vm.writeJson(jsonKey, path); 174 | } 175 | 176 | function write(string memory jsonKey, string memory path, string memory valueKey) internal { 177 | vm.writeJson(jsonKey, path, valueKey); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdStyle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.9.0; 3 | 4 | import {Vm} from "./Vm.sol"; 5 | 6 | library StdStyle { 7 | Vm private constant vm = Vm(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 | -------------------------------------------------------------------------------- /lib/forge-std/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 view virtual returns (uint256 result) { 59 | result = _bound(x, min, max); 60 | console2_log("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 view virtual returns (int256 result) { 84 | result = _bound(x, min, max); 85 | console2_log("Bound result", vm.toString(result)); 86 | } 87 | 88 | function boundPrivateKey(uint256 privateKey) internal view 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 | // forgefmt: disable-start 101 | // The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0. 102 | // A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it. 103 | if (nonce == 0x00) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80)))); 104 | if (nonce <= 0x7f) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce)))); 105 | 106 | // Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length. 107 | if (nonce <= 2**8 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce)))); 108 | if (nonce <= 2**16 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce)))); 109 | if (nonce <= 2**24 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce)))); 110 | // forgefmt: disable-end 111 | 112 | // More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp 113 | // 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce) 114 | // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) 115 | // 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex) 116 | // We assume nobody can have a nonce large enough to require more than 32 bytes. 117 | return addressFromLast20Bytes( 118 | keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce))) 119 | ); 120 | } 121 | 122 | function computeCreate2Address(bytes32 salt, bytes32 initcodeHash, address deployer) 123 | internal 124 | pure 125 | virtual 126 | returns (address) 127 | { 128 | return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initcodeHash))); 129 | } 130 | 131 | /// @dev returns the address of a contract created with CREATE2 using the default CREATE2 deployer 132 | function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) internal pure returns (address) { 133 | return computeCreate2Address(salt, initCodeHash, CREATE2_FACTORY); 134 | } 135 | 136 | /// @dev returns the hash of the init code (creation code + no args) used in CREATE2 with no constructor arguments 137 | /// @param creationCode the creation code of a contract C, as returned by type(C).creationCode 138 | function hashInitCode(bytes memory creationCode) internal pure returns (bytes32) { 139 | return hashInitCode(creationCode, ""); 140 | } 141 | 142 | /// @dev returns the hash of the init code (creation code + ABI-encoded args) used in CREATE2 143 | /// @param creationCode the creation code of a contract C, as returned by type(C).creationCode 144 | /// @param args the ABI-encoded arguments to the constructor of C 145 | function hashInitCode(bytes memory creationCode, bytes memory args) internal pure returns (bytes32) { 146 | return keccak256(abi.encodePacked(creationCode, args)); 147 | } 148 | 149 | // Performs a single call with Multicall3 to query the ERC-20 token balances of the given addresses. 150 | function getTokenBalances(address token, address[] memory addresses) 151 | internal 152 | virtual 153 | returns (uint256[] memory balances) 154 | { 155 | uint256 tokenCodeSize; 156 | assembly { 157 | tokenCodeSize := extcodesize(token) 158 | } 159 | require(tokenCodeSize > 0, "StdUtils getTokenBalances(address,address[]): Token address is not a contract."); 160 | 161 | // ABI encode the aggregate call to Multicall3. 162 | uint256 length = addresses.length; 163 | IMulticall3.Call[] memory calls = new IMulticall3.Call[](length); 164 | for (uint256 i = 0; i < length; ++i) { 165 | // 0x70a08231 = bytes4("balanceOf(address)")) 166 | calls[i] = IMulticall3.Call({target: token, callData: abi.encodeWithSelector(0x70a08231, (addresses[i]))}); 167 | } 168 | 169 | // Make the aggregate call. 170 | (, bytes[] memory returnData) = multicall.aggregate(calls); 171 | 172 | // ABI decode the return data and return the balances. 173 | balances = new uint256[](length); 174 | for (uint256 i = 0; i < length; ++i) { 175 | balances[i] = abi.decode(returnData[i], (uint256)); 176 | } 177 | } 178 | 179 | /*////////////////////////////////////////////////////////////////////////// 180 | PRIVATE FUNCTIONS 181 | //////////////////////////////////////////////////////////////////////////*/ 182 | 183 | function addressFromLast20Bytes(bytes32 bytesValue) private pure returns (address) { 184 | return address(uint160(uint256(bytesValue))); 185 | } 186 | 187 | // Used to prevent the compilation of console, which shortens the compilation time when console is not used elsewhere. 188 | 189 | function console2_log(string memory p0, uint256 p1) private view { 190 | (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,uint256)", p0, p1)); 191 | status; 192 | } 193 | 194 | function console2_log(string memory p0, string memory p1) private view { 195 | (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,string)", p0, p1)); 196 | status; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /lib/forge-std/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 | // Standard Library's default Test 8 | 9 | // 🧩 MODULES 10 | import {console} from "./console.sol"; 11 | import {console2} from "./console2.sol"; 12 | import {StdAssertions} from "./StdAssertions.sol"; 13 | import {StdChains} from "./StdChains.sol"; 14 | import {StdCheats} from "./StdCheats.sol"; 15 | import {stdError} from "./StdError.sol"; 16 | import {StdInvariant} from "./StdInvariant.sol"; 17 | import {stdJson} from "./StdJson.sol"; 18 | import {stdMath} from "./StdMath.sol"; 19 | import {StdStorage, stdStorage} from "./StdStorage.sol"; 20 | import {StdUtils} from "./StdUtils.sol"; 21 | import {Vm} from "./Vm.sol"; 22 | import {StdStyle} from "./StdStyle.sol"; 23 | 24 | // 📦 BOILERPLATE 25 | import {TestBase} from "./Base.sol"; 26 | import {DSTest} from "ds-test/test.sol"; 27 | 28 | // ⭐️ TEST 29 | abstract contract Test is DSTest, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils, TestBase { 30 | // Note: IS_TEST() must return true. 31 | // Note: Must have failure system, https://github.com/dapphub/ds-test/blob/cd98eff28324bfac652e63a239a60632a761790b/src/test.sol#L39-L76. 32 | } 33 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import "./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 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IERC4626.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import "./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 withdraw 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 withdraw. 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 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import "./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 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdChains.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "../src/Test.sol"; 5 | 6 | contract StdChainsTest is Test { 7 | function testChainRpcInitialization() public { 8 | // RPCs specified in `foundry.toml` should be updated. 9 | assertEq(getChain(1).rpcUrl, "https://mainnet.infura.io/v3/b1d3925804e74152b316ca7da97060d3"); 10 | assertEq(getChain("optimism_goerli").rpcUrl, "https://goerli.optimism.io/"); 11 | assertEq(getChain("arbitrum_one_goerli").rpcUrl, "https://goerli-rollup.arbitrum.io/rpc/"); 12 | 13 | // Environment variables should be the next fallback 14 | assertEq(getChain("arbitrum_nova").rpcUrl, "https://nova.arbitrum.io/rpc"); 15 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "myoverride"); 16 | assertEq(getChain("arbitrum_nova").rpcUrl, "myoverride"); 17 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "https://nova.arbitrum.io/rpc"); 18 | 19 | // Cannot override RPCs defined in `foundry.toml` 20 | vm.setEnv("MAINNET_RPC_URL", "myoverride2"); 21 | assertEq(getChain("mainnet").rpcUrl, "https://mainnet.infura.io/v3/b1d3925804e74152b316ca7da97060d3"); 22 | 23 | // Other RPCs should remain unchanged. 24 | assertEq(getChain(31337).rpcUrl, "http://127.0.0.1:8545"); 25 | assertEq(getChain("sepolia").rpcUrl, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001"); 26 | } 27 | 28 | function testRpc(string memory rpcAlias) internal { 29 | string memory rpcUrl = getChain(rpcAlias).rpcUrl; 30 | vm.createSelectFork(rpcUrl); 31 | } 32 | 33 | // Ensure we can connect to the default RPC URL for each chain. 34 | // function testRpcs() public { 35 | // testRpc("mainnet"); 36 | // testRpc("goerli"); 37 | // testRpc("sepolia"); 38 | // testRpc("optimism"); 39 | // testRpc("optimism_goerli"); 40 | // testRpc("arbitrum_one"); 41 | // testRpc("arbitrum_one_goerli"); 42 | // testRpc("arbitrum_nova"); 43 | // testRpc("polygon"); 44 | // testRpc("polygon_mumbai"); 45 | // testRpc("avalanche"); 46 | // testRpc("avalanche_fuji"); 47 | // testRpc("bnb_smart_chain"); 48 | // testRpc("bnb_smart_chain_testnet"); 49 | // testRpc("gnosis_chain"); 50 | // } 51 | 52 | function testChainNoDefault() public { 53 | vm.expectRevert("StdChains getChain(string): Chain with alias \"does_not_exist\" not found."); 54 | getChain("does_not_exist"); 55 | } 56 | 57 | function testSetChainFirstFails() public { 58 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID 31337 already used by \"anvil\"."); 59 | setChain("anvil2", ChainData("Anvil", 31337, "URL")); 60 | } 61 | 62 | function testChainBubbleUp() public { 63 | setChain("needs_undefined_env_var", ChainData("", 123456789, "")); 64 | vm.expectRevert( 65 | "Failed to resolve env var `UNDEFINED_RPC_URL_PLACEHOLDER` in `${UNDEFINED_RPC_URL_PLACEHOLDER}`: environment variable not found" 66 | ); 67 | getChain("needs_undefined_env_var"); 68 | } 69 | 70 | function testCannotSetChain_ChainIdExists() public { 71 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); 72 | 73 | vm.expectRevert('StdChains setChain(string,ChainData): Chain ID 123456789 already used by "custom_chain".'); 74 | 75 | setChain("another_custom_chain", ChainData("", 123456789, "")); 76 | } 77 | 78 | function testSetChain() public { 79 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); 80 | Chain memory customChain = getChain("custom_chain"); 81 | assertEq(customChain.name, "Custom Chain"); 82 | assertEq(customChain.chainId, 123456789); 83 | assertEq(customChain.chainAlias, "custom_chain"); 84 | assertEq(customChain.rpcUrl, "https://custom.chain/"); 85 | Chain memory chainById = getChain(123456789); 86 | assertEq(chainById.name, customChain.name); 87 | assertEq(chainById.chainId, customChain.chainId); 88 | assertEq(chainById.chainAlias, customChain.chainAlias); 89 | assertEq(chainById.rpcUrl, customChain.rpcUrl); 90 | customChain.name = "Another Custom Chain"; 91 | customChain.chainId = 987654321; 92 | setChain("another_custom_chain", customChain); 93 | Chain memory anotherCustomChain = getChain("another_custom_chain"); 94 | assertEq(anotherCustomChain.name, "Another Custom Chain"); 95 | assertEq(anotherCustomChain.chainId, 987654321); 96 | assertEq(anotherCustomChain.chainAlias, "another_custom_chain"); 97 | assertEq(anotherCustomChain.rpcUrl, "https://custom.chain/"); 98 | // Verify the first chain data was not overwritten 99 | chainById = getChain(123456789); 100 | assertEq(chainById.name, "Custom Chain"); 101 | assertEq(chainById.chainId, 123456789); 102 | } 103 | 104 | function testSetNoEmptyAlias() public { 105 | vm.expectRevert("StdChains setChain(string,ChainData): Chain alias cannot be the empty string."); 106 | setChain("", ChainData("", 123456789, "")); 107 | } 108 | 109 | function testSetNoChainId0() public { 110 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID cannot be 0."); 111 | setChain("alias", ChainData("", 0, "")); 112 | } 113 | 114 | function testGetNoChainId0() public { 115 | vm.expectRevert("StdChains getChain(uint256): Chain ID cannot be 0."); 116 | getChain(0); 117 | } 118 | 119 | function testGetNoEmptyAlias() public { 120 | vm.expectRevert("StdChains getChain(string): Chain alias cannot be the empty string."); 121 | getChain(""); 122 | } 123 | 124 | function testChainIdNotFound() public { 125 | vm.expectRevert("StdChains getChain(string): Chain with alias \"no_such_alias\" not found."); 126 | getChain("no_such_alias"); 127 | } 128 | 129 | function testChainAliasNotFound() public { 130 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 321 not found."); 131 | getChain(321); 132 | } 133 | 134 | function testSetChain_ExistingOne() public { 135 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); 136 | assertEq(getChain(123456789).chainId, 123456789); 137 | 138 | setChain("custom_chain", ChainData("Modified Chain", 999999999, "https://modified.chain/")); 139 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 123456789 not found."); 140 | getChain(123456789); 141 | 142 | Chain memory modifiedChain = getChain(999999999); 143 | assertEq(modifiedChain.name, "Modified Chain"); 144 | assertEq(modifiedChain.chainId, 999999999); 145 | assertEq(modifiedChain.rpcUrl, "https://modified.chain/"); 146 | } 147 | 148 | function testDontUseDefaultRpcUrl() public { 149 | // Should error if default RPCs flag is set to false. 150 | setFallbackToDefaultRpcUrls(false); 151 | vm.expectRevert( 152 | "Failed to get environment variable `ANVIL_RPC_URL` as type `string`: environment variable not found" 153 | ); 154 | getChain(31337); 155 | vm.expectRevert( 156 | "Failed to get environment variable `SEPOLIA_RPC_URL` as type `string`: environment variable not found" 157 | ); 158 | getChain("sepolia"); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdError.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "../src/StdError.sol"; 5 | import "../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 testExpectAssertion() public { 15 | vm.expectRevert(stdError.assertionError); 16 | test.assertionError(); 17 | } 18 | 19 | function testExpectArithmetic() public { 20 | vm.expectRevert(stdError.arithmeticError); 21 | test.arithmeticError(10); 22 | } 23 | 24 | function testExpectDiv() public { 25 | vm.expectRevert(stdError.divisionError); 26 | test.divError(0); 27 | } 28 | 29 | function testExpectMod() public { 30 | vm.expectRevert(stdError.divisionError); 31 | test.modError(0); 32 | } 33 | 34 | function testExpectEnum() public { 35 | vm.expectRevert(stdError.enumConversionError); 36 | test.enumConversion(1); 37 | } 38 | 39 | function testExpectEncodeStg() public { 40 | vm.expectRevert(stdError.encodeStorageError); 41 | test.encodeStgError(); 42 | } 43 | 44 | function testExpectPop() public { 45 | vm.expectRevert(stdError.popError); 46 | test.pop(); 47 | } 48 | 49 | function testExpectOOB() public { 50 | vm.expectRevert(stdError.indexOOBError); 51 | test.indexOOBError(1); 52 | } 53 | 54 | function testExpectMem() public { 55 | vm.expectRevert(stdError.memOverflowError); 56 | test.mem(); 57 | } 58 | 59 | function testExpectIntern() public { 60 | vm.expectRevert(stdError.zeroVarError); 61 | test.intern(); 62 | } 63 | } 64 | 65 | contract ErrorsTest { 66 | enum T {T1} 67 | 68 | uint256[] public someArr; 69 | bytes someBytes; 70 | 71 | function assertionError() public pure { 72 | assert(false); 73 | } 74 | 75 | function arithmeticError(uint256 a) public pure { 76 | a -= 100; 77 | } 78 | 79 | function divError(uint256 a) public pure { 80 | 100 / a; 81 | } 82 | 83 | function modError(uint256 a) public pure { 84 | 100 % a; 85 | } 86 | 87 | function enumConversion(uint256 a) public pure { 88 | T(a); 89 | } 90 | 91 | function encodeStgError() public { 92 | /// @solidity memory-safe-assembly 93 | assembly { 94 | sstore(someBytes.slot, 1) 95 | } 96 | keccak256(someBytes); 97 | } 98 | 99 | function pop() public { 100 | someArr.pop(); 101 | } 102 | 103 | function indexOOBError(uint256 a) public pure { 104 | uint256[] memory t = new uint256[](0); 105 | t[a]; 106 | } 107 | 108 | function mem() public pure { 109 | uint256 l = 2 ** 256 / 32; 110 | new uint256[](l); 111 | } 112 | 113 | function intern() public returns (uint256) { 114 | function(uint256) internal returns (uint256) x; 115 | x(2); 116 | return 7; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdMath.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "../src/StdMath.sol"; 5 | import "../src/Test.sol"; 6 | 7 | contract StdMathTest is Test { 8 | function testGetAbs() external { 9 | assertEq(stdMath.abs(-50), 50); 10 | assertEq(stdMath.abs(50), 50); 11 | assertEq(stdMath.abs(-1337), 1337); 12 | assertEq(stdMath.abs(0), 0); 13 | 14 | assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1); 15 | assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1)); 16 | } 17 | 18 | function testGetAbs_Fuzz(int256 a) external { 19 | uint256 manualAbs = getAbs(a); 20 | 21 | uint256 abs = stdMath.abs(a); 22 | 23 | assertEq(abs, manualAbs); 24 | } 25 | 26 | function testGetDelta_Uint() external { 27 | assertEq(stdMath.delta(uint256(0), uint256(0)), 0); 28 | assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337); 29 | assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max); 30 | assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max); 31 | assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max); 32 | 33 | assertEq(stdMath.delta(0, uint256(0)), 0); 34 | assertEq(stdMath.delta(1337, uint256(0)), 1337); 35 | assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max); 36 | assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max); 37 | assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max); 38 | 39 | assertEq(stdMath.delta(1337, uint256(1337)), 0); 40 | assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0); 41 | assertEq(stdMath.delta(5000, uint256(1250)), 3750); 42 | } 43 | 44 | function testGetDelta_Uint_Fuzz(uint256 a, uint256 b) external { 45 | uint256 manualDelta; 46 | if (a > b) { 47 | manualDelta = a - b; 48 | } else { 49 | manualDelta = b - a; 50 | } 51 | 52 | uint256 delta = stdMath.delta(a, b); 53 | 54 | assertEq(delta, manualDelta); 55 | } 56 | 57 | function testGetDelta_Int() external { 58 | assertEq(stdMath.delta(int256(0), int256(0)), 0); 59 | assertEq(stdMath.delta(int256(0), int256(1337)), 1337); 60 | assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1); 61 | assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1); 62 | assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1); 63 | 64 | assertEq(stdMath.delta(0, int256(0)), 0); 65 | assertEq(stdMath.delta(1337, int256(0)), 1337); 66 | assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1); 67 | assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1); 68 | assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1); 69 | 70 | assertEq(stdMath.delta(-0, int256(0)), 0); 71 | assertEq(stdMath.delta(-1337, int256(0)), 1337); 72 | assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1); 73 | assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1); 74 | assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1); 75 | 76 | assertEq(stdMath.delta(int256(0), -0), 0); 77 | assertEq(stdMath.delta(int256(0), -1337), 1337); 78 | assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1); 79 | assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1); 80 | assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1); 81 | 82 | assertEq(stdMath.delta(1337, int256(1337)), 0); 83 | assertEq(stdMath.delta(type(int256).max, type(int256).max), 0); 84 | assertEq(stdMath.delta(type(int256).min, type(int256).min), 0); 85 | assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max); 86 | assertEq(stdMath.delta(5000, int256(1250)), 3750); 87 | } 88 | 89 | function testGetDelta_Int_Fuzz(int256 a, int256 b) external { 90 | uint256 absA = getAbs(a); 91 | uint256 absB = getAbs(b); 92 | uint256 absDelta = absA > absB ? absA - absB : absB - absA; 93 | 94 | uint256 manualDelta; 95 | if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { 96 | manualDelta = absDelta; 97 | } 98 | // (a < 0 && b >= 0) || (a >= 0 && b < 0) 99 | else { 100 | manualDelta = absA + absB; 101 | } 102 | 103 | uint256 delta = stdMath.delta(a, b); 104 | 105 | assertEq(delta, manualDelta); 106 | } 107 | 108 | function testGetPercentDelta_Uint() external { 109 | assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18); 110 | assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18); 111 | assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18); 112 | assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18); 113 | 114 | assertEq(stdMath.percentDelta(1337, uint256(1337)), 0); 115 | assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0); 116 | assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18); 117 | assertEq(stdMath.percentDelta(2500, uint256(2500)), 0); 118 | assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18); 119 | assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18); 120 | 121 | vm.expectRevert(stdError.divisionError); 122 | stdMath.percentDelta(uint256(1), 0); 123 | } 124 | 125 | function testGetPercentDelta_Uint_Fuzz(uint192 a, uint192 b) external { 126 | vm.assume(b != 0); 127 | uint256 manualDelta; 128 | if (a > b) { 129 | manualDelta = a - b; 130 | } else { 131 | manualDelta = b - a; 132 | } 133 | 134 | uint256 manualPercentDelta = manualDelta * 1e18 / b; 135 | uint256 percentDelta = stdMath.percentDelta(a, b); 136 | 137 | assertEq(percentDelta, manualPercentDelta); 138 | } 139 | 140 | function testGetPercentDelta_Int() external { 141 | assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18); 142 | assertEq(stdMath.percentDelta(int256(0), -1337), 1e18); 143 | assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18); 144 | assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18); 145 | assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18); 146 | assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18); 147 | assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18); 148 | assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18); 149 | 150 | assertEq(stdMath.percentDelta(1337, int256(1337)), 0); 151 | assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0); 152 | assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0); 153 | 154 | assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down 155 | assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down 156 | assertEq(stdMath.percentDelta(0, int256(2500)), 1e18); 157 | assertEq(stdMath.percentDelta(2500, int256(2500)), 0); 158 | assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18); 159 | assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18); 160 | 161 | vm.expectRevert(stdError.divisionError); 162 | stdMath.percentDelta(int256(1), 0); 163 | } 164 | 165 | function testGetPercentDelta_Int_Fuzz(int192 a, int192 b) external { 166 | vm.assume(b != 0); 167 | uint256 absA = getAbs(a); 168 | uint256 absB = getAbs(b); 169 | uint256 absDelta = absA > absB ? absA - absB : absB - absA; 170 | 171 | uint256 manualDelta; 172 | if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { 173 | manualDelta = absDelta; 174 | } 175 | // (a < 0 && b >= 0) || (a >= 0 && b < 0) 176 | else { 177 | manualDelta = absA + absB; 178 | } 179 | 180 | uint256 manualPercentDelta = manualDelta * 1e18 / absB; 181 | uint256 percentDelta = stdMath.percentDelta(a, b); 182 | 183 | assertEq(percentDelta, manualPercentDelta); 184 | } 185 | 186 | /*////////////////////////////////////////////////////////////////////////// 187 | HELPERS 188 | //////////////////////////////////////////////////////////////////////////*/ 189 | 190 | function getAbs(int256 a) private pure returns (uint256) { 191 | if (a < 0) { 192 | return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a); 193 | } 194 | 195 | return uint256(a); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdStorage.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "../src/StdStorage.sol"; 5 | import "../src/Test.sol"; 6 | 7 | contract StdStorageTest is Test { 8 | using stdStorage for StdStorage; 9 | 10 | StorageTest internal test; 11 | 12 | function setUp() public { 13 | test = new StorageTest(); 14 | } 15 | 16 | function testStorageHidden() public { 17 | assertEq(uint256(keccak256("my.random.var")), stdstore.target(address(test)).sig("hidden()").find()); 18 | } 19 | 20 | function testStorageObvious() public { 21 | assertEq(uint256(0), stdstore.target(address(test)).sig("exists()").find()); 22 | } 23 | 24 | function testStorageCheckedWriteHidden() public { 25 | stdstore.target(address(test)).sig(test.hidden.selector).checked_write(100); 26 | assertEq(uint256(test.hidden()), 100); 27 | } 28 | 29 | function testStorageCheckedWriteObvious() public { 30 | stdstore.target(address(test)).sig(test.exists.selector).checked_write(100); 31 | assertEq(test.exists(), 100); 32 | } 33 | 34 | function testStorageMapStructA() public { 35 | uint256 slot = 36 | stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).find(); 37 | assertEq(uint256(keccak256(abi.encode(address(this), 4))), slot); 38 | } 39 | 40 | function testStorageMapStructB() public { 41 | uint256 slot = 42 | stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).find(); 43 | assertEq(uint256(keccak256(abi.encode(address(this), 4))) + 1, slot); 44 | } 45 | 46 | function testStorageDeepMap() public { 47 | uint256 slot = stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key( 48 | address(this) 49 | ).find(); 50 | assertEq(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(5)))))), slot); 51 | } 52 | 53 | function testStorageCheckedWriteDeepMap() public { 54 | stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key(address(this)) 55 | .checked_write(100); 56 | assertEq(100, test.deep_map(address(this), address(this))); 57 | } 58 | 59 | function testStorageDeepMapStructA() public { 60 | uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)) 61 | .with_key(address(this)).depth(0).find(); 62 | assertEq( 63 | bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 0), 64 | bytes32(slot) 65 | ); 66 | } 67 | 68 | function testStorageDeepMapStructB() public { 69 | uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)) 70 | .with_key(address(this)).depth(1).find(); 71 | assertEq( 72 | bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 1), 73 | bytes32(slot) 74 | ); 75 | } 76 | 77 | function testStorageCheckedWriteDeepMapStructA() public { 78 | stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key( 79 | address(this) 80 | ).depth(0).checked_write(100); 81 | (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); 82 | assertEq(100, a); 83 | assertEq(0, b); 84 | } 85 | 86 | function testStorageCheckedWriteDeepMapStructB() public { 87 | stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key( 88 | address(this) 89 | ).depth(1).checked_write(100); 90 | (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); 91 | assertEq(0, a); 92 | assertEq(100, b); 93 | } 94 | 95 | function testStorageCheckedWriteMapStructA() public { 96 | stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).checked_write(100); 97 | (uint256 a, uint256 b) = test.map_struct(address(this)); 98 | assertEq(a, 100); 99 | assertEq(b, 0); 100 | } 101 | 102 | function testStorageCheckedWriteMapStructB() public { 103 | stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).checked_write(100); 104 | (uint256 a, uint256 b) = test.map_struct(address(this)); 105 | assertEq(a, 0); 106 | assertEq(b, 100); 107 | } 108 | 109 | function testStorageStructA() public { 110 | uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(0).find(); 111 | assertEq(uint256(7), slot); 112 | } 113 | 114 | function testStorageStructB() public { 115 | uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(1).find(); 116 | assertEq(uint256(7) + 1, slot); 117 | } 118 | 119 | function testStorageCheckedWriteStructA() public { 120 | stdstore.target(address(test)).sig(test.basic.selector).depth(0).checked_write(100); 121 | (uint256 a, uint256 b) = test.basic(); 122 | assertEq(a, 100); 123 | assertEq(b, 1337); 124 | } 125 | 126 | function testStorageCheckedWriteStructB() public { 127 | stdstore.target(address(test)).sig(test.basic.selector).depth(1).checked_write(100); 128 | (uint256 a, uint256 b) = test.basic(); 129 | assertEq(a, 1337); 130 | assertEq(b, 100); 131 | } 132 | 133 | function testStorageMapAddrFound() public { 134 | uint256 slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).find(); 135 | assertEq(uint256(keccak256(abi.encode(address(this), uint256(1)))), slot); 136 | } 137 | 138 | function testStorageMapUintFound() public { 139 | uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find(); 140 | assertEq(uint256(keccak256(abi.encode(100, uint256(2)))), slot); 141 | } 142 | 143 | function testStorageCheckedWriteMapUint() public { 144 | stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).checked_write(100); 145 | assertEq(100, test.map_uint(100)); 146 | } 147 | 148 | function testStorageCheckedWriteMapAddr() public { 149 | stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).checked_write(100); 150 | assertEq(100, test.map_addr(address(this))); 151 | } 152 | 153 | function testStorageCheckedWriteMapBool() public { 154 | stdstore.target(address(test)).sig(test.map_bool.selector).with_key(address(this)).checked_write(true); 155 | assertTrue(test.map_bool(address(this))); 156 | } 157 | 158 | function testFailStorageCheckedWriteMapPacked() public { 159 | // expect PackedSlot error but not external call so cant expectRevert 160 | stdstore.target(address(test)).sig(test.read_struct_lower.selector).with_key(address(uint160(1337))) 161 | .checked_write(100); 162 | } 163 | 164 | function testStorageCheckedWriteMapPackedSuccess() public { 165 | uint256 full = test.map_packed(address(1337)); 166 | // keep upper 128, set lower 128 to 1337 167 | full = (full & (uint256((1 << 128) - 1) << 128)) | 1337; 168 | stdstore.target(address(test)).sig(test.map_packed.selector).with_key(address(uint160(1337))).checked_write( 169 | full 170 | ); 171 | assertEq(1337, test.read_struct_lower(address(1337))); 172 | } 173 | 174 | function testFailStorageConst() public { 175 | // vm.expectRevert(abi.encodeWithSignature("NotStorage(bytes4)", bytes4(keccak256("const()")))); 176 | stdstore.target(address(test)).sig("const()").find(); 177 | } 178 | 179 | function testFailStorageNativePack() public { 180 | stdstore.target(address(test)).sig(test.tA.selector).find(); 181 | stdstore.target(address(test)).sig(test.tB.selector).find(); 182 | 183 | // these both would fail 184 | stdstore.target(address(test)).sig(test.tC.selector).find(); 185 | stdstore.target(address(test)).sig(test.tD.selector).find(); 186 | } 187 | 188 | function testStorageReadBytes32() public { 189 | bytes32 val = stdstore.target(address(test)).sig(test.tE.selector).read_bytes32(); 190 | assertEq(val, hex"1337"); 191 | } 192 | 193 | function testStorageReadBool_False() public { 194 | bool val = stdstore.target(address(test)).sig(test.tB.selector).read_bool(); 195 | assertEq(val, false); 196 | } 197 | 198 | function testStorageReadBool_True() public { 199 | bool val = stdstore.target(address(test)).sig(test.tH.selector).read_bool(); 200 | assertEq(val, true); 201 | } 202 | 203 | function testStorageReadBool_Revert() public { 204 | vm.expectRevert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); 205 | this.readNonBoolValue(); 206 | } 207 | 208 | function readNonBoolValue() public { 209 | stdstore.target(address(test)).sig(test.tE.selector).read_bool(); 210 | } 211 | 212 | function testStorageReadAddress() public { 213 | address val = stdstore.target(address(test)).sig(test.tF.selector).read_address(); 214 | assertEq(val, address(1337)); 215 | } 216 | 217 | function testStorageReadUint() public { 218 | uint256 val = stdstore.target(address(test)).sig(test.exists.selector).read_uint(); 219 | assertEq(val, 1); 220 | } 221 | 222 | function testStorageReadInt() public { 223 | int256 val = stdstore.target(address(test)).sig(test.tG.selector).read_int(); 224 | assertEq(val, type(int256).min); 225 | } 226 | } 227 | 228 | contract StorageTest { 229 | uint256 public exists = 1; 230 | mapping(address => uint256) public map_addr; 231 | mapping(uint256 => uint256) public map_uint; 232 | mapping(address => uint256) public map_packed; 233 | mapping(address => UnpackedStruct) public map_struct; 234 | mapping(address => mapping(address => uint256)) public deep_map; 235 | mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; 236 | UnpackedStruct public basic; 237 | 238 | uint248 public tA; 239 | bool public tB; 240 | 241 | bool public tC = false; 242 | uint248 public tD = 1; 243 | 244 | struct UnpackedStruct { 245 | uint256 a; 246 | uint256 b; 247 | } 248 | 249 | mapping(address => bool) public map_bool; 250 | 251 | bytes32 public tE = hex"1337"; 252 | address public tF = address(1337); 253 | int256 public tG = type(int256).min; 254 | bool public tH = true; 255 | 256 | constructor() { 257 | basic = UnpackedStruct({a: 1337, b: 1337}); 258 | 259 | uint256 two = (1 << 128) | 1; 260 | map_packed[msg.sender] = two; 261 | map_packed[address(uint160(1337))] = 1 << 128; 262 | } 263 | 264 | function read_struct_upper(address who) public view returns (uint256) { 265 | return map_packed[who] >> 128; 266 | } 267 | 268 | function read_struct_lower(address who) public view returns (uint256) { 269 | return map_packed[who] & ((1 << 128) - 1); 270 | } 271 | 272 | function hidden() public view returns (bytes32 t) { 273 | bytes32 slot = keccak256("my.random.var"); 274 | /// @solidity memory-safe-assembly 275 | assembly { 276 | t := sload(slot) 277 | } 278 | } 279 | 280 | function const() public pure returns (bytes32 t) { 281 | t = bytes32(hex"1337"); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdStyle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "../src/Test.sol"; 5 | 6 | contract StdStyleTest is Test { 7 | function testStyleColor() 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 testStyleFontWeight() 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 testStyleCombined() 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 testStyleCustom() 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 | -------------------------------------------------------------------------------- /lib/forge-std/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 "../../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 | -------------------------------------------------------------------------------- /lib/forge-std/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 "../../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 | -------------------------------------------------------------------------------- /lib/forge-std/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 "../../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 | -------------------------------------------------------------------------------- /lib/forge-std/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 "../../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 | -------------------------------------------------------------------------------- /lib/forge-std/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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-token", 3 | "version": "1.0.0", 4 | "main": "ts-scripts/main.ts", 5 | "repository": "git@github.com:wormhole-foundation/hello-token.git", 6 | "author": "Wormhole Contributors", 7 | "license": "Apache 2.0", 8 | "scripts": { 9 | "main": "ts-node ./ts-scripts/main.ts", 10 | "deploy": "npm run build; npm run deployMockToken; npm run deployHelloToken", 11 | "deployMockToken": "ts-node ./ts-scripts/main.ts --deployMockToken", 12 | "deployHelloToken": "ts-node ./ts-scripts/main.ts --deployHelloToken", 13 | "build": "npm install; forge build; forge remappings > remappings.txt; npm run typechain", 14 | "typechain": "typechain --target ethers-v5 --out-dir ./ts-scripts/ethers-contracts ./out/**/*.json", 15 | "test": "jest --config ./ts-scripts/jest.config.js", 16 | "clean": "rm -rf ts-scripts/ethers-contracts; rm -rf out/;" 17 | }, 18 | "dependencies": { 19 | "@certusone/wormhole-sdk": "^0.10.5", 20 | "@improbable-eng/grpc-web-node-http-transport": "^0.15.0", 21 | "@typechain/ethers-v5": "^11.0.0", 22 | "ethers": "^5", 23 | "ts-node": "^10.9.1", 24 | "typechain": "^8.2.0" 25 | }, 26 | "devDependencies": { 27 | "@types/jest": "^29.5.2", 28 | "@types/node": "^20.2.5", 29 | "jest": "^29.7.0", 30 | "ts-jest": "^29.1.0", 31 | "typescript": "^5.1.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=lib/forge-std/lib/ds-test/src/ 2 | forge-std/=lib/forge-std/src/ 3 | wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/ 4 | -------------------------------------------------------------------------------- /src/HelloToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "wormhole-solidity-sdk/WormholeRelayerSDK.sol"; 5 | import "wormhole-solidity-sdk/interfaces/IERC20.sol"; 6 | 7 | contract HelloToken is TokenSender, TokenReceiver { 8 | uint256 constant GAS_LIMIT = 250_000; 9 | 10 | constructor( 11 | address _wormholeRelayer, 12 | address _tokenBridge, 13 | address _wormhole 14 | ) TokenBase(_wormholeRelayer, _tokenBridge, _wormhole) {} 15 | 16 | function quoteCrossChainDeposit( 17 | uint16 targetChain 18 | ) public view returns (uint256 cost) { 19 | // Cost of delivering token and payload to targetChain 20 | uint256 deliveryCost; 21 | (deliveryCost, ) = wormholeRelayer.quoteEVMDeliveryPrice( 22 | targetChain, 23 | 0, 24 | GAS_LIMIT 25 | ); 26 | 27 | // Total cost: delivery cost + cost of publishing the 'sending token' wormhole message 28 | cost = deliveryCost + wormhole.messageFee(); 29 | } 30 | 31 | function sendCrossChainDeposit( 32 | uint16 targetChain, 33 | address targetHelloToken, 34 | address recipient, 35 | uint256 amount, 36 | address token 37 | ) public payable { 38 | uint256 cost = quoteCrossChainDeposit(targetChain); 39 | require( 40 | msg.value == cost, 41 | "msg.value must be quoteCrossChainDeposit(targetChain)" 42 | ); 43 | 44 | IERC20(token).transferFrom(msg.sender, address(this), amount); 45 | 46 | bytes memory payload = abi.encode(recipient); 47 | sendTokenWithPayloadToEvm( 48 | targetChain, 49 | targetHelloToken, // address (on targetChain) to send token and payload to 50 | payload, 51 | 0, // receiver value 52 | GAS_LIMIT, 53 | token, // address of IERC20 token contract 54 | amount 55 | ); 56 | } 57 | 58 | function receivePayloadAndTokens( 59 | bytes memory payload, 60 | TokenReceived[] memory receivedTokens, 61 | bytes32, // sourceAddress 62 | uint16, 63 | bytes32 // deliveryHash 64 | ) internal override onlyWormholeRelayer { 65 | require(receivedTokens.length == 1, "Expected 1 token transfers"); 66 | 67 | address recipient = abi.decode(payload, (address)); 68 | 69 | IERC20(receivedTokens[0].tokenAddress).transfer( 70 | recipient, 71 | receivedTokens[0].amount 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/example-extensions/HelloMultipleTokens.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "wormhole-solidity-sdk/WormholeRelayerSDK.sol"; 5 | import "wormhole-solidity-sdk/interfaces/IERC20.sol"; 6 | 7 | contract HelloMultipleTokens is TokenSender, TokenReceiver { 8 | uint256 constant GAS_LIMIT = 400_000; 9 | 10 | constructor( 11 | address _wormholeRelayer, 12 | address _tokenBridge, 13 | address _wormhole 14 | ) TokenBase(_wormholeRelayer, _tokenBridge, _wormhole) {} 15 | 16 | function quoteCrossChainDeposit( 17 | uint16 targetChain 18 | ) public view returns (uint256 cost) { 19 | uint256 deliveryCost; 20 | (deliveryCost, ) = wormholeRelayer.quoteEVMDeliveryPrice( 21 | targetChain, 22 | 0, 23 | GAS_LIMIT 24 | ); 25 | cost = deliveryCost + 2 * wormhole.messageFee(); 26 | } 27 | 28 | function sendCrossChainDeposit( 29 | uint16 targetChain, 30 | address targetHelloTokens, 31 | address recipient, 32 | uint256 amountA, 33 | address tokenA, 34 | uint256 amountB, 35 | address tokenB 36 | ) public payable { 37 | // emit token transfers 38 | IERC20(tokenA).transferFrom(msg.sender, address(this), amountA); 39 | IERC20(tokenB).transferFrom(msg.sender, address(this), amountB); 40 | 41 | // encode payload to go with both token transfers 42 | bytes memory payload = abi.encode(recipient); 43 | 44 | // add transfers to additionalVaas list so they will be delivered along with the payload 45 | VaaKey[] memory vaaKeys = new VaaKey[](2); 46 | vaaKeys[0] = transferTokens( 47 | tokenA, 48 | amountA, 49 | targetChain, 50 | targetHelloTokens 51 | ); 52 | vaaKeys[1] = transferTokens( 53 | tokenB, 54 | amountB, 55 | targetChain, 56 | targetHelloTokens 57 | ); 58 | 59 | uint256 cost = quoteCrossChainDeposit(targetChain); 60 | require( 61 | msg.value == cost, 62 | "msg.value must be quoteCrossChainDeposit(targetChain)" 63 | ); 64 | 65 | wormholeRelayer.sendVaasToEvm{value: cost - 2 * wormhole.messageFee()}( 66 | targetChain, 67 | targetHelloTokens, 68 | payload, 69 | 0, // no receiver value needed since we're just passing a message + wrapped tokens 70 | GAS_LIMIT, 71 | vaaKeys 72 | ); 73 | } 74 | 75 | function receivePayloadAndTokens( 76 | bytes memory payload, 77 | TokenReceived[] memory receivedTokens, 78 | bytes32, // sourceAddress 79 | uint16, 80 | bytes32 // deliveryHash 81 | ) internal override onlyWormholeRelayer { 82 | require(receivedTokens.length == 2, "Expected 2 token transfers"); 83 | 84 | // decode payload 85 | address recipient = abi.decode(payload, (address)); 86 | 87 | // send tokens to recipient 88 | IERC20(receivedTokens[0].tokenAddress).transfer( 89 | recipient, 90 | receivedTokens[0].amount 91 | ); 92 | IERC20(receivedTokens[1].tokenAddress).transfer( 93 | recipient, 94 | receivedTokens[1].amount 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/example-extensions/HelloTokenNative.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "wormhole-solidity-sdk/WormholeRelayerSDK.sol"; 5 | import "wormhole-solidity-sdk/interfaces/IERC20.sol"; 6 | import "wormhole-solidity-sdk/interfaces/IWETH.sol"; 7 | 8 | contract HelloTokenNative is TokenSender, TokenReceiver { 9 | uint256 constant GAS_LIMIT = 250_000; 10 | 11 | constructor( 12 | address _wormholeRelayer, 13 | address _tokenBridge, 14 | address _wormhole 15 | ) TokenBase(_wormholeRelayer, _tokenBridge, _wormhole) {} 16 | 17 | function quoteCrossChainDeposit( 18 | uint16 targetChain 19 | ) public view returns (uint256 cost) { 20 | // Cost of delivering token and payload to targetChain 21 | uint256 deliveryCost; 22 | (deliveryCost, ) = wormholeRelayer.quoteEVMDeliveryPrice( 23 | targetChain, 24 | 0, 25 | GAS_LIMIT 26 | ); 27 | 28 | // Total cost: delivery cost + cost of publishing the 'sending token' wormhole message 29 | cost = deliveryCost + wormhole.messageFee(); 30 | } 31 | 32 | function sendCrossChainDeposit( 33 | uint16 targetChain, 34 | address targetHelloToken, 35 | address recipient, 36 | uint256 amount, 37 | address token 38 | ) public payable { 39 | uint256 cost = quoteCrossChainDeposit(targetChain); 40 | require( 41 | msg.value == cost, 42 | "msg.value must be quoteCrossChainDeposit(targetChain)" 43 | ); 44 | 45 | IERC20(token).transferFrom(msg.sender, address(this), amount); 46 | 47 | bytes memory payload = abi.encode(recipient); 48 | sendTokenWithPayloadToEvm( 49 | targetChain, 50 | targetHelloToken, // address (on targetChain) to send token and payload to 51 | payload, 52 | 0, // receiver value 53 | GAS_LIMIT, 54 | token, // address of IERC20 token contract 55 | amount 56 | ); 57 | } 58 | 59 | function sendNativeCrossChainDeposit( 60 | uint16 targetChain, 61 | address targetHelloToken, 62 | address recipient, 63 | uint256 amount 64 | ) public payable { 65 | uint256 cost = quoteCrossChainDeposit(targetChain); 66 | require( 67 | msg.value == cost + amount, 68 | "msg.value must be quoteCrossChainDeposit(targetChain) + amount" 69 | ); 70 | 71 | IWETH wrappedNativeToken = tokenBridge.WETH(); 72 | wrappedNativeToken.deposit{value: amount}(); 73 | 74 | bytes memory payload = abi.encode(recipient); 75 | sendTokenWithPayloadToEvm( 76 | targetChain, 77 | targetHelloToken, // address (on targetChain) to send token and payload to 78 | payload, 79 | 0, // receiver value 80 | GAS_LIMIT, 81 | address(wrappedNativeToken), // address of IERC20 token contract 82 | amount 83 | ); 84 | } 85 | 86 | function receivePayloadAndTokens( 87 | bytes memory payload, 88 | TokenReceived[] memory receivedTokens, 89 | bytes32, // sourceAddress 90 | uint16, 91 | bytes32 // deliveryHash 92 | ) internal override onlyWormholeRelayer { 93 | require(receivedTokens.length == 1, "Expected 1 token transfers"); 94 | 95 | address recipient = abi.decode(payload, (address)); 96 | 97 | IERC20(receivedTokens[0].tokenAddress).transfer( 98 | recipient, 99 | receivedTokens[0].amount 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/example-extensions/HelloTokenWithoutSDK.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol"; 5 | import "wormhole-solidity-sdk/interfaces/IWormholeReceiver.sol"; 6 | import "wormhole-solidity-sdk/interfaces/ITokenBridge.sol"; 7 | import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; 8 | import "wormhole-solidity-sdk/interfaces/IERC20.sol"; 9 | 10 | contract HelloTokenWithoutSDK is IWormholeReceiver { 11 | uint256 constant GAS_LIMIT = 250_000; 12 | 13 | IWormholeRelayer public immutable wormholeRelayer; 14 | ITokenBridge public immutable tokenBridge; 15 | IWormhole public immutable wormhole; 16 | 17 | constructor(address _wormholeRelayer, address _tokenBridge, address _wormhole) { 18 | wormholeRelayer = IWormholeRelayer(_wormholeRelayer); 19 | tokenBridge = ITokenBridge(_tokenBridge); 20 | wormhole = IWormhole(_wormhole); 21 | } 22 | 23 | function quoteCrossChainDeposit(uint16 targetChain) public view returns (uint256 cost) { 24 | uint256 deliveryCost; 25 | (deliveryCost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, 0, GAS_LIMIT); 26 | cost = deliveryCost + wormhole.messageFee(); 27 | } 28 | 29 | function sendCrossChainDeposit( 30 | uint16 targetChain, 31 | address targetHelloToken, 32 | address recipient, 33 | uint256 amount, 34 | address token 35 | ) public payable { 36 | // emit token transfers 37 | IERC20(token).transferFrom(msg.sender, address(this), amount); 38 | IERC20(token).approve(address(tokenBridge), amount); 39 | uint64 sequence = tokenBridge.transferTokens{value: wormhole.messageFee()}( 40 | token, amount, targetChain, toWormholeFormat(targetHelloToken), 0, 0 41 | ); 42 | 43 | // specify the token transfer vaa should be delivered along with the payload 44 | VaaKey[] memory additionalVaas = new VaaKey[](1); 45 | additionalVaas[0] = VaaKey({ 46 | emitterAddress: toWormholeFormat(address(tokenBridge)), 47 | sequence: sequence, 48 | chainId: wormhole.chainId() 49 | }); 50 | 51 | uint256 cost = quoteCrossChainDeposit(targetChain); 52 | require(msg.value == cost, "Incorrect payment to cover delivery cost"); 53 | 54 | // encode payload 55 | bytes memory payload = abi.encode(recipient); 56 | 57 | wormholeRelayer.sendVaasToEvm{value: cost - wormhole.messageFee()}( 58 | targetChain, 59 | targetHelloToken, 60 | payload, 61 | 0, // no receiver value needed since we're just passing a message + wrapped token 62 | GAS_LIMIT, 63 | additionalVaas 64 | ); 65 | } 66 | 67 | function receiveWormholeMessages( 68 | bytes memory payload, 69 | bytes[] memory additionalVaas, 70 | bytes32, // sourceAddress 71 | uint16, 72 | bytes32 // deliveryHash 73 | ) public payable override { 74 | require(msg.sender == address(wormholeRelayer), "Only wormhole allowed"); 75 | require(additionalVaas.length == 2, "Expected 2 additional VAA keys for token transfers"); 76 | 77 | address recipient = abi.decode(payload, (address)); 78 | 79 | IWormhole.VM memory parsedVM = wormhole.parseVM(additionalVaas[0]); 80 | ITokenBridge.Transfer memory transfer = tokenBridge.parseTransfer(parsedVM.payload); 81 | tokenBridge.completeTransfer(additionalVaas[0]); 82 | 83 | address wrappedTokenAddress = transfer.tokenChain == wormhole.chainId() ? fromWormholeFormat(transfer.tokenAddress) : tokenBridge.wrappedAsset(transfer.tokenChain, transfer.tokenAddress); 84 | 85 | uint256 decimals = getDecimals(wrappedTokenAddress); 86 | uint256 powerOfTen = 0; 87 | if(decimals > 8) powerOfTen = decimals - 8; 88 | IERC20(wrappedTokenAddress).transfer(recipient, transfer.amount * 10 ** powerOfTen); // 89 | } 90 | 91 | } 92 | 93 | function toWormholeFormat(address addr) pure returns (bytes32) { 94 | return bytes32(uint256(uint160(addr))); 95 | } 96 | 97 | function fromWormholeFormat(bytes32 whFormatAddress) pure returns (address) { 98 | if (uint256(whFormatAddress) >> 160 != 0) { 99 | revert NotAnEvmAddress(whFormatAddress); 100 | } 101 | return address(uint160(uint256(whFormatAddress))); 102 | } 103 | 104 | function getDecimals(address tokenAddress) view returns (uint8 decimals) { 105 | // query decimals 106 | (,bytes memory queriedDecimals) = address(tokenAddress).staticcall(abi.encodeWithSignature("decimals()")); 107 | decimals = abi.decode(queriedDecimals, (uint8)); 108 | } 109 | -------------------------------------------------------------------------------- /test/HelloMultipleTokens.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {HelloMultipleTokens} from "../src/example-extensions/HelloMultipleTokens.sol"; 5 | 6 | import "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; 7 | 8 | import "forge-std/Test.sol"; 9 | import "forge-std/console.sol"; 10 | 11 | contract HelloMultipleTokensTest is WormholeRelayerBasicTest { 12 | event GreetingReceived(string greeting, uint16 senderChain, address sender); 13 | 14 | HelloMultipleTokens public helloSource; 15 | HelloMultipleTokens public helloTarget; 16 | 17 | ERC20Mock public tokenA; 18 | ERC20Mock public tokenB; 19 | 20 | function setUpSource() public override { 21 | helloSource = new HelloMultipleTokens( 22 | address(relayerSource), 23 | address(tokenBridgeSource), 24 | address(wormholeSource) 25 | ); 26 | 27 | tokenA = createAndAttestToken(sourceChain); 28 | tokenB = createAndAttestToken(sourceChain); 29 | } 30 | 31 | function setUpTarget() public override { 32 | helloTarget = new HelloMultipleTokens( 33 | address(relayerTarget), 34 | address(tokenBridgeTarget), 35 | address(wormholeTarget) 36 | ); 37 | } 38 | 39 | function testRemoteLP() public { 40 | uint256 amountA = 19e17; 41 | tokenA.approve(address(helloSource), amountA); 42 | uint256 amountB = 13e17; 43 | tokenB.approve(address(helloSource), amountB); 44 | 45 | vm.selectFork(targetFork); 46 | address recipient = 0x1234567890123456789012345678901234567890; 47 | 48 | vm.selectFork(sourceFork); 49 | uint256 cost = helloSource.quoteCrossChainDeposit(targetChain); 50 | 51 | vm.recordLogs(); 52 | helloSource.sendCrossChainDeposit{value: cost}( 53 | targetChain, address(helloTarget), recipient, amountA, address(tokenA), amountB, address(tokenB) 54 | ); 55 | performDelivery(); 56 | 57 | vm.selectFork(targetFork); 58 | 59 | address wormholeWrappedTokenA = tokenBridgeTarget.wrappedAsset(sourceChain, toWormholeFormat(address(tokenA))); 60 | assertEq(IERC20(wormholeWrappedTokenA).balanceOf(recipient), amountA); 61 | 62 | address wormholeWrappedTokenB = tokenBridgeTarget.wrappedAsset(sourceChain, toWormholeFormat(address(tokenB))); 63 | assertEq(IERC20(wormholeWrappedTokenB).balanceOf(recipient), amountB); 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/HelloToken.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {HelloToken} from "../src/HelloToken.sol"; 5 | 6 | import "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; 7 | 8 | import "forge-std/Test.sol"; 9 | import "forge-std/console.sol"; 10 | 11 | contract HelloTokenTest is WormholeRelayerBasicTest { 12 | HelloToken public helloSource; 13 | HelloToken public helloTarget; 14 | 15 | ERC20Mock public token; 16 | 17 | function setUpSource() public override { 18 | helloSource = new HelloToken( 19 | address(relayerSource), 20 | address(tokenBridgeSource), 21 | address(wormholeSource) 22 | ); 23 | 24 | token = createAndAttestToken(sourceChain); 25 | } 26 | 27 | function setUpTarget() public override { 28 | helloTarget = new HelloToken( 29 | address(relayerTarget), 30 | address(tokenBridgeTarget), 31 | address(wormholeTarget) 32 | ); 33 | } 34 | 35 | function testRemoteDeposit() public { 36 | uint256 amount = 19e17; 37 | token.approve(address(helloSource), amount); 38 | 39 | vm.selectFork(targetFork); 40 | address recipient = 0x1234567890123456789012345678901234567890; 41 | 42 | vm.selectFork(sourceFork); 43 | uint256 cost = helloSource.quoteCrossChainDeposit(targetChain); 44 | 45 | vm.recordLogs(); 46 | helloSource.sendCrossChainDeposit{value: cost}( 47 | targetChain, address(helloTarget), recipient, amount, address(token) 48 | ); 49 | performDelivery(); 50 | 51 | vm.selectFork(targetFork); 52 | address wormholeWrappedToken = tokenBridgeTarget.wrappedAsset(sourceChain, toWormholeFormat(address(token))); 53 | assertEq(IERC20(wormholeWrappedToken).balanceOf(recipient), amount); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/HelloTokenNative.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {HelloTokenNative} from "../src/example-extensions/HelloTokenNative.sol"; 5 | 6 | import "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; 7 | 8 | import "forge-std/Test.sol"; 9 | import "forge-std/console.sol"; 10 | 11 | contract HelloTokenNativeTest is WormholeRelayerBasicTest { 12 | HelloTokenNative public helloSource; 13 | HelloTokenNative public helloTarget; 14 | 15 | ERC20Mock public token; 16 | 17 | function setUpSource() public override { 18 | helloSource = new HelloTokenNative( 19 | address(relayerSource), 20 | address(tokenBridgeSource), 21 | address(wormholeSource) 22 | ); 23 | 24 | token = createAndAttestToken(sourceChain); 25 | } 26 | 27 | function setUpTarget() public override { 28 | helloTarget = new HelloTokenNative( 29 | address(relayerTarget), 30 | address(tokenBridgeTarget), 31 | address(wormholeTarget) 32 | ); 33 | } 34 | 35 | function testRemoteDeposit() public { 36 | uint256 amount = 19e17; 37 | token.approve(address(helloSource), amount); 38 | 39 | vm.selectFork(targetFork); 40 | address recipient = 0x1234567890123456789012345678901234567890; 41 | 42 | vm.selectFork(sourceFork); 43 | uint256 cost = helloSource.quoteCrossChainDeposit(targetChain); 44 | 45 | vm.recordLogs(); 46 | helloSource.sendCrossChainDeposit{value: cost}( 47 | targetChain, address(helloTarget), recipient, amount, address(token) 48 | ); 49 | performDelivery(); 50 | 51 | vm.selectFork(targetFork); 52 | address wormholeWrappedToken = tokenBridgeTarget.wrappedAsset(sourceChain, toWormholeFormat(address(token))); 53 | assertEq(IERC20(wormholeWrappedToken).balanceOf(recipient), amount); 54 | } 55 | 56 | function testRemoteNativeDeposit() public { 57 | uint256 amount = 19e17; 58 | 59 | vm.selectFork(targetFork); 60 | address recipient = 0x1234567890123456789012345678901234567890; 61 | 62 | vm.selectFork(sourceFork); 63 | uint256 cost = helloSource.quoteCrossChainDeposit(targetChain); 64 | 65 | address wethAddress = address(tokenBridgeSource.WETH()); 66 | 67 | vm.recordLogs(); 68 | helloSource.sendNativeCrossChainDeposit{value: cost + amount}( 69 | targetChain, address(helloTarget), recipient, amount 70 | ); 71 | performDelivery(); 72 | 73 | vm.selectFork(targetFork); 74 | address wormholeWrappedToken = tokenBridgeTarget.wrappedAsset(sourceChain, toWormholeFormat(wethAddress)); 75 | assertEq(IERC20(wormholeWrappedToken).balanceOf(recipient), amount); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ts-scripts/deploy-mock-tokens.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | import { ERC20Mock__factory } from "./ethers-contracts"; 3 | import { 4 | loadDeployedAddresses, 5 | getWallet, 6 | wait, 7 | loadConfig, 8 | storeDeployedAddresses, 9 | getChain, 10 | } from "./utils"; 11 | import { 12 | ChainId, 13 | attestFromEth, 14 | createWrappedOnEth, 15 | getSignedVAAWithRetry, 16 | parseSequenceFromLogEth, 17 | tryNativeToHexString, 18 | } from "@certusone/wormhole-sdk"; 19 | import * as grpcWebNodeHttpTransport from "@improbable-eng/grpc-web-node-http-transport"; 20 | import { ChainInfo, getArg } from "./utils"; 21 | 22 | const sourceChain = loadConfig().sourceChain; 23 | const targetChain = loadConfig().targetChain; 24 | 25 | export async function deployMockToken() { 26 | const deployed = loadDeployedAddresses(); 27 | const from = getChain(sourceChain); 28 | 29 | const signer = getWallet(from.chainId); 30 | const HT = await new ERC20Mock__factory(signer).deploy("HelloToken", "HT"); 31 | await HT.deployed(); 32 | console.log(`HT deployed to ${HT.address} on chain ${from.chainId}`); 33 | deployed.erc20s[sourceChain] = [HT.address]; 34 | 35 | console.log("Minting..."); 36 | await HT.mint(signer.address, ethers.utils.parseEther("10")).then(wait); 37 | console.log("Minted 10 HT to signer"); 38 | 39 | console.log( 40 | `Attesting tokens with token bridge on chain(s) ${loadConfig() 41 | .chains.map((c) => c.chainId) 42 | .filter((c) => c === targetChain) 43 | .join(", ")}` 44 | ); 45 | for (const chain of loadConfig().chains) { 46 | if (chain.chainId !== targetChain) { 47 | continue; 48 | } 49 | await attestWorkflow({ 50 | from: getChain(sourceChain), 51 | to: chain, 52 | token: HT.address, 53 | }); 54 | } 55 | 56 | storeDeployedAddresses(deployed); 57 | } 58 | 59 | async function attestWorkflow({ 60 | to, 61 | from, 62 | token, 63 | }: { 64 | to: ChainInfo; 65 | from: ChainInfo; 66 | token: string; 67 | }) { 68 | const attestRx: ethers.ContractReceipt = await attestFromEth( 69 | from.tokenBridge!, 70 | getWallet(from.chainId), 71 | token 72 | ); 73 | const seq = parseSequenceFromLogEth(attestRx, from.wormhole); 74 | 75 | const res = await getSignedVAAWithRetry( 76 | ["https://api.testnet.wormscan.io"], 77 | Number(from) as ChainId, 78 | tryNativeToHexString(from.tokenBridge, "ethereum"), 79 | seq.toString(), 80 | { transport: grpcWebNodeHttpTransport.NodeHttpTransport() } 81 | ); 82 | const createWrappedRx = await createWrappedOnEth( 83 | to.tokenBridge, 84 | getWallet(to.chainId), 85 | res.vaaBytes 86 | ); 87 | console.log( 88 | `Attested token from chain ${from.chainId} to chain ${to.chainId}` 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /ts-scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | import { HelloToken__factory, ERC20Mock__factory } from "./ethers-contracts"; 3 | import { 4 | loadConfig, 5 | getWallet, 6 | storeDeployedAddresses, 7 | getChain, 8 | wait, 9 | loadDeployedAddresses, 10 | } from "./utils"; 11 | 12 | export async function deploy() { 13 | const config = loadConfig(); 14 | 15 | // fuij and celo 16 | const deployed = loadDeployedAddresses(); 17 | for (const chainId of [config.sourceChain, config.targetChain]) { 18 | const chain = getChain(chainId); 19 | const signer = getWallet(chainId); 20 | 21 | const helloToken = await new HelloToken__factory(signer).deploy( 22 | chain.wormholeRelayer, 23 | chain.tokenBridge!, 24 | chain.wormhole 25 | ); 26 | await helloToken.deployed(); 27 | 28 | deployed.helloToken[chainId] = helloToken.address; 29 | console.log( 30 | `HelloToken deployed to ${helloToken.address} on chain ${chainId}` 31 | ); 32 | } 33 | 34 | storeDeployedAddresses(deployed); 35 | } 36 | -------------------------------------------------------------------------------- /ts-scripts/getStatus.ts: -------------------------------------------------------------------------------- 1 | import { relayer, ChainName } from "@certusone/wormhole-sdk"; 2 | 3 | export async function getStatus( 4 | sourceChain: ChainName, 5 | transactionHash: string 6 | ): Promise<{ status: string; info: string }> { 7 | const info = await relayer.getWormholeRelayerInfo( 8 | sourceChain, 9 | transactionHash, 10 | { environment: "TESTNET" } 11 | ); 12 | const status = 13 | info.targetChainStatus.events[0]?.status || DeliveryStatus.PendingDelivery; 14 | return { status, info: info.stringified || "Info not obtained" }; 15 | } 16 | 17 | export const DeliveryStatus = relayer.DeliveryStatus; 18 | 19 | export const waitForDelivery = async ( 20 | sourceChain: ChainName, 21 | transactionHash: string 22 | ) => { 23 | let pastStatusString = ""; 24 | let waitCount = 0; 25 | while (true) { 26 | let waitTime = 15; 27 | if (waitCount > 5) { 28 | waitTime = 60; 29 | } 30 | await new Promise((resolve) => setTimeout(resolve, 1000 * waitTime)); 31 | waitCount += 1; 32 | 33 | const res = await getStatus(sourceChain, transactionHash); 34 | if (res.info !== pastStatusString) { 35 | console.log(res.info); 36 | pastStatusString = res.info; 37 | } 38 | if (res.status !== DeliveryStatus.PendingDelivery) break; 39 | console.log(`\nContinuing to wait for delivery\n`); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /ts-scripts/hello_token.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "@jest/globals"; 2 | import { ethers } from "ethers"; 3 | import { 4 | getHelloToken, 5 | loadDeployedAddresses as getDeployedAddresses, 6 | getWallet, 7 | getChain, 8 | wait, 9 | loadConfig, 10 | } from "./utils"; 11 | import { waitForDelivery } from "./getStatus"; 12 | import { ERC20Mock__factory, ITokenBridge__factory } from "./ethers-contracts"; 13 | import { 14 | tryNativeToUint8Array, 15 | CHAIN_ID_TO_NAME, 16 | CHAINS, 17 | } from "@certusone/wormhole-sdk"; 18 | 19 | const sourceChain = loadConfig().sourceChain; 20 | const targetChain = loadConfig().targetChain; 21 | 22 | describe("Hello Tokens Integration Tests on Testnet", () => { 23 | test( 24 | "Tests the sending of a token", 25 | async () => { 26 | // Token Bridge can only deal with 8 decimal places 27 | // So we send a multiple of 10^10, since this MockToken has 18 decimal places 28 | const arbitraryTokenAmount = ethers.BigNumber.from( 29 | new Date().getTime() % 10 ** 7 30 | ).mul(10 ** 10); 31 | 32 | const HTtoken = ERC20Mock__factory.connect( 33 | getDeployedAddresses().erc20s[sourceChain][0], 34 | getWallet(sourceChain) 35 | ); 36 | 37 | const wormholeWrappedHTTokenAddressOnTargetChain = 38 | await ITokenBridge__factory.connect( 39 | getChain(targetChain).tokenBridge, 40 | getWallet(targetChain) 41 | ).wrappedAsset( 42 | sourceChain, 43 | tryNativeToUint8Array(HTtoken.address, "ethereum") 44 | ); 45 | const wormholeWrappedHTTokenOnTargetChain = ERC20Mock__factory.connect( 46 | wormholeWrappedHTTokenAddressOnTargetChain, 47 | getWallet(targetChain) 48 | ); 49 | 50 | const walletTargetChainAddress = getWallet(targetChain).address; 51 | 52 | const sourceHelloTokenContract = getHelloToken(sourceChain); 53 | const targetHelloTokenContract = getHelloToken(targetChain); 54 | 55 | const walletOriginalBalanceOfWrappedHTToken = 56 | await wormholeWrappedHTTokenOnTargetChain.balanceOf( 57 | walletTargetChainAddress 58 | ); 59 | 60 | const cost = await sourceHelloTokenContract.quoteCrossChainDeposit( 61 | targetChain 62 | ); 63 | console.log( 64 | `Cost of sending the tokens: ${ethers.utils.formatEther( 65 | cost 66 | )} testnet ${CHAIN_ID_TO_NAME[sourceChain]}` 67 | ); 68 | 69 | // Approve the HelloToken contract to use 'arbitraryTokenAmount' of our HT token 70 | const approveTx = await HTtoken.approve( 71 | sourceHelloTokenContract.address, 72 | arbitraryTokenAmount 73 | ).then(wait); 74 | console.log( 75 | `HelloToken contract approved to spend ${ethers.utils.formatEther( 76 | arbitraryTokenAmount 77 | )} of our HT token` 78 | ); 79 | 80 | console.log( 81 | `Sending ${ethers.utils.formatEther( 82 | arbitraryTokenAmount 83 | )} of the HT token` 84 | ); 85 | 86 | const tx = await sourceHelloTokenContract.sendCrossChainDeposit( 87 | targetChain, 88 | targetHelloTokenContract.address, 89 | walletTargetChainAddress, 90 | arbitraryTokenAmount, 91 | HTtoken.address, 92 | { value: cost } 93 | ); 94 | 95 | console.log(`Transaction hash: ${tx.hash}`); 96 | await tx.wait(); 97 | 98 | await waitForDelivery(CHAIN_ID_TO_NAME[sourceChain], tx.hash); 99 | 100 | console.log(`Seeing if token was sent`); 101 | const walletCurrentBalanceOfWrappedHTToken = 102 | await wormholeWrappedHTTokenOnTargetChain.balanceOf( 103 | walletTargetChainAddress 104 | ); 105 | 106 | expect( 107 | walletCurrentBalanceOfWrappedHTToken 108 | .sub(walletOriginalBalanceOfWrappedHTToken) 109 | .toString() 110 | ).toBe(arbitraryTokenAmount.toString()); 111 | }, 112 | 60 * 1000 * 60 113 | ); // timeout 114 | }); 115 | -------------------------------------------------------------------------------- /ts-scripts/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | }; -------------------------------------------------------------------------------- /ts-scripts/main.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from "ethers" 2 | import { 3 | checkFlag, 4 | getHelloToken, 5 | getWallet, 6 | loadDeployedAddresses as getDeployedAddresses, 7 | wait, 8 | getArg 9 | } from "./utils" 10 | import { ERC20Mock__factory, } from "./ethers-contracts" 11 | import { deploy } from "./deploy" 12 | import { deployMockToken } from "./deploy-mock-tokens" 13 | 14 | async function main() { 15 | if (checkFlag("--sendRemoteDeposit")) { 16 | await sendRemoteDeposit() 17 | return 18 | } 19 | if (checkFlag("--deployHelloToken")) { 20 | await deploy() 21 | return 22 | } 23 | if (checkFlag("--deployMockToken")) { 24 | await deployMockToken() 25 | return 26 | } 27 | } 28 | 29 | async function sendRemoteDeposit() { 30 | // const from = Number(getArg(["--from", "-f"])) 31 | // const to = Number(getArg(["--to", "-t"])) 32 | // const amount = getArg(["--amount", "-a"]) 33 | const recipient = getArg(["--recipient", "-r"]) || ""; 34 | 35 | const from = 6 36 | const to = 14 37 | const amount = ethers.utils.parseEther("10") 38 | 39 | const helloToken = getHelloToken(from) 40 | const cost = await helloToken.quoteCrossChainDeposit(to) 41 | console.log(`cost: ${ethers.utils.formatEther(cost)}`) 42 | 43 | const HT = ERC20Mock__factory.connect(getDeployedAddresses().erc20s[from][0], getWallet(from)); 44 | 45 | const rx = await helloToken 46 | .sendCrossChainDeposit( 47 | to, 48 | getHelloToken(to).address, 49 | recipient, 50 | amount, 51 | HT.address 52 | ) 53 | .then(wait) 54 | } 55 | 56 | main().catch(e => { 57 | console.error(e) 58 | process.exit(1) 59 | }) 60 | -------------------------------------------------------------------------------- /ts-scripts/testnet/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceChain": 6, 3 | "targetChain": 14, 4 | "chains": [ 5 | { 6 | "description": "Goerli - Ethereum", 7 | "chainId": 2, 8 | "rpc": "https://ethereum-goerli.publicnode.com", 9 | "tokenBridge": "0xF890982f9310df57d00f659cf4fd87e65adEd8d7", 10 | "wormholeRelayer": "0x28D8F1Be96f97C1387e94A53e00eCcFb4E75175a", 11 | "wormhole": "0x706abc4E45D419950511e474C7B9Ed348A4a716c" 12 | }, 13 | { 14 | "description": "Avalanche testnet fuji", 15 | "chainId": 6, 16 | "rpc": "https://api.avax-test.network/ext/bc/C/rpc", 17 | "tokenBridge": "0x61E44E506Ca5659E6c0bba9b678586fA2d729756", 18 | "wormholeRelayer": "0xA3cF45939bD6260bcFe3D66bc73d60f19e49a8BB", 19 | "wormhole": "0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C" 20 | }, 21 | { 22 | "description": "Celo Testnet", 23 | "chainId": 14, 24 | "rpc": "https://alfajores-forno.celo-testnet.org", 25 | "tokenBridge": "0x05ca6037eC51F8b712eD2E6Fa72219FEaE74E153", 26 | "wormholeRelayer": "0x306B68267Deb7c5DfCDa3619E22E9Ca39C374f84", 27 | "wormhole": "0x88505117CA88e7dd2eC6EA1E13f0948db2D50D56" 28 | }, 29 | { 30 | "description": "BSC Testnet", 31 | "chainId": 4, 32 | "rpc": "https://bsc-testnet.public.blastapi.io", 33 | "tokenBridge": "0x9dcF9D205C9De35334D646BeE44b2D2859712A09", 34 | "wormholeRelayer": "0x80aC94316391752A193C1c47E27D382b507c93F3", 35 | "wormhole": "0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D" 36 | }, 37 | { 38 | "description": "Polygon Mumbai", 39 | "chainId": 5, 40 | "rpc": "https://rpc.ankr.com/polygon_mumbai", 41 | "tokenBridge": "0x377D55a7928c046E18eEbb61977e714d2a76472a", 42 | "wormholeRelayer": "0x0591C25ebd0580E0d4F27A82Fc2e24E7489CB5e0", 43 | "wormhole": "0x0CBE91CF822c73C2315FB05100C2F714765d5c20" 44 | }, 45 | { 46 | "description": "Moonbase Alpha - Moonbeam", 47 | "chainId": 16, 48 | "rpc": "https://rpc.testnet.moonbeam.network", 49 | "tokenBridge": "0xbc976D4b9D57E57c3cA52e1Fd136C45FF7955A96", 50 | "wormholeRelayer": "0x0591C25ebd0580E0d4F27A82Fc2e24E7489CB5e0", 51 | "wormhole": "0xa5B7D85a8f27dd7907dc8FdC21FA5657D5E2F901" 52 | }, 53 | { 54 | "description": "Goerli - Arbitrum", 55 | "chainId": 23, 56 | "rpc": "https://arbitrum-goerli.publicnode.com", 57 | "tokenBridge": "0x23908A62110e21C04F3A4e011d24F901F911744A", 58 | "wormholeRelayer": "0xAd753479354283eEE1b86c9470c84D42f229FF43", 59 | "wormhole": "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e" 60 | }, 61 | { 62 | "description": "Goerli - Optimism", 63 | "chainId": 24, 64 | "rpc": "https://optimism-goerli.publicnode.com", 65 | "tokenBridge": "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e", 66 | "wormholeRelayer": "0x01A957A525a5b7A72808bA9D10c389674E459891", 67 | "wormhole": "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35" 68 | }, 69 | { 70 | "description": "Goerli - Base", 71 | "chainId": 30, 72 | "rpc": "https://goerli.base.org", 73 | "tokenBridge": "0xA31aa3FDb7aF7Db93d18DDA4e19F811342EDF780", 74 | "wormholeRelayer": "0xea8029CD7FCAEFFcD1F53686430Db0Fc8ed384E1", 75 | "wormhole": "0x23908A62110e21C04F3A4e011d24F901F911744A" 76 | } 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /ts-scripts/testnet/contracts.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "This file contains the addresses for the contracts on each chain. If useLastRun is true, this file will be ignored, and the addresses will be taken from the lastrun.json of the deployment scripts.", 3 | "useLastRun": true, 4 | "deliveryProviders": [ 5 | { 6 | "chainId": 6, 7 | "address": "0xd5903a063f604D4615E5c2760b7b80D491564BBe" 8 | }, 9 | { 10 | "chainId": 14, 11 | "address": "0x93d56f29542c156B3e36f10dE41124B499664ff7" 12 | }, 13 | { 14 | "chainId": 4, 15 | "address": "0x813AB43ab264362c55BF35A1448d0fd8135049a6" 16 | }, 17 | { 18 | "chainId": 5, 19 | "address": "0xBF684878906629E72079D4f07D75Ef7165238092" 20 | }, 21 | { 22 | "chainId": 16, 23 | "address": "0xBF684878906629E72079D4f07D75Ef7165238092" 24 | } 25 | ], 26 | "wormholeRelayers": [ 27 | { 28 | "chainId": 6, 29 | "address": "0xA3cF45939bD6260bcFe3D66bc73d60f19e49a8BB" 30 | }, 31 | { 32 | "chainId": 14, 33 | "address": "0x306B68267Deb7c5DfCDa3619E22E9Ca39C374f84" 34 | }, 35 | { 36 | "chainId": 4, 37 | "address": "0x80aC94316391752A193C1c47E27D382b507c93F3" 38 | }, 39 | { 40 | "chainId": 5, 41 | "address": "0x0591C25ebd0580E0d4F27A82Fc2e24E7489CB5e0" 42 | }, 43 | { 44 | "chainId": 16, 45 | "address": "0x0591C25ebd0580E0d4F27A82Fc2e24E7489CB5e0" 46 | } 47 | ], 48 | "mockIntegrations": [ 49 | { 50 | "chainId": 6, 51 | "address": "0x5E52f3eB0774E5e5f37760BD3Fca64951D8F74Ae" 52 | }, 53 | { 54 | "chainId": 14, 55 | "address": "0x7f1d8E809aBB3F6Dc9B90F0131C3E8308046E190" 56 | }, 57 | { 58 | "chainId": 4, 59 | "address": "0xb6A04D6672F005787147472Be20d39741929Aa03" 60 | }, 61 | { 62 | "chainId": 5, 63 | "address": "0x3bF0c43d88541BBCF92bE508ec41e540FbF28C56" 64 | }, 65 | { 66 | "chainId": 16, 67 | "address": "0x3bF0c43d88541BBCF92bE508ec41e540FbF28C56" 68 | } 69 | ], 70 | "create2Factories": [ 71 | { 72 | "chainId": 6, 73 | "address": "0xbB2C5134cCDAC02047D915aCd451B697E4D71E1a" 74 | }, 75 | { 76 | "chainId": 14, 77 | "address": "0xF4325DD26aF3cEeAB3Bb35ECC916F332505d9847" 78 | }, 79 | { 80 | "chainId": 4, 81 | "address": "0x1B8d30A267041693b7DAA3c24E47746A28885054" 82 | }, 83 | { 84 | "chainId": 5, 85 | "address": "0xE49B157DA7B71308d32512ec150ffEa8677F4986" 86 | }, 87 | { 88 | "chainId": 16, 89 | "address": "0xE49B157DA7B71308d32512ec150ffEa8677F4986" 90 | } 91 | ] 92 | } -------------------------------------------------------------------------------- /ts-scripts/testnet/deployedAddresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "erc20s": [ 3 | null, 4 | null, 5 | null, 6 | null, 7 | null, 8 | null, 9 | [ 10 | "0xf2D5cCC108bb977e1F8a9C7239E572EFFffF09a1" 11 | ], 12 | null, 13 | null, 14 | null, 15 | null, 16 | null, 17 | null, 18 | null, 19 | null, 20 | null, 21 | null, 22 | null, 23 | null, 24 | null, 25 | null, 26 | null, 27 | null, 28 | [ 29 | "0x1a4dE85e856599142980f2fBed8587ff53F1530B" 30 | ], 31 | [ 32 | "0x8D80B0B5De3C0a214dA6Fd8022AbAa6E77180B08" 33 | ], 34 | null, 35 | null, 36 | null, 37 | null, 38 | null, 39 | [ 40 | "0x46379De09a56178ed43bD79Ff20DE6F163eB7D20" 41 | ] 42 | ], 43 | "helloToken": [ 44 | null, 45 | null, 46 | "0x47bBC88Ae36803C405CE1E0CaA8419E9eE35CFb8", 47 | null, 48 | "0x5C8f6E90e59ea4c6C8EA0899c6529b3b49DAeB30", 49 | null, 50 | "0x64EDBf0f70b60d30eFB0e8d79Af30164f6996fA9", 51 | null, 52 | null, 53 | null, 54 | null, 55 | null, 56 | null, 57 | null, 58 | "0xED8510d4ddcD34Ec352dF45F20185F90F24eD0b4", 59 | null, 60 | "0x2768B7BDc5cD3154553021A74534e85Ac6cdc07D", 61 | null, 62 | null, 63 | null, 64 | null, 65 | null, 66 | null, 67 | "0xb7F4Ff1ddd5F46fB40cc51C763D2DDeF7C957859", 68 | "0x93d56f29542c156B3e36f10dE41124B499664ff7", 69 | null, 70 | null, 71 | null, 72 | null, 73 | null, 74 | "0xFE1BE9F3f86bDA2Eb36D317DCFA1C63207952fC2", 75 | null 76 | ] 77 | } -------------------------------------------------------------------------------- /ts-scripts/utils.ts: -------------------------------------------------------------------------------- 1 | import { ethers, Wallet } from "ethers"; 2 | import { readFileSync, writeFileSync } from "fs"; 3 | 4 | import { HelloToken, HelloToken__factory } from "./ethers-contracts"; 5 | import { ChainId } from "@certusone/wormhole-sdk"; 6 | 7 | export interface ChainInfo { 8 | description: string; 9 | chainId: number; 10 | rpc: string; 11 | tokenBridge: string; 12 | wormholeRelayer: string; 13 | wormhole: string; 14 | } 15 | 16 | export interface Config { 17 | chains: ChainInfo[]; 18 | sourceChain: ChainId; 19 | targetChain: ChainId; 20 | } 21 | export interface DeployedAddresses { 22 | helloToken: Record; 23 | erc20s: Record; 24 | } 25 | 26 | export function getHelloToken(chainId: number) { 27 | const deployed = loadDeployedAddresses().helloToken[chainId]; 28 | if (!deployed) { 29 | throw new Error(`No deployed hello token on chain ${chainId}`); 30 | } 31 | return HelloToken__factory.connect(deployed, getWallet(chainId)); 32 | } 33 | 34 | export function getChain(chainId: number): ChainInfo { 35 | const chain = loadConfig().chains.find((c) => c.chainId === chainId)!; 36 | if (!chain) { 37 | throw new Error(`Chain ${chainId} not found`); 38 | } 39 | return chain; 40 | } 41 | 42 | export function getWallet(chainId: number): Wallet { 43 | const rpc = loadConfig().chains.find((c) => c.chainId === chainId)?.rpc; 44 | let provider = new ethers.providers.JsonRpcProvider(rpc); 45 | if (!process.env.EVM_PRIVATE_KEY) 46 | throw Error( 47 | "No private key provided (use the EVM_PRIVATE_KEY environment variable)" 48 | ); 49 | return new Wallet(process.env.EVM_PRIVATE_KEY!, provider); 50 | } 51 | 52 | let _config: Config | undefined; 53 | let _deployed: DeployedAddresses | undefined; 54 | 55 | export function loadConfig(): Config { 56 | if (!_config) { 57 | _config = JSON.parse( 58 | readFileSync("ts-scripts/testnet/config.json", { encoding: "utf-8" }) 59 | ); 60 | } 61 | return _config!; 62 | } 63 | 64 | export function loadDeployedAddresses(): DeployedAddresses { 65 | if (!_deployed) { 66 | _deployed = JSON.parse( 67 | readFileSync("ts-scripts/testnet/deployedAddresses.json", { 68 | encoding: "utf-8", 69 | }) 70 | ); 71 | if (!deployed) { 72 | _deployed = { 73 | erc20s: [], 74 | helloToken: [], 75 | }; 76 | } 77 | } 78 | return _deployed!; 79 | } 80 | 81 | export function storeDeployedAddresses(deployed: DeployedAddresses) { 82 | writeFileSync( 83 | "ts-scripts/testnet/deployedAddresses.json", 84 | JSON.stringify(deployed, undefined, 2) 85 | ); 86 | } 87 | 88 | export function checkFlag(patterns: string | string[]) { 89 | return getArg(patterns, { required: false, isFlag: true }); 90 | } 91 | 92 | export function getArg( 93 | patterns: string | string[], 94 | { 95 | isFlag = false, 96 | required = true, 97 | }: { isFlag?: boolean; required?: boolean } = { 98 | isFlag: false, 99 | required: true, 100 | } 101 | ): string | undefined { 102 | let idx: number = -1; 103 | if (typeof patterns === "string") { 104 | patterns = [patterns]; 105 | } 106 | for (const pattern of patterns) { 107 | idx = process.argv.findIndex((x) => x === pattern); 108 | if (idx !== -1) { 109 | break; 110 | } 111 | } 112 | if (idx === -1) { 113 | if (required) { 114 | throw new Error( 115 | "Missing required cmd line arg: " + JSON.stringify(patterns) 116 | ); 117 | } 118 | return undefined; 119 | } 120 | if (isFlag) { 121 | return process.argv[idx]; 122 | } 123 | return process.argv[idx + 1]; 124 | } 125 | 126 | export const deployed = (x: any) => x.deployed(); 127 | export const wait = (x: any) => x.wait(); 128 | --------------------------------------------------------------------------------