├── .commitlintrc ├── .env.template ├── .eslintignore ├── .eslintrc ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── checks.yaml │ └── tests.yaml ├── .gitignore ├── .gitmodules ├── .husky ├── commit-msg └── pre-commit ├── .npmignore ├── .prettierignore ├── .prettierrc ├── .release-it.js ├── .solcover.js ├── .solhint.json ├── .solhintignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── SECURITY.md ├── contracts ├── BlastERC20RebasingYield.sol ├── BlastNativeYield.sol ├── BlastPoints.sol ├── OwnableTwoSteps.sol ├── PackableReentrancyGuard.sol ├── Pausable.sol ├── ProtocolFee.sol ├── ReentrancyGuard.sol ├── SignatureCheckerCalldata.sol ├── SignatureCheckerMemory.sol ├── constants │ ├── AssemblyConstants.sol │ └── StandardConstants.sol ├── errors │ ├── GenericErrors.sol │ ├── LowLevelErrors.sol │ └── SignatureCheckerErrors.sol ├── interfaces │ ├── IBlast.sol │ ├── IBlastPoints.sol │ ├── IERC20Rebasing.sol │ ├── IOwnableTwoSteps.sol │ ├── IReentrancyGuard.sol │ └── generic │ │ ├── IERC1155.sol │ │ ├── IERC1271.sol │ │ ├── IERC165.sol │ │ ├── IERC20.sol │ │ ├── IERC2981.sol │ │ ├── IERC721.sol │ │ └── IWETH.sol ├── libraries │ └── UnsafeMathUint256.sol └── lowLevelCallers │ ├── LowLevelERC1155Transfer.sol │ ├── LowLevelERC20Approve.sol │ ├── LowLevelERC20Transfer.sol │ ├── LowLevelERC721Transfer.sol │ ├── LowLevelETHReturnETHIfAny.sol │ ├── LowLevelETHReturnETHIfAnyExceptOneWei.sol │ ├── LowLevelETHTransfer.sol │ └── LowLevelWETH.sol ├── foundry.toml ├── hardhat.config.ts ├── package.json ├── test ├── foundry │ ├── BlastERC20RebasingYield.t.sol │ ├── BlastNativeYield.t.sol │ ├── BlastPoints.t.sol │ ├── LowLevelERC1155Transfer.t.sol │ ├── LowLevelERC20Approve.t.sol │ ├── LowLevelERC20Transfer.t.sol │ ├── LowLevelERC721Transfer.t.sol │ ├── LowLevelETH.t.sol │ ├── LowLevelWETH.t.sol │ ├── OwnableTwoSteps.t.sol │ ├── PackableReentrancyGuard.t.sol │ ├── Pausable.t.sol │ ├── ProtocolFee.t.sol │ ├── ReentrancyGuard.t.sol │ ├── SignatureChecker.t.sol │ ├── USDTLowLevelERC20Test.sol │ ├── UnsafeMathUint256.t.sol │ └── utils │ │ ├── BytesLib.sol │ │ ├── ERC1271Contract.sol │ │ ├── PublicSignatureChecker.sol │ │ ├── TestHelpers.sol │ │ └── reentrancy │ │ ├── Faucet.sol │ │ ├── ReentrancyCaller.sol │ │ └── UnsafeFaucet.sol └── mock │ ├── MockBlastERC20.sol │ ├── MockBlastPoints.sol │ ├── MockBlastWETH.sol │ ├── MockBlastYield.sol │ ├── MockERC1155.sol │ ├── MockERC20.sol │ ├── MockERC721.sol │ └── MockWETH.sol ├── tsconfig.json └── yarn.lock /.commitlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@commitlint/config-conventional" 4 | ], 5 | "rules": { 6 | "subject-case": [ 7 | 2, 8 | "always", 9 | "sentence-case" 10 | ], 11 | "type-enum": [ 12 | 2, 13 | "always", 14 | [ 15 | "build", 16 | "ci", 17 | "chore", 18 | "docs", 19 | "feat", 20 | "fix", 21 | "perf", 22 | "refactor", 23 | "revert", 24 | "style", 25 | "test" 26 | ] 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | GITHUB_TOKEN= 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | typechain 7 | lib 8 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "es2021": true, 5 | "mocha": true, 6 | "node": true 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "extends": [ 10 | "standard", 11 | "plugin:@typescript-eslint/recommended", 12 | "plugin:prettier/recommended", 13 | "plugin:node/recommended" 14 | ], 15 | "parser": "@typescript-eslint/parser", 16 | "parserOptions": { 17 | "ecmaVersion": 12 18 | }, 19 | "rules": { 20 | "node/no-unsupported-features/es-syntax": ["error", { "ignores": ["modules"] }] 21 | }, 22 | "settings": { 23 | "node": { 24 | "tryExtensions": [".js", ".json", ".ts", ".d.ts"] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 2 | 3 | @0xShisui @0xJurassicPunk @0xhiroshi 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help improve this repo 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | - **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | - **To reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | - **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | - **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this repo 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | - **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | - **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | - **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | - **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/workflows/checks.yaml: -------------------------------------------------------------------------------- 1 | name: Format and lint checks 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | format: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | submodules: recursive 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 16.x 21 | - name: Get yarn cache directory path 22 | id: yarn-cache-dir-path 23 | run: echo "::set-output name=dir::$(yarn cache dir)" 24 | - uses: actions/cache@v2 25 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 26 | with: 27 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 28 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-yarn- 31 | - name: Install dependencies 32 | run: yarn install --frozen-lockfile 33 | - name: Run format checks 34 | run: yarn format:check 35 | - name: Run lint 36 | run: yarn lint 37 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | submodules: recursive 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 16.x 21 | - name: Get yarn cache directory path 22 | id: yarn-cache-dir-path 23 | run: echo "::set-output name=dir::$(yarn cache dir)" 24 | - uses: actions/cache@v2 25 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 26 | with: 27 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 28 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-yarn- 31 | - name: Install dev dependencies 32 | run: yarn install --frozen-lockfile 33 | - name: Install Foundry 34 | uses: onbjerg/foundry-toolchain@v1 35 | with: 36 | version: nightly 37 | - name: Compile code (Hardhat) 38 | run: yarn compile:hardhat:force 39 | - name: Run TypeScript/Waffle tests 40 | run: yarn test:hardhat 41 | - name: Run Solidity/Forge tests 42 | run: yarn test:forge 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .env* 4 | !.env.template 5 | coverage 6 | coverage.json 7 | typechain 8 | .DS_Store 9 | 10 | # Hardhat files 11 | cache 12 | artifacts 13 | abis/ 14 | 15 | # Others 16 | lib 17 | .gas-snapshot 18 | lcov* 19 | out/ 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint 5 | yarn format:check 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .env* 4 | coverage 5 | coverage.json 6 | typechain 7 | 8 | # Hardhat files 9 | cache 10 | artifacts 11 | 12 | hardhat.config.ts 13 | contracts/test 14 | test/ 15 | 16 | # Others 17 | lib 18 | .DS_Store 19 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | typechain 7 | lib* 8 | LowLevelETH.sol 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /.release-it.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | // Doc: https://github.com/release-it/release-it 4 | module.exports = { 5 | git: { 6 | commitMessage: "build: Release v${version}", 7 | requireUpstream: false, 8 | pushRepo: "upstream", // Push tags and commit to the remote `upstream` (fails if doesn't exist) 9 | requireBranch: "master", // Push commit to the branch `master` (fail if on other branch) 10 | requireCommits: true, // Require new commits since latest tag 11 | }, 12 | github: { 13 | release: true, 14 | }, 15 | hooks: { 16 | "after:bump": "yarn compile:forge", 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | silent: true, 3 | measureStatementCoverage: true, 4 | measureFunctionCoverage: true, 5 | skipFiles: ["interfaces", "test"], 6 | configureYulOptimizer: true, 7 | }; 8 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "code-complexity": ["warn", 7], 5 | "compiler-version": ["error", "^0.8.17"], 6 | "func-visibility": ["warn", { "ignoreConstructors": true }], 7 | "func-name-mixedcase": "off", 8 | "max-line-length": ["error", 120], 9 | "ordering": "warn", 10 | "reason-string": "off", 11 | "var-name-mixedcase": "off" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test 3 | demo.sol 4 | *.t.sol 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 LooksRare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @looksrare/contracts-libs 2 | 3 | [![Tests](https://github.com/LooksRare/contracts-libs/actions/workflows/tests.yaml/badge.svg)](https://github.com/LooksRare/contracts-libs/actions/workflows/tests.yaml) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 5 | 6 | This repository contains a set of Solidity contracts that can be used across contracts for purposes such as verifying signatures, protecting contracts against reentrancy attacks, low-level call functions, and a library for managing the ownership of a contract. 7 | 8 | It also contains generic contract interfaces (for EIP/ERC) that can be used. 9 | 10 | ## Installation 11 | 12 | ```shell 13 | # Yarn 14 | yarn add @looksrare/contracts-libs 15 | 16 | # NPM 17 | npm install @looksrare/contracts-libs 18 | ``` 19 | 20 | ## NPM package 21 | 22 | The NPM package contains the following: 23 | 24 | - Solidity smart contracts (_".sol"_) 25 | - ABIs (_".json"_) 26 | 27 | ## Current contracts 28 | 29 | | Name | Description | Type | Latest version | Audited? | 30 | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------- | -------------- | -------- | 31 | | OwnableTwoSteps | Contract for managing ownership of a smart contract. The transfer of ownership is done in a 2-step process. | Contract | 2.5.0 | Yes | 32 | | SignatureCheckerCalldata | Contract for verifying the validity of a (calldata) signature for EOA (64-byte, 65-byte signatures) and EIP-1271. | Contract | 3.0.0 | Yes | 33 | | SignatureCheckerMemory | Contract for verifying the validity of a (memory) signature for EOA (64-byte, 65-byte signatures) and EIP-1271. | Contract | 3.0.0 | Yes | 34 | | ReentrancyGuard | Contract with a modifier to prevent reentrancy calls. | Contract | 2.4.4 | Yes | 35 | | PackedReentrancyGuard | Contract with a modifier to prevent reentrancy calls. Adapted from ReentrancyGuard. | Contract | 2.5.1 | Yes | 36 | | LowLevelETHTransfer | Low-level call function to transfer ETH | Contract | 2.4.4 | Yes | 37 | | LowLevelETHReturnETHIfAny | Low-level call function to return all ETH left | Contract | 2.4.4 | Yes | 38 | | LowLevelETHReturnETHIfAnyExceptOneWei | Low-level call function to return all ETH left except one wei | Contract | 2.4.4 | Yes | 39 | | LowLevelWETH | Low-level call functions to transfer ETH with an option to wrap to WETH if the original ETH transfer fails within a gas limit | Contract | 2.4.4 | Yes | 40 | | LowLevelERC20Approve | Low-level call functions for ERC20 approve functions | Contract | 2.4.4 | Yes | 41 | | LowLevelERC20Transfer | Low-level call functions for ERC20 transfer functions | Contract | 2.4.4 | Yes | 42 | | LowLevelERC721Transfer | Low-level call functions for ERC721 functions | Contract | 2.4.4 | Yes | 43 | | LowLevelERC1155Transfer | Low-level call functions for ERC1155 functions | Contract | 2.4.4 | Yes | 44 | | ProtocolFee | Contract for defining protocol fee recipient and basis points | Contract | 3.2.0 | No | 45 | | BlastNativeYield | Contract for claiming Blast native yield | Contract | 3.5.1 | No | 46 | | BlastPoints | Contract for configuring Blast points | Contract | 3.5.1 | No | 47 | | BlastERC20RebasingYield | Contract for claiming Blast WETH/USDB yield | Contract | 3.5.1 | No | 48 | 49 | ## About this repo 50 | 51 | ### Structure 52 | 53 | It is a hybrid [Hardhat](https://hardhat.org/) repo that also requires [Foundry](https://book.getfoundry.sh/index.html) to run Solidity tests powered by the [ds-test library](https://github.com/dapphub/ds-test/). 54 | 55 | > To install Foundry, please follow the instructions [here](https://book.getfoundry.sh/getting-started/installation.html). 56 | 57 | ### Run tests 58 | 59 | - Solidity tests are included in the `foundry` folder in the `test` folder. 60 | 61 | ### Example of Foundry/Forge commands 62 | 63 | ```shell 64 | forge build 65 | forge test 66 | forge test -vv 67 | forge tree 68 | ``` 69 | 70 | ### Example of other commands 71 | 72 | ```shell 73 | npx eslint '**/*.{js,ts}' 74 | npx eslint '**/*.{js,ts}' --fix 75 | npx prettier '**/*.{json,sol,md}' --check 76 | npx prettier '**/*.{json,sol,md}' --write 77 | npx solhint 'contracts/**/*.sol' 78 | npx solhint 'contracts/**/*.sol' --fix 79 | ``` 80 | 81 | ### Coverage 82 | 83 | It is required to install lcov. 84 | 85 | ```shell 86 | brew install lcov 87 | ``` 88 | 89 | To run the coverage report, the below command can be executed. 90 | 91 | ``` 92 | forge coverage --report lcov 93 | LCOV_EXCLUDE=("test/*" "contracts/interfaces/*" "contracts/errors/*.sol") 94 | echo $LCOV_EXCLUDE | xargs lcov --output-file lcov-filtered.info --remove lcov.info 95 | genhtml lcov-filtered.info --output-directory out 96 | open out/index.html 97 | ``` 98 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security policy 2 | 3 | ## Supported versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 1.0.x | :white_check_mark: | 8 | 9 | ## Reporting a vulnerability 10 | 11 | Vulnerabilities can be reported using [Immunefi](https://immunefi.com/). The process is secure and vulnerabilities can be disclosed anonymously. 12 | 13 | To learn more, [please visit the Immunefi bug bounty page](https://immunefi.com/bounty/looksrare/). 14 | 15 | ## Current audits 16 | 17 | Third-party audits are available [here](https://docs.looksrare.org/about/audits). 18 | -------------------------------------------------------------------------------- /contracts/BlastERC20RebasingYield.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | import {BlastNativeYield} from "./BlastNativeYield.sol"; 6 | import {IERC20Rebasing, YieldMode} from "./interfaces/IERC20Rebasing.sol"; 7 | 8 | /** 9 | * @title BlastERC20RebasingYield 10 | * @notice This contract is a base contract for inheriting functions to claim Blast WETH or USDB yield 11 | * @author LooksRare protocol team (👀,💎) 12 | */ 13 | contract BlastERC20RebasingYield is BlastNativeYield { 14 | address public immutable WETH; 15 | address public immutable USDB; 16 | 17 | /** 18 | * @param _blast Blast precompile 19 | * @param _blastPoints Blast points 20 | * @param _blastPointsOperator Blast points operator 21 | * @param _governor The address that’s allowed to claim the contract’s yield and gas 22 | * @param _usdb USDB address 23 | * @param _weth WETH address 24 | */ 25 | constructor( 26 | address _blast, 27 | address _blastPoints, 28 | address _blastPointsOperator, 29 | address _governor, 30 | address _usdb, 31 | address _weth 32 | ) BlastNativeYield(_blast, _blastPoints, _blastPointsOperator, _governor) { 33 | WETH = _weth; 34 | USDB = _usdb; 35 | 36 | IERC20Rebasing(_weth).configure(YieldMode.CLAIMABLE); 37 | IERC20Rebasing(_usdb).configure(YieldMode.CLAIMABLE); 38 | } 39 | 40 | /** 41 | * @notice Claim Blast yield. Guarding of the function is dependent on the inherited contract. 42 | * Inheriting does not allow claiming by default. 43 | * A public or external function is required in the child contract to access the _claim function. 44 | * @param wethReceiver The receiver of WETH. 45 | * @param usdbReceiver The receiver of USDB. 46 | */ 47 | function _claimERC20RebasingYield(address wethReceiver, address usdbReceiver) internal { 48 | uint256 claimableWETH = IERC20Rebasing(WETH).getClaimableAmount(address(this)); 49 | if (claimableWETH != 0) { 50 | IERC20Rebasing(WETH).claim(wethReceiver, claimableWETH); 51 | } 52 | 53 | uint256 claimableUSDB = IERC20Rebasing(USDB).getClaimableAmount(address(this)); 54 | if (claimableUSDB != 0) { 55 | IERC20Rebasing(USDB).claim(usdbReceiver, claimableUSDB); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/BlastNativeYield.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | import {IBlast, YieldMode, GasMode} from "./interfaces/IBlast.sol"; 6 | import {BlastPoints} from "./BlastPoints.sol"; 7 | 8 | /** 9 | * @title BlastNativeYield 10 | * @notice This contract is a base contract for inheriting functions to claim native yield and for those that wish to recieve Blast points 11 | * @author LooksRare protocol team (👀,💎) 12 | */ 13 | contract BlastNativeYield is BlastPoints { 14 | /** 15 | * @param _blast Blast precompile 16 | * @param _blastPoints Blast points 17 | * @param _blastPointsOperator Blast points operator 18 | * @param _governor The address that’s allowed to claim the contract’s yield and gas 19 | */ 20 | constructor( 21 | address _blast, 22 | address _blastPoints, 23 | address _blastPointsOperator, 24 | address _governor 25 | ) BlastPoints(_blastPoints, _blastPointsOperator) { 26 | IBlast(_blast).configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, _governor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/BlastPoints.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | import {IBlastPoints} from "./interfaces/IBlastPoints.sol"; 6 | 7 | /** 8 | * @title BlastPoints 9 | * @notice This contract is a base for future contracts that wish to be recipients of Blast points to inherit from 10 | * @author LooksRare protocol team (👀,💎) 11 | */ 12 | contract BlastPoints { 13 | /** 14 | * @param _blastPoints Blast points 15 | * @param _blastPointsOperator Blast points operator 16 | */ 17 | constructor(address _blastPoints, address _blastPointsOperator) { 18 | IBlastPoints(_blastPoints).configurePointsOperator(_blastPointsOperator); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/OwnableTwoSteps.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IOwnableTwoSteps} from "./interfaces/IOwnableTwoSteps.sol"; 6 | 7 | /** 8 | * @title OwnableTwoSteps 9 | * @notice This contract offers transfer of ownership in two steps with potential owner 10 | * having to confirm the transaction to become the owner. 11 | * Renouncement of the ownership is also a two-step process since the next potential owner is the address(0). 12 | * @author LooksRare protocol team (👀,💎) 13 | */ 14 | abstract contract OwnableTwoSteps is IOwnableTwoSteps { 15 | /** 16 | * @notice Address of the current owner. 17 | */ 18 | address public owner; 19 | 20 | /** 21 | * @notice Address of the potential owner. 22 | */ 23 | address public potentialOwner; 24 | 25 | /** 26 | * @notice Ownership status. 27 | */ 28 | Status public ownershipStatus; 29 | 30 | /** 31 | * @notice Modifier to wrap functions for contracts that inherit this contract. 32 | */ 33 | modifier onlyOwner() { 34 | _onlyOwner(); 35 | _; 36 | } 37 | 38 | /** 39 | * @notice Constructor 40 | * @param _owner The contract's owner 41 | */ 42 | constructor(address _owner) { 43 | owner = _owner; 44 | emit NewOwner(_owner); 45 | } 46 | 47 | /** 48 | * @notice This function is used to cancel the ownership transfer. 49 | * @dev This function can be used for both cancelling a transfer to a new owner and 50 | * cancelling the renouncement of the ownership. 51 | */ 52 | function cancelOwnershipTransfer() external onlyOwner { 53 | Status _ownershipStatus = ownershipStatus; 54 | if (_ownershipStatus == Status.NoOngoingTransfer) { 55 | revert NoOngoingTransferInProgress(); 56 | } 57 | 58 | if (_ownershipStatus == Status.TransferInProgress) { 59 | delete potentialOwner; 60 | } 61 | 62 | delete ownershipStatus; 63 | 64 | emit CancelOwnershipTransfer(); 65 | } 66 | 67 | /** 68 | * @notice This function is used to confirm the ownership renouncement. 69 | */ 70 | function confirmOwnershipRenouncement() external onlyOwner { 71 | if (ownershipStatus != Status.RenouncementInProgress) { 72 | revert RenouncementNotInProgress(); 73 | } 74 | 75 | delete owner; 76 | delete ownershipStatus; 77 | 78 | emit NewOwner(address(0)); 79 | } 80 | 81 | /** 82 | * @notice This function is used to confirm the ownership transfer. 83 | * @dev This function can only be called by the current potential owner. 84 | */ 85 | function confirmOwnershipTransfer() external { 86 | if (ownershipStatus != Status.TransferInProgress) { 87 | revert TransferNotInProgress(); 88 | } 89 | 90 | if (msg.sender != potentialOwner) { 91 | revert WrongPotentialOwner(); 92 | } 93 | 94 | owner = msg.sender; 95 | delete ownershipStatus; 96 | delete potentialOwner; 97 | 98 | emit NewOwner(msg.sender); 99 | } 100 | 101 | /** 102 | * @notice This function is used to initiate the transfer of ownership to a new owner. 103 | * @param newPotentialOwner New potential owner address 104 | */ 105 | function initiateOwnershipTransfer(address newPotentialOwner) external onlyOwner { 106 | if (ownershipStatus != Status.NoOngoingTransfer) { 107 | revert TransferAlreadyInProgress(); 108 | } 109 | 110 | ownershipStatus = Status.TransferInProgress; 111 | potentialOwner = newPotentialOwner; 112 | 113 | /** 114 | * @dev This function can only be called by the owner, so msg.sender is the owner. 115 | * We don't have to SLOAD the owner again. 116 | */ 117 | emit InitiateOwnershipTransfer(msg.sender, newPotentialOwner); 118 | } 119 | 120 | /** 121 | * @notice This function is used to initiate the ownership renouncement. 122 | */ 123 | function initiateOwnershipRenouncement() external onlyOwner { 124 | if (ownershipStatus != Status.NoOngoingTransfer) { 125 | revert TransferAlreadyInProgress(); 126 | } 127 | 128 | ownershipStatus = Status.RenouncementInProgress; 129 | 130 | emit InitiateOwnershipRenouncement(); 131 | } 132 | 133 | function _onlyOwner() private view { 134 | if (msg.sender != owner) revert NotOwner(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /contracts/PackableReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IReentrancyGuard} from "./interfaces/IReentrancyGuard.sol"; 6 | 7 | /** 8 | * @title PackableReentrancyGuard 9 | * @notice This contract protects against reentrancy attacks. 10 | * It is adjusted from OpenZeppelin. 11 | * The only difference between this contract and ReentrancyGuard 12 | * is that _status is uint8 instead of uint256 so that it can be 13 | * packed with other contracts' storage variables. 14 | * @author LooksRare protocol team (👀,💎) 15 | */ 16 | abstract contract PackableReentrancyGuard is IReentrancyGuard { 17 | uint8 private _status; 18 | 19 | /** 20 | * @notice Modifier to wrap functions to prevent reentrancy calls. 21 | */ 22 | modifier nonReentrant() { 23 | if (_status == 2) { 24 | revert ReentrancyFail(); 25 | } 26 | 27 | _status = 2; 28 | _; 29 | _status = 1; 30 | } 31 | 32 | constructor() { 33 | _status = 1; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/Pausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @title Pausable 6 | * @notice This contract makes it possible to pause the contract. 7 | * It is adjusted from OpenZeppelin. 8 | * @author LooksRare protocol team (👀,💎) 9 | */ 10 | abstract contract Pausable { 11 | /** 12 | * @dev Emitted when the pause is triggered by `account`. 13 | */ 14 | event Paused(address account); 15 | 16 | /** 17 | * @dev Emitted when the pause is lifted by `account`. 18 | */ 19 | event Unpaused(address account); 20 | 21 | error IsPaused(); 22 | error NotPaused(); 23 | 24 | bool private _paused; 25 | 26 | /** 27 | * @dev Modifier to make a function callable only when the contract is not paused. 28 | * 29 | * Requirements: 30 | * 31 | * - The contract must not be paused. 32 | */ 33 | modifier whenNotPaused() { 34 | _requireNotPaused(); 35 | _; 36 | } 37 | 38 | /** 39 | * @dev Modifier to make a function callable only when the contract is paused. 40 | * 41 | * Requirements: 42 | * 43 | * - The contract must be paused. 44 | */ 45 | modifier whenPaused() { 46 | _requirePaused(); 47 | _; 48 | } 49 | 50 | /** 51 | * @dev Returns true if the contract is paused, and false otherwise. 52 | */ 53 | function paused() public view virtual returns (bool) { 54 | return _paused; 55 | } 56 | 57 | /** 58 | * @dev Throws if the contract is paused. 59 | */ 60 | function _requireNotPaused() internal view virtual { 61 | if (paused()) { 62 | revert IsPaused(); 63 | } 64 | } 65 | 66 | /** 67 | * @dev Throws if the contract is not paused. 68 | */ 69 | function _requirePaused() internal view virtual { 70 | if (!paused()) { 71 | revert NotPaused(); 72 | } 73 | } 74 | 75 | /** 76 | * @dev Triggers stopped state. 77 | * 78 | * Requirements: 79 | * 80 | * - The contract must not be paused. 81 | */ 82 | function _pause() internal virtual whenNotPaused { 83 | _paused = true; 84 | emit Paused(msg.sender); 85 | } 86 | 87 | /** 88 | * @dev Returns to normal state. 89 | * 90 | * Requirements: 91 | * 92 | * - The contract must be paused. 93 | */ 94 | function _unpause() internal virtual whenPaused { 95 | _paused = false; 96 | emit Unpaused(msg.sender); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /contracts/ProtocolFee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @title ProtocolFee 6 | * @notice This contract makes it possible for a contract to charge a protocol fee. 7 | * @author LooksRare protocol team (👀,💎) 8 | */ 9 | abstract contract ProtocolFee { 10 | /** 11 | * @dev Emitted when the protocol fee basis points is updated. 12 | */ 13 | event ProtocolFeeBpUpdated(uint16 protocolFeeBp); 14 | 15 | /** 16 | * @dev Emitted when the protocol fee recipient is updated. 17 | */ 18 | event ProtocolFeeRecipientUpdated(address protocolFeeRecipient); 19 | 20 | /** 21 | * @dev This error is used when the protocol fee basis points is too high 22 | * or when the protocol fee recipient is a zero address. 23 | */ 24 | error ProtocolFee__InvalidValue(); 25 | 26 | /** 27 | * @notice The maximum protocol fee in basis points, which is 25%. 28 | */ 29 | uint16 public constant MAXIMUM_PROTOCOL_FEE_BP = 2_500; 30 | 31 | /** 32 | * @notice The address of the protocol fee recipient. 33 | */ 34 | address public protocolFeeRecipient; 35 | 36 | /** 37 | * @notice The protocol fee basis points. 38 | */ 39 | uint16 public protocolFeeBp; 40 | 41 | /** 42 | * @dev This function is used to update the protocol fee recipient. It should be overridden 43 | * by the contract that inherits from this contract. The function should be guarded 44 | * by an access control mechanism to prevent unauthorized users from calling it. 45 | * @param _protocolFeeRecipient The address of the protocol fee recipient 46 | */ 47 | function updateProtocolFeeRecipient(address _protocolFeeRecipient) external virtual; 48 | 49 | /** 50 | * @dev This function is used to update the protocol fee basis points. It should be overridden 51 | * by the contract that inherits from this contract. The function should be guarded 52 | * by an access control mechanism to prevent unauthorized users from calling it. 53 | * @param _protocolFeeBp The protocol fee basis points 54 | */ 55 | function updateProtocolFeeBp(uint16 _protocolFeeBp) external virtual; 56 | 57 | /** 58 | * @param _protocolFeeRecipient The new protocol fee recipient address 59 | */ 60 | function _updateProtocolFeeRecipient(address _protocolFeeRecipient) internal { 61 | if (_protocolFeeRecipient == address(0)) { 62 | revert ProtocolFee__InvalidValue(); 63 | } 64 | protocolFeeRecipient = _protocolFeeRecipient; 65 | emit ProtocolFeeRecipientUpdated(_protocolFeeRecipient); 66 | } 67 | 68 | /** 69 | * @param _protocolFeeBp The new protocol fee in basis points 70 | */ 71 | function _updateProtocolFeeBp(uint16 _protocolFeeBp) internal { 72 | if (_protocolFeeBp > MAXIMUM_PROTOCOL_FEE_BP) { 73 | revert ProtocolFee__InvalidValue(); 74 | } 75 | protocolFeeBp = _protocolFeeBp; 76 | emit ProtocolFeeBpUpdated(_protocolFeeBp); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /contracts/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IReentrancyGuard} from "./interfaces/IReentrancyGuard.sol"; 6 | 7 | /** 8 | * @title ReentrancyGuard 9 | * @notice This contract protects against reentrancy attacks. 10 | * It is adjusted from OpenZeppelin. 11 | * @author LooksRare protocol team (👀,💎) 12 | */ 13 | abstract contract ReentrancyGuard is IReentrancyGuard { 14 | uint256 private _status; 15 | 16 | /** 17 | * @notice Modifier to wrap functions to prevent reentrancy calls. 18 | */ 19 | modifier nonReentrant() { 20 | if (_status == 2) { 21 | revert ReentrancyFail(); 22 | } 23 | 24 | _status = 2; 25 | _; 26 | _status = 1; 27 | } 28 | 29 | constructor() { 30 | _status = 1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/SignatureCheckerCalldata.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IERC1271} from "./interfaces/generic/IERC1271.sol"; 6 | 7 | // Constants 8 | import {ERC1271_MAGIC_VALUE} from "./constants/StandardConstants.sol"; 9 | 10 | // Errors 11 | import {SignatureParameterSInvalid, SignatureParameterVInvalid, SignatureERC1271Invalid, SignatureEOAInvalid, NullSignerAddress, SignatureLengthInvalid} from "./errors/SignatureCheckerErrors.sol"; 12 | 13 | /** 14 | * @title SignatureCheckerCalldata 15 | * @notice This library is used to verify signatures for EOAs (with lengths of both 65 and 64 bytes) 16 | * and contracts (ERC1271). 17 | * @author LooksRare protocol team (👀,💎) 18 | */ 19 | library SignatureCheckerCalldata { 20 | /** 21 | * @notice This function verifies whether the signer is valid for a hash and raw signature. 22 | * @param hash Data hash 23 | * @param signer Signer address (to confirm message validity) 24 | * @param signature Signature parameters encoded (v, r, s) 25 | * @dev For EIP-712 signatures, the hash must be the digest (computed with signature hash and domain separator) 26 | */ 27 | function verify(bytes32 hash, address signer, bytes calldata signature) internal view { 28 | if (signer.code.length == 0) { 29 | if (_recoverEOASigner(hash, signature) == signer) return; 30 | revert SignatureEOAInvalid(); 31 | } else { 32 | if (IERC1271(signer).isValidSignature(hash, signature) == ERC1271_MAGIC_VALUE) return; 33 | revert SignatureERC1271Invalid(); 34 | } 35 | } 36 | 37 | /** 38 | * @notice This function is internal and splits a signature into r, s, v outputs. 39 | * @param signature A 64 or 65 bytes signature 40 | * @return r The r output of the signature 41 | * @return s The s output of the signature 42 | * @return v The recovery identifier, must be 27 or 28 43 | */ 44 | function splitSignature(bytes calldata signature) internal pure returns (bytes32 r, bytes32 s, uint8 v) { 45 | uint256 length = signature.length; 46 | if (length == 65) { 47 | assembly { 48 | r := calldataload(signature.offset) 49 | s := calldataload(add(signature.offset, 0x20)) 50 | v := byte(0, calldataload(add(signature.offset, 0x40))) 51 | } 52 | } else if (length == 64) { 53 | assembly { 54 | r := calldataload(signature.offset) 55 | let vs := calldataload(add(signature.offset, 0x20)) 56 | s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 57 | v := add(shr(255, vs), 27) 58 | } 59 | } else { 60 | revert SignatureLengthInvalid(length); 61 | } 62 | 63 | if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { 64 | revert SignatureParameterSInvalid(); 65 | } 66 | 67 | if (v != 27 && v != 28) { 68 | revert SignatureParameterVInvalid(v); 69 | } 70 | } 71 | 72 | /** 73 | * @notice This function is private and recovers the signer of a signature (for EOA only). 74 | * @param hash Hash of the signed message 75 | * @param signature Bytes containing the signature (64 or 65 bytes) 76 | * @return signer The address that signed the signature 77 | */ 78 | function _recoverEOASigner(bytes32 hash, bytes calldata signature) private pure returns (address signer) { 79 | (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature); 80 | 81 | // If the signature is valid (and not malleable), return the signer's address 82 | signer = ecrecover(hash, v, r, s); 83 | 84 | if (signer == address(0)) { 85 | revert NullSignerAddress(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /contracts/SignatureCheckerMemory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IERC1271} from "./interfaces/generic/IERC1271.sol"; 6 | 7 | // Constants 8 | import {ERC1271_MAGIC_VALUE} from "./constants/StandardConstants.sol"; 9 | 10 | // Errors 11 | import {SignatureParameterSInvalid, SignatureParameterVInvalid, SignatureERC1271Invalid, SignatureEOAInvalid, NullSignerAddress, SignatureLengthInvalid} from "./errors/SignatureCheckerErrors.sol"; 12 | 13 | /** 14 | * @title SignatureCheckerMemory 15 | * @notice This library is used to verify signatures for EOAs (with lengths of both 65 and 64 bytes) 16 | * and contracts (ERC1271). 17 | * @author LooksRare protocol team (👀,💎) 18 | */ 19 | library SignatureCheckerMemory { 20 | /** 21 | * @notice This function verifies whether the signer is valid for a hash and raw signature. 22 | * @param hash Data hash 23 | * @param signer Signer address (to confirm message validity) 24 | * @param signature Signature parameters encoded (v, r, s) 25 | * @dev For EIP-712 signatures, the hash must be the digest (computed with signature hash and domain separator) 26 | */ 27 | function verify(bytes32 hash, address signer, bytes memory signature) internal view { 28 | if (signer.code.length == 0) { 29 | if (_recoverEOASigner(hash, signature) == signer) return; 30 | revert SignatureEOAInvalid(); 31 | } else { 32 | if (IERC1271(signer).isValidSignature(hash, signature) == ERC1271_MAGIC_VALUE) return; 33 | revert SignatureERC1271Invalid(); 34 | } 35 | } 36 | 37 | /** 38 | * @notice This function is internal and splits a signature into r, s, v outputs. 39 | * @param signature A 64 or 65 bytes signature 40 | * @return r The r output of the signature 41 | * @return s The s output of the signature 42 | * @return v The recovery identifier, must be 27 or 28 43 | */ 44 | function splitSignature(bytes memory signature) internal pure returns (bytes32 r, bytes32 s, uint8 v) { 45 | uint256 length = signature.length; 46 | 47 | if (length == 65) { 48 | assembly { 49 | r := mload(add(signature, 0x20)) 50 | s := mload(add(signature, 0x40)) 51 | v := byte(0, mload(add(signature, 0x60))) 52 | } 53 | } else if (length == 64) { 54 | assembly { 55 | r := mload(add(signature, 0x20)) 56 | let vs := mload(add(signature, 0x40)) 57 | s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 58 | v := add(shr(255, vs), 27) 59 | } 60 | } else { 61 | revert SignatureLengthInvalid(length); 62 | } 63 | 64 | if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { 65 | revert SignatureParameterSInvalid(); 66 | } 67 | 68 | if (v != 27 && v != 28) { 69 | revert SignatureParameterVInvalid(v); 70 | } 71 | } 72 | 73 | /** 74 | * @notice This function is private and recovers the signer of a signature (for EOA only). 75 | * @param hash Hash of the signed message 76 | * @param signature Bytes containing the signature (64 or 65 bytes) 77 | * @return signer The address that signed the signature 78 | */ 79 | function _recoverEOASigner(bytes32 hash, bytes memory signature) private pure returns (address signer) { 80 | (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature); 81 | 82 | // If the signature is valid (and not malleable), return the signer's address 83 | signer = ecrecover(hash, v, r, s); 84 | 85 | if (signer == address(0)) { 86 | revert NullSignerAddress(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /contracts/constants/AssemblyConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /* 5 | * @dev error ETHTransferFail() 6 | * Memory layout: 7 | * - 0x00: Left-padded selector (data begins at 0x1c) 8 | * Revert buffer is memory[0x1c:0x20] 9 | */ 10 | uint256 constant ETHTransferFail_error_selector = 0x07246cf4; 11 | uint256 constant ETHTransferFail_error_length = 0x04; 12 | uint256 constant Error_selector_offset = 0x1c; 13 | -------------------------------------------------------------------------------- /contracts/constants/StandardConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @dev ERC1271's magic value (bytes4(keccak256("isValidSignature(bytes32,bytes)")) 6 | */ 7 | bytes4 constant ERC1271_MAGIC_VALUE = 0x1626ba7e; 8 | -------------------------------------------------------------------------------- /contracts/errors/GenericErrors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @notice It is emitted if the call recipient is not a contract. 6 | */ 7 | error NotAContract(); 8 | -------------------------------------------------------------------------------- /contracts/errors/LowLevelErrors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @notice It is emitted if the ETH transfer fails. 6 | */ 7 | error ETHTransferFail(); 8 | 9 | /** 10 | * @notice It is emitted if the ERC20 approval fails. 11 | */ 12 | error ERC20ApprovalFail(); 13 | 14 | /** 15 | * @notice It is emitted if the ERC20 transfer fails. 16 | */ 17 | error ERC20TransferFail(); 18 | 19 | /** 20 | * @notice It is emitted if the ERC20 transferFrom fails. 21 | */ 22 | error ERC20TransferFromFail(); 23 | 24 | /** 25 | * @notice It is emitted if the ERC721 transferFrom fails. 26 | */ 27 | error ERC721TransferFromFail(); 28 | 29 | /** 30 | * @notice It is emitted if the ERC1155 safeTransferFrom fails. 31 | */ 32 | error ERC1155SafeTransferFromFail(); 33 | 34 | /** 35 | * @notice It is emitted if the ERC1155 safeBatchTransferFrom fails. 36 | */ 37 | error ERC1155SafeBatchTransferFromFail(); 38 | -------------------------------------------------------------------------------- /contracts/errors/SignatureCheckerErrors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @notice It is emitted if the signer is null. 6 | */ 7 | error NullSignerAddress(); 8 | 9 | /** 10 | * @notice It is emitted if the signature is invalid for an EOA (the address recovered is not the expected one). 11 | */ 12 | error SignatureEOAInvalid(); 13 | 14 | /** 15 | * @notice It is emitted if the signature is invalid for a ERC1271 contract signer. 16 | */ 17 | error SignatureERC1271Invalid(); 18 | 19 | /** 20 | * @notice It is emitted if the signature's length is neither 64 nor 65 bytes. 21 | */ 22 | error SignatureLengthInvalid(uint256 length); 23 | 24 | /** 25 | * @notice It is emitted if the signature is invalid due to S parameter. 26 | */ 27 | error SignatureParameterSInvalid(); 28 | 29 | /** 30 | * @notice It is emitted if the signature is invalid due to V parameter. 31 | */ 32 | error SignatureParameterVInvalid(uint8 v); 33 | -------------------------------------------------------------------------------- /contracts/interfaces/IBlast.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | enum YieldMode { 5 | AUTOMATIC, 6 | VOID, 7 | CLAIMABLE 8 | } 9 | 10 | enum GasMode { 11 | VOID, 12 | CLAIMABLE 13 | } 14 | 15 | interface IBlast { 16 | // configure 17 | function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external; 18 | 19 | function configure(YieldMode _yield, GasMode gasMode, address governor) external; 20 | 21 | // base configuration options 22 | function configureClaimableYield() external; 23 | 24 | function configureClaimableYieldOnBehalf(address contractAddress) external; 25 | 26 | function configureAutomaticYield() external; 27 | 28 | function configureAutomaticYieldOnBehalf(address contractAddress) external; 29 | 30 | function configureVoidYield() external; 31 | 32 | function configureVoidYieldOnBehalf(address contractAddress) external; 33 | 34 | function configureClaimableGas() external; 35 | 36 | function configureClaimableGasOnBehalf(address contractAddress) external; 37 | 38 | function configureVoidGas() external; 39 | 40 | function configureVoidGasOnBehalf(address contractAddress) external; 41 | 42 | function configureGovernor(address _governor) external; 43 | 44 | function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external; 45 | 46 | // claim yield 47 | function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256); 48 | 49 | function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256); 50 | 51 | // claim gas 52 | function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256); 53 | 54 | function claimGasAtMinClaimRate( 55 | address contractAddress, 56 | address recipientOfGas, 57 | uint256 minClaimRateBips 58 | ) external returns (uint256); 59 | 60 | function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256); 61 | 62 | function claimGas( 63 | address contractAddress, 64 | address recipientOfGas, 65 | uint256 gasToClaim, 66 | uint256 gasSecondsToConsume 67 | ) external returns (uint256); 68 | 69 | // read functions 70 | function readClaimableYield(address contractAddress) external view returns (uint256); 71 | 72 | function readYieldConfiguration(address contractAddress) external view returns (uint8); 73 | 74 | function readGasParams( 75 | address contractAddress 76 | ) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode); 77 | } 78 | -------------------------------------------------------------------------------- /contracts/interfaces/IBlastPoints.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IBlastPoints { 5 | function configurePointsOperator(address operator) external; 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20Rebasing.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | enum YieldMode { 5 | AUTOMATIC, 6 | VOID, 7 | CLAIMABLE 8 | } 9 | 10 | interface IERC20Rebasing { 11 | // changes the yield mode of the caller and update the balance 12 | // to reflect the configuration 13 | function configure(YieldMode) external returns (uint256); 14 | 15 | // "claimable" yield mode accounts can call this this claim their yield 16 | // to another address 17 | function claim(address recipient, uint256 amount) external returns (uint256); 18 | 19 | // read the claimable amount for an account 20 | function getClaimableAmount(address account) external view returns (uint256); 21 | } 22 | -------------------------------------------------------------------------------- /contracts/interfaces/IOwnableTwoSteps.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @title IOwnableTwoSteps 6 | * @author LooksRare protocol team (👀,💎) 7 | */ 8 | interface IOwnableTwoSteps { 9 | /** 10 | * @notice This enum keeps track of the ownership status. 11 | * @param NoOngoingTransfer The default status when the owner is set 12 | * @param TransferInProgress The status when a transfer to a new owner is initialized 13 | * @param RenouncementInProgress The status when a transfer to address(0) is initialized 14 | */ 15 | enum Status { 16 | NoOngoingTransfer, 17 | TransferInProgress, 18 | RenouncementInProgress 19 | } 20 | 21 | /** 22 | * @notice This is returned when there is no transfer of ownership in progress. 23 | */ 24 | error NoOngoingTransferInProgress(); 25 | 26 | /** 27 | * @notice This is returned when the caller is not the owner. 28 | */ 29 | error NotOwner(); 30 | 31 | /** 32 | * @notice This is returned when there is no renouncement in progress but 33 | * the owner tries to validate the ownership renouncement. 34 | */ 35 | error RenouncementNotInProgress(); 36 | 37 | /** 38 | * @notice This is returned when the transfer is already in progress but the owner tries 39 | * initiate a new ownership transfer. 40 | */ 41 | error TransferAlreadyInProgress(); 42 | 43 | /** 44 | * @notice This is returned when there is no ownership transfer in progress but the 45 | * ownership change tries to be approved. 46 | */ 47 | error TransferNotInProgress(); 48 | 49 | /** 50 | * @notice This is returned when the ownership transfer is attempted to be validated by the 51 | * a caller that is not the potential owner. 52 | */ 53 | error WrongPotentialOwner(); 54 | 55 | /** 56 | * @notice This is emitted if the ownership transfer is cancelled. 57 | */ 58 | event CancelOwnershipTransfer(); 59 | 60 | /** 61 | * @notice This is emitted if the ownership renouncement is initiated. 62 | */ 63 | event InitiateOwnershipRenouncement(); 64 | 65 | /** 66 | * @notice This is emitted if the ownership transfer is initiated. 67 | * @param previousOwner Previous/current owner 68 | * @param potentialOwner Potential/future owner 69 | */ 70 | event InitiateOwnershipTransfer(address previousOwner, address potentialOwner); 71 | 72 | /** 73 | * @notice This is emitted when there is a new owner. 74 | */ 75 | event NewOwner(address newOwner); 76 | } 77 | -------------------------------------------------------------------------------- /contracts/interfaces/IReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /** 5 | * @title IReentrancyGuard 6 | * @author LooksRare protocol team (👀,💎) 7 | */ 8 | interface IReentrancyGuard { 9 | /** 10 | * @notice This is returned when there is a reentrant call. 11 | */ 12 | error ReentrancyFail(); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interfaces/generic/IERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | interface IERC1155 { 5 | event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); 6 | 7 | event TransferBatch( 8 | address indexed operator, 9 | address indexed from, 10 | address indexed to, 11 | uint256[] ids, 12 | uint256[] values 13 | ); 14 | 15 | event ApprovalForAll(address indexed account, address indexed operator, bool approved); 16 | 17 | event URI(string value, uint256 indexed id); 18 | 19 | function balanceOf(address account, uint256 id) external view returns (uint256); 20 | 21 | function balanceOfBatch( 22 | address[] calldata accounts, 23 | uint256[] calldata ids 24 | ) external view returns (uint256[] memory); 25 | 26 | function setApprovalForAll(address operator, bool approved) external; 27 | 28 | function isApprovedForAll(address account, address operator) external view returns (bool); 29 | 30 | function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; 31 | 32 | function safeBatchTransferFrom( 33 | address from, 34 | address to, 35 | uint256[] calldata ids, 36 | uint256[] calldata amounts, 37 | bytes calldata data 38 | ) external; 39 | } 40 | -------------------------------------------------------------------------------- /contracts/interfaces/generic/IERC1271.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | interface IERC1271 { 5 | function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/generic/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | interface IERC165 { 5 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/generic/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | interface IERC20 { 5 | event Transfer(address indexed from, address indexed to, uint256 value); 6 | 7 | event Approval(address indexed owner, address indexed spender, uint256 value); 8 | 9 | function totalSupply() external view returns (uint256); 10 | 11 | function balanceOf(address account) external view returns (uint256); 12 | 13 | function transfer(address to, uint256 amount) external returns (bool); 14 | 15 | function allowance(address owner, address spender) external view returns (uint256); 16 | 17 | function approve(address spender, uint256 amount) external returns (bool); 18 | 19 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 20 | 21 | function decimals() external view returns (uint8); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/interfaces/generic/IERC2981.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IERC165} from "./IERC165.sol"; 6 | 7 | interface IERC2981 is IERC165 { 8 | function royaltyInfo( 9 | uint256 tokenId, 10 | uint256 salePrice 11 | ) external view returns (address receiver, uint256 royaltyAmount); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/generic/IERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | interface IERC721 { 5 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 6 | 7 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 8 | 9 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 10 | 11 | function balanceOf(address owner) external view returns (uint256 balance); 12 | 13 | function ownerOf(uint256 tokenId) external view returns (address owner); 14 | 15 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; 16 | 17 | function safeTransferFrom(address from, address to, uint256 tokenId) external; 18 | 19 | function transferFrom(address from, address to, uint256 tokenId) external; 20 | 21 | function approve(address to, uint256 tokenId) external; 22 | 23 | function setApprovalForAll(address operator, bool _approved) external; 24 | 25 | function getApproved(uint256 tokenId) external view returns (address operator); 26 | 27 | function isApprovedForAll(address owner, address operator) external view returns (bool); 28 | } 29 | -------------------------------------------------------------------------------- /contracts/interfaces/generic/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | interface IWETH { 5 | function deposit() external payable; 6 | 7 | function transfer(address dst, uint256 wad) external returns (bool); 8 | 9 | function withdraw(uint256 wad) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/libraries/UnsafeMathUint256.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | library UnsafeMathUint256 { 5 | function unsafeAdd(uint256 a, uint256 b) internal pure returns (uint256) { 6 | unchecked { 7 | return a + b; 8 | } 9 | } 10 | 11 | function unsafeSubtract(uint256 a, uint256 b) internal pure returns (uint256) { 12 | unchecked { 13 | return a - b; 14 | } 15 | } 16 | 17 | function unsafeMultiply(uint256 a, uint256 b) internal pure returns (uint256) { 18 | unchecked { 19 | return a * b; 20 | } 21 | } 22 | 23 | function unsafeDivide(uint256 a, uint256 b) internal pure returns (uint256) { 24 | unchecked { 25 | return a / b; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelERC1155Transfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IERC1155} from "../interfaces/generic/IERC1155.sol"; 6 | 7 | // Errors 8 | import {ERC1155SafeTransferFromFail, ERC1155SafeBatchTransferFromFail} from "../errors/LowLevelErrors.sol"; 9 | import {NotAContract} from "../errors/GenericErrors.sol"; 10 | 11 | /** 12 | * @title LowLevelERC1155Transfer 13 | * @notice This contract contains low-level calls to transfer ERC1155 tokens. 14 | * @author LooksRare protocol team (👀,💎) 15 | */ 16 | contract LowLevelERC1155Transfer { 17 | /** 18 | * @notice Execute ERC1155 safeTransferFrom 19 | * @param collection Address of the collection 20 | * @param from Address of the sender 21 | * @param to Address of the recipient 22 | * @param tokenId tokenId to transfer 23 | * @param amount Amount to transfer 24 | */ 25 | function _executeERC1155SafeTransferFrom( 26 | address collection, 27 | address from, 28 | address to, 29 | uint256 tokenId, 30 | uint256 amount 31 | ) internal { 32 | if (collection.code.length == 0) { 33 | revert NotAContract(); 34 | } 35 | 36 | (bool status, ) = collection.call(abi.encodeCall(IERC1155.safeTransferFrom, (from, to, tokenId, amount, ""))); 37 | 38 | if (!status) { 39 | revert ERC1155SafeTransferFromFail(); 40 | } 41 | } 42 | 43 | /** 44 | * @notice Execute ERC1155 safeBatchTransferFrom 45 | * @param collection Address of the collection 46 | * @param from Address of the sender 47 | * @param to Address of the recipient 48 | * @param tokenIds Array of tokenIds to transfer 49 | * @param amounts Array of amounts to transfer 50 | */ 51 | function _executeERC1155SafeBatchTransferFrom( 52 | address collection, 53 | address from, 54 | address to, 55 | uint256[] calldata tokenIds, 56 | uint256[] calldata amounts 57 | ) internal { 58 | if (collection.code.length == 0) { 59 | revert NotAContract(); 60 | } 61 | 62 | (bool status, ) = collection.call( 63 | abi.encodeCall(IERC1155.safeBatchTransferFrom, (from, to, tokenIds, amounts, "")) 64 | ); 65 | 66 | if (!status) { 67 | revert ERC1155SafeBatchTransferFromFail(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelERC20Approve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IERC20} from "../interfaces/generic/IERC20.sol"; 6 | 7 | // Errors 8 | import {ERC20ApprovalFail} from "../errors/LowLevelErrors.sol"; 9 | import {NotAContract} from "../errors/GenericErrors.sol"; 10 | 11 | /** 12 | * @title LowLevelERC20Approve 13 | * @notice This contract contains low-level calls to approve ERC20 tokens. 14 | * @author LooksRare protocol team (👀,💎) 15 | */ 16 | contract LowLevelERC20Approve { 17 | /** 18 | * @notice Execute ERC20 approve 19 | * @param currency Currency address 20 | * @param to Operator address 21 | * @param amount Amount to approve 22 | */ 23 | function _executeERC20Approve(address currency, address to, uint256 amount) internal { 24 | if (currency.code.length == 0) { 25 | revert NotAContract(); 26 | } 27 | 28 | (bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.approve, (to, amount))); 29 | 30 | if (!status) { 31 | revert ERC20ApprovalFail(); 32 | } 33 | 34 | if (data.length > 0) { 35 | if (!abi.decode(data, (bool))) { 36 | revert ERC20ApprovalFail(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelERC20Transfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IERC20} from "../interfaces/generic/IERC20.sol"; 6 | 7 | // Errors 8 | import {ERC20TransferFail, ERC20TransferFromFail} from "../errors/LowLevelErrors.sol"; 9 | import {NotAContract} from "../errors/GenericErrors.sol"; 10 | 11 | /** 12 | * @title LowLevelERC20Transfer 13 | * @notice This contract contains low-level calls to transfer ERC20 tokens. 14 | * @author LooksRare protocol team (👀,💎) 15 | */ 16 | contract LowLevelERC20Transfer { 17 | /** 18 | * @notice Execute ERC20 transferFrom 19 | * @param currency Currency address 20 | * @param from Sender address 21 | * @param to Recipient address 22 | * @param amount Amount to transfer 23 | */ 24 | function _executeERC20TransferFrom(address currency, address from, address to, uint256 amount) internal { 25 | if (currency.code.length == 0) { 26 | revert NotAContract(); 27 | } 28 | 29 | (bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount))); 30 | 31 | if (!status) { 32 | revert ERC20TransferFromFail(); 33 | } 34 | 35 | if (data.length > 0) { 36 | if (!abi.decode(data, (bool))) { 37 | revert ERC20TransferFromFail(); 38 | } 39 | } 40 | } 41 | 42 | /** 43 | * @notice Execute ERC20 (direct) transfer 44 | * @param currency Currency address 45 | * @param to Recipient address 46 | * @param amount Amount to transfer 47 | */ 48 | function _executeERC20DirectTransfer(address currency, address to, uint256 amount) internal { 49 | if (currency.code.length == 0) { 50 | revert NotAContract(); 51 | } 52 | 53 | (bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transfer, (to, amount))); 54 | 55 | if (!status) { 56 | revert ERC20TransferFail(); 57 | } 58 | 59 | if (data.length > 0) { 60 | if (!abi.decode(data, (bool))) { 61 | revert ERC20TransferFail(); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelERC721Transfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IERC721} from "../interfaces/generic/IERC721.sol"; 6 | 7 | // Errors 8 | import {ERC721TransferFromFail} from "../errors/LowLevelErrors.sol"; 9 | import {NotAContract} from "../errors/GenericErrors.sol"; 10 | 11 | /** 12 | * @title LowLevelERC721Transfer 13 | * @notice This contract contains low-level calls to transfer ERC721 tokens. 14 | * @author LooksRare protocol team (👀,💎) 15 | */ 16 | contract LowLevelERC721Transfer { 17 | /** 18 | * @notice Execute ERC721 transferFrom 19 | * @param collection Address of the collection 20 | * @param from Address of the sender 21 | * @param to Address of the recipient 22 | * @param tokenId tokenId to transfer 23 | */ 24 | function _executeERC721TransferFrom(address collection, address from, address to, uint256 tokenId) internal { 25 | if (collection.code.length == 0) { 26 | revert NotAContract(); 27 | } 28 | 29 | (bool status, ) = collection.call(abi.encodeCall(IERC721.transferFrom, (from, to, tokenId))); 30 | 31 | if (!status) { 32 | revert ERC721TransferFromFail(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelETHReturnETHIfAny.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Assembly constants 5 | import {ETHTransferFail_error_selector, ETHTransferFail_error_length, Error_selector_offset} from "../constants/AssemblyConstants.sol"; 6 | 7 | /** 8 | * @title LowLevelETHReturnETHIfAny 9 | * @notice This contract contains a function to return all ETH held in a contract. 10 | * @author LooksRare protocol team (👀,💎) 11 | */ 12 | contract LowLevelETHReturnETHIfAny { 13 | /** 14 | * @notice It returns ETH back to the original sender if any ETH is left in the payable call. 15 | * @dev It does not revert if self balance is equal to 0. 16 | */ 17 | function _returnETHIfAny() internal { 18 | assembly { 19 | let selfBalance := selfbalance() 20 | if gt(selfBalance, 0) { 21 | let status := call(gas(), caller(), selfBalance, 0, 0, 0, 0) 22 | if iszero(status) { 23 | mstore(0x00, ETHTransferFail_error_selector) 24 | revert(Error_selector_offset, ETHTransferFail_error_length) 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelETHReturnETHIfAnyExceptOneWei.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Assembly constants 5 | import {ETHTransferFail_error_selector, ETHTransferFail_error_length, Error_selector_offset} from "../constants/AssemblyConstants.sol"; 6 | 7 | /** 8 | * @title LowLevelETHReturnETHIfAnyExceptOneWei 9 | * @notice This contract contains a function to return all ETH except 1 wei held. 10 | * @author LooksRare protocol team (👀,💎) 11 | */ 12 | contract LowLevelETHReturnETHIfAnyExceptOneWei { 13 | /** 14 | * @notice It returns ETH to the original sender if any is left in the payable call 15 | * but this leaves 1 wei of ETH in the contract. 16 | * @dev It does not revert if self balance is equal to 1 or 0. 17 | */ 18 | function _returnETHIfAnyWithOneWeiLeft() internal { 19 | assembly { 20 | let selfBalance := selfbalance() 21 | if gt(selfBalance, 1) { 22 | let status := call(gas(), caller(), sub(selfBalance, 1), 0, 0, 0, 0) 23 | if iszero(status) { 24 | mstore(0x00, ETHTransferFail_error_selector) 25 | revert(Error_selector_offset, ETHTransferFail_error_length) 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelETHTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Assembly constants 5 | import {ETHTransferFail_error_selector, ETHTransferFail_error_length, Error_selector_offset} from "../constants/AssemblyConstants.sol"; 6 | 7 | /** 8 | * @title LowLevelETHTransfer 9 | * @notice This contract contains a low-level transfer function for ETH. 10 | * @author LooksRare protocol team (👀,💎) 11 | */ 12 | contract LowLevelETHTransfer { 13 | /** 14 | * @notice It transfers ETH to a recipient address. 15 | * @param _to Recipient address 16 | * @param _amount Amount to transfer 17 | * @dev It reverts if amount is equal to 0. 18 | */ 19 | function _transferETH(address _to, uint256 _amount) internal { 20 | assembly { 21 | let status := call(gas(), _to, _amount, 0, 0, 0, 0) 22 | 23 | if iszero(status) { 24 | mstore(0x00, ETHTransferFail_error_selector) 25 | revert(Error_selector_offset, ETHTransferFail_error_length) 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/lowLevelCallers/LowLevelWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | // Interfaces 5 | import {IWETH} from "../interfaces/generic/IWETH.sol"; 6 | 7 | /** 8 | * @title LowLevelWETH 9 | * @notice This contract contains a function to transfer ETH with an option to wrap to WETH. 10 | * If the ETH transfer fails within a gas limit, the amount in ETH is wrapped to WETH and then transferred. 11 | * @author LooksRare protocol team (👀,💎) 12 | */ 13 | contract LowLevelWETH { 14 | /** 15 | * @notice It transfers ETH to a recipient with a specified gas limit. 16 | * If the original transfers fails, it wraps to WETH and transfers the WETH to recipient. 17 | * @param _WETH WETH address 18 | * @param _to Recipient address 19 | * @param _amount Amount to transfer 20 | * @param _gasLimit Gas limit to perform the ETH transfer 21 | */ 22 | function _transferETHAndWrapIfFailWithGasLimit( 23 | address _WETH, 24 | address _to, 25 | uint256 _amount, 26 | uint256 _gasLimit 27 | ) internal { 28 | bool status; 29 | 30 | assembly { 31 | status := call(_gasLimit, _to, _amount, 0, 0, 0, 0) 32 | } 33 | 34 | if (!status) { 35 | IWETH(_WETH).deposit{value: _amount}(); 36 | IWETH(_WETH).transfer(_to, _amount); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | auto_detect_solc = true 3 | block_base_fee_per_gas = 0 4 | block_coinbase = '0x0000000000000000000000000000000000000000' 5 | block_difficulty = 0 6 | block_number = 0 7 | block_timestamp = 0 8 | cache = true 9 | cache_path = 'cache' 10 | evm_version = 'london' 11 | extra_output = [] 12 | extra_output_files = [] 13 | ffi = false 14 | force = false 15 | fuzz_max_global_rejects = 65536 16 | fuzz_max_local_rejects = 1024 17 | fuzz_runs = 1000 18 | gas_limit = 9223372036854775807 19 | gas_price = 0 20 | gas_reports = ['*'] 21 | ignored_error_codes = [1878] 22 | initial_balance = '0xffffffffffffffffffffffff' 23 | libraries = [] 24 | libs = ["node_modules", "lib"] 25 | names = false 26 | offline = false 27 | optimizer = true 28 | optimizer_runs = 888888 29 | out = 'artifacts' 30 | sender = '0x00a329c0648769a73afac7f9381e08fb43dbea72' 31 | sizes = false 32 | src = 'contracts' 33 | test = 'test/foundry' 34 | tx_origin = '0x00a329c0648769a73afac7f9381e08fb43dbea72' 35 | verbosity = 0 36 | via_ir = false 37 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import type { HardhatUserConfig } from "hardhat/types"; 2 | import { task } from "hardhat/config"; 3 | 4 | import "@nomiclabs/hardhat-etherscan"; 5 | import "@nomiclabs/hardhat-waffle"; 6 | import "@typechain/hardhat"; 7 | import "hardhat-abi-exporter"; 8 | import "hardhat-gas-reporter"; 9 | import "solidity-coverage"; 10 | import "dotenv/config"; 11 | 12 | task("accounts", "Prints the list of accounts", async (_args, hre) => { 13 | const accounts = await hre.ethers.getSigners(); 14 | accounts.forEach(async (account) => console.info(account.address)); 15 | }); 16 | 17 | const config: HardhatUserConfig = { 18 | defaultNetwork: "hardhat", 19 | networks: { 20 | hardhat: { 21 | allowUnlimitedContractSize: false, 22 | hardfork: "berlin", // Berlin is used (temporarily) to avoid issues with coverage 23 | mining: { 24 | auto: true, 25 | interval: 50000, 26 | }, 27 | gasPrice: "auto", 28 | }, 29 | }, 30 | etherscan: { 31 | apiKey: process.env.ETHERSCAN_KEY, 32 | }, 33 | solidity: { 34 | compilers: [ 35 | { 36 | version: "0.8.20", 37 | settings: { optimizer: { enabled: true, runs: 888888 } }, 38 | }, 39 | { 40 | version: "0.4.18", 41 | settings: { optimizer: { enabled: true, runs: 999 } }, 42 | }, 43 | ], 44 | }, 45 | paths: { 46 | sources: "./contracts/", 47 | tests: "./test", 48 | cache: "./cache", 49 | artifacts: "./artifacts", 50 | }, 51 | abiExporter: { 52 | path: "./abis", 53 | runOnCompile: true, 54 | clear: true, 55 | flat: true, 56 | pretty: false, 57 | except: ["test*", "lib*"], 58 | }, 59 | gasReporter: { 60 | enabled: !!process.env.REPORT_GAS, 61 | excludeContracts: ["test*", "lib*"], 62 | }, 63 | }; 64 | 65 | export default config; 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@looksrare/contracts-libs", 3 | "version": "3.5.1", 4 | "description": "LooksRare contract helper libraries", 5 | "author": "LooksRare", 6 | "license": "MIT", 7 | "private": false, 8 | "files": [ 9 | "/abis/*.json", 10 | "/contracts/*.sol", 11 | "/contracts/constants/*.sol", 12 | "/contracts/errors/*.sol", 13 | "/contracts/interfaces/*.sol", 14 | "/contracts/interfaces/generic/*.sol", 15 | "/contracts/lowLevelCallers/*.sol" 16 | ], 17 | "keywords": [ 18 | "looksrare", 19 | "solidity" 20 | ], 21 | "engines": { 22 | "node": ">=8.3.0" 23 | }, 24 | "homepage": "https://looksrare.org/", 25 | "bugs": "https://github.com/LooksRare/contracts-libs/issues", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/LooksRare/contracts-libs.git" 29 | }, 30 | "publishConfig": { 31 | "access": "public", 32 | "registry": "https://registry.npmjs.org" 33 | }, 34 | "scripts": { 35 | "compile:hardhat": "hardhat compile", 36 | "compile:forge": "forge build", 37 | "compile:hardhat:force": "hardhat compile --force", 38 | "format:check": "prettier --check '**/*.{js,jsx,ts,tsx,sol,json,yaml,md}'", 39 | "format:write": "prettier --write '**/*.{js,jsx,ts,tsx,json,yaml,sol,md}'", 40 | "lint": "eslint '**/*.{js,jsx,ts,tsx}'", 41 | "prepare": "husky install", 42 | "release": "hardhat export-abi && release-it", 43 | "test:forge": "forge test", 44 | "test:hardhat": "hardhat test", 45 | "test:hardhat:gas": "REPORT_GAS=true hardhat test", 46 | "test:hardhat:coverage": "hardhat coverage && hardhat compile --force" 47 | }, 48 | "devDependencies": { 49 | "@commitlint/cli": "^16.2.3", 50 | "@commitlint/config-conventional": "^16.2.1", 51 | "@nomiclabs/hardhat-ethers": "^2.0.6", 52 | "@nomiclabs/hardhat-etherscan": "^3.0.3", 53 | "@nomiclabs/hardhat-waffle": "^2.0.3", 54 | "@typechain/ethers-v5": "^7.0.1", 55 | "@typechain/hardhat": "^2.3.0", 56 | "@types/chai": "^4.2.21", 57 | "@types/mocha": "^9.0.0", 58 | "@types/node": "^12.0.0", 59 | "@typescript-eslint/eslint-plugin": "^4.29.1", 60 | "@typescript-eslint/parser": "^4.29.1", 61 | "chai": "^4.2.0", 62 | "dotenv": "^10.0.0", 63 | "eslint": "^7.29.0", 64 | "eslint-config-prettier": "^8.3.0", 65 | "eslint-config-standard": "^16.0.3", 66 | "eslint-plugin-import": "^2.23.4", 67 | "eslint-plugin-node": "^11.1.0", 68 | "eslint-plugin-prettier": "^3.4.0", 69 | "eslint-plugin-promise": "^5.1.0", 70 | "ethereum-waffle": "^3.4.4", 71 | "ethers": "^5.6.4", 72 | "hardhat": "^2.9.4", 73 | "hardhat-abi-exporter": "^2.9.0", 74 | "hardhat-gas-reporter": "^1.0.8", 75 | "husky": "^7.0.4", 76 | "merkletreejs": "^0.2.31", 77 | "@openzeppelin/contracts": "^5.0.2", 78 | "prettier": "^2.3.2", 79 | "prettier-plugin-solidity": "^1.3.0", 80 | "release-it": "^15.0.0", 81 | "solhint": "^3.3.7", 82 | "solidity-coverage": "^0.7.21", 83 | "ts-node": "^10.1.0", 84 | "typechain": "^5.1.2", 85 | "typescript": "^4.5.2" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /test/foundry/BlastERC20RebasingYield.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | import {IOwnableTwoSteps} from "../../contracts/interfaces/IOwnableTwoSteps.sol"; 6 | import {OwnableTwoSteps} from "../../contracts/OwnableTwoSteps.sol"; 7 | import {BlastERC20RebasingYield} from "../../contracts/BlastERC20RebasingYield.sol"; 8 | import {TestHelpers} from "./utils/TestHelpers.sol"; 9 | import {YieldMode} from "../../contracts/interfaces/IERC20Rebasing.sol"; 10 | 11 | import {MockBlastERC20} from "../mock/MockBlastERC20.sol"; 12 | import {MockBlastPoints} from "../mock/MockBlastPoints.sol"; 13 | import {MockBlastWETH} from "../mock/MockBlastWETH.sol"; 14 | import {MockBlastYield} from "../mock/MockBlastYield.sol"; 15 | 16 | contract BlastERC20RebasingYieldGuarded is BlastERC20RebasingYield, OwnableTwoSteps { 17 | constructor( 18 | address _blast, 19 | address _blastPoints, 20 | address _blastPointsOperator, 21 | address _owner, 22 | address _usdb, 23 | address _weth 24 | ) 25 | BlastERC20RebasingYield(_blast, _blastPoints, _blastPointsOperator, _owner, _usdb, _weth) 26 | OwnableTwoSteps(_owner) 27 | {} 28 | 29 | function claim(address wethReceiver, address usdbReceiver) public onlyOwner { 30 | _claimERC20RebasingYield(wethReceiver, usdbReceiver); 31 | } 32 | } 33 | 34 | contract BlastERC20RebasingYield_Test is TestHelpers { 35 | MockBlastWETH private weth; 36 | MockBlastERC20 private usdb; 37 | MockBlastYield private mockBlastYield; 38 | MockBlastPoints private mockBlastPoints; 39 | BlastERC20RebasingYieldGuarded private blastERC20RebasingYieldGuarded; 40 | 41 | address public owner = address(69); 42 | address public operator = address(420); 43 | address public user1 = address(1); 44 | address private constant TREASURY = address(69420); 45 | 46 | function setUp() public { 47 | weth = new MockBlastWETH(); 48 | usdb = new MockBlastERC20("USDB", "USDB"); 49 | mockBlastYield = new MockBlastYield(); 50 | mockBlastPoints = new MockBlastPoints(); 51 | blastERC20RebasingYieldGuarded = new BlastERC20RebasingYieldGuarded( 52 | address(mockBlastYield), 53 | address(mockBlastPoints), 54 | operator, 55 | owner, 56 | address(usdb), 57 | address(weth) 58 | ); 59 | } 60 | 61 | function test_setUpState() public { 62 | assertEq(blastERC20RebasingYieldGuarded.WETH(), address(weth)); 63 | assertEq(blastERC20RebasingYieldGuarded.USDB(), address(usdb)); 64 | 65 | YieldMode wethYieldMode = weth.yieldMode(address(blastERC20RebasingYieldGuarded)); 66 | assertEq(uint8(wethYieldMode), uint8(YieldMode.CLAIMABLE)); 67 | 68 | YieldMode usdbYieldMode = usdb.yieldMode(address(blastERC20RebasingYieldGuarded)); 69 | assertEq(uint8(usdbYieldMode), uint8(YieldMode.CLAIMABLE)); 70 | } 71 | 72 | function test_claim() public asPrankedUser(owner) { 73 | blastERC20RebasingYieldGuarded.claim(TREASURY, TREASURY); 74 | 75 | assertEq(weth.balanceOf(address(blastERC20RebasingYieldGuarded)), 0); 76 | assertEq(usdb.balanceOf(address(blastERC20RebasingYieldGuarded)), 0); 77 | assertEq(weth.balanceOf(TREASURY), 1 ether); 78 | assertEq(usdb.balanceOf(TREASURY), 1 ether); 79 | } 80 | 81 | function test_claim_RevertIf_NotOwner() public asPrankedUser(user1) { 82 | vm.expectRevert(IOwnableTwoSteps.NotOwner.selector); 83 | blastERC20RebasingYieldGuarded.claim(TREASURY, TREASURY); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/foundry/BlastNativeYield.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | import {BlastNativeYield} from "../../contracts/BlastNativeYield.sol"; 6 | import {TestHelpers} from "./utils/TestHelpers.sol"; 7 | import {YieldMode, GasMode} from "../../contracts/interfaces/IBlast.sol"; 8 | 9 | import {MockBlastPoints} from "../mock/MockBlastPoints.sol"; 10 | import {MockBlastYield} from "../mock/MockBlastYield.sol"; 11 | 12 | contract BlastNativeYield_Test is TestHelpers { 13 | MockBlastYield private mockBlastYield; 14 | MockBlastPoints private mockBlastPoints; 15 | BlastNativeYield private blastNativeYield; 16 | 17 | address public owner = address(69); 18 | address public operator = address(420); 19 | 20 | function setUp() public { 21 | mockBlastPoints = new MockBlastPoints(); 22 | mockBlastYield = new MockBlastYield(); 23 | blastNativeYield = new BlastNativeYield(address(mockBlastYield), address(mockBlastPoints), operator, owner); 24 | } 25 | 26 | function test_setUpState() public { 27 | (YieldMode yieldMode, GasMode gasMode, address governor) = mockBlastYield.config(address(blastNativeYield)); 28 | assertEq(uint8(yieldMode), uint8(YieldMode.CLAIMABLE)); 29 | assertEq(uint8(gasMode), uint8(GasMode.CLAIMABLE)); 30 | assertEq(governor, owner); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/foundry/BlastPoints.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | import {BlastPoints} from "../../contracts/BlastPoints.sol"; 6 | import {TestHelpers} from "./utils/TestHelpers.sol"; 7 | 8 | import {MockBlastPoints} from "../mock/MockBlastPoints.sol"; 9 | 10 | contract BlastPoints_Test is TestHelpers { 11 | MockBlastPoints private mockBlastPoints; 12 | BlastPoints private blastPoints; 13 | 14 | address public operator = address(420); 15 | 16 | function setUp() public { 17 | mockBlastPoints = new MockBlastPoints(); 18 | blastPoints = new BlastPoints(address(mockBlastPoints), operator); 19 | } 20 | 21 | function test_setUpState() public { 22 | assertEq(mockBlastPoints.contractOperators(address(blastPoints)), operator); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/foundry/LowLevelERC1155Transfer.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {LowLevelERC1155Transfer} from "../../contracts/lowLevelCallers/LowLevelERC1155Transfer.sol"; 5 | import {NotAContract} from "../../contracts/errors/GenericErrors.sol"; 6 | import {ERC1155SafeTransferFromFail, ERC1155SafeBatchTransferFromFail} from "../../contracts/errors/LowLevelErrors.sol"; 7 | import {MockERC721} from "../mock/MockERC721.sol"; 8 | import {MockERC1155} from "../mock/MockERC1155.sol"; 9 | import {TestHelpers} from "./utils/TestHelpers.sol"; 10 | 11 | contract ImplementedLowLevelERC1155Transfer is LowLevelERC1155Transfer { 12 | function safeTransferFromERC1155( 13 | address collection, 14 | address from, 15 | address to, 16 | uint256 tokenId, 17 | uint256 amount 18 | ) external { 19 | _executeERC1155SafeTransferFrom(collection, from, to, tokenId, amount); 20 | } 21 | 22 | function safeBatchTransferFromERC1155( 23 | address collection, 24 | address from, 25 | address to, 26 | uint256[] calldata tokenIds, 27 | uint256[] calldata amounts 28 | ) external { 29 | _executeERC1155SafeBatchTransferFrom(collection, from, to, tokenIds, amounts); 30 | } 31 | } 32 | 33 | abstract contract TestParameters { 34 | address internal _sender = address(100); 35 | address internal _recipient = address(101); 36 | } 37 | 38 | contract LowLevelERC1155TransferTest is TestParameters, TestHelpers { 39 | ImplementedLowLevelERC1155Transfer public lowLevelERC1155Transfer; 40 | MockERC1155 public mockERC1155; 41 | 42 | function setUp() external { 43 | lowLevelERC1155Transfer = new ImplementedLowLevelERC1155Transfer(); 44 | mockERC1155 = new MockERC1155(); 45 | } 46 | 47 | function testSafeTransferFromERC1155(uint256 tokenId, uint256 amount) external asPrankedUser(_sender) { 48 | mockERC1155.mint(_sender, tokenId, amount); 49 | mockERC1155.setApprovalForAll(address(lowLevelERC1155Transfer), true); 50 | lowLevelERC1155Transfer.safeTransferFromERC1155(address(mockERC1155), _sender, _recipient, tokenId, amount); 51 | assertEq(mockERC1155.balanceOf(_recipient, tokenId), amount); 52 | } 53 | 54 | function testSafeBatchTransferFromERC1155( 55 | uint256 tokenId0, 56 | uint256 amount0, 57 | uint256 amount1 58 | ) external asPrankedUser(_sender) { 59 | vm.assume(tokenId0 < type(uint256).max); 60 | uint256 tokenId1 = tokenId0 + 1; 61 | mockERC1155.mint(_sender, tokenId0, amount0); 62 | mockERC1155.mint(_sender, tokenId1, amount1); 63 | mockERC1155.setApprovalForAll(address(lowLevelERC1155Transfer), true); 64 | 65 | uint256[] memory amounts = new uint256[](2); 66 | amounts[0] = amount0; 67 | amounts[1] = amount1; 68 | 69 | uint256[] memory tokenIds = new uint256[](2); 70 | tokenIds[0] = tokenId0; 71 | tokenIds[1] = tokenId1; 72 | 73 | lowLevelERC1155Transfer.safeBatchTransferFromERC1155( 74 | address(mockERC1155), 75 | _sender, 76 | _recipient, 77 | tokenIds, 78 | amounts 79 | ); 80 | assertEq(mockERC1155.balanceOf(_recipient, tokenId0), amount0); 81 | assertEq(mockERC1155.balanceOf(_recipient, tokenId1), amount1); 82 | } 83 | 84 | function testSafeTransferFromERC1155NotAContract(uint256 tokenId, uint256 amount) external asPrankedUser(_sender) { 85 | vm.expectRevert(NotAContract.selector); 86 | lowLevelERC1155Transfer.safeTransferFromERC1155(address(0), _sender, _recipient, tokenId, amount); 87 | } 88 | 89 | function testSafeBatchTransferFromERC1155NotAContract( 90 | uint256 tokenId0, 91 | uint256 amount0, 92 | uint256 amount1 93 | ) external asPrankedUser(_sender) { 94 | vm.assume(tokenId0 < type(uint256).max); 95 | uint256 tokenId1 = tokenId0 + 1; 96 | 97 | uint256[] memory amounts = new uint256[](2); 98 | amounts[0] = amount0; 99 | amounts[1] = amount1; 100 | 101 | uint256[] memory tokenIds = new uint256[](2); 102 | tokenIds[0] = tokenId0; 103 | tokenIds[1] = tokenId1; 104 | 105 | vm.expectRevert(NotAContract.selector); 106 | lowLevelERC1155Transfer.safeBatchTransferFromERC1155(address(0), _sender, _recipient, tokenIds, amounts); 107 | } 108 | 109 | function testSafeTransferFromERC1155WithERC721Fails( 110 | uint256 tokenId, 111 | uint256 amount 112 | ) external asPrankedUser(_sender) { 113 | MockERC721 mockERC721 = new MockERC721(); 114 | mockERC721.mint(_sender, tokenId); 115 | mockERC721.setApprovalForAll(address(lowLevelERC1155Transfer), true); 116 | vm.expectRevert(ERC1155SafeTransferFromFail.selector); 117 | lowLevelERC1155Transfer.safeTransferFromERC1155(address(mockERC721), _sender, _recipient, tokenId, amount); 118 | } 119 | 120 | function testSafeBatchTransferFromERC1155WithERC721Fails( 121 | uint256 tokenId0, 122 | uint256 amount0, 123 | uint256 amount1 124 | ) external asPrankedUser(_sender) { 125 | vm.assume(tokenId0 < type(uint256).max); 126 | uint256 tokenId1 = tokenId0 + 1; 127 | 128 | MockERC721 mockERC721 = new MockERC721(); 129 | mockERC721.mint(_sender, tokenId0); 130 | mockERC721.mint(_sender, tokenId1); 131 | mockERC721.setApprovalForAll(address(lowLevelERC1155Transfer), true); 132 | 133 | uint256[] memory amounts = new uint256[](2); 134 | amounts[0] = amount0; 135 | amounts[1] = amount1; 136 | 137 | uint256[] memory tokenIds = new uint256[](2); 138 | tokenIds[0] = tokenId0; 139 | tokenIds[1] = tokenId1; 140 | 141 | vm.expectRevert(ERC1155SafeBatchTransferFromFail.selector); 142 | lowLevelERC1155Transfer.safeBatchTransferFromERC1155( 143 | address(mockERC721), 144 | _sender, 145 | _recipient, 146 | tokenIds, 147 | amounts 148 | ); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /test/foundry/LowLevelERC20Approve.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {LowLevelERC20Approve} from "../../contracts/lowLevelCallers/LowLevelERC20Approve.sol"; 5 | import {NotAContract} from "../../contracts/errors/GenericErrors.sol"; 6 | import {ERC20ApprovalFail} from "../../contracts/errors/LowLevelErrors.sol"; 7 | import {MockERC20} from "../mock/MockERC20.sol"; 8 | import {MockERC1155} from "../mock/MockERC1155.sol"; 9 | import {TestHelpers} from "./utils/TestHelpers.sol"; 10 | 11 | contract ImplementedLowLevelERC20Approve is LowLevelERC20Approve { 12 | function approveERC20(address currency, address to, uint256 amount) external { 13 | _executeERC20Approve(currency, to, amount); 14 | } 15 | } 16 | 17 | abstract contract TestParameters { 18 | address internal _sender = address(100); 19 | address internal _operator = address(102); 20 | } 21 | 22 | contract LowLevelERC20ApproveTest is TestHelpers, TestParameters { 23 | ImplementedLowLevelERC20Approve public lowLevelERC20Approve; 24 | MockERC20 public mockERC20; 25 | 26 | function setUp() external { 27 | lowLevelERC20Approve = new ImplementedLowLevelERC20Approve(); 28 | mockERC20 = new MockERC20(); 29 | } 30 | 31 | function testApproveERC20(uint256 amount) external { 32 | lowLevelERC20Approve.approveERC20(address(mockERC20), _operator, amount); 33 | assertEq(mockERC20.allowance(address(lowLevelERC20Approve), _operator), amount); 34 | } 35 | 36 | function testApproveERC20NotAContract(uint256 amount) external { 37 | vm.expectRevert(NotAContract.selector); 38 | lowLevelERC20Approve.approveERC20(address(0), _operator, amount); 39 | } 40 | 41 | function testApproveERC20WithERC1155Fails(uint256 amount) external { 42 | MockERC1155 mockERC1155 = new MockERC1155(); 43 | vm.expectRevert(ERC20ApprovalFail.selector); 44 | lowLevelERC20Approve.approveERC20(address(mockERC1155), _operator, amount); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/foundry/LowLevelERC20Transfer.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {LowLevelERC20Transfer} from "../../contracts/lowLevelCallers/LowLevelERC20Transfer.sol"; 5 | import {NotAContract} from "../../contracts/errors/GenericErrors.sol"; 6 | import {ERC20ApprovalFail, ERC20TransferFail, ERC20TransferFromFail} from "../../contracts/errors/LowLevelErrors.sol"; 7 | import {MockERC20} from "../mock/MockERC20.sol"; 8 | import {MockERC1155} from "../mock/MockERC1155.sol"; 9 | import {TestHelpers} from "./utils/TestHelpers.sol"; 10 | 11 | contract ImplementedLowLevelERC20Transfer is LowLevelERC20Transfer { 12 | function transferERC20(address currency, address to, uint256 amount) external { 13 | _executeERC20DirectTransfer(currency, to, amount); 14 | } 15 | 16 | function transferFromERC20(address currency, address from, address to, uint256 amount) external { 17 | _executeERC20TransferFrom(currency, from, to, amount); 18 | } 19 | } 20 | 21 | abstract contract TestParameters { 22 | address internal _sender = address(100); 23 | address internal _recipient = address(101); 24 | } 25 | 26 | contract LowLevelERC20TransferTest is TestHelpers, TestParameters { 27 | ImplementedLowLevelERC20Transfer public lowLevelERC20Transfer; 28 | MockERC20 public mockERC20; 29 | 30 | function setUp() external { 31 | lowLevelERC20Transfer = new ImplementedLowLevelERC20Transfer(); 32 | mockERC20 = new MockERC20(); 33 | } 34 | 35 | function testTransferFromERC20(uint256 amount) external asPrankedUser(_sender) { 36 | mockERC20.mint(_sender, amount); 37 | mockERC20.approve(address(lowLevelERC20Transfer), amount); 38 | lowLevelERC20Transfer.transferFromERC20(address(mockERC20), _sender, _recipient, amount); 39 | assertEq(mockERC20.balanceOf(_recipient), amount); 40 | } 41 | 42 | function testTransferERC20(uint256 amount) external asPrankedUser(_sender) { 43 | mockERC20.mint(address(lowLevelERC20Transfer), amount); 44 | lowLevelERC20Transfer.transferERC20(address(mockERC20), _recipient, amount); 45 | assertEq(mockERC20.balanceOf(_recipient), amount); 46 | } 47 | 48 | function testTransferFromERC20NotAContract(uint256 amount) external asPrankedUser(_sender) { 49 | vm.expectRevert(NotAContract.selector); 50 | lowLevelERC20Transfer.transferFromERC20(address(0), _sender, _recipient, amount); 51 | } 52 | 53 | function testTransferERC20NotAContract(uint256 amount) external asPrankedUser(_sender) { 54 | vm.expectRevert(NotAContract.selector); 55 | lowLevelERC20Transfer.transferERC20(address(0), _recipient, amount); 56 | } 57 | 58 | function testTransferERC20WithERC1155Fails(uint256 amount) external { 59 | MockERC1155 mockERC1155 = new MockERC1155(); 60 | vm.expectRevert(ERC20TransferFail.selector); 61 | lowLevelERC20Transfer.transferERC20(address(mockERC1155), _recipient, amount); 62 | } 63 | 64 | function testTransferFromERC20WithERC1155Fails(uint256 amount) external { 65 | MockERC1155 mockERC1155 = new MockERC1155(); 66 | vm.expectRevert(ERC20TransferFromFail.selector); 67 | lowLevelERC20Transfer.transferFromERC20(address(mockERC1155), _sender, _recipient, amount); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/foundry/LowLevelERC721Transfer.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {LowLevelERC721Transfer} from "../../contracts/lowLevelCallers/LowLevelERC721Transfer.sol"; 5 | import {NotAContract} from "../../contracts/errors/GenericErrors.sol"; 6 | import {ERC721TransferFromFail} from "../../contracts/errors/LowLevelErrors.sol"; 7 | import {MockERC721} from "../mock/MockERC721.sol"; 8 | import {MockERC1155} from "../mock/MockERC1155.sol"; 9 | import {TestHelpers} from "./utils/TestHelpers.sol"; 10 | 11 | contract ImplementedLowLevelERC721Transfer is LowLevelERC721Transfer { 12 | function transferERC721(address collection, address from, address to, uint256 tokenId) external { 13 | _executeERC721TransferFrom(collection, from, to, tokenId); 14 | } 15 | } 16 | 17 | abstract contract TestParameters { 18 | address internal _sender = address(100); 19 | address internal _recipient = address(101); 20 | } 21 | 22 | contract LowLevelERC721TransferTest is TestParameters, TestHelpers { 23 | ImplementedLowLevelERC721Transfer public lowLevelERC721Transfer; 24 | MockERC721 public mockERC721; 25 | 26 | function setUp() external { 27 | lowLevelERC721Transfer = new ImplementedLowLevelERC721Transfer(); 28 | mockERC721 = new MockERC721(); 29 | } 30 | 31 | function testTransferFromERC721(uint256 tokenId) external asPrankedUser(_sender) { 32 | mockERC721.mint(_sender, tokenId); 33 | mockERC721.setApprovalForAll(address(lowLevelERC721Transfer), true); 34 | lowLevelERC721Transfer.transferERC721(address(mockERC721), _sender, _recipient, tokenId); 35 | assertEq(mockERC721.ownerOf(tokenId), _recipient); 36 | } 37 | 38 | function testTransferFromERC721NotAContract(uint256 tokenId) external asPrankedUser(_sender) { 39 | vm.expectRevert(NotAContract.selector); 40 | lowLevelERC721Transfer.transferERC721(address(0), _sender, _recipient, tokenId); 41 | } 42 | 43 | function testTransferFromERC721WithERC1155Fails(uint256 tokenId) external asPrankedUser(_sender) { 44 | MockERC1155 mockERC1155 = new MockERC1155(); 45 | mockERC1155.mint(_sender, tokenId, 1); 46 | mockERC1155.setApprovalForAll(address(lowLevelERC721Transfer), true); 47 | 48 | vm.expectRevert(ERC721TransferFromFail.selector); 49 | lowLevelERC721Transfer.transferERC721(address(mockERC1155), _sender, _recipient, tokenId); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/foundry/LowLevelETH.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {LowLevelETHTransfer} from "../../contracts/lowLevelCallers/LowLevelETHTransfer.sol"; 5 | import {LowLevelETHReturnETHIfAny} from "../../contracts/lowLevelCallers/LowLevelETHReturnETHIfAny.sol"; 6 | import {LowLevelETHReturnETHIfAnyExceptOneWei} from "../../contracts/lowLevelCallers/LowLevelETHReturnETHIfAnyExceptOneWei.sol"; 7 | import {ETHTransferFail} from "../../contracts/errors/LowLevelErrors.sol"; 8 | 9 | import {TestHelpers} from "./utils/TestHelpers.sol"; 10 | 11 | contract ImplementedLowLevelETH is 12 | LowLevelETHTransfer, 13 | LowLevelETHReturnETHIfAny, 14 | LowLevelETHReturnETHIfAnyExceptOneWei 15 | { 16 | function transferETH(address _to) external payable { 17 | _transferETH(_to, msg.value); 18 | } 19 | 20 | function transferETHAndReturnFunds() external payable { 21 | _returnETHIfAny(); 22 | } 23 | 24 | function transferETHAndReturnFundsExceptOneWei() external payable { 25 | _returnETHIfAnyWithOneWeiLeft(); 26 | } 27 | } 28 | 29 | contract AlwaysReject { 30 | ImplementedLowLevelETH lowLevelETH; 31 | 32 | error NoMeGustaDinero(); 33 | 34 | constructor(ImplementedLowLevelETH _lowLevelETH) { 35 | lowLevelETH = _lowLevelETH; 36 | } 37 | 38 | function transferETHAndReturnFunds() external payable { 39 | lowLevelETH.transferETHAndReturnFunds{value: msg.value}(); 40 | } 41 | 42 | function transferETHAndReturnFundsExceptOneWei() external payable { 43 | lowLevelETH.transferETHAndReturnFundsExceptOneWei{value: msg.value}(); 44 | } 45 | 46 | receive() external payable { 47 | revert NoMeGustaDinero(); 48 | } 49 | } 50 | 51 | abstract contract TestParameters { 52 | address internal _sender = address(100); 53 | address internal _recipient = address(101); 54 | uint256 internal _GAS_LIMIT = 10000; 55 | } 56 | 57 | contract LowLevelETHTest is TestParameters, TestHelpers { 58 | ImplementedLowLevelETH public lowLevelETH; 59 | 60 | function setUp() external { 61 | lowLevelETH = new ImplementedLowLevelETH(); 62 | } 63 | 64 | function testTransferETH(address randomSender, uint112 amount) external { 65 | vm.assume(amount > 0); 66 | vm.deal(randomSender, amount); 67 | vm.prank(randomSender); 68 | lowLevelETH.transferETH{value: amount}(_recipient); 69 | assertEq(_recipient.balance, amount); 70 | } 71 | 72 | function testTransferETHFail(address randomSender, uint112 amount) external { 73 | // The test starts failing with OutOfFund when amount == 79228162514264337593543950336, 74 | // some truncation issues. 75 | vm.assume(amount < 79228162514264337593543950336); 76 | vm.deal(randomSender, amount); 77 | vm.prank(randomSender); 78 | AlwaysReject alwaysReject = new AlwaysReject(lowLevelETH); 79 | vm.expectRevert(ETHTransferFail.selector); 80 | lowLevelETH.transferETH{value: amount}(address(alwaysReject)); 81 | } 82 | 83 | function testTransferETHAndReturnFunds(uint112 amount) external asPrankedUser(_sender) { 84 | vm.deal(_sender, amount); 85 | lowLevelETH.transferETHAndReturnFunds{value: amount}(); 86 | assertEq(_sender.balance, amount); 87 | } 88 | 89 | function testTransferETHAndReturnFundsFail(uint112 amount) external asPrankedUser(_sender) { 90 | vm.assume(amount > 0); 91 | vm.deal(_sender, amount); 92 | AlwaysReject alwaysReject = new AlwaysReject(lowLevelETH); 93 | vm.expectRevert(ETHTransferFail.selector); 94 | alwaysReject.transferETHAndReturnFunds{value: amount}(); 95 | } 96 | 97 | function testTransferETHAndReturnFundsExceptOneWei(uint112 amount) external asPrankedUser(_sender) { 98 | vm.deal(_sender, amount); 99 | 100 | if (amount > 1) { 101 | lowLevelETH.transferETHAndReturnFundsExceptOneWei{value: amount}(); 102 | assertEq(_sender.balance, amount - 1); 103 | assertEq(address(lowLevelETH).balance, 1); 104 | } else { 105 | lowLevelETH.transferETHAndReturnFundsExceptOneWei{value: amount}(); 106 | assertEq(_sender.balance, 0); 107 | assertEq(address(lowLevelETH).balance, amount); 108 | } 109 | } 110 | 111 | function testTransferETHAndReturnFundsExceptOneWeiFail(uint112 amount) external asPrankedUser(_sender) { 112 | vm.assume(amount > 1); 113 | vm.deal(_sender, amount); 114 | AlwaysReject alwaysReject = new AlwaysReject(lowLevelETH); 115 | vm.expectRevert(ETHTransferFail.selector); 116 | alwaysReject.transferETHAndReturnFundsExceptOneWei{value: amount}(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /test/foundry/LowLevelWETH.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {MockWETH} from "../mock/MockWETH.sol"; 5 | import {LowLevelWETH} from "../../contracts/lowLevelCallers/LowLevelWETH.sol"; 6 | import {TestHelpers} from "./utils/TestHelpers.sol"; 7 | 8 | contract ImplementedLowLevelWETH is LowLevelWETH { 9 | function transferETH(address _WETH, address _to, uint256 _gasLimit) external payable { 10 | _transferETHAndWrapIfFailWithGasLimit(_WETH, _to, msg.value, _gasLimit); 11 | } 12 | } 13 | 14 | abstract contract TestParameters { 15 | address internal _sender = address(100); 16 | uint256 internal _GAS_LIMIT = 10_000; 17 | } 18 | 19 | contract RecipientFallback { 20 | ImplementedLowLevelWETH public lowLevelWETH; 21 | 22 | receive() external payable { 23 | // Infinite loop 24 | for (uint256 i; i < type(uint256).max; i++) { 25 | keccak256(abi.encode(i)); 26 | } 27 | } 28 | } 29 | 30 | contract LowLevelWETHTest is TestParameters, TestHelpers { 31 | ImplementedLowLevelWETH public lowLevelWETH; 32 | RecipientFallback public recipientFallback; 33 | MockWETH public mockWeth; 34 | 35 | function setUp() external { 36 | lowLevelWETH = new ImplementedLowLevelWETH(); 37 | recipientFallback = new RecipientFallback(); 38 | mockWeth = new MockWETH(); 39 | } 40 | 41 | function testTransferETHAndRevertsinWETH(uint256 amount) external payable asPrankedUser(_sender) { 42 | vm.deal(_sender, amount); 43 | lowLevelWETH.transferETH{value: amount}(address(mockWeth), address(recipientFallback), _GAS_LIMIT); 44 | assertEq(address(recipientFallback).balance, 0); 45 | assertEq(mockWeth.balanceOf(address(recipientFallback)), amount); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/foundry/OwnableTwoSteps.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {OwnableTwoSteps} from "../../contracts/OwnableTwoSteps.sol"; 5 | import {IOwnableTwoSteps} from "../../contracts/interfaces/IOwnableTwoSteps.sol"; 6 | import {TestHelpers} from "./utils/TestHelpers.sol"; 7 | 8 | abstract contract TestParameters { 9 | address internal _owner = address(42); 10 | } 11 | 12 | contract ImplementedOwnableTwoSteps is TestParameters, OwnableTwoSteps { 13 | constructor() OwnableTwoSteps(_owner) {} 14 | } 15 | 16 | contract OwnableTwoStepsTest is TestParameters, TestHelpers, IOwnableTwoSteps { 17 | ImplementedOwnableTwoSteps public ownableTwoSteps; 18 | 19 | function setUp() public asPrankedUser(_owner) { 20 | vm.expectEmit(true, false, false, true); 21 | emit NewOwner(_owner); 22 | ownableTwoSteps = new ImplementedOwnableTwoSteps(); 23 | } 24 | 25 | function testConstructor() public { 26 | assertEq(ownableTwoSteps.owner(), _owner); 27 | assertEq(ownableTwoSteps.potentialOwner(), address(0)); 28 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.NoOngoingTransfer)); 29 | } 30 | 31 | function testTransferOwnershipToNewOwner() public { 32 | address newOwner = address(45); 33 | 34 | // 1. Initiate ownership transfer 35 | vm.prank(_owner); 36 | vm.expectEmit(false, false, false, true); 37 | emit InitiateOwnershipTransfer(_owner, newOwner); 38 | ownableTwoSteps.initiateOwnershipTransfer(newOwner); 39 | assertEq(ownableTwoSteps.potentialOwner(), newOwner); 40 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.TransferInProgress)); 41 | 42 | // 2. Accept ownership transfer 43 | vm.prank(newOwner); 44 | vm.expectEmit(false, false, false, true); 45 | emit NewOwner(newOwner); 46 | ownableTwoSteps.confirmOwnershipTransfer(); 47 | assertEq(ownableTwoSteps.potentialOwner(), address(0)); 48 | assertEq(ownableTwoSteps.owner(), newOwner); 49 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.NoOngoingTransfer)); 50 | } 51 | 52 | function testRenounceOwnership() public asPrankedUser(_owner) { 53 | // 1. Initiate renouncement of ownership 54 | vm.expectEmit(false, false, false, true); 55 | emit InitiateOwnershipRenouncement(); 56 | ownableTwoSteps.initiateOwnershipRenouncement(); 57 | assertEq(ownableTwoSteps.potentialOwner(), address(0)); 58 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.RenouncementInProgress)); 59 | 60 | // 2. Confirm renouncement of ownership 61 | vm.expectEmit(false, false, false, true); 62 | emit NewOwner(address(0)); 63 | ownableTwoSteps.confirmOwnershipRenouncement(); 64 | assertEq(ownableTwoSteps.potentialOwner(), address(0)); 65 | assertEq(ownableTwoSteps.owner(), address(0)); 66 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.NoOngoingTransfer)); 67 | } 68 | 69 | function testCancelTransferOwnership() public asPrankedUser(_owner) { 70 | address newOwner = address(45); 71 | 72 | // 1. Initiate ownership transfer 73 | vm.expectEmit(false, false, false, true); 74 | emit InitiateOwnershipTransfer(_owner, newOwner); 75 | ownableTwoSteps.initiateOwnershipTransfer(newOwner); 76 | assertEq(ownableTwoSteps.potentialOwner(), newOwner); 77 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.TransferInProgress)); 78 | 79 | // 2. Cancel ownership transfer 80 | vm.expectEmit(false, false, false, true); 81 | emit CancelOwnershipTransfer(); 82 | ownableTwoSteps.cancelOwnershipTransfer(); 83 | assertEq(ownableTwoSteps.potentialOwner(), address(0)); 84 | assertEq(ownableTwoSteps.owner(), _owner); 85 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.NoOngoingTransfer)); 86 | 87 | // 3. Initiate ownership renouncement 88 | vm.expectEmit(false, false, false, true); 89 | emit InitiateOwnershipRenouncement(); 90 | ownableTwoSteps.initiateOwnershipRenouncement(); 91 | assertEq(ownableTwoSteps.potentialOwner(), address(0)); 92 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.RenouncementInProgress)); 93 | 94 | // 4. Cancel ownership renouncement 95 | vm.expectEmit(false, false, false, true); 96 | emit CancelOwnershipTransfer(); 97 | ownableTwoSteps.cancelOwnershipTransfer(); 98 | assertEq(ownableTwoSteps.potentialOwner(), address(0)); 99 | assertEq(ownableTwoSteps.owner(), _owner); 100 | assertEq(uint8(ownableTwoSteps.ownershipStatus()), uint8(Status.NoOngoingTransfer)); 101 | } 102 | 103 | function testWrongRecipientCannotClaim() public { 104 | address newOwner = address(45); 105 | address wrongOwner = address(30); 106 | 107 | // 1. Initiate ownership transfer 108 | vm.prank(_owner); 109 | vm.expectEmit(false, false, false, true); 110 | emit InitiateOwnershipTransfer(_owner, newOwner); 111 | ownableTwoSteps.initiateOwnershipTransfer(newOwner); 112 | 113 | vm.prank(wrongOwner); 114 | vm.expectRevert(WrongPotentialOwner.selector); 115 | ownableTwoSteps.confirmOwnershipTransfer(); 116 | } 117 | 118 | function testOwnableFunctionsOnlyCallableByOwner(address randomUser) public asPrankedUser(randomUser) { 119 | vm.assume(randomUser != _owner); 120 | 121 | vm.expectRevert(NotOwner.selector); 122 | ownableTwoSteps.cancelOwnershipTransfer(); 123 | 124 | vm.expectRevert(NotOwner.selector); 125 | ownableTwoSteps.confirmOwnershipRenouncement(); 126 | 127 | vm.expectRevert(NotOwner.selector); 128 | ownableTwoSteps.initiateOwnershipTransfer(randomUser); 129 | 130 | vm.expectRevert(NotOwner.selector); 131 | ownableTwoSteps.initiateOwnershipRenouncement(); 132 | } 133 | 134 | function testCannotConfirmOwnershipOrRenouncementTransferIfNotInProgress() public asPrankedUser(_owner) { 135 | vm.expectRevert(TransferNotInProgress.selector); 136 | ownableTwoSteps.confirmOwnershipTransfer(); 137 | 138 | vm.expectRevert(RenouncementNotInProgress.selector); 139 | ownableTwoSteps.confirmOwnershipRenouncement(); 140 | } 141 | 142 | function testCannotCancelTransferIfNoTransferInProgress() public asPrankedUser(_owner) { 143 | vm.expectRevert(NoOngoingTransferInProgress.selector); 144 | ownableTwoSteps.cancelOwnershipTransfer(); 145 | } 146 | 147 | function testCannotRenounceOrInitiateTransferASecondtime() public asPrankedUser(_owner) { 148 | // 1. Cannot initiate renouncement/transfer to new owner after transfer to new owner is initiated 149 | address newOwner = address(45); 150 | ownableTwoSteps.initiateOwnershipTransfer(newOwner); 151 | vm.expectRevert(TransferAlreadyInProgress.selector); 152 | ownableTwoSteps.initiateOwnershipTransfer(newOwner); 153 | vm.expectRevert(TransferAlreadyInProgress.selector); 154 | ownableTwoSteps.initiateOwnershipRenouncement(); 155 | 156 | // Reset ownership status 157 | ownableTwoSteps.cancelOwnershipTransfer(); 158 | 159 | // 2. Cannot initiate renouncement/transfer to new owner after renouncement is initiated 160 | ownableTwoSteps.initiateOwnershipRenouncement(); 161 | vm.expectRevert(TransferAlreadyInProgress.selector); 162 | ownableTwoSteps.initiateOwnershipRenouncement(); 163 | vm.expectRevert(TransferAlreadyInProgress.selector); 164 | ownableTwoSteps.initiateOwnershipTransfer(newOwner); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /test/foundry/PackableReentrancyGuard.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {PackableReentrancyGuard, IReentrancyGuard} from "../../contracts/PackableReentrancyGuard.sol"; 5 | import {TestHelpers} from "./utils/TestHelpers.sol"; 6 | import {Faucet} from "./utils/reentrancy/Faucet.sol"; 7 | import {UnsafeFaucet} from "./utils/reentrancy/UnsafeFaucet.sol"; 8 | import {ReentrancyCaller} from "./utils/reentrancy/ReentrancyCaller.sol"; 9 | 10 | contract SafeFaucet is Faucet, PackableReentrancyGuard { 11 | function claim() external override nonReentrant { 12 | _claim(); 13 | } 14 | } 15 | 16 | contract ReentrancyGuardTest is TestHelpers, IReentrancyGuard { 17 | SafeFaucet public safeFaucet; 18 | UnsafeFaucet public unsafeFaucet; 19 | 20 | function setUp() public { 21 | safeFaucet = new SafeFaucet(); 22 | unsafeFaucet = new UnsafeFaucet(); 23 | // Top up the faucets 24 | vm.deal(address(safeFaucet), 200 ether); 25 | vm.deal(address(unsafeFaucet), 200 ether); 26 | } 27 | 28 | function testUnsafeFaucet() public { 29 | ReentrancyCaller reentrancyCaller = new ReentrancyCaller(address(unsafeFaucet)); 30 | 31 | reentrancyCaller.claim(); 32 | // 5 iterations + original claim = 6 * 0.01 = 0.06 ether 33 | assertEq(uint256(address(reentrancyCaller).balance), 0.06 ether); 34 | } 35 | 36 | function testSafeFaucet() public { 37 | ReentrancyCaller reentrancyCaller = new ReentrancyCaller(address(safeFaucet)); 38 | 39 | vm.expectRevert(ReentrancyFail.selector); 40 | reentrancyCaller.claim(); 41 | assertEq(uint256(address(reentrancyCaller).balance), 0 ether); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/foundry/Pausable.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {Pausable} from "../../contracts/Pausable.sol"; 5 | import {TestHelpers} from "./utils/TestHelpers.sol"; 6 | 7 | contract PausableContract is Pausable { 8 | bool public called; 9 | 10 | function pause() external { 11 | _pause(); 12 | } 13 | 14 | function unpause() external { 15 | _unpause(); 16 | } 17 | 18 | function callFunction() external whenNotPaused { 19 | called = true; 20 | } 21 | } 22 | 23 | contract PausableTest is TestHelpers { 24 | PausableContract private pausable; 25 | 26 | event Paused(address account); 27 | event Unpaused(address account); 28 | 29 | function setUp() public { 30 | pausable = new PausableContract(); 31 | } 32 | 33 | function test_pause() public { 34 | vm.expectEmit({checkTopic1: true, checkTopic2: true, checkTopic3: true, checkData: true}); 35 | emit Paused(address(this)); 36 | pausable.pause(); 37 | assertTrue(pausable.paused()); 38 | } 39 | 40 | function test_pause_RevertIf_IsPaused() public { 41 | pausable.pause(); 42 | vm.expectRevert(Pausable.IsPaused.selector); 43 | pausable.pause(); 44 | } 45 | 46 | function test_unpause() public { 47 | pausable.pause(); 48 | vm.expectEmit({checkTopic1: true, checkTopic2: true, checkTopic3: true, checkData: true}); 49 | emit Unpaused(address(this)); 50 | pausable.unpause(); 51 | assertFalse(pausable.paused()); 52 | } 53 | 54 | function test_unpause_RevertIf_NotPaused() public { 55 | vm.expectRevert(Pausable.NotPaused.selector); 56 | pausable.unpause(); 57 | } 58 | 59 | function test_callFunction() public { 60 | pausable.callFunction(); 61 | assertTrue(pausable.called()); 62 | } 63 | 64 | function test_callFunction_RevertIf_Paused() public { 65 | pausable.pause(); 66 | vm.expectRevert(Pausable.IsPaused.selector); 67 | pausable.callFunction(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/foundry/ProtocolFee.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {ProtocolFee} from "../../contracts/ProtocolFee.sol"; 5 | import {TestHelpers} from "./utils/TestHelpers.sol"; 6 | 7 | contract ProtocolFeeContract is ProtocolFee { 8 | function updateProtocolFeeRecipient(address _protocolFeeRecipient) external override { 9 | _updateProtocolFeeRecipient(_protocolFeeRecipient); 10 | } 11 | 12 | function updateProtocolFeeBp(uint16 _protocolFeeBp) external override { 13 | _updateProtocolFeeBp(_protocolFeeBp); 14 | } 15 | } 16 | 17 | contract ProtocolFeeTest is TestHelpers { 18 | ProtocolFeeContract private protocolFee; 19 | 20 | event ProtocolFeeBpUpdated(uint16 protocolFeeBp); 21 | event ProtocolFeeRecipientUpdated(address protocolFeeRecipient); 22 | 23 | function setUp() public { 24 | protocolFee = new ProtocolFeeContract(); 25 | } 26 | 27 | function test_setUpState() public { 28 | assertEq(protocolFee.protocolFeeRecipient(), address(0)); 29 | assertEq(protocolFee.protocolFeeBp(), 0); 30 | } 31 | 32 | function test_updateProtocolFeeRecipient() public { 33 | vm.expectEmit({checkTopic1: true, checkTopic2: true, checkTopic3: true, checkData: true}); 34 | emit ProtocolFeeRecipientUpdated(address(0x1)); 35 | 36 | protocolFee.updateProtocolFeeRecipient(address(0x1)); 37 | assertEq(protocolFee.protocolFeeRecipient(), address(0x1)); 38 | } 39 | 40 | function test_updateProtocolFeeRecipient_RevertIf_InvalidValue() public { 41 | protocolFee.updateProtocolFeeRecipient(address(0x1)); 42 | 43 | vm.expectRevert(ProtocolFee.ProtocolFee__InvalidValue.selector); 44 | protocolFee.updateProtocolFeeRecipient(address(0)); 45 | } 46 | 47 | function test_updateProtocolFeeBp() public { 48 | vm.expectEmit({checkTopic1: true, checkTopic2: true, checkTopic3: true, checkData: true}); 49 | emit ProtocolFeeBpUpdated(2_500); 50 | 51 | protocolFee.updateProtocolFeeBp(2_500); 52 | assertEq(protocolFee.protocolFeeBp(), 2_500); 53 | } 54 | 55 | function test_updateProtocolFeeBp_RevertIf_InvalidValue() public { 56 | vm.expectRevert(ProtocolFee.ProtocolFee__InvalidValue.selector); 57 | protocolFee.updateProtocolFeeBp(2_501); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/foundry/ReentrancyGuard.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {ReentrancyGuard, IReentrancyGuard} from "../../contracts/ReentrancyGuard.sol"; 5 | import {TestHelpers} from "./utils/TestHelpers.sol"; 6 | import {Faucet} from "./utils/reentrancy/Faucet.sol"; 7 | import {UnsafeFaucet} from "./utils/reentrancy/UnsafeFaucet.sol"; 8 | import {ReentrancyCaller} from "./utils/reentrancy/ReentrancyCaller.sol"; 9 | 10 | contract SafeFaucet is Faucet, ReentrancyGuard { 11 | function claim() external override nonReentrant { 12 | _claim(); 13 | } 14 | } 15 | 16 | contract ReentrancyGuardTest is TestHelpers, IReentrancyGuard { 17 | SafeFaucet public safeFaucet; 18 | UnsafeFaucet public unsafeFaucet; 19 | 20 | function setUp() public { 21 | safeFaucet = new SafeFaucet(); 22 | unsafeFaucet = new UnsafeFaucet(); 23 | // Top up the faucets 24 | vm.deal(address(safeFaucet), 200 ether); 25 | vm.deal(address(unsafeFaucet), 200 ether); 26 | } 27 | 28 | function testUnsafeFaucet() public { 29 | ReentrancyCaller reentrancyCaller = new ReentrancyCaller(address(unsafeFaucet)); 30 | 31 | reentrancyCaller.claim(); 32 | // 5 iterations + original claim = 6 * 0.01 = 0.06 ether 33 | assertEq(uint256(address(reentrancyCaller).balance), 0.06 ether); 34 | } 35 | 36 | function testSafeFaucet() public { 37 | ReentrancyCaller reentrancyCaller = new ReentrancyCaller(address(safeFaucet)); 38 | 39 | vm.expectRevert(ReentrancyFail.selector); 40 | reentrancyCaller.claim(); 41 | assertEq(uint256(address(reentrancyCaller).balance), 0 ether); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/foundry/SignatureChecker.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {ERC1271Contract} from "./utils/ERC1271Contract.sol"; 5 | import {PublicSignatureChecker} from "./utils/PublicSignatureChecker.sol"; 6 | import {TestHelpers} from "./utils/TestHelpers.sol"; 7 | import "../../contracts/errors/SignatureCheckerErrors.sol"; 8 | 9 | abstract contract TestParameters is TestHelpers { 10 | // Generate two random private keys 11 | uint256 internal privateKeyUser1 = 0x00aaf; 12 | uint256 internal privateKeyUser2 = 0x00aabf; 13 | 14 | // Derive two public keys from private keys 15 | address internal user1 = vm.addr(privateKeyUser1); 16 | address internal user2 = vm.addr(privateKeyUser2); 17 | } 18 | 19 | contract SignatureCheckerCalldataTest is TestHelpers, TestParameters { 20 | PublicSignatureChecker public signatureChecker; 21 | 22 | function setUp() public { 23 | signatureChecker = new PublicSignatureChecker(); 24 | } 25 | 26 | function testSignEOA(bytes32 randomMessage) public { 27 | bytes memory signature = _signMessage(randomMessage, privateKeyUser1); 28 | bytes32 hashedMessage = _computeHash(randomMessage); 29 | 30 | assertTrue(signatureChecker.verifyCalldata(hashedMessage, user1, signature)); 31 | assertTrue(signatureChecker.verifyMemory(hashedMessage, user1, signature)); 32 | } 33 | 34 | function testSignEOAEIP2098(bytes32 randomMessage) public { 35 | bytes memory signature = _eip2098Signature(_signMessage(randomMessage, privateKeyUser1)); 36 | bytes32 hashedMessage = _computeHash(randomMessage); 37 | 38 | assertTrue(signatureChecker.verifyCalldata(hashedMessage, user1, signature)); 39 | assertTrue(signatureChecker.verifyMemory(hashedMessage, user1, signature)); 40 | } 41 | 42 | function testSignERC1271(bytes32 randomMessage) public { 43 | ERC1271Contract erc1271Contract = new ERC1271Contract(user1); 44 | bytes memory signature = _signMessage(randomMessage, privateKeyUser1); 45 | bytes32 hashedMessage = _computeHash(randomMessage); 46 | 47 | assertTrue(signatureChecker.verifyCalldata(hashedMessage, address(erc1271Contract), signature)); 48 | assertTrue(signatureChecker.verifyMemory(hashedMessage, address(erc1271Contract), signature)); 49 | } 50 | 51 | function testRevertIfSignatureERC1271IsInvalid(bytes32 randomMessage) public { 52 | ERC1271Contract erc1271Contract = new ERC1271Contract(user1); 53 | bytes memory signature = _signMessage(randomMessage, privateKeyUser2); 54 | bytes32 hashedMessage = _computeHash(randomMessage); 55 | 56 | vm.expectRevert(SignatureERC1271Invalid.selector); 57 | signatureChecker.verifyCalldata(hashedMessage, address(erc1271Contract), signature); 58 | 59 | vm.expectRevert(SignatureERC1271Invalid.selector); 60 | signatureChecker.verifyMemory(hashedMessage, address(erc1271Contract), signature); 61 | } 62 | 63 | function testRevertIfSignatureEOAIsInvalid(bytes32 randomMessage) public { 64 | bytes memory signature = _signMessage(randomMessage, privateKeyUser2); 65 | bytes32 hashedMessage = _computeHash(randomMessage); 66 | 67 | vm.expectRevert(SignatureEOAInvalid.selector); 68 | signatureChecker.verifyCalldata(hashedMessage, user1, signature); 69 | 70 | vm.expectRevert(SignatureEOAInvalid.selector); 71 | signatureChecker.verifyMemory(hashedMessage, user1, signature); 72 | } 73 | 74 | function testRevertIfVParameterIsInvalid(bytes32 randomMessage, uint8 v) public { 75 | vm.assume(v != 27 && v != 28); 76 | (, bytes32 r, bytes32 s) = vm.sign(privateKeyUser1, keccak256(abi.encodePacked(randomMessage))); 77 | 78 | // Encode the signature 79 | bytes memory signature = abi.encodePacked(r, s, v); 80 | bytes32 hashedMessage = _computeHash(randomMessage); 81 | 82 | vm.expectRevert(abi.encodeWithSelector(SignatureParameterVInvalid.selector, v)); 83 | signatureChecker.verifyCalldata(hashedMessage, user1, signature); 84 | 85 | vm.expectRevert(abi.encodeWithSelector(SignatureParameterVInvalid.selector, v)); 86 | signatureChecker.verifyMemory(hashedMessage, user1, signature); 87 | } 88 | 89 | function testRevertIfSParameterIsInvalid(bytes32 randomMessage, bytes32 s) public { 90 | vm.assume(uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0); 91 | 92 | (uint8 v, bytes32 r, ) = vm.sign(privateKeyUser1, keccak256(abi.encodePacked(randomMessage))); 93 | 94 | // Encode the signature with the fuzzed s 95 | bytes memory signature = abi.encodePacked(r, s, v); 96 | bytes32 hashedMessage = _computeHash(randomMessage); 97 | 98 | vm.expectRevert(abi.encodeWithSelector(SignatureParameterSInvalid.selector)); 99 | signatureChecker.verifyCalldata(hashedMessage, user1, signature); 100 | 101 | vm.expectRevert(abi.encodeWithSelector(SignatureParameterSInvalid.selector)); 102 | signatureChecker.verifyMemory(hashedMessage, user1, signature); 103 | } 104 | 105 | function testRevertIfRecoveredAddressIsNull(bytes32 randomMessage) public { 106 | (uint8 v, , bytes32 s) = vm.sign(privateKeyUser1, keccak256(abi.encodePacked(randomMessage))); 107 | 108 | // Encode the signature with empty bytes32 for r 109 | bytes32 r; 110 | bytes memory signature = abi.encodePacked(r, s, v); 111 | bytes32 hashedMessage = _computeHash(randomMessage); 112 | 113 | vm.expectRevert(abi.encodeWithSelector(NullSignerAddress.selector)); 114 | signatureChecker.verifyCalldata(hashedMessage, user1, signature); 115 | 116 | vm.expectRevert(abi.encodeWithSelector(NullSignerAddress.selector)); 117 | signatureChecker.verifyMemory(hashedMessage, user1, signature); 118 | } 119 | 120 | function testRevertIfSignatureLengthIsInvalid(bytes32 randomMessage, uint256 length) public { 121 | // @dev Getting OutOfGas starting from 16,776,985, probably due to memory cost 122 | vm.assume(length != 64 && length != 65 && length < 16_776_985); 123 | bytes memory signature = new bytes(length); 124 | 125 | bytes32 hashedMessage = _computeHash(randomMessage); 126 | vm.expectRevert(abi.encodeWithSelector(SignatureLengthInvalid.selector, length)); 127 | signatureChecker.verifyCalldata(hashedMessage, user1, signature); 128 | 129 | vm.expectRevert(abi.encodeWithSelector(SignatureLengthInvalid.selector, length)); 130 | signatureChecker.verifyMemory(hashedMessage, user1, signature); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /test/foundry/USDTLowLevelERC20Test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {LowLevelERC20Approve} from "../../contracts/lowLevelCallers/LowLevelERC20Approve.sol"; 5 | import {LowLevelERC20Transfer} from "../../contracts/lowLevelCallers/LowLevelERC20Transfer.sol"; 6 | import {TestHelpers} from "./utils/TestHelpers.sol"; 7 | 8 | contract ImplementedLowLevelERC20 is LowLevelERC20Approve, LowLevelERC20Transfer { 9 | function approveERC20(address currency, address to, uint256 amount) external { 10 | _executeERC20Approve(currency, to, amount); 11 | } 12 | 13 | function transferERC20(address currency, address to, uint256 amount) external { 14 | _executeERC20DirectTransfer(currency, to, amount); 15 | } 16 | 17 | function transferFromERC20(address currency, address from, address to, uint256 amount) external { 18 | _executeERC20TransferFrom(currency, from, to, amount); 19 | } 20 | } 21 | 22 | abstract contract TestParameters { 23 | address internal _usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7; // Mainnet USDT address 24 | address internal _tetherTreasury = 0x5754284f345afc66a98fbB0a0Afe71e0F007B949; // Mainnet Tether treasury 25 | address internal _sender = _tetherTreasury; 26 | address internal _recipient = address(250); 27 | address internal _operator = address(69); 28 | uint256 internal _amount = 10_000 * (10 ** 6); // USDT has 6 decimals 29 | 30 | // Ankr RPC endpoint is public 31 | string internal _MAINNET_RPC_URL = "https://rpc.ankr.com/eth"; 32 | } 33 | 34 | interface IUSDT { 35 | function approve(address _spender, uint256 _value) external; 36 | 37 | function allowance(address _owner, address _operator) external returns (uint256); 38 | 39 | function transfer(address _to, uint256 _value) external; 40 | 41 | function balanceOf(address who) external view returns (uint256); 42 | 43 | function owner() external view returns (address); 44 | } 45 | 46 | contract USDTLowLevelERC20Test is TestHelpers, TestParameters { 47 | ImplementedLowLevelERC20 public lowLevelERC20; 48 | 49 | uint256 internal _mainnetFork; 50 | 51 | function setUp() external { 52 | _mainnetFork = vm.createFork(_MAINNET_RPC_URL); 53 | vm.selectFork(_mainnetFork); 54 | lowLevelERC20 = new ImplementedLowLevelERC20(); 55 | } 56 | 57 | function testApproveUSDT() external { 58 | lowLevelERC20.approveERC20(_usdt, _operator, _amount); 59 | assertEq(IUSDT(_usdt).allowance(address(lowLevelERC20), _operator), _amount); 60 | } 61 | 62 | function testTransferFromUSDT() external asPrankedUser(_sender) { 63 | IUSDT(_usdt).approve(address(lowLevelERC20), _amount); 64 | lowLevelERC20.transferFromERC20(_usdt, _sender, _recipient, _amount); 65 | assertEq(IUSDT(_usdt).balanceOf(_recipient), _amount); 66 | } 67 | 68 | function testTransferUSDT() external asPrankedUser(_sender) { 69 | IUSDT(_usdt).transfer(address(lowLevelERC20), _amount); 70 | lowLevelERC20.transferERC20(_usdt, _recipient, _amount); 71 | assertEq(IUSDT(_usdt).balanceOf(_recipient), _amount); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/foundry/UnsafeMathUint256.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {UnsafeMathUint256} from "../../contracts/libraries/UnsafeMathUint256.sol"; 5 | import {TestHelpers} from "./utils/TestHelpers.sol"; 6 | 7 | contract UnsafeMathUint256Test is TestHelpers { 8 | using UnsafeMathUint256 for uint256; 9 | 10 | function testFuzz_unsafeAdd(uint256 number) public { 11 | vm.assume(number < type(uint256).max); 12 | assertEq(number.unsafeAdd(1), number + 1); 13 | } 14 | 15 | function test_unsafeAdd_NumberIsUint256Max() public { 16 | assertEq(type(uint256).max.unsafeAdd(1), 0); 17 | } 18 | 19 | function testFuzz_unsafeSubtract(uint256 number) public { 20 | vm.assume(number > 0); 21 | assertEq(number.unsafeSubtract(1), number - 1); 22 | } 23 | 24 | function test_unsafeSubtract_NumberIs0() public { 25 | assertEq(uint256(0).unsafeSubtract(1), type(uint256).max); 26 | } 27 | 28 | function testFuzz_unsafeMultiply(uint256 a, uint256 b) public { 29 | vm.assume(a <= type(uint128).max && b <= type(uint128).max); 30 | assertEq(a.unsafeMultiply(b), a * b); 31 | } 32 | 33 | function test_unsafeMultiply_NumbersAreGreaterThanUint128() public { 34 | uint256 number = uint256(type(uint128).max) + 1; 35 | assertEq(number.unsafeMultiply(number), 0); 36 | } 37 | 38 | function testFuzz_unsafeDivide(uint256 a, uint256 b) public { 39 | vm.assume(b != 0); 40 | assertEq(a.unsafeDivide(b), a / b); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/foundry/utils/BytesLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /* 5 | * @title Solidity Bytes Arrays Utils 6 | * @author Gonçalo Sá 7 | * @notice We only copied the `slice` function from the original https://github.com/GNSPS/solidity-bytes-utils 8 | * as that's the only function needed. 9 | * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. 10 | * The library lets you slice bytes arrays in memory. 11 | */ 12 | library BytesLib { 13 | function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { 14 | require(_length + 31 >= _length, "slice_overflow"); 15 | require(_bytes.length >= _start + _length, "slice_outOfBounds"); 16 | 17 | bytes memory tempBytes; 18 | 19 | assembly { 20 | switch iszero(_length) 21 | case 0 { 22 | // Get a location of some free memory and store it in tempBytes as 23 | // Solidity does for memory variables. 24 | tempBytes := mload(0x40) 25 | 26 | // The first word of the slice result is potentially a partial 27 | // word read from the original array. To read it, we calculate 28 | // the length of that partial word and start copying that many 29 | // bytes into the array. The first word we copy will start with 30 | // data we don't care about, but the last `lengthmod` bytes will 31 | // land at the beginning of the contents of the new array. When 32 | // we're done copying, we overwrite the full first word with 33 | // the actual length of the slice. 34 | let lengthmod := and(_length, 31) 35 | 36 | // The multiplication in the next line is necessary 37 | // because when slicing multiples of 32 bytes (lengthmod == 0) 38 | // the following copy loop was copying the origin's length 39 | // and then ending prematurely not copying everything it should. 40 | let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) 41 | let end := add(mc, _length) 42 | 43 | for { 44 | // The multiplication in the next line has the same exact purpose 45 | // as the one above. 46 | let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) 47 | } lt(mc, end) { 48 | mc := add(mc, 0x20) 49 | cc := add(cc, 0x20) 50 | } { 51 | mstore(mc, mload(cc)) 52 | } 53 | 54 | mstore(tempBytes, _length) 55 | 56 | //update free-memory pointer 57 | //allocating the array padded to 32 bytes like the compiler does now 58 | mstore(0x40, and(add(mc, 31), not(31))) 59 | } 60 | //if we want a zero-length slice let's just return a zero-length array 61 | default { 62 | tempBytes := mload(0x40) 63 | //zero out the 32 bytes slice we are about to return 64 | //we need to do it because Solidity does not garbage collect 65 | mstore(tempBytes, 0) 66 | 67 | mstore(0x40, add(tempBytes, 0x20)) 68 | } 69 | } 70 | 71 | return tempBytes; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/foundry/utils/ERC1271Contract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import {ERC1271_MAGIC_VALUE} from "../../../contracts/constants/StandardConstants.sol"; 5 | import {IERC1271} from "../../../contracts/interfaces/generic/IERC1271.sol"; 6 | 7 | contract ERC1271Contract is IERC1271 { 8 | // Custom errors 9 | error SignatureParameterSInvalid(); 10 | error SignatureParameterVInvalid(uint8 v); 11 | error SignatureLengthInvalid(uint256 length); 12 | 13 | address public owner; 14 | 15 | constructor(address _owner) { 16 | owner = _owner; 17 | } 18 | 19 | /** 20 | * @notice Verifies that the signer is the owner of the signing contract. 21 | */ 22 | function isValidSignature(bytes32 hash, bytes calldata signature) external view override returns (bytes4) { 23 | uint8 v; 24 | bytes32 r; 25 | bytes32 s; 26 | 27 | if (signature.length == 64) { 28 | bytes32 vs; 29 | assembly { 30 | r := calldataload(signature.offset) 31 | vs := calldataload(add(signature.offset, 0x20)) 32 | s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 33 | v := add(shr(255, vs), 27) 34 | } 35 | } else if (signature.length == 65) { 36 | assembly { 37 | r := calldataload(signature.offset) 38 | s := calldataload(add(signature.offset, 0x20)) 39 | v := byte(0, calldataload(add(signature.offset, 0x40))) 40 | } 41 | } else { 42 | revert SignatureLengthInvalid(signature.length); 43 | } 44 | 45 | if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { 46 | revert SignatureParameterSInvalid(); 47 | } 48 | 49 | if (v != 27 && v != 28) { 50 | revert SignatureParameterVInvalid(v); 51 | } 52 | 53 | address signer = ecrecover(hash, v, r, s); 54 | 55 | if (signer == owner) { 56 | return ERC1271_MAGIC_VALUE; 57 | } else { 58 | return 0xffffffff; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/foundry/utils/PublicSignatureChecker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {SignatureCheckerCalldata} from "../../../contracts/SignatureCheckerCalldata.sol"; 5 | import {SignatureCheckerMemory} from "../../../contracts/SignatureCheckerMemory.sol"; 6 | 7 | contract PublicSignatureChecker { 8 | function verifyCalldata(bytes32 hash, address signer, bytes calldata signature) external view returns (bool) { 9 | // It will revert if it is wrong 10 | SignatureCheckerCalldata.verify(hash, signer, signature); 11 | return true; 12 | } 13 | 14 | function verifyMemory(bytes32 hash, address signer, bytes memory signature) external view returns (bool) { 15 | // It will revert if it is wrong 16 | SignatureCheckerMemory.verify(hash, signer, signature); 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/foundry/utils/TestHelpers.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {Test} from "../../../lib/forge-std/src/Test.sol"; 5 | import {BytesLib} from "./BytesLib.sol"; 6 | 7 | abstract contract TestHelpers is Test { 8 | using BytesLib for bytes; 9 | 10 | modifier asPrankedUser(address _user) { 11 | vm.startPrank(_user); 12 | _; 13 | vm.stopPrank(); 14 | } 15 | 16 | function _signMessage(bytes32 message, uint256 _key) internal pure returns (bytes memory) { 17 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(_key, keccak256(abi.encodePacked(message))); 18 | return abi.encodePacked(r, s, v); 19 | } 20 | 21 | /** 22 | * @dev Transforms a standard signature into an EIP-2098 compliant signature 23 | * @param signature The secp256k1 65-bytes signature 24 | * @return eip2098Signature The 64-bytes EIP-2098 compliant signature 25 | */ 26 | function _eip2098Signature(bytes memory signature) internal pure returns (bytes memory eip2098Signature) { 27 | eip2098Signature = signature.slice(0, 64); 28 | uint8 parityBit = uint8(eip2098Signature[32]) | ((uint8(signature[64]) % 27) << 7); 29 | eip2098Signature[32] = bytes1(parityBit); 30 | } 31 | 32 | function _computeHash(bytes32 message) internal pure returns (bytes32) { 33 | return keccak256(abi.encodePacked(message)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/foundry/utils/reentrancy/Faucet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | abstract contract Faucet { 5 | error AlreadyClaimed(); 6 | 7 | mapping(address => bool) internal _hasClaimed; 8 | 9 | function claim() external virtual; 10 | 11 | function _claim() internal { 12 | if (_hasClaimed[msg.sender]) { 13 | revert AlreadyClaimed(); 14 | } 15 | 16 | bool status; 17 | address to = msg.sender; 18 | uint256 amount = 0.01 ether; 19 | 20 | assembly { 21 | status := call(gas(), to, amount, 0, 0, 0, 0) 22 | // returndatacopy(t, f, s) 23 | // copy s bytes from returndata at position f to mem at position t 24 | returndatacopy(0, 0, returndatasize()) 25 | switch status 26 | case 0 { 27 | // revert(p, s) 28 | // end execution, revert state changes, return data mem[p…(p+s)) 29 | revert(0, returndatasize()) 30 | } 31 | } 32 | 33 | _hasClaimed[msg.sender] = true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/foundry/utils/reentrancy/ReentrancyCaller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import {Faucet} from "./Faucet.sol"; 5 | 6 | contract ReentrancyCaller { 7 | uint256 private _counter; 8 | Faucet public faucet; 9 | 10 | constructor(address _faucet) { 11 | faucet = Faucet(_faucet); 12 | } 13 | 14 | receive() external payable { 15 | if (_counter++ < 5) { 16 | faucet.claim(); 17 | } 18 | } 19 | 20 | function claim() external { 21 | faucet.claim(); 22 | // reset counter 23 | _counter = 0; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/foundry/utils/reentrancy/UnsafeFaucet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import {Faucet} from "./Faucet.sol"; 5 | 6 | contract UnsafeFaucet is Faucet { 7 | function claim() external override { 8 | _claim(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/mock/MockBlastERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.23; 3 | 4 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | import {YieldMode} from "../../contracts/interfaces/IERC20Rebasing.sol"; 7 | 8 | contract MockBlastERC20 is ERC20 { 9 | mapping(address _contract => YieldMode) public yieldMode; 10 | 11 | constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {} 12 | 13 | function mint(address to, uint256 amount) public { 14 | _mint(to, amount); 15 | } 16 | 17 | function configure(YieldMode _yieldMode) external returns (uint256) { 18 | yieldMode[msg.sender] = _yieldMode; 19 | return uint256(_yieldMode); 20 | } 21 | 22 | function getClaimableAmount(address) external pure returns (uint256) { 23 | return 1 ether; 24 | } 25 | 26 | function claim(address receiver, uint256 amount) external returns (uint256) { 27 | _mint(receiver, amount); 28 | return amount; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/mock/MockBlastPoints.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract MockBlastPoints { 5 | mapping(address _contract => address operator) public contractOperators; 6 | 7 | function configurePointsOperator(address operator) external { 8 | contractOperators[msg.sender] = operator; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/mock/MockBlastWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {YieldMode} from "../../contracts/interfaces/IERC20Rebasing.sol"; 5 | 6 | contract MockBlastWETH { 7 | string public name = "Wrapped Ether"; 8 | string public symbol = "WETH"; 9 | uint8 public decimals = 18; 10 | 11 | event Approval(address indexed src, address indexed guy, uint256 wad); 12 | event Transfer(address indexed src, address indexed dst, uint256 wad); 13 | event Deposit(address indexed dst, uint256 wad); 14 | event Withdrawal(address indexed src, uint256 wad); 15 | 16 | mapping(address => uint256) public balanceOf; 17 | mapping(address => mapping(address => uint256)) public allowance; 18 | 19 | mapping(address => YieldMode) public yieldMode; 20 | 21 | receive() external payable { 22 | deposit(); 23 | } 24 | 25 | function configure(YieldMode _yieldMode) external returns (uint256) { 26 | yieldMode[msg.sender] = _yieldMode; 27 | return uint256(_yieldMode); 28 | } 29 | 30 | function getClaimableAmount(address) external pure returns (uint256) { 31 | return 1 ether; 32 | } 33 | 34 | function claim(address receiver, uint256 amount) external returns (uint256) { 35 | balanceOf[receiver] += amount; 36 | return amount; 37 | } 38 | 39 | function deposit() public payable { 40 | balanceOf[msg.sender] += msg.value; 41 | emit Deposit(msg.sender, msg.value); 42 | } 43 | 44 | function withdraw(uint256 wad) public { 45 | require(balanceOf[msg.sender] >= wad); 46 | balanceOf[msg.sender] -= wad; 47 | payable(msg.sender).transfer(wad); 48 | emit Withdrawal(msg.sender, wad); 49 | } 50 | 51 | function totalSupply() public view returns (uint256) { 52 | return address(this).balance; 53 | } 54 | 55 | function approve(address guy, uint256 wad) public returns (bool) { 56 | allowance[msg.sender][guy] = wad; 57 | emit Approval(msg.sender, guy, wad); 58 | return true; 59 | } 60 | 61 | function transfer(address dst, uint256 wad) public returns (bool) { 62 | return transferFrom(msg.sender, dst, wad); 63 | } 64 | 65 | function transferFrom(address src, address dst, uint256 wad) public returns (bool) { 66 | require(balanceOf[src] >= wad); 67 | 68 | if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { 69 | require(allowance[src][msg.sender] >= wad); 70 | allowance[src][msg.sender] -= wad; 71 | } 72 | 73 | balanceOf[src] -= wad; 74 | balanceOf[dst] += wad; 75 | 76 | emit Transfer(src, dst, wad); 77 | 78 | return true; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/mock/MockBlastYield.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {YieldMode, GasMode} from "../../contracts/interfaces/IBlast.sol"; 5 | 6 | contract MockBlastYield { 7 | struct Config { 8 | YieldMode yieldMode; 9 | GasMode gasMode; 10 | address governor; 11 | } 12 | 13 | mapping(address _contract => Config) public config; 14 | 15 | function configure(YieldMode _yield, GasMode _gasMode, address _governor) external { 16 | config[msg.sender] = Config(_yield, _gasMode, _governor); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/mock/MockERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 5 | 6 | contract MockERC1155 is ERC1155("MockURI") { 7 | function uri(uint256) public pure override returns (string memory) { 8 | return "uri"; 9 | } 10 | 11 | function mint(address to, uint256 tokenId, uint256 amount) public { 12 | _mint(to, tokenId, amount, ""); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/mock/MockERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract MockERC20 is ERC20("MockERC20", "MockERC20") { 7 | function mint(address to, uint256 amount) public { 8 | _mint(to, amount); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/mock/MockERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | import {IERC165} from "../../contracts/interfaces/generic/IERC165.sol"; 6 | 7 | contract MockERC721 is ERC721("MockERC721", "MockERC721") { 8 | function mint(address to, uint256 tokenId) public { 9 | _mint(to, tokenId); 10 | } 11 | 12 | function batchMint(address to, uint256[] memory tokenIds) public { 13 | for (uint256 i; i < tokenIds.length; i++) { 14 | _mint(to, tokenIds[i]); 15 | } 16 | } 17 | 18 | function tokenURI(uint256) public pure override returns (string memory) { 19 | return "tokenURI"; 20 | } 21 | 22 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 23 | return super.supportsInterface(interfaceId); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/mock/MockWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | contract MockWETH { 5 | string public name = "Wrapped Ether"; 6 | string public symbol = "WETH"; 7 | uint8 public decimals = 18; 8 | 9 | event Approval(address indexed src, address indexed guy, uint256 wad); 10 | event Transfer(address indexed src, address indexed dst, uint256 wad); 11 | event Deposit(address indexed dst, uint256 wad); 12 | event Withdrawal(address indexed src, uint256 wad); 13 | 14 | mapping(address => uint256) public balanceOf; 15 | mapping(address => mapping(address => uint256)) public allowance; 16 | 17 | receive() external payable { 18 | deposit(); 19 | } 20 | 21 | function deposit() public payable { 22 | balanceOf[msg.sender] += msg.value; 23 | emit Deposit(msg.sender, msg.value); 24 | } 25 | 26 | function withdraw(uint256 wad) public { 27 | require(balanceOf[msg.sender] >= wad); 28 | balanceOf[msg.sender] -= wad; 29 | payable(msg.sender).transfer(wad); 30 | emit Withdrawal(msg.sender, wad); 31 | } 32 | 33 | function totalSupply() public view returns (uint256) { 34 | return address(this).balance; 35 | } 36 | 37 | function approve(address guy, uint256 wad) public returns (bool) { 38 | allowance[msg.sender][guy] = wad; 39 | emit Approval(msg.sender, guy, wad); 40 | return true; 41 | } 42 | 43 | function transfer(address dst, uint256 wad) public returns (bool) { 44 | return transferFrom(msg.sender, dst, wad); 45 | } 46 | 47 | function transferFrom(address src, address dst, uint256 wad) public returns (bool) { 48 | require(balanceOf[src] >= wad); 49 | 50 | if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { 51 | require(allowance[src][msg.sender] >= wad); 52 | allowance[src][msg.sender] -= wad; 53 | } 54 | 55 | balanceOf[src] -= wad; 56 | balanceOf[dst] += wad; 57 | 58 | emit Transfer(src, dst, wad); 59 | 60 | return true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist", 8 | "declaration": true 9 | }, 10 | "include": ["./scripts", "./test", "./typechain"], 11 | "files": ["./hardhat.config.ts"] 12 | } 13 | --------------------------------------------------------------------------------