├── .commitlintrc.json ├── .env.fork ├── .env.goerli ├── .env.mainnet ├── .github └── workflows │ ├── lint_pr.yml │ ├── pr.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .nvmrc ├── .prettierignore ├── .releaserc.json ├── .vscode ├── extensions.json └── settings.json ├── ContributionAgreement ├── LICENSE ├── README.md ├── contracts ├── core │ ├── AccountFactoryV3.sol │ ├── AddressProviderV3.sol │ ├── BotListV3.sol │ └── PriceOracleV3.sol ├── credit │ ├── CreditAccountV3.sol │ ├── CreditConfiguratorV3.sol │ ├── CreditFacadeV3.sol │ ├── CreditManagerV3.sol │ └── CreditManagerV3_USDT.sol ├── governance │ ├── ControllerTimelockV3.sol │ ├── GaugeV3.sol │ ├── GearStakingV3.sol │ └── PolicyManagerV3.sol ├── interfaces │ ├── IAccountFactoryV3.sol │ ├── IAddressProviderV3.sol │ ├── IBotListV3.sol │ ├── IControllerTimelockV3.sol │ ├── ICreditAccountV3.sol │ ├── ICreditConfiguratorV3.sol │ ├── ICreditFacadeV3.sol │ ├── ICreditFacadeV3Multicall.sol │ ├── ICreditManagerV3.sol │ ├── IExceptions.sol │ ├── IGaugeV3.sol │ ├── IGearStakingV3.sol │ ├── ILinearInterestRateModelV3.sol │ ├── IPoolQuotaKeeperV3.sol │ ├── IPoolV3.sol │ ├── IPriceOracleV3.sol │ ├── IVotingContractV3.sol │ ├── LICENSE │ └── external │ │ └── IUSDT.sol ├── libraries │ ├── BalancesLogic.sol │ ├── BitMask.sol │ ├── CollateralLogic.sol │ ├── CreditAccountHelper.sol │ ├── CreditLogic.sol │ ├── QuotasLogic.sol │ ├── USDTFees.sol │ └── UnsafeERC20.sol ├── pool │ ├── LinearInterestRateModelV3.sol │ ├── PoolQuotaKeeperV3.sol │ ├── PoolV3.sol │ └── PoolV3_USDT.sol ├── test │ ├── config │ │ ├── ConfigManager.sol │ │ ├── MockCreditConfig.sol │ │ └── MockTokensData.sol │ ├── gas │ │ ├── ArrayAlloc.gas.t.sol │ │ ├── credit │ │ │ └── CreditFacade.gas.t.sol │ │ └── pool │ │ │ └── Gauge.gas.t.sol │ ├── helpers │ │ ├── BalanceEngine.sol │ │ ├── BalanceHelper.sol │ │ └── IntegrationTestHelper.sol │ ├── integration │ │ ├── credit │ │ │ ├── Bots.int.sol │ │ │ ├── CloseCreditAccount.int.sol │ │ │ ├── CreditConfigurator.int.t.sol │ │ │ ├── LiquidateCreditAccount.int.t.sol │ │ │ ├── ManageDebt.int.t.sol │ │ │ ├── Multicall.int.t.sol │ │ │ ├── OpenCreditAccount.int.t.sol │ │ │ └── Quotas.int.t.sol │ │ └── governance │ │ │ └── GaugeMigration.int.t.sol │ ├── interfaces │ │ ├── ICreditConfig.sol │ │ └── ITokenTestSuite.sol │ ├── invaritants │ │ ├── Deployer.sol │ │ ├── Handler.sol │ │ ├── OpenInvariants.t.sol │ │ └── TargetAttacker.sol │ ├── lib │ │ ├── AddressList.sol │ │ ├── MultiCallBuilder.sol │ │ ├── ParseLib.sol │ │ ├── constants.sol │ │ └── helper.sol │ ├── mocks │ │ ├── GeneralMock.sol │ │ ├── core │ │ │ ├── ACLTraitTest.sol │ │ │ ├── AccountFactoryMock.sol │ │ │ ├── AdapterCallMock.sol │ │ │ ├── AdapterMock.sol │ │ │ ├── AddressProviderV3ACLMock.sol │ │ │ ├── BotListMock.sol │ │ │ └── TargetContractMock.sol │ │ ├── credit │ │ │ ├── CreditAccountMock.sol │ │ │ └── CreditManagerMock.sol │ │ ├── governance │ │ │ ├── GaugeMock.sol │ │ │ └── GearStakingMock.sol │ │ ├── oracles │ │ │ ├── PriceFeedMock.sol │ │ │ ├── PriceFeedOnDemandMock.sol │ │ │ └── PriceOracleMock.sol │ │ ├── pool │ │ │ ├── PoolMock.sol │ │ │ └── PoolQuotaKeeperMock.sol │ │ └── token │ │ │ ├── DegenNFTMock.sol │ │ │ ├── ERC20ApproveRestricted.sol │ │ │ ├── ERC20Blacklistable.sol │ │ │ ├── ERC20FeeMock.sol │ │ │ ├── ERC20Mock.sol │ │ │ ├── ERC20PermitMock.sol │ │ │ └── WETHMock.sol │ ├── suites │ │ ├── CreditManagerFactory.sol │ │ ├── GenesisFactory.sol │ │ ├── PoolFactory.sol │ │ ├── TokensTestSuite.sol │ │ ├── TokensTestSuiteHelper.sol │ │ └── WETHMock.sol │ └── unit │ │ ├── core │ │ ├── AccountFactoryV3.unit.t.sol │ │ ├── AccountFactoryV3Harness.sol │ │ ├── BotListV3.unit.t.sol │ │ ├── PriceOracleV3.unit.t.sol │ │ └── PriceOracleV3Harness.sol │ │ ├── credit │ │ ├── CreditAccountV3.unit.t.sol │ │ ├── CreditFacadeV3.unit.t.sol │ │ ├── CreditFacadeV3Harness.sol │ │ ├── CreditManagerV3.unit.t.sol │ │ └── CreditManagerV3Harness.sol │ │ ├── governance │ │ ├── ControllerTimelockV3.unit.t.sol │ │ ├── GaugeV3.unit.t.sol │ │ ├── GaugeV3Harness.sol │ │ ├── GearStakingV3.unit.t.sol │ │ ├── PolicyManagerV3.unit.t.sol │ │ └── PolicyManagerV3Harness.sol │ │ ├── libraries │ │ ├── BalancesLogic.unit.t.sol │ │ ├── BitMask.unit.t.sol │ │ ├── CollateralLogic.unit.t.sol │ │ ├── CollateralLogicHelper.sol │ │ ├── CreditAccountHelper.unit.t.sol │ │ ├── CreditLogic.unit.t.sol │ │ ├── QuotasLogic.unit.t.sol │ │ └── UnsafeERC20.unit.t.sol │ │ ├── pool │ │ ├── LinearInterestRateModelV3.unit.t.sol │ │ ├── PoolEquivalence.t.sol │ │ ├── PoolQuotaKeeperV3.unit.t.sol │ │ ├── PoolV3.unit.t.sol │ │ └── PoolV3Harness.sol │ │ └── traits │ │ └── USDT_Transfer.unit.t.sol └── traits │ ├── ACLNonReentrantTrait.sol │ ├── ACLTrait.sol │ ├── ContractsRegisterTrait.sol │ ├── PriceFeedValidationTrait.sol │ ├── ReentrancyGuardTrait.sol │ ├── SanityCheckTrait.sol │ └── USDT_Transfer.sol ├── foundry.toml ├── package.json ├── remappings.txt └── yarn.lock /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /.env.fork: -------------------------------------------------------------------------------- 1 | 2 | REACT_APP_BACKEND_ADDR=http://localhost:8000 3 | REACT_APP_ADDRESS_PROVIDER=0xcF64698AFF7E5f27A11dff868AF228653ba53be0 4 | REACT_APP_CHAIN_ID=1337 5 | REACT_APP_PATHFINDER=0x67C9a1B633e47172Fa609DCAebafec3C72d09f7E 6 | REACT_APP_TOKEN_DISTRIBUTOR=0xBF57539473913685688d224ad4E262684B23dD4c 7 | REACT_APP_ACCOUNT_MINER=0x7B1AAF21AC0D420666B5966338FF9aEe763C29DF 8 | -------------------------------------------------------------------------------- /.env.goerli: -------------------------------------------------------------------------------- 1 | REACT_APP_ADDRESS_PROVIDER=0x95f4cea53121b8A2Cb783C6BFB0915cEc44827D3 2 | REACT_APP_CHAIN_ID=5 3 | REACT_APP_DEGEN_NFT=0xc4cA5B61e58cDAa3cc283906b65aeFc8A80EA04A 4 | REACT_APP_DEGEN_DISTRIBUTOR=0x75f74B4A665BFcc78df0Ff82c2eB677E610B7313 -------------------------------------------------------------------------------- /.env.mainnet: -------------------------------------------------------------------------------- 1 | 2 | REACT_APP_BACKEND_ADDR=http://localhost:8000 3 | REACT_APP_ADDRESS_PROVIDER=0xcF64698AFF7E5f27A11dff868AF228653ba53be0 4 | REACT_APP_CHAIN_ID=1 5 | REACT_APP_PATHFINDER=0xBC0DE81339Da70e41897FB377b4D5C33A304f44f 6 | REACT_APP_TOKEN_DISTRIBUTOR=0xBF57539473913685688d224ad4E262684B23dD4c 7 | REACT_APP_ACCOUNT_MINER=0x7B1AAF21AC0D420666B5966338FF9aEe763C29DF 8 | -------------------------------------------------------------------------------- /.github/workflows/lint_pr.yml: -------------------------------------------------------------------------------- 1 | name: "Lint PR" 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - reopened 8 | - edited 9 | - synchronize 10 | 11 | jobs: 12 | main: 13 | name: Validate PR title 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: amannn/action-semantic-pull-request@v4 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Check PR 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, synchronize] 6 | 7 | env: 8 | HUSKY: 0 9 | CI: true 10 | 11 | jobs: 12 | checks: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Setup node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | cache: "yarn" 22 | node-version-file: ".nvmrc" 23 | 24 | - name: Install dependencies 25 | run: | 26 | yarn install --frozen-lockfile 27 | 28 | - name: Install Foundry 29 | uses: foundry-rs/foundry-toolchain@v1 30 | with: 31 | version: nightly 32 | 33 | - name: Compile contracts 34 | run: forge build 35 | 36 | - name: Run unit tests 37 | run: forge test --mt test_U 38 | timeout-minutes: 10 39 | 40 | - name: Run integration tests 41 | run: forge test --mt test_I 42 | timeout-minutes: 10 43 | 44 | - name: Run gas tests 45 | run: forge test --mt test_G -vv 46 | 47 | - name: Perform checks 48 | run: | 49 | yarn prettier:ci 50 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | - "next" 8 | 9 | env: 10 | HUSKY: 0 11 | CI: true 12 | 13 | jobs: 14 | release: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - uses: actions/setup-node@v3 21 | with: 22 | cache: "yarn" 23 | node-version-file: ".nvmrc" 24 | 25 | # prepare script runs before publish, and it needs husky 26 | - name: Install dependencies 27 | run: | 28 | yarn install --frozen-lockfile 29 | 30 | - name: Install Foundry 31 | uses: foundry-rs/foundry-toolchain@v1 32 | with: 33 | version: nightly 34 | 35 | - name: Run forge build 36 | run: forge build 37 | timeout-minutes: 10 38 | 39 | - name: Semantic Release 40 | uses: cycjimmy/semantic-release-action@v3 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .history/ 2 | .idea/ 3 | abi/ 4 | node_modules/ 5 | cache/ 6 | build/ 7 | typechain/ 8 | artifacts/ 9 | coverage* 10 | .env 11 | .verifier* 12 | .DS_Store 13 | /security/Peckshield.sol 14 | /security/ClickerAttackSolution.sol 15 | /security/TickerBombAttackSolution.sol 16 | /.env.local 17 | out/ 18 | forge-out/ 19 | .mainnet.test.cache 20 | *.log 21 | .eslintcache 22 | .eslint.local.json 23 | .favorites.json 24 | dist/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | branch = v1.3.0 5 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | types 5 | forge-out 6 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | { 4 | "name": "main" 5 | }, 6 | { 7 | "name": "next", 8 | "channel": "next", 9 | "prerelease": "next" 10 | } 11 | ], 12 | "plugins": [ 13 | "@semantic-release/commit-analyzer", 14 | "@semantic-release/release-notes-generator", 15 | "@semantic-release/npm", 16 | "@semantic-release/github" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "JuanBlanco.solidity", 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[json]": { 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | }, 5 | "[solidity]": { 6 | "editor.defaultFormatter": "JuanBlanco.solidity", 7 | "editor.tabSize": 4 8 | }, 9 | 10 | "editor.defaultFormatter": "esbenp.prettier-vscode", 11 | "editor.formatOnSave": true, 12 | "editor.tabSize": 2, 13 | "eslint.validate": ["javascript", "typescript"], 14 | "files.eol": "\n", 15 | "solidity.packageDefaultDependenciesContractsDirectory": "contracts", 16 | "solidity.packageDefaultDependenciesDirectory": "node_modules", 17 | "solidity.compileUsingRemoteVersion": "v0.8.17", 18 | "solidity.formatter": "forge" 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Business Source License 1.1 2 | License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. 3 | "Business Source License" is a trademark of MariaDB Corporation Ab. 4 | 5 | ----------------------------------------------------------------------------- 6 | 7 | Parameters 8 | 9 | Licensor: GearBox Foundation 10 | 11 | Licensed Work: Gearbox Contracts (Gearbox Protocol v3) 12 | The Licensed Work is (c) 2023 GearBox Foundation 13 | 14 | Additional Use Grant: Any production use of the Licensed Work is permitted if, 15 | and as long as, such use is expressly mentioned at 16 | gearbox.eth (the “List”). Once any specific use is added 17 | to the List, such production use shall be deemed permitted. 18 | Once any specific use is removed from the List (including 19 | if the List is updated to indicate that any specific such 20 | use is no longer allowed), such use shall no longer be 21 | permitted, and any license to make such production use 22 | shall be deemed immediately revoked. 23 | 24 | Change Date: November 15, 2027 25 | 26 | Change License: GNU General Public License v2.0 or later 27 | 28 | ----------------------------------------------------------------------------- 29 | 30 | Terms 31 | 32 | The Licensor hereby grants you the right to copy, modify, create derivative works, 33 | redistribute, and make non-production use of the Licensed Work. The Licensor may 34 | make an Additional Use Grant, above, permitting limited production use. 35 | 36 | Effective on the Change Date, or the fourth anniversary of the first publicly 37 | available distribution of a specific version of the Licensed Work under this License, 38 | whichever comes first, the Licensor hereby grants you rights under the terms of the 39 | Change License, and the rights granted in the paragraph above terminate. 40 | 41 | If your use of the Licensed Work does not comply with the requirements currently in 42 | effect as described in this License, you must purchase a commercial license from the 43 | Licensor, its affiliated entities, or authorized resellers, or you must refrain from 44 | using the Licensed Work. 45 | 46 | All copies of the original and modified Licensed Work, and derivative works of the 47 | Licensed Work, are subject to this License. This License applies separately for each 48 | version of the Licensed Work and the Change Date may vary for each version of the 49 | Licensed Work released by Licensor. 50 | 51 | You must conspicuously display this License on each original or modified copy of the 52 | Licensed Work. If you receive the Licensed Work in original or modified form from a 53 | third party, the terms and conditions set forth in this License apply to your use of 54 | that work. 55 | 56 | Any use of the Licensed Work in violation of this License will automatically terminate 57 | your rights under this License for the current and all other versions of the Licensed 58 | Work. 59 | 60 | This License does not grant you any right in any trademark or logo of Licensor or its 61 | affiliates (provided that you may use a trademark or logo of Licensor as expressly 62 | required by this License). 63 | 64 | TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” 65 | BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, 66 | INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 67 | PURPOSE, NON-INFRINGEMENT, AND TITLE. 68 | 69 | MariaDB hereby grants you permission to use this License’s text to license your works, 70 | and to refer to it using the trademark “Business Source License”, as long as you comply 71 | with the Covenants of Licensor below. 72 | 73 | 74 | ----------------------------------------------------------------------------- 75 | 76 | Covenants of Licensor 77 | 78 | In consideration of the right to use this License’s text and the “Business Source License” 79 | name and trademark, Licensor covenants to MariaDB, and to all other recipients of the 80 | licensed work to be provided by Licensor: 81 | 82 | 1. To specify as the Change License the GPL Version 2.0 or any later version, or a 83 | license that is compatible with GPL Version 2.0 or a later version, where “compatible” 84 | means that software provided under the Change License can be included in a program with 85 | software provided under GPL Version 2.0 or a later version. Licensor may specify 86 | additional Change Licenses without limitation. 87 | 88 | 2. To either: (a) specify an additional grant of rights to use that does not impose 89 | any additional restriction on the right granted in this License, as the Additional 90 | Use Grant; or (b) insert the text “None”. 91 | 92 | 3. To specify a Change Date. 93 | 94 | 4. Not to modify this License in any other way. 95 | 96 | ----------------------------------------------------------------------------- 97 | 98 | Notice 99 | 100 | The Business Source License (this document, or the “License”) is not an Open Source license. 101 | However, the Licensed Work will eventually be made available under an Open Source License, 102 | as stated in this License. 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gearbox Protocol 2 | 3 | Gearbox Protocol brings you **onchain credit**, allowing anyone to margin trade on Uniswap, leverage farm on Curve, leverage stake on Lido, and use 10X more capital on many DeFi protocols you love, as well as RWA & NFTs. Making decentralized leverage a reality thanks to Credit Account abstraction! 4 | 5 | _[See the blog post for more information on V3 and new features.](https://blog.gearbox.fi/gearbox-protocol-v3-the-onchain-credit-layer/)_ 6 | 7 | Gearbox Protocol uses Credit Account abstraction to bring together lending and prime brokerage in the same protocol. Lenders deposit assets to earn passive yield, while the composable leverage side users borrow these assets to create spot leverage positions, which can be used across DeFi. That could be margin trading on Uniswap, farming on Curve and Balancer, leverage staking on Lido and Rocketpool, and a lot more. All of that is made possible with Gearbox’s innovative Credit Account abstraction, creating the base layer of DeFi leverage. 8 | 9 | ## Documentation 10 | 11 | General documentation of the Gearbox Protocol can be found [here](https://docs.gearbox.fi). Developer documentation with more tech-related infromation about the protocol, contract interfaces, integration guides and audits is available on the [Gearbox dev protal](https://dev.gearbox.fi). 12 | 13 | ## Licensing 14 | 15 | The primary license for the Gearbox-protocol/core-v3 is the Business Source License 1.1 (BUSL-1.1), see [LICENSE](/LICENSE). The files which are NOT licensed under the BUSL-1.1 have appropriate SPDX headers. 16 | 17 | ### Important information for contributors 18 | 19 | As a contributor to the Gearbox Protocol GitHub repository, your pull requests indicate acceptance of our Gearbox Contribution Agreement. This agreement outlines that you assign the Intellectual Property Rights of your contributions to the Gearbox Foundation. This helps safeguard the Gearbox protocol and ensure the accumulation of its intellectual property. Contributions become part of the repository and may be used for various purposes, including commercial. As recognition for your expertise and work, you receive the opportunity to participate in the protocol's development and the potential to see your work integrated within it. The full Gearbox Contribution Agreement is accessible within the [repository](/ContributionAgreement) for comprehensive understanding. [Let's innovate together!] 20 | -------------------------------------------------------------------------------- /contracts/core/AddressProviderV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IACL} from "@gearbox-protocol/core-v2/contracts/interfaces/IACL.sol"; 7 | 8 | import "../interfaces/IAddressProviderV3.sol"; 9 | import {AddressNotFoundException, CallerNotConfiguratorException} from "../interfaces/IExceptions.sol"; 10 | 11 | /// @title Address provider V3 12 | /// @notice Stores addresses of important contracts 13 | contract AddressProviderV3 is IAddressProviderV3 { 14 | /// @notice Contract version 15 | uint256 public constant override version = 3_00; 16 | 17 | /// @notice Mapping from (contract key, version) to contract addresses 18 | mapping(bytes32 => mapping(uint256 => address)) public override addresses; 19 | 20 | /// @dev Ensures that function caller is configurator 21 | modifier configuratorOnly() { 22 | _revertIfNotConfigurator(); 23 | _; 24 | } 25 | 26 | /// @dev Reverts if `msg.sender` is not configurator 27 | function _revertIfNotConfigurator() internal view { 28 | if (!IACL(getAddressOrRevert(AP_ACL, NO_VERSION_CONTROL)).isConfigurator(msg.sender)) { 29 | revert CallerNotConfiguratorException(); 30 | } 31 | } 32 | 33 | constructor(address _acl) { 34 | // The first event is emitted for the address provider itself to aid in contract discovery 35 | emit SetAddress("ADDRESS_PROVIDER", address(this), version); 36 | 37 | _setAddress(AP_ACL, _acl, NO_VERSION_CONTROL); 38 | } 39 | 40 | /// @notice Returns the address of a contract with a given key and version 41 | function getAddressOrRevert(bytes32 key, uint256 _version) public view virtual override returns (address result) { 42 | result = addresses[key][_version]; 43 | if (result == address(0)) revert AddressNotFoundException(); 44 | } 45 | 46 | /// @notice Sets the address for the passed contract key 47 | /// @param key Contract key 48 | /// @param value Contract address 49 | /// @param saveVersion Whether to save contract's version 50 | function setAddress(bytes32 key, address value, bool saveVersion) external override configuratorOnly { 51 | _setAddress(key, value, saveVersion ? IVersion(value).version() : NO_VERSION_CONTROL); 52 | } 53 | 54 | /// @dev Implementation of `setAddress` 55 | function _setAddress(bytes32 key, address value, uint256 _version) internal virtual { 56 | addresses[key][_version] = value; 57 | emit SetAddress(key, value, _version); 58 | } 59 | 60 | // ---------------------- // 61 | // BACKWARD COMPATIBILITY // 62 | // ---------------------- // 63 | 64 | /// @notice ACL contract address 65 | function getACL() external view returns (address) { 66 | return getAddressOrRevert(AP_ACL, NO_VERSION_CONTROL); 67 | } 68 | 69 | /// @notice Contracts register contract address 70 | function getContractsRegister() external view returns (address) { 71 | return getAddressOrRevert(AP_CONTRACTS_REGISTER, NO_VERSION_CONTROL); 72 | } 73 | 74 | /// @notice Price oracle contract address 75 | function getPriceOracle() external view returns (address) { 76 | return getAddressOrRevert(AP_PRICE_ORACLE, 2); 77 | } 78 | 79 | /// @notice Account factory contract address 80 | function getAccountFactory() external view returns (address) { 81 | return getAddressOrRevert(AP_ACCOUNT_FACTORY, NO_VERSION_CONTROL); 82 | } 83 | 84 | /// @notice Data compressor contract address 85 | function getDataCompressor() external view returns (address) { 86 | return getAddressOrRevert(AP_DATA_COMPRESSOR, 2); 87 | } 88 | 89 | /// @notice Treasury contract address 90 | function getTreasuryContract() external view returns (address) { 91 | return getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL); 92 | } 93 | 94 | /// @notice GEAR token address 95 | function getGearToken() external view returns (address) { 96 | return getAddressOrRevert(AP_GEAR_TOKEN, NO_VERSION_CONTROL); 97 | } 98 | 99 | /// @notice WETH token address 100 | function getWethToken() external view returns (address) { 101 | return getAddressOrRevert(AP_WETH_TOKEN, NO_VERSION_CONTROL); 102 | } 103 | 104 | /// @notice WETH gateway contract address 105 | function getWETHGateway() external view returns (address) { 106 | return getAddressOrRevert(AP_WETH_GATEWAY, 1); 107 | } 108 | 109 | /// @notice Router contract address 110 | function getLeveragedActions() external view returns (address) { 111 | return getAddressOrRevert(AP_ROUTER, 1); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /contracts/credit/CreditAccountV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | pragma abicoder v1; 6 | 7 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | import {SafeERC20} from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; 9 | import {Address} from "@openzeppelin/contracts/utils/Address.sol"; 10 | 11 | import {ICreditAccountV3} from "../interfaces/ICreditAccountV3.sol"; 12 | import {CallerNotAccountFactoryException, CallerNotCreditManagerException} from "../interfaces/IExceptions.sol"; 13 | 14 | /// @title Credit account V3 15 | contract CreditAccountV3 is ICreditAccountV3 { 16 | using SafeERC20 for IERC20; 17 | using Address for address; 18 | 19 | /// @notice Contract version 20 | uint256 public constant override version = 3_00; 21 | 22 | /// @notice Account factory this account was deployed with 23 | address public immutable override factory; 24 | 25 | /// @notice Credit manager this account is connected to 26 | address public immutable override creditManager; 27 | 28 | /// @dev Ensures that function caller is account factory 29 | modifier factoryOnly() { 30 | if (msg.sender != factory) { 31 | revert CallerNotAccountFactoryException(); 32 | } 33 | _; 34 | } 35 | 36 | /// @dev Ensures that function caller is credit manager 37 | modifier creditManagerOnly() { 38 | _revertIfNotCreditManager(); 39 | _; 40 | } 41 | 42 | /// @dev Reverts if `msg.sender` is not credit manager 43 | function _revertIfNotCreditManager() internal view { 44 | if (msg.sender != creditManager) { 45 | revert CallerNotCreditManagerException(); 46 | } 47 | } 48 | 49 | /// @notice Constructor 50 | /// @param _creditManager Credit manager to connect this account to 51 | constructor(address _creditManager) { 52 | creditManager = _creditManager; // U:[CA-1] 53 | factory = msg.sender; // U:[CA-1] 54 | } 55 | 56 | /// @notice Transfers tokens from the credit account, can only be called by the credit manager 57 | /// @param token Token to transfer 58 | /// @param to Transfer recipient 59 | /// @param amount Amount to transfer 60 | function safeTransfer(address token, address to, uint256 amount) 61 | external 62 | override 63 | creditManagerOnly // U:[CA-2] 64 | { 65 | IERC20(token).safeTransfer(to, amount); // U:[CA-3] 66 | } 67 | 68 | /// @notice Executes function call from the account to the target contract with provided data, 69 | /// can only be called by the credit manager 70 | /// @param target Contract to call 71 | /// @param data Data to call the target contract with 72 | /// @return result Call result 73 | function execute(address target, bytes calldata data) 74 | external 75 | override 76 | creditManagerOnly // U:[CA-2] 77 | returns (bytes memory result) 78 | { 79 | result = target.functionCall(data); // U:[CA-4] 80 | } 81 | 82 | /// @notice Executes function call from the account to the target contract with provided data, 83 | /// can only be called by the factory. 84 | /// Allows to rescue funds that were accidentally left on the account upon closure. 85 | /// @param target Contract to call 86 | /// @param data Data to call the target contract with 87 | function rescue(address target, bytes calldata data) 88 | external 89 | override 90 | factoryOnly // U:[CA-2] 91 | { 92 | target.functionCall(data); // U:[CA-5] 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /contracts/credit/CreditManagerV3_USDT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {CreditManagerV3} from "./CreditManagerV3.sol"; 7 | import {USDT_Transfer} from "../traits/USDT_Transfer.sol"; 8 | import {IPoolV3} from "../interfaces/IPoolV3.sol"; 9 | 10 | /// @title Credit manager V3 USDT 11 | /// @notice Credit manager variation for USDT underlying with enabled transfer fees 12 | contract CreditManagerV3_USDT is CreditManagerV3, USDT_Transfer { 13 | constructor(address _addressProvider, address _pool, string memory _name) 14 | CreditManagerV3(_addressProvider, _pool, _name) 15 | USDT_Transfer(IPoolV3(_pool).asset()) 16 | {} 17 | 18 | function _amountWithFee(uint256 amount) internal view override returns (uint256) { 19 | return _amountUSDTWithFee(amount); 20 | } 21 | 22 | function _amountMinusFee(uint256 amount) internal view override returns (uint256) { 23 | return _amountUSDTMinusFee(amount); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/interfaces/IAccountFactoryV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | /// @title Account factory base interface 9 | /// @notice Functions shared accross newer and older versions 10 | interface IAccountFactoryBase is IVersion { 11 | function takeCreditAccount(uint256, uint256) external returns (address creditAccount); 12 | function returnCreditAccount(address creditAccount) external; 13 | } 14 | 15 | interface IAccountFactoryV3Events { 16 | /// @notice Emitted when new credit account is deployed 17 | event DeployCreditAccount(address indexed creditAccount, address indexed creditManager); 18 | 19 | /// @notice Emitted when credit account is taken by the credit manager 20 | event TakeCreditAccount(address indexed creditAccount, address indexed creditManager); 21 | 22 | /// @notice Emitted when used credit account is returned to the queue 23 | event ReturnCreditAccount(address indexed creditAccount, address indexed creditManager); 24 | 25 | /// @notice Emitted when new credit manager is added to the factory 26 | event AddCreditManager(address indexed creditManager, address masterCreditAccount); 27 | 28 | /// @notice Emitted when the DAO performs a proxy call from Credit Account to rescue funds 29 | event Rescue(address indexed creditAccount, address indexed target, bytes data); 30 | } 31 | 32 | /// @title Account factory V3 interface 33 | interface IAccountFactoryV3 is IAccountFactoryBase, IAccountFactoryV3Events { 34 | function delay() external view returns (uint40); 35 | 36 | function takeCreditAccount(uint256, uint256) external override returns (address creditAccount); 37 | 38 | function returnCreditAccount(address creditAccount) external override; 39 | 40 | function addCreditManager(address creditManager) external; 41 | 42 | function rescue(address creditAccount, address target, bytes calldata data) external; 43 | } 44 | -------------------------------------------------------------------------------- /contracts/interfaces/IAddressProviderV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | uint256 constant NO_VERSION_CONTROL = 0; 9 | 10 | bytes32 constant AP_CONTRACTS_REGISTER = "CONTRACTS_REGISTER"; 11 | bytes32 constant AP_ACL = "ACL"; 12 | bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE"; 13 | bytes32 constant AP_ACCOUNT_FACTORY = "ACCOUNT_FACTORY"; 14 | bytes32 constant AP_DATA_COMPRESSOR = "DATA_COMPRESSOR"; 15 | bytes32 constant AP_TREASURY = "TREASURY"; 16 | bytes32 constant AP_GEAR_TOKEN = "GEAR_TOKEN"; 17 | bytes32 constant AP_WETH_TOKEN = "WETH_TOKEN"; 18 | bytes32 constant AP_WETH_GATEWAY = "WETH_GATEWAY"; 19 | bytes32 constant AP_ROUTER = "ROUTER"; 20 | bytes32 constant AP_BOT_LIST = "BOT_LIST"; 21 | bytes32 constant AP_GEAR_STAKING = "GEAR_STAKING"; 22 | bytes32 constant AP_ZAPPER_REGISTER = "ZAPPER_REGISTER"; 23 | 24 | interface IAddressProviderV3Events { 25 | /// @notice Emitted when an address is set for a contract key 26 | event SetAddress(bytes32 indexed key, address indexed value, uint256 indexed version); 27 | } 28 | 29 | /// @title Address provider V3 interface 30 | interface IAddressProviderV3 is IAddressProviderV3Events, IVersion { 31 | function addresses(bytes32 key, uint256 _version) external view returns (address); 32 | 33 | function getAddressOrRevert(bytes32 key, uint256 _version) external view returns (address result); 34 | 35 | function setAddress(bytes32 key, address value, bool saveVersion) external; 36 | } 37 | -------------------------------------------------------------------------------- /contracts/interfaces/IBotListV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | /// @notice Bot info 9 | /// @param forbidden Whether bot is forbidden 10 | /// @param specialPermissions Mapping credit manager => bot's special permissions 11 | /// @param permissions Mapping credit manager => credit account => bot's permissions 12 | struct BotInfo { 13 | bool forbidden; 14 | mapping(address => uint192) specialPermissions; 15 | mapping(address => mapping(address => uint192)) permissions; 16 | } 17 | 18 | interface IBotListV3Events { 19 | // ----------- // 20 | // PERMISSIONS // 21 | // ----------- // 22 | 23 | /// @notice Emitted when new `bot`'s permissions and funding params are set for `creditAccount` in `creditManager` 24 | event SetBotPermissions( 25 | address indexed bot, address indexed creditManager, address indexed creditAccount, uint192 permissions 26 | ); 27 | 28 | /// @notice Emitted when `bot`'s permissions and funding params are removed for `creditAccount` in `creditManager` 29 | event EraseBot(address indexed bot, address indexed creditManager, address indexed creditAccount); 30 | 31 | // ------------- // 32 | // CONFIGURATION // 33 | // ------------- // 34 | 35 | /// @notice Emitted when `bot`'s forbidden status is set 36 | event SetBotForbiddenStatus(address indexed bot, bool forbidden); 37 | 38 | /// @notice Emitted when `bot`'s special permissions in `creditManager` are set 39 | event SetBotSpecialPermissions(address indexed bot, address indexed creditManager, uint192 permissions); 40 | 41 | /// @notice Emitted when `creditManager`'s approved status is set 42 | event SetCreditManagerApprovedStatus(address indexed creditManager, bool approved); 43 | } 44 | 45 | /// @title Bot list V3 interface 46 | interface IBotListV3 is IBotListV3Events, IVersion { 47 | // ----------- // 48 | // PERMISSIONS // 49 | // ----------- // 50 | 51 | function botPermissions(address bot, address creditManager, address creditAccount) 52 | external 53 | view 54 | returns (uint192); 55 | 56 | function activeBots(address creditManager, address creditAccount) external view returns (address[] memory); 57 | 58 | function getBotStatus(address bot, address creditManager, address creditAccount) 59 | external 60 | view 61 | returns (uint192 permissions, bool forbidden, bool hasSpecialPermissions); 62 | 63 | function setBotPermissions(address bot, address creditManager, address creditAccount, uint192 permissions) 64 | external 65 | returns (uint256 activeBotsRemaining); 66 | 67 | function eraseAllBotPermissions(address creditManager, address creditAccount) external; 68 | 69 | // ------------- // 70 | // CONFIGURATION // 71 | // ------------- // 72 | 73 | function botForbiddenStatus(address bot) external view returns (bool); 74 | 75 | function botSpecialPermissions(address bot, address creditManager) external view returns (uint192); 76 | 77 | function approvedCreditManager(address creditManager) external view returns (bool); 78 | 79 | function setBotForbiddenStatus(address bot, bool forbidden) external; 80 | 81 | function setBotSpecialPermissions(address bot, address creditManager, uint192 permissions) external; 82 | 83 | function setCreditManagerApprovedStatus(address creditManager, bool approved) external; 84 | } 85 | -------------------------------------------------------------------------------- /contracts/interfaces/IControllerTimelockV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | struct QueuedTransactionData { 9 | bool queued; 10 | address executor; 11 | address target; 12 | uint40 eta; 13 | string signature; 14 | bytes data; 15 | uint256 sanityCheckValue; 16 | bytes sanityCheckCallData; 17 | } 18 | 19 | interface IControllerTimelockV3Events { 20 | /// @notice Emitted when the veto admin of the controller is updated 21 | event SetVetoAdmin(address indexed newAdmin); 22 | 23 | /// @notice Emitted when a transaction is queued 24 | event QueueTransaction( 25 | bytes32 indexed txHash, address indexed executor, address target, string signature, bytes data, uint40 eta 26 | ); 27 | 28 | /// @notice Emitted when a transaction is executed 29 | event ExecuteTransaction(bytes32 indexed txHash); 30 | 31 | /// @notice Emitted when a transaction is cancelled 32 | event CancelTransaction(bytes32 indexed txHash); 33 | } 34 | 35 | /// @title Controller timelock V3 interface 36 | interface IControllerTimelockV3 is IControllerTimelockV3Events, IVersion { 37 | // -------- // 38 | // QUEUEING // 39 | // -------- // 40 | 41 | function setExpirationDate(address creditManager, uint40 expirationDate) external; 42 | 43 | function setMaxDebtPerBlockMultiplier(address creditManager, uint8 multiplier) external; 44 | 45 | function setMinDebtLimit(address creditManager, uint128 minDebt) external; 46 | 47 | function setMaxDebtLimit(address creditManager, uint128 maxDebt) external; 48 | 49 | function setCreditManagerDebtLimit(address creditManager, uint256 debtLimit) external; 50 | 51 | function rampLiquidationThreshold( 52 | address creditManager, 53 | address token, 54 | uint16 liquidationThresholdFinal, 55 | uint40 rampStart, 56 | uint24 rampDuration 57 | ) external; 58 | 59 | function forbidAdapter(address creditManager, address adapter) external; 60 | 61 | function setTotalDebtLimit(address pool, uint256 newLimit) external; 62 | 63 | function setTokenLimit(address pool, address token, uint96 limit) external; 64 | 65 | function setTokenQuotaIncreaseFee(address pool, address token, uint16 quotaIncreaseFee) external; 66 | 67 | function setMinQuotaRate(address pool, address token, uint16 rate) external; 68 | 69 | function setMaxQuotaRate(address pool, address token, uint16 rate) external; 70 | 71 | function setWithdrawFee(address pool, uint256 newFee) external; 72 | 73 | function setLPPriceFeedLimiter(address priceFeed, uint256 lowerBound) external; 74 | 75 | function setReservePriceFeedStatus(address priceOracle, address token, bool active) external; 76 | 77 | function forbidBoundsUpdate(address priceFeed) external; 78 | 79 | // --------- // 80 | // EXECUTION // 81 | // --------- // 82 | 83 | function GRACE_PERIOD() external view returns (uint256); 84 | 85 | function queuedTransactions(bytes32 txHash) 86 | external 87 | view 88 | returns ( 89 | bool queued, 90 | address executor, 91 | address target, 92 | uint40 eta, 93 | string memory signature, 94 | bytes memory data, 95 | uint256 sanityCheckValue, 96 | bytes memory sanityCheckCallData 97 | ); 98 | 99 | function executeTransaction(bytes32 txHash) external; 100 | 101 | function cancelTransaction(bytes32 txHash) external; 102 | 103 | // ------------- // 104 | // CONFIGURATION // 105 | // ------------- // 106 | 107 | function vetoAdmin() external view returns (address); 108 | 109 | function setVetoAdmin(address newAdmin) external; 110 | } 111 | -------------------------------------------------------------------------------- /contracts/interfaces/ICreditAccountV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | /// @title Credit account base interface 9 | /// @notice Functions shared accross newer and older versions 10 | interface ICreditAccountBase is IVersion { 11 | function creditManager() external view returns (address); 12 | function safeTransfer(address token, address to, uint256 amount) external; 13 | function execute(address target, bytes calldata data) external returns (bytes memory result); 14 | } 15 | 16 | /// @title Credit account V3 interface 17 | interface ICreditAccountV3 is ICreditAccountBase { 18 | function factory() external view returns (address); 19 | 20 | function creditManager() external view override returns (address); 21 | 22 | function safeTransfer(address token, address to, uint256 amount) external override; 23 | 24 | function execute(address target, bytes calldata data) external override returns (bytes memory result); 25 | 26 | function rescue(address target, bytes calldata data) external; 27 | } 28 | -------------------------------------------------------------------------------- /contracts/interfaces/IGaugeV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | import {IVotingContractV3} from "./IVotingContractV3.sol"; 9 | 10 | struct QuotaRateParams { 11 | uint16 minRate; 12 | uint16 maxRate; 13 | uint96 totalVotesLpSide; 14 | uint96 totalVotesCaSide; 15 | } 16 | 17 | struct UserVotes { 18 | uint96 votesLpSide; 19 | uint96 votesCaSide; 20 | } 21 | 22 | interface IGaugeV3Events { 23 | /// @notice Emitted when epoch is updated 24 | event UpdateEpoch(uint16 epochNow); 25 | 26 | /// @notice Emitted when a user submits a vote 27 | event Vote(address indexed user, address indexed token, uint96 votes, bool lpSide); 28 | 29 | /// @notice Emitted when a user removes a vote 30 | event Unvote(address indexed user, address indexed token, uint96 votes, bool lpSide); 31 | 32 | /// @notice Emitted when a new quota token is added in the PoolQuotaKeeper 33 | event AddQuotaToken(address indexed token, uint16 minRate, uint16 maxRate); 34 | 35 | /// @notice Emitted when quota interest rate parameters are changed 36 | event SetQuotaTokenParams(address indexed token, uint16 minRate, uint16 maxRate); 37 | 38 | /// @notice Emitted when the frozen epoch status changes 39 | event SetFrozenEpoch(bool status); 40 | } 41 | 42 | /// @title Gauge V3 interface 43 | interface IGaugeV3 is IGaugeV3Events, IVotingContractV3, IVersion { 44 | function pool() external view returns (address); 45 | 46 | function voter() external view returns (address); 47 | 48 | function updateEpoch() external; 49 | 50 | function epochLastUpdate() external view returns (uint16); 51 | 52 | function getRates(address[] calldata tokens) external view returns (uint16[] memory rates); 53 | 54 | function userTokenVotes(address user, address token) 55 | external 56 | view 57 | returns (uint96 votesLpSide, uint96 votesCaSide); 58 | 59 | function quotaRateParams(address token) 60 | external 61 | view 62 | returns (uint16 minRate, uint16 maxRate, uint96 totalVotesLpSide, uint96 totalVotesCaSide); 63 | 64 | // ------------- // 65 | // CONFIGURATION // 66 | // ------------- // 67 | 68 | function epochFrozen() external view returns (bool); 69 | 70 | function setFrozenEpoch(bool status) external; 71 | 72 | function isTokenAdded(address token) external view returns (bool); 73 | 74 | function addQuotaToken(address token, uint16 minRate, uint16 maxRate) external; 75 | 76 | function changeQuotaMinRate(address token, uint16 minRate) external; 77 | 78 | function changeQuotaMaxRate(address token, uint16 maxRate) external; 79 | } 80 | -------------------------------------------------------------------------------- /contracts/interfaces/IGearStakingV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | uint256 constant EPOCH_LENGTH = 7 days; 9 | 10 | uint256 constant EPOCHS_TO_WITHDRAW = 4; 11 | 12 | /// @notice Voting contract status 13 | /// * NOT_ALLOWED - cannot vote or unvote 14 | /// * ALLOWED - can both vote and unvote 15 | /// * UNVOTE_ONLY - can only unvote 16 | enum VotingContractStatus { 17 | NOT_ALLOWED, 18 | ALLOWED, 19 | UNVOTE_ONLY 20 | } 21 | 22 | struct UserVoteLockData { 23 | uint96 totalStaked; 24 | uint96 available; 25 | } 26 | 27 | struct WithdrawalData { 28 | uint96[EPOCHS_TO_WITHDRAW] withdrawalsPerEpoch; 29 | uint16 epochLastUpdate; 30 | } 31 | 32 | /// @notice Multi vote 33 | /// @param votingContract Contract to submit a vote to 34 | /// @param voteAmount Amount of staked GEAR to vote with 35 | /// @param isIncrease Whether to add or remove votes 36 | /// @param extraData Data to pass to the voting contract 37 | struct MultiVote { 38 | address votingContract; 39 | uint96 voteAmount; 40 | bool isIncrease; 41 | bytes extraData; 42 | } 43 | 44 | interface IGearStakingV3Events { 45 | /// @notice Emitted when the user deposits GEAR into staked GEAR 46 | event DepositGear(address indexed user, uint256 amount); 47 | 48 | /// @notice Emitted Emits when the user migrates GEAR into a successor contract 49 | event MigrateGear(address indexed user, address indexed successor, uint256 amount); 50 | 51 | /// @notice Emitted Emits when the user starts a withdrawal from staked GEAR 52 | event ScheduleGearWithdrawal(address indexed user, uint256 amount); 53 | 54 | /// @notice Emitted Emits when the user claims a mature withdrawal from staked GEAR 55 | event ClaimGearWithdrawal(address indexed user, address to, uint256 amount); 56 | 57 | /// @notice Emitted Emits when the configurator adds or removes a voting contract 58 | event SetVotingContractStatus(address indexed votingContract, VotingContractStatus status); 59 | 60 | /// @notice Emitted Emits when the new successor contract is set 61 | event SetSuccessor(address indexed successor); 62 | 63 | /// @notice Emitted Emits when the new migrator contract is set 64 | event SetMigrator(address indexed migrator); 65 | } 66 | 67 | /// @title Gear staking V3 interface 68 | interface IGearStakingV3 is IGearStakingV3Events, IVersion { 69 | function gear() external view returns (address); 70 | 71 | function firstEpochTimestamp() external view returns (uint256); 72 | 73 | function getCurrentEpoch() external view returns (uint16); 74 | 75 | function balanceOf(address user) external view returns (uint256); 76 | 77 | function availableBalance(address user) external view returns (uint256); 78 | 79 | function getWithdrawableAmounts(address user) 80 | external 81 | view 82 | returns (uint256 withdrawableNow, uint256[EPOCHS_TO_WITHDRAW] memory withdrawableInEpochs); 83 | 84 | function deposit(uint96 amount, MultiVote[] calldata votes) external; 85 | 86 | function depositWithPermit( 87 | uint96 amount, 88 | MultiVote[] calldata votes, 89 | uint256 deadline, 90 | uint8 v, 91 | bytes32 r, 92 | bytes32 s 93 | ) external; 94 | 95 | function multivote(MultiVote[] calldata votes) external; 96 | 97 | function withdraw(uint96 amount, address to, MultiVote[] calldata votes) external; 98 | 99 | function claimWithdrawals(address to) external; 100 | 101 | function migrate(uint96 amount, MultiVote[] calldata votesBefore, MultiVote[] calldata votesAfter) external; 102 | 103 | function depositOnMigration(uint96 amount, address onBehalfOf, MultiVote[] calldata votes) external; 104 | 105 | // ------------- // 106 | // CONFIGURATION // 107 | // ------------- // 108 | 109 | function allowedVotingContract(address) external view returns (VotingContractStatus); 110 | 111 | function setVotingContractStatus(address votingContract, VotingContractStatus status) external; 112 | 113 | function successor() external view returns (address); 114 | 115 | function setSuccessor(address newSuccessor) external; 116 | 117 | function migrator() external view returns (address); 118 | 119 | function setMigrator(address newMigrator) external; 120 | } 121 | -------------------------------------------------------------------------------- /contracts/interfaces/ILinearInterestRateModelV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | /// @title Linear interest rate model V3 interface 9 | interface ILinearInterestRateModelV3 is IVersion { 10 | function calcBorrowRate(uint256 expectedLiquidity, uint256 availableLiquidity, bool checkOptimalBorrowing) 11 | external 12 | view 13 | returns (uint256); 14 | 15 | function availableToBorrow(uint256 expectedLiquidity, uint256 availableLiquidity) external view returns (uint256); 16 | 17 | function isBorrowingMoreU2Forbidden() external view returns (bool); 18 | 19 | function getModelParameters() 20 | external 21 | view 22 | returns (uint16 U_1, uint16 U_2, uint16 R_base, uint16 R_slope1, uint16 R_slope2, uint16 R_slope3); 23 | } 24 | -------------------------------------------------------------------------------- /contracts/interfaces/IPoolQuotaKeeperV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 7 | 8 | struct TokenQuotaParams { 9 | uint16 rate; 10 | uint192 cumulativeIndexLU; 11 | uint16 quotaIncreaseFee; 12 | uint96 totalQuoted; 13 | uint96 limit; 14 | } 15 | 16 | struct AccountQuota { 17 | uint96 quota; 18 | uint192 cumulativeIndexLU; 19 | } 20 | 21 | interface IPoolQuotaKeeperV3Events { 22 | /// @notice Emitted when account's quota for a token is updated 23 | event UpdateQuota(address indexed creditAccount, address indexed token, int96 quotaChange); 24 | 25 | /// @notice Emitted when token's quota rate is updated 26 | event UpdateTokenQuotaRate(address indexed token, uint16 rate); 27 | 28 | /// @notice Emitted when the gauge is updated 29 | event SetGauge(address indexed newGauge); 30 | 31 | /// @notice Emitted when a new credit manager is allowed 32 | event AddCreditManager(address indexed creditManager); 33 | 34 | /// @notice Emitted when a new token is added as quoted 35 | event AddQuotaToken(address indexed token); 36 | 37 | /// @notice Emitted when a new total quota limit is set for a token 38 | event SetTokenLimit(address indexed token, uint96 limit); 39 | 40 | /// @notice Emitted when a new one-time quota increase fee is set for a token 41 | event SetQuotaIncreaseFee(address indexed token, uint16 fee); 42 | } 43 | 44 | /// @title Pool quota keeper V3 interface 45 | interface IPoolQuotaKeeperV3 is IPoolQuotaKeeperV3Events, IVersion { 46 | function pool() external view returns (address); 47 | 48 | function underlying() external view returns (address); 49 | 50 | // ----------------- // 51 | // QUOTAS MANAGEMENT // 52 | // ----------------- // 53 | 54 | function updateQuota(address creditAccount, address token, int96 requestedChange, uint96 minQuota, uint96 maxQuota) 55 | external 56 | returns (uint128 caQuotaInterestChange, uint128 fees, bool enableToken, bool disableToken); 57 | 58 | function removeQuotas(address creditAccount, address[] calldata tokens, bool setLimitsToZero) external; 59 | 60 | function accrueQuotaInterest(address creditAccount, address[] calldata tokens) external; 61 | 62 | function getQuotaRate(address) external view returns (uint16); 63 | 64 | function cumulativeIndex(address token) external view returns (uint192); 65 | 66 | function isQuotedToken(address token) external view returns (bool); 67 | 68 | function getQuota(address creditAccount, address token) 69 | external 70 | view 71 | returns (uint96 quota, uint192 cumulativeIndexLU); 72 | 73 | function getTokenQuotaParams(address token) 74 | external 75 | view 76 | returns ( 77 | uint16 rate, 78 | uint192 cumulativeIndexLU, 79 | uint16 quotaIncreaseFee, 80 | uint96 totalQuoted, 81 | uint96 limit, 82 | bool isActive 83 | ); 84 | 85 | function getQuotaAndOutstandingInterest(address creditAccount, address token) 86 | external 87 | view 88 | returns (uint96 quoted, uint128 outstandingInterest); 89 | 90 | function poolQuotaRevenue() external view returns (uint256); 91 | 92 | function lastQuotaRateUpdate() external view returns (uint40); 93 | 94 | // ------------- // 95 | // CONFIGURATION // 96 | // ------------- // 97 | 98 | function gauge() external view returns (address); 99 | 100 | function setGauge(address _gauge) external; 101 | 102 | function creditManagers() external view returns (address[] memory); 103 | 104 | function addCreditManager(address _creditManager) external; 105 | 106 | function quotedTokens() external view returns (address[] memory); 107 | 108 | function addQuotaToken(address token) external; 109 | 110 | function updateRates() external; 111 | 112 | function setTokenLimit(address token, uint96 limit) external; 113 | 114 | function setTokenQuotaIncreaseFee(address token, uint16 fee) external; 115 | } 116 | -------------------------------------------------------------------------------- /contracts/interfaces/IPoolV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | pragma abicoder v1; 6 | 7 | import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; 8 | import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; 9 | import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; 10 | 11 | interface IPoolV3Events { 12 | /// @notice Emitted when depositing liquidity with referral code 13 | event Refer(address indexed onBehalfOf, uint256 indexed referralCode, uint256 amount); 14 | 15 | /// @notice Emitted when credit account borrows funds from the pool 16 | event Borrow(address indexed creditManager, address indexed creditAccount, uint256 amount); 17 | 18 | /// @notice Emitted when credit account's debt is repaid to the pool 19 | event Repay(address indexed creditManager, uint256 borrowedAmount, uint256 profit, uint256 loss); 20 | 21 | /// @notice Emitted when incurred loss can't be fully covered by burning treasury's shares 22 | event IncurUncoveredLoss(address indexed creditManager, uint256 loss); 23 | 24 | /// @notice Emitted when new interest rate model contract is set 25 | event SetInterestRateModel(address indexed newInterestRateModel); 26 | 27 | /// @notice Emitted when new pool quota keeper contract is set 28 | event SetPoolQuotaKeeper(address indexed newPoolQuotaKeeper); 29 | 30 | /// @notice Emitted when new total debt limit is set 31 | event SetTotalDebtLimit(uint256 limit); 32 | 33 | /// @notice Emitted when new credit manager is connected to the pool 34 | event AddCreditManager(address indexed creditManager); 35 | 36 | /// @notice Emitted when new debt limit is set for a credit manager 37 | event SetCreditManagerDebtLimit(address indexed creditManager, uint256 newLimit); 38 | 39 | /// @notice Emitted when new withdrawal fee is set 40 | event SetWithdrawFee(uint256 fee); 41 | } 42 | 43 | /// @title Pool V3 interface 44 | interface IPoolV3 is IVersion, IPoolV3Events, IERC4626, IERC20Permit { 45 | function addressProvider() external view returns (address); 46 | 47 | function underlyingToken() external view returns (address); 48 | 49 | function treasury() external view returns (address); 50 | 51 | function withdrawFee() external view returns (uint16); 52 | 53 | function creditManagers() external view returns (address[] memory); 54 | 55 | function availableLiquidity() external view returns (uint256); 56 | 57 | function expectedLiquidity() external view returns (uint256); 58 | 59 | function expectedLiquidityLU() external view returns (uint256); 60 | 61 | // ---------------- // 62 | // ERC-4626 LENDING // 63 | // ---------------- // 64 | 65 | function depositWithReferral(uint256 assets, address receiver, uint256 referralCode) 66 | external 67 | returns (uint256 shares); 68 | 69 | function mintWithReferral(uint256 shares, address receiver, uint256 referralCode) 70 | external 71 | returns (uint256 assets); 72 | 73 | // --------- // 74 | // BORROWING // 75 | // --------- // 76 | 77 | function totalBorrowed() external view returns (uint256); 78 | 79 | function totalDebtLimit() external view returns (uint256); 80 | 81 | function creditManagerBorrowed(address creditManager) external view returns (uint256); 82 | 83 | function creditManagerDebtLimit(address creditManager) external view returns (uint256); 84 | 85 | function creditManagerBorrowable(address creditManager) external view returns (uint256 borrowable); 86 | 87 | function lendCreditAccount(uint256 borrowedAmount, address creditAccount) external; 88 | 89 | function repayCreditAccount(uint256 repaidAmount, uint256 profit, uint256 loss) external; 90 | 91 | // ------------- // 92 | // INTEREST RATE // 93 | // ------------- // 94 | 95 | function interestRateModel() external view returns (address); 96 | 97 | function baseInterestRate() external view returns (uint256); 98 | 99 | function supplyRate() external view returns (uint256); 100 | 101 | function baseInterestIndex() external view returns (uint256); 102 | 103 | function baseInterestIndexLU() external view returns (uint256); 104 | 105 | function lastBaseInterestUpdate() external view returns (uint40); 106 | 107 | // ------ // 108 | // QUOTAS // 109 | // ------ // 110 | 111 | function poolQuotaKeeper() external view returns (address); 112 | 113 | function quotaRevenue() external view returns (uint256); 114 | 115 | function lastQuotaRevenueUpdate() external view returns (uint40); 116 | 117 | function updateQuotaRevenue(int256 quotaRevenueDelta) external; 118 | 119 | function setQuotaRevenue(uint256 newQuotaRevenue) external; 120 | 121 | // ------------- // 122 | // CONFIGURATION // 123 | // ------------- // 124 | 125 | function setInterestRateModel(address newInterestRateModel) external; 126 | 127 | function setPoolQuotaKeeper(address newPoolQuotaKeeper) external; 128 | 129 | function setTotalDebtLimit(uint256 newLimit) external; 130 | 131 | function setCreditManagerDebtLimit(address creditManager, uint256 newLimit) external; 132 | 133 | function setWithdrawFee(uint256 newWithdrawFee) external; 134 | } 135 | -------------------------------------------------------------------------------- /contracts/interfaces/IPriceOracleV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IPriceOracleBase} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceOracleBase.sol"; 7 | 8 | struct PriceFeedParams { 9 | address priceFeed; 10 | uint32 stalenessPeriod; 11 | bool skipCheck; 12 | uint8 decimals; 13 | bool useReserve; 14 | bool trusted; 15 | } 16 | 17 | interface IPriceOracleV3Events { 18 | /// @notice Emitted when new price feed is set for token 19 | event SetPriceFeed( 20 | address indexed token, address indexed priceFeed, uint32 stalenessPeriod, bool skipCheck, bool trusted 21 | ); 22 | 23 | /// @notice Emitted when new reserve price feed is set for token 24 | event SetReservePriceFeed(address indexed token, address indexed priceFeed, uint32 stalenessPeriod, bool skipCheck); 25 | 26 | /// @notice Emitted when new reserve price feed status is set for a token 27 | event SetReservePriceFeedStatus(address indexed token, bool active); 28 | } 29 | 30 | /// @title Price oracle V3 interface 31 | interface IPriceOracleV3 is IPriceOracleBase, IPriceOracleV3Events { 32 | function getPriceSafe(address token) external view returns (uint256); 33 | 34 | function getPriceRaw(address token, bool reserve) external view returns (uint256); 35 | 36 | function priceFeedsRaw(address token, bool reserve) external view returns (address); 37 | 38 | function priceFeedParams(address token) 39 | external 40 | view 41 | returns (address priceFeed, uint32 stalenessPeriod, bool skipCheck, uint8 decimals, bool trusted); 42 | 43 | function safeConvertToUSD(uint256 amount, address token) external view returns (uint256); 44 | 45 | // ------------- // 46 | // CONFIGURATION // 47 | // ------------- // 48 | 49 | function setPriceFeed(address token, address priceFeed, uint32 stalenessPeriod, bool trusted) external; 50 | 51 | function setReservePriceFeed(address token, address priceFeed, uint32 stalenessPeriod) external; 52 | 53 | function setReservePriceFeedStatus(address token, bool active) external; 54 | } 55 | -------------------------------------------------------------------------------- /contracts/interfaces/IVotingContractV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | interface IVotingContractV3 { 7 | function vote(address user, uint96 votes, bytes calldata extraData) external; 8 | function unvote(address user, uint96 votes, bytes calldata extraData) external; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/LICENSE: -------------------------------------------------------------------------------- 1 | (c) Gearbox Foundation, 2023. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /contracts/interfaces/external/IUSDT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | interface IUSDT { 7 | function basisPointsRate() external view returns (uint256); 8 | function maximumFee() external view returns (uint256); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/libraries/BitMask.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IncorrectParameterException} from "../interfaces/IExceptions.sol"; 7 | 8 | uint256 constant UNDERLYING_TOKEN_MASK = 1; 9 | 10 | /// @title Bit mask library 11 | /// @notice Implements functions that manipulate bit masks 12 | /// Bit masks are utilized extensively by Gearbox to efficiently store token sets (enabled tokens on accounts 13 | /// or forbidden tokens) and check for set inclusion. A mask is a uint256 number that has its i-th bit set to 14 | /// 1 if i-th item is included into the set. For example, each token has a mask equal to 2**i, so set inclusion 15 | /// can be checked by checking tokenMask & setMask != 0. 16 | library BitMask { 17 | /// @dev Calculates an index of an item based on its mask (using a binary search) 18 | /// @dev The input should always have only 1 bit set, otherwise the result may be unpredictable 19 | function calcIndex(uint256 mask) internal pure returns (uint8 index) { 20 | if (mask == 0) revert IncorrectParameterException(); // U:[BM-1] 21 | uint16 lb = 0; // U:[BM-2] 22 | uint16 ub = 256; // U:[BM-2] 23 | uint16 mid = 128; // U:[BM-2] 24 | 25 | unchecked { 26 | while (true) { 27 | uint256 newMask = 1 << mid; 28 | if (newMask & mask != 0) return uint8(mid); // U:[BM-2] 29 | 30 | if (newMask > mask) ub = mid; // U:[BM-2] 31 | 32 | else lb = mid; // U:[BM-2] 33 | mid = (lb + ub) >> 1; // U:[BM-2] 34 | } 35 | } 36 | } 37 | 38 | /// @dev Calculates the number of `1` bits 39 | /// @param enabledTokensMask Bit mask to compute the number of `1` bits in 40 | function calcEnabledTokens(uint256 enabledTokensMask) internal pure returns (uint256 totalTokensEnabled) { 41 | unchecked { 42 | while (enabledTokensMask > 0) { 43 | enabledTokensMask &= enabledTokensMask - 1; // U:[BM-3] 44 | ++totalTokensEnabled; // U:[BM-3] 45 | } 46 | } 47 | } 48 | 49 | /// @dev Enables bits from the second mask in the first mask 50 | /// @param enabledTokenMask The initial mask 51 | /// @param bitsToEnable Mask of bits to enable 52 | function enable(uint256 enabledTokenMask, uint256 bitsToEnable) internal pure returns (uint256) { 53 | return enabledTokenMask | bitsToEnable; // U:[BM-4] 54 | } 55 | 56 | /// @dev Disables bits from the second mask in the first mask 57 | /// @param enabledTokenMask The initial mask 58 | /// @param bitsToDisable Mask of bits to disable 59 | function disable(uint256 enabledTokenMask, uint256 bitsToDisable) internal pure returns (uint256) { 60 | return enabledTokenMask & ~bitsToDisable; // U:[BM-4] 61 | } 62 | 63 | /// @dev Computes a new mask with sets of new enabled and disabled bits 64 | /// @dev bitsToEnable and bitsToDisable are applied sequentially to original mask 65 | /// @param enabledTokensMask The initial mask 66 | /// @param bitsToEnable Mask with bits to enable 67 | /// @param bitsToDisable Mask with bits to disable 68 | function enableDisable(uint256 enabledTokensMask, uint256 bitsToEnable, uint256 bitsToDisable) 69 | internal 70 | pure 71 | returns (uint256) 72 | { 73 | return (enabledTokensMask | bitsToEnable) & (~bitsToDisable); // U:[BM-5] 74 | } 75 | 76 | /// @dev Enables bits from the second mask in the first mask, skipping specified bits 77 | /// @param enabledTokenMask The initial mask 78 | /// @param bitsToEnable Mask with bits to enable 79 | /// @param invertedSkipMask An inversion of mask of immutable bits 80 | function enable(uint256 enabledTokenMask, uint256 bitsToEnable, uint256 invertedSkipMask) 81 | internal 82 | pure 83 | returns (uint256) 84 | { 85 | return enabledTokenMask | (bitsToEnable & invertedSkipMask); // U:[BM-6] 86 | } 87 | 88 | /// @dev Disables bits from the second mask in the first mask, skipping specified bits 89 | /// @param enabledTokenMask The initial mask 90 | /// @param bitsToDisable Mask with bits to disable 91 | /// @param invertedSkipMask An inversion of mask of immutable bits 92 | function disable(uint256 enabledTokenMask, uint256 bitsToDisable, uint256 invertedSkipMask) 93 | internal 94 | pure 95 | returns (uint256) 96 | { 97 | return enabledTokenMask & (~(bitsToDisable & invertedSkipMask)); // U:[BM-6] 98 | } 99 | 100 | /// @dev Computes a new mask with sets of new enabled and disabled bits, skipping some bits 101 | /// @dev bitsToEnable and bitsToDisable are applied sequentially to original mask. Skipmask is applied in both cases. 102 | /// @param enabledTokensMask The initial mask 103 | /// @param bitsToEnable Mask with bits to enable 104 | /// @param bitsToDisable Mask with bits to disable 105 | /// @param invertedSkipMask An inversion of mask of immutable bits 106 | function enableDisable( 107 | uint256 enabledTokensMask, 108 | uint256 bitsToEnable, 109 | uint256 bitsToDisable, 110 | uint256 invertedSkipMask 111 | ) internal pure returns (uint256) { 112 | return (enabledTokensMask | (bitsToEnable & invertedSkipMask)) & (~(bitsToDisable & invertedSkipMask)); // U:[BM-7] 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /contracts/libraries/CreditAccountHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import {SafeERC20} from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; 8 | 9 | import {ICreditAccountBase} from "../interfaces/ICreditAccountV3.sol"; 10 | import {AllowanceFailedException} from "../interfaces/IExceptions.sol"; 11 | 12 | /// @title Credit account helper library 13 | /// @notice Implements functions that help manage assets on a credit account 14 | library CreditAccountHelper { 15 | using SafeERC20 for IERC20; 16 | 17 | /// @dev Requests a credit account to do an approval with support for various kinds of tokens 18 | /// @dev Supports up-to-spec ERC20 tokens, ERC20 tokens that revert on transfer failure, 19 | /// tokens that require 0 allowance before changing to non-zero value, and non-ERC20 tokens 20 | /// that do not return a `success` value 21 | /// @param creditAccount Credit account to approve tokens from 22 | /// @param token Token to approve 23 | /// @param spender Address to approve to 24 | /// @param amount Amount to approve 25 | function safeApprove(ICreditAccountBase creditAccount, address token, address spender, uint256 amount) internal { 26 | if (!_approve(creditAccount, token, spender, amount, false)) { 27 | _approve(creditAccount, token, spender, 0, true); //U:[CAH-1,2] 28 | _approve(creditAccount, token, spender, amount, true); // U:[CAH-1,2] 29 | } 30 | } 31 | 32 | /// @dev Internal function used to approve tokens from a credit account to a third-party contrat. 33 | /// Uses credit account's `execute` to properly handle both ERC20-compliant and on-compliant 34 | /// (no returned value from "approve") tokens 35 | /// @param creditAccount Credit account to approve tokens from 36 | /// @param token Token to approve 37 | /// @param spender Address to approve to 38 | /// @param amount Amount to approve 39 | /// @param revertIfFailed Whether to revert or return `false` on receiving `false` or an error from `approve` 40 | function _approve( 41 | ICreditAccountBase creditAccount, 42 | address token, 43 | address spender, 44 | uint256 amount, 45 | bool revertIfFailed 46 | ) private returns (bool) { 47 | // Makes a low-level call to approve from the credit account and parses the value. 48 | // If nothing or true was returned, assumes that the call succeeded. 49 | try creditAccount.execute(token, abi.encodeCall(IERC20.approve, (spender, amount))) returns ( 50 | bytes memory result 51 | ) { 52 | if (result.length == 0 || abi.decode(result, (bool))) return true; 53 | } catch {} 54 | 55 | // On the first try, failure is allowed to handle tokens that prohibit changing allowance from non-zero value. 56 | // After that, failure results in a revert. 57 | if (revertIfFailed) revert AllowanceFailedException(); 58 | return false; 59 | } 60 | 61 | /// @dev Performs a token transfer from a credit account, accounting for non-ERC20 tokens 62 | /// @param creditAccount Credit account to send tokens from 63 | /// @param token Token to send 64 | /// @param to Address to send to 65 | /// @param amount Amount to send 66 | function transfer(ICreditAccountBase creditAccount, address token, address to, uint256 amount) internal { 67 | creditAccount.safeTransfer(token, to, amount); 68 | } 69 | 70 | /// @dev Performs a token transfer from a Credit account and returns the actual amount of token transferred 71 | /// @dev For some tokens, such as stETH or USDT (with fee enabled), the amount that arrives to the recipient can 72 | /// differ from the sent amount. This ensures that calculations are correct in such cases. 73 | /// @param creditAccount Credit account to send tokens from 74 | /// @param token Token to send 75 | /// @param to Address to send to 76 | /// @param amount Amount to send 77 | /// @return delivered The actual amount that the `to` address received 78 | function transferDeliveredBalanceControl( 79 | ICreditAccountBase creditAccount, 80 | address token, 81 | address to, 82 | uint256 amount 83 | ) internal returns (uint256 delivered) { 84 | uint256 balanceBefore = IERC20(token).safeBalanceOf({account: to}); 85 | transfer(creditAccount, token, to, amount); 86 | delivered = IERC20(token).safeBalanceOf({account: to}) - balanceBefore; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /contracts/libraries/QuotasLogic.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; 7 | import {RAY, SECONDS_PER_YEAR, PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol"; 8 | 9 | uint192 constant RAY_DIVIDED_BY_PERCENTAGE = uint192(RAY / PERCENTAGE_FACTOR); 10 | 11 | /// @title Quotas logic library 12 | library QuotasLogic { 13 | using SafeCast for uint256; 14 | 15 | /// @dev Computes the new interest index value, given the previous value, the interest rate, and time delta 16 | /// @dev Unlike pool's base interest, interest on quotas is not compounding, so additive index is used 17 | function cumulativeIndexSince(uint192 cumulativeIndexLU, uint16 rate, uint256 lastQuotaRateUpdate) 18 | internal 19 | view 20 | returns (uint192) 21 | { 22 | return uint192( 23 | uint256(cumulativeIndexLU) 24 | + RAY_DIVIDED_BY_PERCENTAGE * (block.timestamp - lastQuotaRateUpdate) * rate / SECONDS_PER_YEAR 25 | ); // U:[QL-1] 26 | } 27 | 28 | /// @dev Computes interest accrued on the quota since the last update 29 | function calcAccruedQuotaInterest(uint96 quoted, uint192 cumulativeIndexNow, uint192 cumulativeIndexLU) 30 | internal 31 | pure 32 | returns (uint128) 33 | { 34 | // `quoted` is `uint96`, and `cumulativeIndex / RAY` won't reach `2 ** 32` in reasonable time, so casting is safe 35 | return uint128(uint256(quoted) * (cumulativeIndexNow - cumulativeIndexLU) / RAY); // U:[QL-2] 36 | } 37 | 38 | /// @dev Computes the pool quota revenue change given the current rate and the quota change 39 | function calcQuotaRevenueChange(uint16 rate, int256 change) internal pure returns (int256) { 40 | return change * int256(uint256(rate)) / int16(PERCENTAGE_FACTOR); 41 | } 42 | 43 | /// @dev Upper-bounds requested quota increase such that the resulting total quota doesn't exceed the limit 44 | function calcActualQuotaChange(uint96 totalQuoted, uint96 limit, int96 requestedChange) 45 | internal 46 | pure 47 | returns (int96 quotaChange) 48 | { 49 | if (totalQuoted >= limit) { 50 | return 0; 51 | } 52 | 53 | unchecked { 54 | uint96 maxQuotaCapacity = limit - totalQuoted; 55 | // The function is never called with `requestedChange < 0`, so casting it to `uint96` is safe 56 | // With correct configuration, `limit < type(int96).max`, so casting `maxQuotaCapacity` to `int96` is safe 57 | return uint96(requestedChange) > maxQuotaCapacity ? int96(maxQuotaCapacity) : requestedChange; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/libraries/USDTFees.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; 7 | import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol"; 8 | 9 | /// @title USDT fees library 10 | /// @notice Helps to calculate USDT amounts adjusted for fees 11 | library USDTFees { 12 | /// @dev Computes amount of USDT that should be sent to receive `amount` 13 | function amountUSDTWithFee(uint256 amount, uint256 basisPointsRate, uint256 maximumFee) 14 | internal 15 | pure 16 | returns (uint256) 17 | { 18 | uint256 fee = amount * basisPointsRate / (PERCENTAGE_FACTOR - basisPointsRate); // U:[UTT_01] 19 | fee = Math.min(maximumFee, fee); // U:[UTT_01] 20 | unchecked { 21 | return fee > type(uint256).max - amount ? type(uint256).max : amount + fee; // U:[UTT_01] 22 | } 23 | } 24 | 25 | /// @dev Computes amount of USDT that would be received if `amount` is sent 26 | function amountUSDTMinusFee(uint256 amount, uint256 basisPointsRate, uint256 maximumFee) 27 | internal 28 | pure 29 | returns (uint256) 30 | { 31 | uint256 fee = amount * basisPointsRate / PERCENTAGE_FACTOR; // U:[UTT_01] 32 | fee = Math.min(maximumFee, fee); // U:[UTT_01] 33 | return amount - fee; // U:[UTT_01] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/libraries/UnsafeERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | 8 | /// @title UnsafeERC20 library 9 | library UnsafeERC20 { 10 | /// @dev Same as OpenZeppelin's `safeTransfer`, but, instead of reverting, returns `false` when transfer fails 11 | function unsafeTransfer(IERC20 token, address to, uint256 amount) internal returns (bool success) { 12 | return _unsafeCall(address(token), abi.encodeCall(IERC20.transfer, (to, amount))); // U:[UE-1] 13 | } 14 | 15 | /// @dev Same as OpenZeppelin's `safeTransferFrom`, but, instead of reverting, returns `false` when transfer fails 16 | function unsafeTransferFrom(IERC20 token, address from, address to, uint256 amount) 17 | internal 18 | returns (bool success) 19 | { 20 | return _unsafeCall(address(token), abi.encodeCall(IERC20.transferFrom, (from, to, amount))); // U:[UE-2] 21 | } 22 | 23 | /// @dev Executes call to a function that returns either boolean value indicating call success or nothing 24 | /// Returns `true` if call is successful (didn't revert, didn't return false) or `false` otherwise 25 | function _unsafeCall(address addr, bytes memory data) private returns (bool) { 26 | (bool success, bytes memory returndata) = addr.call(data); 27 | return success && (returndata.length == 0 || abi.decode(returndata, (bool))); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/pool/PoolV3_USDT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {PoolV3} from "./PoolV3.sol"; 7 | import {USDT_Transfer} from "../traits/USDT_Transfer.sol"; 8 | 9 | /// @title Pool V3 USDT 10 | /// @notice Pool variation for USDT underlying with enabled transfer fees 11 | contract PoolV3_USDT is PoolV3, USDT_Transfer { 12 | constructor( 13 | address addressProvider_, 14 | address underlyingToken_, 15 | address interestRateModel_, 16 | uint256 totalDebtLimit_, 17 | string memory name_, 18 | string memory symbol_ 19 | ) 20 | PoolV3(addressProvider_, underlyingToken_, interestRateModel_, totalDebtLimit_, name_, symbol_) 21 | USDT_Transfer(underlyingToken_) 22 | {} 23 | 24 | function _amountWithFee(uint256 amount) internal view override returns (uint256) { 25 | return _amountUSDTWithFee(amount); 26 | } 27 | 28 | function _amountMinusFee(uint256 amount) internal view override returns (uint256) { 29 | return _amountUSDTMinusFee(amount); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/test/config/ConfigManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox. Generalized leverage protocol that allows to take leverage and then use it across other DeFi protocols and platforms in a composable way. 3 | // (c) Gearbox Foundation, 2023 4 | pragma solidity ^0.8.17; 5 | 6 | import {IPoolV3DeployConfig, CollateralTokenHuman} from "../interfaces/ICreditConfig.sol"; 7 | 8 | contract ConfigManager { 9 | error ConfigNotFound(string id); 10 | error ConfigAlreadyExists(string id); 11 | 12 | mapping(bytes32 => IPoolV3DeployConfig) _configs; 13 | 14 | function addDeployConfig(IPoolV3DeployConfig config) internal { 15 | bytes32 key = keccak256(abi.encodePacked(config.id())); 16 | if (isDeployConfigExists(config.id())) revert ConfigAlreadyExists(config.id()); 17 | _configs[key] = config; 18 | } 19 | 20 | function getDeployConfig(string memory id) internal view returns (IPoolV3DeployConfig) { 21 | bytes32 key = keccak256(abi.encodePacked(id)); 22 | 23 | if (!isDeployConfigExists(id)) revert ConfigNotFound(id); 24 | 25 | return _configs[key]; 26 | } 27 | 28 | function isDeployConfigExists(string memory id) internal view returns (bool) { 29 | bytes32 key = keccak256(abi.encodePacked(id)); 30 | IPoolV3DeployConfig config = _configs[key]; 31 | if (address(config) == address(0)) return false; 32 | return key == keccak256(abi.encodePacked(_configs[key].id())); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/test/config/MockCreditConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {TokensTestSuite} from "../suites/TokensTestSuite.sol"; 7 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 8 | import "@gearbox-protocol/sdk-gov/contracts/Tokens.sol"; 9 | import {NetworkDetector} from "@gearbox-protocol/sdk-gov/contracts/NetworkDetector.sol"; 10 | import {Contracts} from "@gearbox-protocol/sdk-gov/contracts/SupportedContracts.sol"; 11 | import "forge-std/console.sol"; 12 | import {CreditManagerOpts} from "../../credit/CreditConfiguratorV3.sol"; 13 | 14 | import { 15 | LinearIRMV3DeployParams, 16 | PoolV3DeployParams, 17 | CreditManagerV3DeployParams, 18 | GaugeRate, 19 | PoolQuotaLimit, 20 | IPoolV3DeployConfig, 21 | CollateralTokenHuman 22 | } from "../interfaces/ICreditConfig.sol"; 23 | import {ITokenTestSuite} from "../interfaces/ITokenTestSuite.sol"; 24 | 25 | import "../lib/constants.sol"; 26 | import "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol"; 27 | 28 | contract MockCreditConfig is Test, IPoolV3DeployConfig { 29 | string public id; 30 | string public symbol; 31 | string public name; 32 | 33 | uint128 public minDebt; 34 | uint128 public maxDebt; 35 | uint256 public chainId; 36 | 37 | uint256 public underlying; 38 | bool public constant supportsQuotas = true; 39 | 40 | PoolV3DeployParams _poolParams; 41 | 42 | LinearIRMV3DeployParams _irm = LinearIRMV3DeployParams({ 43 | U_1: 80_00, 44 | U_2: 90_00, 45 | R_base: 0, 46 | R_slope1: 5, 47 | R_slope2: 20, 48 | R_slope3: 100_00, 49 | _isBorrowingMoreU2Forbidden: true 50 | }); 51 | 52 | GaugeRate[] _gaugeRates; 53 | PoolQuotaLimit[] _quotaLimits; 54 | CreditManagerV3DeployParams[] _creditManagers; 55 | 56 | constructor(TokensTestSuite tokenTestSuite_, uint256 _underlying) { 57 | NetworkDetector nd = new NetworkDetector(); 58 | chainId = nd.chainId(); 59 | 60 | underlying = _underlying; 61 | // underlying = tokenTestSuite_.addressOf(_underlying); 62 | id = string(abi.encodePacked("mock-test-", tokenTestSuite_.symbols(_underlying))); 63 | symbol = string(abi.encodePacked("d", tokenTestSuite_.symbols(_underlying))); 64 | name = string(abi.encodePacked("diesel", tokenTestSuite_.symbols(_underlying))); 65 | 66 | uint256 accountAmount = getAccountAmount(); 67 | 68 | _poolParams = PoolV3DeployParams({withdrawalFee: 0, totalDebtLimit: type(uint256).max}); 69 | 70 | // uint8 decimals = ERC20(tokenTestSuite_.addressOf(_underlying)).decimals(); 71 | 72 | minDebt = uint128(accountAmount / 2); //150_000 * (10 ** decimals)); 73 | maxDebt = uint128(10 * accountAmount); 74 | 75 | CreditManagerV3DeployParams storage cp = _creditManagers.push(); 76 | 77 | cp.minDebt = minDebt; 78 | cp.maxDebt = maxDebt; 79 | cp.feeInterest = DEFAULT_FEE_INTEREST; 80 | cp.feeLiquidation = DEFAULT_FEE_LIQUIDATION; 81 | cp.liquidationPremium = DEFAULT_LIQUIDATION_PREMIUM; 82 | cp.feeLiquidationExpired = DEFAULT_FEE_LIQUIDATION_EXPIRED; 83 | cp.liquidationPremiumExpired = DEFAULT_LIQUIDATION_PREMIUM_EXPIRED; 84 | cp.whitelisted = false; 85 | cp.expirable = false; 86 | cp.skipInit = false; 87 | cp.poolLimit = type(uint256).max; 88 | cp.name = string.concat("Mock Credit Manager ", tokenTestSuite_.symbols(_underlying)); 89 | 90 | pushCollateralToken(_underlying, cp.collateralTokens); 91 | } 92 | 93 | function pushCollateralToken(uint256 _underlying, CollateralTokenHuman[] storage cth) private { 94 | CollateralTokenHuman[8] memory collateralTokenOpts = [ 95 | CollateralTokenHuman({token: TOKEN_USDC, lt: 90_00}), 96 | CollateralTokenHuman({token: TOKEN_USDT, lt: 88_00}), 97 | CollateralTokenHuman({token: TOKEN_DAI, lt: 83_00}), 98 | CollateralTokenHuman({token: TOKEN_WETH, lt: 83_00}), 99 | CollateralTokenHuman({token: TOKEN_LINK, lt: 73_00}), 100 | CollateralTokenHuman({token: TOKEN_CRV, lt: 73_00}), 101 | CollateralTokenHuman({token: TOKEN_CVX, lt: 73_00}), 102 | CollateralTokenHuman({token: TOKEN_STETH, lt: 73_00}) 103 | ]; 104 | 105 | uint256 len = collateralTokenOpts.length; 106 | 107 | for (uint256 i = 0; i < len; i++) { 108 | if (collateralTokenOpts[i].token == _underlying) continue; 109 | cth.push(collateralTokenOpts[i]); 110 | } 111 | } 112 | 113 | function getAccountAmount() public view override returns (uint256) { 114 | return (underlying == TOKEN_DAI) 115 | ? DAI_ACCOUNT_AMOUNT 116 | : (underlying == TOKEN_USDC) ? USDC_ACCOUNT_AMOUNT : WETH_ACCOUNT_AMOUNT; 117 | } 118 | 119 | // GETTERS 120 | 121 | function poolParams() external view override returns (PoolV3DeployParams memory) { 122 | return _poolParams; 123 | } 124 | 125 | function irm() external view override returns (LinearIRMV3DeployParams memory) { 126 | return _irm; 127 | } 128 | 129 | function gaugeRates() external view override returns (GaugeRate[] memory) { 130 | return _gaugeRates; 131 | } 132 | 133 | function quotaLimits() external view override returns (PoolQuotaLimit[] memory) { 134 | return _quotaLimits; 135 | } 136 | 137 | function creditManagers() external view override returns (CreditManagerV3DeployParams[] memory) { 138 | return _creditManagers; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /contracts/test/config/MockTokensData.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import "@gearbox-protocol/sdk-gov/contracts/Tokens.sol"; 7 | import "../lib/constants.sol"; 8 | 9 | struct MockToken { 10 | uint256 index; 11 | string symbol; 12 | uint8 decimals; 13 | int256 price; 14 | uint256 underlying; 15 | } 16 | 17 | library MockTokensData { 18 | function getTokenData() internal pure returns (MockToken[] memory result) { 19 | MockToken[9] memory testTokensData = [ 20 | MockToken({index: TOKEN_DAI, symbol: "DAI", decimals: 18, price: 10 ** 8, underlying: TOKEN_NO_TOKEN}), 21 | MockToken({index: TOKEN_USDC, symbol: "USDC", decimals: 6, price: 10 ** 8, underlying: TOKEN_NO_TOKEN}), 22 | MockToken({ 23 | index: TOKEN_WETH, 24 | symbol: "WETH", 25 | decimals: 18, 26 | price: int256(DAI_WETH_RATE) * 10 ** 8, 27 | underlying: TOKEN_NO_TOKEN 28 | }), 29 | MockToken({index: TOKEN_LINK, symbol: "LINK", decimals: 18, price: 15 * 10 ** 8, underlying: TOKEN_NO_TOKEN}), 30 | MockToken({ 31 | index: TOKEN_USDT, 32 | symbol: "USDT", 33 | decimals: 18, 34 | price: 99 * 10 ** 7, // .99 for test purposes 35 | underlying: TOKEN_NO_TOKEN 36 | }), 37 | MockToken({index: TOKEN_STETH, symbol: "stETH", decimals: 18, price: 3300 * 10 ** 8, underlying: TOKEN_NO_TOKEN}), 38 | MockToken({index: TOKEN_CRV, symbol: "CRV", decimals: 18, price: 14 * 10 ** 7, underlying: TOKEN_NO_TOKEN}), 39 | MockToken({index: TOKEN_CVX, symbol: "CVX", decimals: 18, price: 7 * 10 ** 8, underlying: TOKEN_NO_TOKEN}), 40 | MockToken({ 41 | index: TOKEN_wstETH, 42 | symbol: "wstETH", 43 | decimals: 18, 44 | price: 3300 * 10 ** 8, 45 | underlying: TOKEN_NO_TOKEN 46 | }) 47 | ]; 48 | 49 | uint256 len = testTokensData.length; 50 | result = new MockToken[](len); 51 | 52 | unchecked { 53 | for (uint256 i = 0; i < len; ++i) { 54 | result[i] = testTokensData[i]; 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/test/gas/ArrayAlloc.gas.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {Test} from "forge-std/Test.sol"; 7 | import "forge-std/console.sol"; 8 | 9 | struct Tuple { 10 | address a; 11 | uint256 b; 12 | } 13 | 14 | contract ArrayAllocGasTest is Test { 15 | function test_G_AA_01_array_of_tuples_allocation_gas_usage() public view { 16 | uint256 gas = gasleft(); 17 | _allocate_array_of_tuples(1_000); 18 | console.log("allocate_array_of_tupls: %d", gas - gasleft()); // 384225 19 | } 20 | 21 | function test_G_AA_02_tuple_of_arrays_allocation_gas_usage() public view { 22 | uint256 gas = gasleft(); 23 | _allocate_tuple_of_arrays(1_000); 24 | console.log("allocate_tuple_of_arrays: %d", gas - gasleft()); // 276311 25 | } 26 | 27 | function _allocate_array_of_tuples(uint256 num) internal pure returns (Tuple[] memory result) { 28 | result = new Tuple[](num); 29 | for (uint256 i; i < num; ++i) { 30 | result[i] = Tuple(address(uint160(i)), i); 31 | } 32 | } 33 | 34 | function _allocate_tuple_of_arrays(uint256 num) 35 | internal 36 | pure 37 | returns (address[] memory addrs, uint256[] memory uints) 38 | { 39 | addrs = new address[](num); 40 | uints = new uint256[](num); 41 | 42 | for (uint256 i; i < num; ++i) { 43 | addrs[i] = address(uint160(i)); 44 | uints[i] = i; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/test/gas/pool/Gauge.gas.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | // TESTS 7 | import "../../lib/constants.sol"; 8 | import {IntegrationTestHelper} from "../../helpers/IntegrationTestHelper.sol"; 9 | import {ERC20Mock} from "../../mocks/token/ERC20Mock.sol"; 10 | 11 | import "forge-std/console.sol"; 12 | 13 | // Low limit set for speed test, if you need real value, use 30_000_000 14 | uint256 constant GAS_LIMIT = 20_000; // 30_000_000; 15 | 16 | contract GaugeGasTest is IntegrationTestHelper { 17 | /// 18 | /// 19 | /// TESTS 20 | /// 21 | /// 22 | 23 | /// @dev G:[GA-1]: updateEpoch gas usage 24 | function test_G_GA_01_updateEpoch_gas_usage() public creditTest { 25 | uint256 gasUsed; 26 | uint256 i; 27 | unchecked { 28 | while (gasUsed < GAS_LIMIT) { 29 | ERC20Mock token = new ERC20Mock("TST", "TEST Token", 18); 30 | 31 | vm.startPrank(CONFIGURATOR); 32 | gauge.addQuotaToken(address(token), 500, 500); 33 | poolQuotaKeeper.setTokenLimit(address(token), type(uint96).max); 34 | vm.stopPrank(); 35 | 36 | vm.warp(block.timestamp + 7 days); 37 | 38 | uint256 gasBefore = gasleft(); 39 | gauge.updateEpoch(); 40 | gasUsed = gasBefore - gasleft(); 41 | ++i; 42 | } 43 | } 44 | 45 | console.log("[%d tokens] gas used: %d", i, gasUsed); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/test/helpers/BalanceEngine.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; 8 | 9 | import {Test} from "forge-std/Test.sol"; 10 | 11 | contract BalanceEngine is Test { 12 | function expectBalance(address token, address holder, uint256 expectedBalance) internal { 13 | expectBalance(token, holder, expectedBalance, ""); 14 | } 15 | 16 | function expectBalanceGe(address token, address holder, uint256 minBalance, string memory reason) internal { 17 | uint256 balance = IERC20(token).balanceOf(holder); 18 | 19 | if (balance < minBalance) { 20 | emit log_named_address( 21 | string( 22 | abi.encodePacked(reason, "\nInsufficient ", IERC20Metadata(token).symbol(), " balance on account: ") 23 | ), 24 | holder 25 | ); 26 | } 27 | 28 | assertGe(balance, minBalance); 29 | } 30 | 31 | function expectBalanceLe(address token, address holder, uint256 maxBalance, string memory reason) internal { 32 | uint256 balance = IERC20(token).balanceOf(holder); 33 | 34 | if (balance > maxBalance) { 35 | emit log_named_address( 36 | string( 37 | abi.encodePacked(reason, "\nExceeding ", IERC20Metadata(token).symbol(), " balance on account: ") 38 | ), 39 | holder 40 | ); 41 | } 42 | 43 | assertLe(balance, maxBalance); 44 | } 45 | 46 | function expectBalance(address token, address holder, uint256 expectedBalance, string memory reason) internal { 47 | uint256 balance = IERC20(token).balanceOf(holder); 48 | 49 | if (balance != expectedBalance) { 50 | emit log_named_address( 51 | string( 52 | abi.encodePacked(reason, "\nIncorrect ", IERC20Metadata(token).symbol(), " balance on account: ") 53 | ), 54 | holder 55 | ); 56 | } 57 | 58 | assertEq(balance, expectedBalance); 59 | } 60 | 61 | function expectEthBalance(address account, uint256 expectedBalance) internal { 62 | expectEthBalance(account, expectedBalance, ""); 63 | } 64 | 65 | function expectEthBalance(address account, uint256 expectedBalance, string memory reason) internal { 66 | uint256 balance = account.balance; 67 | if (balance != expectedBalance) { 68 | emit log_named_address(string(abi.encodePacked(reason, "Incorrect ETH balance on account: ")), account); 69 | } 70 | 71 | assertEq(balance, expectedBalance); 72 | } 73 | 74 | function expectAllowance(address token, address owner, address spender, uint256 expectedAllowance) internal { 75 | expectAllowance(token, owner, spender, expectedAllowance, ""); 76 | } 77 | 78 | function expectAllowance( 79 | address token, 80 | address owner, 81 | address spender, 82 | uint256 expectedAllowance, 83 | string memory reason 84 | ) internal { 85 | uint256 allowance = IERC20(token).allowance(owner, spender); 86 | 87 | if (allowance != expectedAllowance) { 88 | emit log_named_address( 89 | string( 90 | abi.encodePacked( 91 | reason, "Incorrect ", IERC20Metadata(token).symbol(), " AllowanceAction on account: " 92 | ) 93 | ), 94 | owner 95 | ); 96 | emit log_named_address(" spender: ", spender); 97 | } 98 | assertEq(allowance, expectedAllowance); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /contracts/test/interfaces/ICreditConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import "@gearbox-protocol/sdk-gov/contracts/Tokens.sol"; 7 | 8 | import {ITokenTestSuite} from "./ITokenTestSuite.sol"; 9 | import {CreditManagerOpts} from "../../interfaces/ICreditConfiguratorV3.sol"; 10 | import {Contracts} from "@gearbox-protocol/sdk-gov/contracts/SupportedContracts.sol"; 11 | 12 | struct PriceFeedConfig { 13 | address token; 14 | address priceFeed; 15 | uint32 stalenessPeriod; 16 | bool trusted; 17 | } 18 | 19 | struct LinearIRMV3DeployParams { 20 | uint16 U_1; 21 | uint16 U_2; 22 | uint16 R_base; 23 | uint16 R_slope1; 24 | uint16 R_slope2; 25 | uint16 R_slope3; 26 | bool _isBorrowingMoreU2Forbidden; 27 | } 28 | 29 | struct PoolV3DeployParams { 30 | uint16 withdrawalFee; 31 | uint256 totalDebtLimit; 32 | } 33 | 34 | struct BalancerPool { 35 | bytes32 poolId; 36 | uint8 status; 37 | } 38 | 39 | struct GenericSwapPair { 40 | Contracts router; 41 | uint256 token0; 42 | uint256 token1; 43 | } 44 | 45 | struct UniswapV3Pair { 46 | Contracts router; 47 | uint256 token0; 48 | uint256 token1; 49 | uint24 fee; 50 | } 51 | 52 | struct VelodromeV2Pool { 53 | uint256 token0; 54 | uint256 token1; 55 | bool stable; 56 | address factory; 57 | } 58 | 59 | struct PendlePair { 60 | address market; 61 | uint256 inputToken; 62 | uint256 pendleToken; 63 | uint8 status; 64 | } 65 | 66 | struct MellowUnderlyingConfig { 67 | Contracts vault; 68 | uint256 underlying; 69 | } 70 | 71 | struct AdapterConfig { 72 | BalancerPool[] balancerPools; 73 | UniswapV3Pair[] uniswapV3Pairs; 74 | GenericSwapPair[] genericSwapPairs; 75 | VelodromeV2Pool[] velodromeV2Pools; 76 | PendlePair[] pendlePairs; 77 | MellowUnderlyingConfig[] mellowUnderlyings; 78 | } 79 | 80 | /// @dev A struct representing the initial Credit Manager configuration parameters 81 | struct CreditManagerV3DeployParams { 82 | /// @dev The Credit Manager's name 83 | string name; 84 | /// @dev The minimal debt principal amount 85 | uint128 minDebt; 86 | /// @dev The maximal debt principal amount 87 | uint128 maxDebt; 88 | /// @dev Percentage DAO fee on interest 89 | uint16 feeInterest; 90 | /// @dev Percentage DAO fee on liquidation amount for normal liquidations 91 | uint16 feeLiquidation; 92 | /// @dev Liquidation premium for normal liquidations 93 | uint16 liquidationPremium; 94 | /// @dev Percentage DAO fee on liquidation amount for liquidations due to expiration 95 | uint16 feeLiquidationExpired; 96 | /// @dev Liquidation premium for liquidations due to expiration 97 | uint16 liquidationPremiumExpired; 98 | /// @dev The initial list of collateral tokens to allow 99 | CollateralTokenHuman[] collateralTokens; 100 | /// @dev Address of DegenNFT, address(0) if whitelisted mode is not used 101 | bool whitelisted; 102 | /// @dev Whether the Credit Manager is connected to an expirable pool (and the CreditFacade is expirable) 103 | bool expirable; 104 | /// @dev Whether to skip normal initialization - used for new Credit Configurators that are deployed for existing CMs 105 | bool skipInit; 106 | /// @dev Contracts which should become adapters 107 | Contracts[] contracts; 108 | /// @dev Pool limit 109 | uint256 poolLimit; 110 | // 111 | // ADAPTER CIONFIGURATION 112 | AdapterConfig adapterConfig; 113 | } 114 | 115 | struct GaugeRate { 116 | uint256 token; 117 | uint16 minRate; 118 | uint16 maxRate; 119 | } 120 | 121 | struct PoolQuotaLimit { 122 | uint256 token; 123 | uint16 quotaIncreaseFee; 124 | uint96 limit; 125 | } 126 | 127 | struct CollateralTokenHuman { 128 | uint256 token; 129 | uint16 lt; 130 | } 131 | 132 | interface IPoolV3DeployConfig { 133 | function id() external view returns (string memory); 134 | function symbol() external view returns (string memory); 135 | function name() external view returns (string memory); 136 | 137 | function chainId() external view returns (uint256); 138 | function underlying() external view returns (uint256); 139 | function supportsQuotas() external view returns (bool); 140 | 141 | function poolParams() external view returns (PoolV3DeployParams memory); 142 | 143 | function irm() external view returns (LinearIRMV3DeployParams memory); 144 | 145 | function gaugeRates() external view returns (GaugeRate[] memory); 146 | 147 | function quotaLimits() external view returns (PoolQuotaLimit[] memory); 148 | 149 | function getAccountAmount() external view returns (uint256); 150 | 151 | function creditManagers() external view returns (CreditManagerV3DeployParams[] memory); 152 | } 153 | -------------------------------------------------------------------------------- /contracts/test/interfaces/ITokenTestSuite.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | interface ITokenTestSuite { 7 | function wethToken() external view returns (address); 8 | 9 | function approve(address token, address holder, address targetContract) external; 10 | 11 | function approve(address token, address holder, address targetContract, uint256 amount) external; 12 | 13 | function topUpWETH() external payable; 14 | 15 | function topUpWETH(address onBehalfOf, uint256 value) external; 16 | 17 | function balanceOf(address token, address holder) external view returns (uint256 balance); 18 | 19 | function mint(address token, address to, uint256 amount) external; 20 | 21 | function burn(address token, address from, uint256 amount) external; 22 | } 23 | -------------------------------------------------------------------------------- /contracts/test/invaritants/Deployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IntegrationTestHelper} from "../helpers/IntegrationTestHelper.sol"; 7 | import {AdapterMock} from "../mocks/core/AdapterMock.sol"; 8 | import {TargetContractMock} from "../mocks/core/TargetContractMock.sol"; 9 | import "../lib/constants.sol"; 10 | import "forge-std/Vm.sol"; 11 | 12 | contract GearboxInstance is IntegrationTestHelper { 13 | function _setUp() public { 14 | _setupCore(); 15 | 16 | _deployMockCreditAndPool(); 17 | 18 | targetMock = new TargetContractMock(); 19 | adapterMock = new AdapterMock(address(creditManager), address(targetMock)); 20 | 21 | vm.prank(CONFIGURATOR); 22 | creditConfigurator.allowAdapter(address(adapterMock)); 23 | } 24 | 25 | function mf() external { 26 | vm.roll(block.number + 1); 27 | } 28 | 29 | function getVm() external pure returns (Vm) { 30 | return vm; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/test/invaritants/Handler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {GearboxInstance} from "./Deployer.sol"; 7 | 8 | import {ICreditFacadeV3Multicall} from "../../interfaces/ICreditFacadeV3.sol"; 9 | import {MultiCall} from "../../interfaces/ICreditFacadeV3.sol"; 10 | import {MultiCallBuilder} from "../lib/MultiCallBuilder.sol"; 11 | import "forge-std/Test.sol"; 12 | import "../lib/constants.sol"; 13 | import "forge-std/console.sol"; 14 | import "forge-std/Vm.sol"; 15 | 16 | contract Handler { 17 | Vm internal vm; 18 | GearboxInstance gi; 19 | 20 | uint256 b; 21 | uint256 counter; 22 | 23 | constructor(GearboxInstance _gi) { 24 | gi = _gi; 25 | vm = gi.getVm(); 26 | b = block.timestamp; 27 | } 28 | 29 | function openCA(uint256 _debt) public { 30 | vm.roll(++b); 31 | console.log(++counter); 32 | (uint256 minDebt, uint256 maxDebt) = gi.creditFacade().debtLimits(); 33 | 34 | uint256 debt = minDebt + (_debt % (maxDebt - minDebt)); 35 | 36 | if (gi.pool().availableLiquidity() < 2 * debt) { 37 | gi.tokenTestSuite().mint(gi.underlyingT(), INITIAL_LP, 3 * debt); 38 | gi.tokenTestSuite().approve(gi.underlyingT(), INITIAL_LP, address(gi.pool())); 39 | 40 | vm.startPrank(INITIAL_LP); 41 | gi.pool().deposit(3 * debt, INITIAL_LP); 42 | vm.stopPrank(); 43 | } 44 | 45 | if (gi.pool().creditManagerBorrowable(address(gi.creditManager())) > debt) { 46 | gi.tokenTestSuite().mint(gi.underlyingT(), address(this), debt); 47 | gi.tokenTestSuite().approve(gi.underlyingT(), address(this), address(gi.creditManager())); 48 | 49 | gi.creditFacade().openCreditAccount( 50 | address(this), 51 | MultiCallBuilder.build( 52 | MultiCall({ 53 | target: address(gi.creditFacade()), 54 | callData: abi.encodeCall(ICreditFacadeV3Multicall.increaseDebt, (debt)) 55 | }), 56 | MultiCall({ 57 | target: address(gi.creditFacade()), 58 | callData: abi.encodeCall(ICreditFacadeV3Multicall.addCollateral, (gi.underlying(), debt)) 59 | }) 60 | ), 61 | 0 62 | ); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/test/invaritants/OpenInvariants.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {GearboxInstance} from "./Deployer.sol"; 7 | import {Handler} from "./Handler.sol"; 8 | 9 | import "forge-std/Test.sol"; 10 | import "../lib/constants.sol"; 11 | 12 | contract InvariantGearboxTest is Test { 13 | GearboxInstance gi; 14 | Handler handler; 15 | 16 | function setUp() public { 17 | gi = new GearboxInstance(); 18 | gi._setUp(); 19 | handler = new Handler(gi); 20 | targetContract(address(handler)); 21 | } 22 | 23 | // function invariant_example() external {} 24 | } 25 | -------------------------------------------------------------------------------- /contracts/test/invaritants/TargetAttacker.sol: -------------------------------------------------------------------------------- 1 | // // SPDX-License-Identifier: UNLICENSED 2 | // // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // // (c) Gearbox Foundation, 2023. 4 | // pragma solidity ^0.8.17; 5 | 6 | // import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | // import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; 8 | // import {ICreditManagerV3, CollateralCalcTask, CollateralDebtData} from "../../interfaces/ICreditManagerV3.sol"; 9 | // import {IPriceOracleV3} from "../../interfaces/IPriceOracleV3.sol"; 10 | // import {ITokenTestSuite} from "../interfaces/ITokenTestSuite.sol"; 11 | // import {PriceFeedMock} from "../mocks/oracles/PriceFeedMock.sol"; 12 | // import {Random} from "./Random.sol"; 13 | 14 | // /// @title Target Hacker 15 | // /// This contract simulates different technics to hack the system by provided seed 16 | 17 | // contract TargetAttacker is Random { 18 | // using Math for uint256; 19 | 20 | // ICreditManagerV3 creditManager; 21 | // IPriceOracleV3 priceOracle; 22 | // ITokenTestSuite tokenTestSuite; 23 | // address creditAccount; 24 | 25 | // constructor(address _creditManager, address _priceOracle, address _tokenTestSuite) { 26 | // creditManager = ICreditManagerV3(_creditManager); 27 | // priceOracle = IPriceOracleV3(_priceOracle); 28 | // tokenTestSuite = ITokenTestSuite(_tokenTestSuite); 29 | // } 30 | 31 | // // Act function tests different scenarios related to any action 32 | // // which could potential attacker use. Calling internal contracts 33 | // // depositing funds into pools, withdrawing, liquidating, etc. 34 | 35 | // // it also could update prices for updatable price oracles 36 | 37 | // function act(uint256 _seed) external { 38 | // setSeed(_seed); 39 | // creditAccount = msg.sender; 40 | 41 | // function ()[3] memory fnActions = [_stealTokens, _changeTokenPrice, _swapTokens]; 42 | 43 | // fnActions[getRandomInRange(fnActions.length)](); 44 | // } 45 | 46 | // function _changeTokenPrice() internal { 47 | // uint256 cTokensQty = creditManager.collateralTokensCount(); 48 | // uint256 mask = 1 << getRandomInRange(cTokensQty); 49 | // (address token,) = creditManager.collateralTokenByMask(mask); 50 | 51 | // address priceFeed = IPriceOracleV3(priceOracle).priceFeeds(token); 52 | 53 | // (, int256 price,,,) = PriceFeedMock(priceFeed).latestRoundData(); 54 | 55 | // uint256 sign = getRandomInRange(2); 56 | // uint256 deltaPct = getRandomInRange(500); 57 | 58 | // int256 newPrice = 59 | // sign == 1 ? price * (10000 + int256(deltaPct)) / 10000 : price * (10000 - int256(deltaPct)) / 10000; 60 | 61 | // PriceFeedMock(priceFeed).setPrice(newPrice); 62 | // } 63 | 64 | // function _swapTokens() internal { 65 | // uint256 cTokensQty = creditManager.collateralTokensCount(); 66 | // uint256 mask0 = 1 << getRandomInRange(cTokensQty); 67 | // uint256 mask1 = 1 << getRandomInRange(cTokensQty); 68 | 69 | // (address tokenIn,) = creditManager.collateralTokenByMask(mask0); 70 | // (address tokenOut,) = creditManager.collateralTokenByMask(mask1); 71 | 72 | // uint256 balance = IERC20(tokenIn).balanceOf(creditAccount); 73 | 74 | // uint256 tokenInAmount = getRandomInRange(balance); 75 | 76 | // uint256 tokenInEq = priceOracle.convert(tokenInAmount, tokenIn, tokenOut); 77 | 78 | // IERC20(tokenIn).transferFrom(creditAccount, address(this), tokenInAmount); 79 | // tokenTestSuite.mint(tokenOut, creditAccount, getRandomInRange(tokenInEq)); 80 | // } 81 | 82 | // function _stealTokens() internal { 83 | // uint256 cTokensQty = creditManager.collateralTokensCount(); 84 | // uint256 mask = 1 << getRandomInRange(cTokensQty); 85 | // (tokenIn,) = creditManager.collateralTokenByMask(mask); 86 | // uint256 balance = IERC20(tokenIn).balanceOf(creditAccount); 87 | // IERC20(tokenIn).transferFrom(creditAccount, address(this), getRandomInRange(balance)); 88 | // } 89 | 90 | // /// Swaps token with some deviation from oracle price 91 | 92 | // function _swap() internal { 93 | // uint256 cTokensQty = creditManager.collateralTokensCount(); 94 | 95 | // (tokenIn,) = creditManager.collateralTokenByMask(1 << getRandomInRange(cTokensQty)); 96 | // uint256 balance = IERC20(tokenIn).balanceOf(creditAccount); 97 | // uint256 amount = getRandomInRange(balance); 98 | // IERC20(tokenIn).transferFrom(creditAccount, address(this), amount); 99 | 100 | // (tokenOut,) = creditManager.collateralTokenByMask(1 << getRandomInRange(cTokensQty)); 101 | 102 | // uint256 amountOut = (priceOracle.convert(amount, tokenIn, tokenOut) * (120 - getRandomInRange(40))) / 100; 103 | // amountOut = Math.min(amountOut, IERC20(tokenOut).balanceOf(address(this))); 104 | // IERC20(tokenOut).transfer(creditAccount, amountOut); 105 | // } 106 | 107 | // function _deposit() internal { 108 | // uint256 amount = getRandomInRange95(pool.availableLiquidity()); 109 | // pool.deposit(amount, address(this)); 110 | // } 111 | 112 | // function _withdraw() internal { 113 | // uint256 amount = getRandomInRange95(pool.balanceOf(address(this))); 114 | // pool.withdraw(amount, address(this), address(this)); 115 | // } 116 | // } 117 | -------------------------------------------------------------------------------- /contracts/test/lib/AddressList.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.10; 3 | 4 | library AddressList { 5 | function includes(address[] memory array, address item) internal pure returns (bool) { 6 | uint256 len = array.length; 7 | 8 | for (uint256 i; i < len;) { 9 | if (array[i] == item) return true; 10 | unchecked { 11 | ++i; 12 | } 13 | } 14 | 15 | return false; 16 | } 17 | 18 | function trim(address[] memory array) internal pure returns (address[] memory trimmed) { 19 | uint256 len = array.length; 20 | 21 | if (len == 0) return array; 22 | 23 | uint256 foundLen; 24 | while (array[foundLen] != address(0)) { 25 | unchecked { 26 | ++foundLen; 27 | if (foundLen == len) return array; 28 | } 29 | } 30 | 31 | if (foundLen > 0) return copy(array, foundLen); 32 | } 33 | 34 | function copy(address[] memory array, uint256 len) internal pure returns (address[] memory res) { 35 | res = new address[](len); 36 | for (uint256 i; i < len;) { 37 | res[i] = array[i]; 38 | unchecked { 39 | ++i; 40 | } 41 | } 42 | } 43 | 44 | function concat(address[] memory calls1, address[] memory calls2) internal pure returns (address[] memory res) { 45 | uint256 len1 = calls1.length; 46 | uint256 lenTotal = len1 + calls2.length; 47 | 48 | if (lenTotal == len1) return calls1; 49 | 50 | res = new address[](lenTotal); 51 | 52 | for (uint256 i; i < lenTotal;) { 53 | res[i] = (i < len1) ? calls1[i] : calls2[i - len1]; 54 | unchecked { 55 | ++i; 56 | } 57 | } 58 | } 59 | 60 | function append(address[] memory addrs, address newAddr) internal pure returns (address[] memory res) { 61 | address[] memory newAddrArray = new address[](1); 62 | newAddrArray[0] = newAddr; 63 | return concat(addrs, newAddrArray); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/test/lib/MultiCallBuilder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {MultiCall} from "../../interfaces/ICreditFacadeV3.sol"; 7 | 8 | library MultiCallBuilder { 9 | function build() internal pure returns (MultiCall[] memory calls) {} 10 | 11 | function build(MultiCall memory call1) internal pure returns (MultiCall[] memory calls) { 12 | calls = new MultiCall[](1); 13 | calls[0] = call1; 14 | } 15 | 16 | function build(MultiCall memory call1, MultiCall memory call2) internal pure returns (MultiCall[] memory calls) { 17 | calls = new MultiCall[](2); 18 | calls[0] = call1; 19 | calls[1] = call2; 20 | } 21 | 22 | function build(MultiCall memory call1, MultiCall memory call2, MultiCall memory call3) 23 | internal 24 | pure 25 | returns (MultiCall[] memory calls) 26 | { 27 | calls = new MultiCall[](3); 28 | calls[0] = call1; 29 | calls[1] = call2; 30 | calls[2] = call3; 31 | } 32 | 33 | function build(MultiCall memory call1, MultiCall memory call2, MultiCall memory call3, MultiCall memory call4) 34 | internal 35 | pure 36 | returns (MultiCall[] memory calls) 37 | { 38 | calls = new MultiCall[](4); 39 | calls[0] = call1; 40 | calls[1] = call2; 41 | calls[2] = call3; 42 | calls[3] = call4; 43 | } 44 | 45 | function build( 46 | MultiCall memory call1, 47 | MultiCall memory call2, 48 | MultiCall memory call3, 49 | MultiCall memory call4, 50 | MultiCall memory call5 51 | ) internal pure returns (MultiCall[] memory calls) { 52 | calls = new MultiCall[](5); 53 | calls[0] = call1; 54 | calls[1] = call2; 55 | calls[2] = call3; 56 | calls[3] = call4; 57 | calls[4] = call5; 58 | } 59 | 60 | function build( 61 | MultiCall memory call1, 62 | MultiCall memory call2, 63 | MultiCall memory call3, 64 | MultiCall memory call4, 65 | MultiCall memory call5, 66 | MultiCall memory call6 67 | ) internal pure returns (MultiCall[] memory calls) { 68 | calls = new MultiCall[](6); 69 | calls[0] = call1; 70 | calls[1] = call2; 71 | calls[2] = call3; 72 | calls[3] = call4; 73 | calls[4] = call5; 74 | calls[5] = call6; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /contracts/test/lib/ParseLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023 4 | pragma solidity ^0.8.10; 5 | 6 | import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; 7 | import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; 8 | 9 | /// @notice Designed for test purposes only 10 | library ParseLib { 11 | using Strings for uint256; 12 | 13 | function add(string memory init, string memory name, address addr) internal pure returns (string memory) { 14 | return string.concat(init, name, uint256(uint160(addr)).toHexString(20)); 15 | } 16 | 17 | function add(string memory init, string memory name, uint256 value) internal pure returns (string memory) { 18 | return string.concat(init, name, value.toString()); 19 | } 20 | 21 | function add_amount_decimals(string memory init, string memory name, uint256 value, uint8 decimals) 22 | internal 23 | pure 24 | returns (string memory) 25 | { 26 | return string.concat(init, name, toFixString(value, decimals)); 27 | } 28 | 29 | function add_amount_token(string memory init, string memory name, uint256 value, address token) 30 | internal 31 | view 32 | returns (string memory) 33 | { 34 | return add_amount_decimals(init, name, value, IERC20Metadata(token).decimals()); 35 | } 36 | 37 | function add_token(string memory init, string memory name, address addr) internal view returns (string memory) { 38 | return string.concat(init, name, IERC20Metadata(addr).symbol()); 39 | } 40 | 41 | function toFixString(uint256 value, uint8 decimals) internal pure returns (string memory) { 42 | uint8 divider = (decimals > 4) ? 4 : decimals; 43 | uint256 biggerPart = value / (10 ** (decimals)); 44 | uint256 smallerPart = value * (10 ** divider) - biggerPart * (10 ** (decimals + divider)); 45 | return string.concat(biggerPart.toString(), ".", smallerPart.toString()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/test/lib/constants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import { 5 | WAD, 6 | RAY, 7 | DEFAULT_FEE_LIQUIDATION, 8 | DEFAULT_LIQUIDATION_PREMIUM 9 | } from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol"; 10 | import {Test} from "forge-std/Test.sol"; 11 | 12 | uint16 constant DEFAULT_UNDERLYING_LT = 10000 - DEFAULT_FEE_LIQUIDATION - DEFAULT_LIQUIDATION_PREMIUM; 13 | 14 | address constant DUMB_ADDRESS = 0xC4375B7De8af5a38a93548eb8453a498222C4fF2; 15 | address constant DUMB_ADDRESS2 = 0x93548eB8453a498222C4FF2C4375b7De8af5A38a; 16 | address constant DUMB_ADDRESS3 = 0x822293548EB8453A49c4fF2c4375B7DE8AF5a38A; 17 | address constant DUMB_ADDRESS4 = 0x498222C4Ff2C4393548eb8453a75B7dE8AF5A38a; 18 | 19 | address constant USER = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; 20 | address constant CONFIGURATOR = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; 21 | address constant LIQUIDATOR = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC; 22 | 23 | address constant INITIAL_LP = 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f; 24 | 25 | address constant FRIEND = 0x90F79bf6EB2c4f870365E785982E1f101E93b906; 26 | address constant FRIEND2 = 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65; 27 | 28 | address constant ADAPTER = 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc; 29 | 30 | string constant PAUSABLE_ERROR = "Pausable: paused"; 31 | string constant OWNABLE_ERROR = "Ownable: caller is not the owner"; 32 | 33 | uint128 constant DAI_MIN_BORROWED_AMOUNT = uint128(1000 * WAD); 34 | uint128 constant DAI_MAX_BORROWED_AMOUNT = uint128(10000 * WAD); 35 | 36 | uint256 constant DAI_ACCOUNT_AMOUNT = 20000 * WAD; 37 | uint256 constant DAI_EXCHANGE_AMOUNT = DAI_ACCOUNT_AMOUNT / 2; 38 | 39 | uint256 constant USDC_ACCOUNT_AMOUNT = 20000 * (10 ** 6); 40 | uint256 constant USDC_EXCHANGE_AMOUNT = 1000 * (10 ** 6); 41 | uint256 constant USDT_ACCOUNT_AMOUNT = 42000 * WAD; 42 | uint256 constant LINK_ACCOUNT_AMOUNT = 12000 * WAD; 43 | uint256 constant LINK_EXCHANGE_AMOUNT = 300 * WAD; 44 | 45 | uint256 constant CURVE_LP_ACCOUNT_AMOUNT = 100 * WAD; 46 | uint256 constant CURVE_LP_OPERATION_AMOUNT = 55 * WAD; 47 | 48 | uint256 constant DAI_POOL_AMOUNT = 500000 * WAD; 49 | uint256 constant DAI_WETH_RATE = 1000; 50 | 51 | uint256 constant WETH_ACCOUNT_AMOUNT = 200 * WAD; 52 | uint256 constant WETH_EXCHANGE_AMOUNT = 3 * WAD; 53 | uint256 constant STETH_ACCOUNT_AMOUNT = 150 * WAD; 54 | uint256 constant wstETH_ACCOUNT_AMOUNT = 50 * WAD; 55 | 56 | contract Roles is Test { 57 | constructor() { 58 | vm.label(USER, "USER"); 59 | vm.label(FRIEND, "FRIEND"); 60 | vm.label(LIQUIDATOR, "LIQUIDATOR"); 61 | vm.label(INITIAL_LP, "INITIAL_LP"); 62 | vm.label(DUMB_ADDRESS, "DUMB_ADDRESS"); 63 | vm.label(ADAPTER, "ADAPTER"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/test/mocks/GeneralMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | contract GeneralMock { 7 | bytes public data; 8 | 9 | fallback() external { 10 | data = msg.data; 11 | } 12 | 13 | receive() external payable {} 14 | } 15 | -------------------------------------------------------------------------------- /contracts/test/mocks/core/ACLTraitTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {ACLNonReentrantTrait} from "../../../traits/ACLNonReentrantTrait.sol"; 7 | 8 | /** 9 | * @title Pausable Trait Test 10 | * @notice this contract is used to test how poolOnly modifier works 11 | */ 12 | contract ACLNonReentrantTraitTest is ACLNonReentrantTrait { 13 | constructor(address addressProvider) ACLNonReentrantTrait(addressProvider) {} 14 | 15 | function accessWhenNotPaused() external view whenNotPaused {} 16 | 17 | function accessWhenPaused() external view whenPaused {} 18 | 19 | function accessConfiguratorOnly() external view configuratorOnly {} 20 | } 21 | -------------------------------------------------------------------------------- /contracts/test/mocks/core/AccountFactoryMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | //pragma abicoder v1; 6 | 7 | import {IAccountFactoryBase} from "../../../interfaces/IAccountFactoryV3.sol"; 8 | import {CreditAccountMock} from "../credit/CreditAccountMock.sol"; 9 | 10 | // EXCEPTIONS 11 | 12 | import {Test} from "forge-std/Test.sol"; 13 | 14 | /// @title Disposable credit accounts factory 15 | contract AccountFactoryMock is Test, IAccountFactoryBase { 16 | /// @dev Contract version 17 | uint256 public version; 18 | 19 | address public usedAccount; 20 | 21 | address public returnedAccount; 22 | 23 | constructor(uint256 _version) { 24 | usedAccount = address(new CreditAccountMock()); 25 | 26 | version = _version; 27 | 28 | vm.label(usedAccount, "CREDIT_ACCOUNT"); 29 | } 30 | 31 | /// @dev Provides a new credit account to a Credit Manager 32 | /// @return creditAccount Address of credit account 33 | function takeCreditAccount(uint256, uint256) external view override returns (address creditAccount) { 34 | return usedAccount; 35 | } 36 | 37 | function returnCreditAccount(address creditAccount) external override { 38 | returnedAccount = creditAccount; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/test/mocks/core/AdapterCallMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {Address} from "@openzeppelin/contracts/utils/Address.sol"; 7 | 8 | contract AdapterCallMock { 9 | using Address for address; 10 | 11 | function makeCall(address target, bytes memory data) external returns (uint256, uint256) { 12 | target.functionCall(data); 13 | return (0, 0); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/test/mocks/core/AdapterMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {AdapterType} from "@gearbox-protocol/sdk-gov/contracts/AdapterType.sol"; 7 | import {IAdapter} from "@gearbox-protocol/core-v2/contracts/interfaces/IAdapter.sol"; 8 | 9 | import {ICreditManagerV3} from "../../../interfaces/ICreditManagerV3.sol"; 10 | 11 | /// @title Adapter Mock 12 | contract AdapterMock is IAdapter { 13 | AdapterType public constant override _gearboxAdapterType = AdapterType.ABSTRACT; 14 | uint16 public constant override _gearboxAdapterVersion = 1; 15 | 16 | address public immutable override creditManager; 17 | address public immutable override addressProvider; 18 | address public immutable override targetContract; 19 | 20 | constructor(address _creditManager, address _targetContract) { 21 | creditManager = _creditManager; 22 | addressProvider = ICreditManagerV3(_creditManager).addressProvider(); 23 | targetContract = _targetContract; 24 | } 25 | 26 | function executeSwapSafeApprove(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) 27 | external 28 | returns (uint256 tokensToEnable, uint256 tokensToDisable, bytes memory result) 29 | { 30 | tokensToEnable = _getMaskOrRevert(tokenOut); 31 | if (disableTokenIn) tokensToDisable = _getMaskOrRevert(tokenIn); 32 | _approveToken(tokenIn, type(uint256).max); 33 | result = _execute(callData); 34 | _approveToken(tokenIn, 1); 35 | } 36 | 37 | function dumbCall(uint256 _tokensToEnable, uint256 _tokensToDisable) 38 | external 39 | returns (uint256 tokensToEnable, uint256 tokensToDisable) 40 | { 41 | _execute(dumbCallData()); 42 | tokensToEnable = _tokensToEnable; 43 | tokensToDisable = _tokensToDisable; 44 | } 45 | 46 | function dumbCallData() public pure returns (bytes memory) { 47 | return abi.encodeWithSignature("hello(string)", "world"); 48 | } 49 | 50 | fallback() external { 51 | _execute(msg.data); 52 | } 53 | 54 | function _getMaskOrRevert(address token) internal view returns (uint256 tokenMask) { 55 | tokenMask = ICreditManagerV3(creditManager).getTokenMaskOrRevert(token); 56 | } 57 | 58 | function _execute(bytes memory data) internal returns (bytes memory result) { 59 | result = ICreditManagerV3(creditManager).execute(data); 60 | } 61 | 62 | function _approveToken(address token, uint256 amount) internal { 63 | ICreditManagerV3(creditManager).approveCreditAccount(token, amount); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/test/mocks/core/AddressProviderV3ACLMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import "../../../core/AddressProviderV3.sol"; 7 | import {AccountFactoryMock} from "../core/AccountFactoryMock.sol"; 8 | import {PriceOracleMock} from "../oracles/PriceOracleMock.sol"; 9 | import {BotListMock} from "../core/BotListMock.sol"; 10 | import {WETHMock} from "../token/WETHMock.sol"; 11 | 12 | import {Test} from "forge-std/Test.sol"; 13 | 14 | import "forge-std/console.sol"; 15 | 16 | /// 17 | /// @title Address Provider that returns ACL and isConfigurator 18 | 19 | contract AddressProviderV3ACLMock is Test, AddressProviderV3 { 20 | address public owner; 21 | mapping(address => bool) public isConfigurator; 22 | 23 | mapping(address => bool) public isPool; 24 | mapping(address => bool) public isCreditManager; 25 | 26 | mapping(address => bool) public isPausableAdmin; 27 | mapping(address => bool) public isUnpausableAdmin; 28 | 29 | constructor() AddressProviderV3(address(this)) { 30 | PriceOracleMock priceOracleMock = new PriceOracleMock(); 31 | _setAddress(AP_PRICE_ORACLE, address(priceOracleMock), priceOracleMock.version()); 32 | 33 | AccountFactoryMock accountFactoryMock = new AccountFactoryMock(3_00); 34 | _setAddress(AP_ACCOUNT_FACTORY, address(accountFactoryMock), NO_VERSION_CONTROL); 35 | 36 | BotListMock botListMock = new BotListMock(); 37 | _setAddress(AP_BOT_LIST, address(botListMock), 3_00); 38 | 39 | _setAddress(AP_CONTRACTS_REGISTER, address(this), 0); 40 | 41 | _setAddress(AP_TREASURY, makeAddr("TREASURY"), 0); 42 | 43 | _setAddress(AP_WETH_TOKEN, address(new WETHMock()), 0); 44 | 45 | isConfigurator[msg.sender] = true; 46 | owner = msg.sender; 47 | } 48 | 49 | function addPool(address pool) external { 50 | isPool[pool] = true; 51 | } 52 | 53 | function addCreditManager(address creditManager) external { 54 | isCreditManager[creditManager] = true; 55 | } 56 | 57 | /// @dev Adds an address to the set of admins that can pause contracts 58 | /// @param newAdmin Address of a new pausable admin 59 | function addPausableAdmin(address newAdmin) external { 60 | isPausableAdmin[newAdmin] = true; 61 | } 62 | 63 | /// @dev Adds unpausable admin address to the list 64 | /// @param newAdmin Address of new unpausable admin 65 | function addUnpausableAdmin(address newAdmin) external { 66 | isUnpausableAdmin[newAdmin] = true; 67 | } 68 | 69 | function getAddressOrRevert(bytes32 key, uint256 _version) public view override returns (address result) { 70 | result = addresses[key][_version]; 71 | if (result == address(0)) { 72 | string memory keyString = bytes32ToString(key); 73 | console.log("AddressProviderV3: Cant find ", keyString, ", version:", _version); 74 | } 75 | 76 | return super.getAddressOrRevert(key, _version); 77 | } 78 | 79 | function bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) { 80 | uint8 i = 0; 81 | while (i < 32 && _bytes32[i] != 0) { 82 | i++; 83 | } 84 | bytes memory bytesArray = new bytes(i); 85 | for (i = 0; i < 32 && _bytes32[i] != 0; i++) { 86 | bytesArray[i] = _bytes32[i]; 87 | } 88 | return string(bytesArray); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /contracts/test/mocks/core/BotListMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | contract BotListMock { 7 | bool revertOnErase; 8 | 9 | uint256 return_botPermissions; 10 | bool return_forbidden; 11 | bool return_hasSpecialPermissions; 12 | 13 | uint256 return_activeBotsRemaining; 14 | 15 | function setBotStatusReturns(uint256 botPermissions, bool forbidden, bool hasSpecialPermissions) external { 16 | return_botPermissions = botPermissions; 17 | return_forbidden = forbidden; 18 | return_hasSpecialPermissions = hasSpecialPermissions; 19 | } 20 | 21 | function getBotStatus(address, address, address) 22 | external 23 | view 24 | returns (uint256 botPermissions, bool forbidden, bool hasSpecialPermissions) 25 | { 26 | botPermissions = return_botPermissions; 27 | forbidden = return_forbidden; 28 | hasSpecialPermissions = return_hasSpecialPermissions; 29 | } 30 | 31 | function eraseAllBotPermissions(address, address) external view { 32 | if (revertOnErase) { 33 | revert("Unexpected call to eraseAllBotPermissions"); 34 | } 35 | } 36 | 37 | function setRevertOnErase(bool _value) external { 38 | revertOnErase = _value; 39 | } 40 | 41 | function setBotPermissionsReturn(uint256 activeBotsRemaining) external { 42 | return_activeBotsRemaining = activeBotsRemaining; 43 | } 44 | 45 | function setBotPermissions(address, address, address, uint192) 46 | external 47 | view 48 | returns (uint256 activeBotsRemaining) 49 | { 50 | activeBotsRemaining = return_activeBotsRemaining; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/test/mocks/core/TargetContractMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | /// @title Target Contract Mock 7 | contract TargetContractMock { 8 | bytes public callData; 9 | 10 | constructor() {} 11 | 12 | fallback() external { 13 | callData = msg.data; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/test/mocks/credit/CreditAccountMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | pragma abicoder v1; 6 | 7 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | import {Address} from "@openzeppelin/contracts/utils/Address.sol"; 9 | import {ICreditAccountBase} from "../../../interfaces/ICreditAccountV3.sol"; 10 | 11 | interface CreditAccountMockEvents { 12 | event TransferCall(address token, address to, uint256 amount); 13 | 14 | event ExecuteCall(address destination, bytes data); 15 | } 16 | 17 | contract CreditAccountMock is ICreditAccountBase, CreditAccountMockEvents { 18 | using Address for address; 19 | 20 | address public creditManager; 21 | 22 | // Contract version 23 | uint256 public constant version = 3_00; 24 | 25 | bytes public return_executeResult; 26 | 27 | mapping(address => mapping(address => bool)) public revertsOnTransfer; 28 | 29 | function setRevertOnTransfer(address token, address to) external { 30 | revertsOnTransfer[token][to] = true; 31 | } 32 | 33 | function safeTransfer(address token, address to, uint256 amount) external { 34 | if (revertsOnTransfer[token][to]) { 35 | revert("Token transfer reverted"); 36 | } 37 | 38 | if (token.isContract()) IERC20(token).transfer(to, amount); 39 | emit TransferCall(token, to, amount); 40 | } 41 | 42 | function execute(address destination, bytes memory data) external returns (bytes memory) { 43 | emit ExecuteCall(destination, data); 44 | return return_executeResult; 45 | } 46 | 47 | function setReturnExecuteResult(bytes calldata _result) external { 48 | return_executeResult = _result; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test/mocks/governance/GaugeMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | 8 | import {SafeERC20} from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; 9 | import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 10 | import {ACLNonReentrantTrait} from "../../../traits/ACLNonReentrantTrait.sol"; 11 | 12 | // interfaces 13 | 14 | import {IPoolQuotaKeeperV3} from "../../../interfaces/IPoolQuotaKeeperV3.sol"; 15 | 16 | import {PoolV3} from "../../../pool/PoolV3.sol"; 17 | 18 | /// @title Gauge fore new 4626 pools 19 | contract GaugeMock is ACLNonReentrantTrait { 20 | using EnumerableSet for EnumerableSet.AddressSet; 21 | using SafeERC20 for IERC20; 22 | 23 | /// @dev Address provider 24 | address public immutable addressProvider; 25 | 26 | /// @dev Address of the pool 27 | PoolV3 public immutable pool; 28 | 29 | /// @dev Mapping from token address to its rate parameters 30 | mapping(address => uint16) public rates; 31 | 32 | // 33 | // CONSTRUCTOR 34 | // 35 | 36 | /// @dev Constructor 37 | 38 | constructor(address _pool) ACLNonReentrantTrait(address(PoolV3(_pool).addressProvider())) nonZeroAddress(_pool) { 39 | addressProvider = address(PoolV3(_pool).addressProvider()); // F:[P4-01] 40 | pool = PoolV3(payable(_pool)); // F:[P4-01] 41 | } 42 | 43 | /// @dev Rolls the new epoch and updates all quota rates 44 | function updateEpoch() external { 45 | /// compute all compounded rates 46 | IPoolQuotaKeeperV3 keeper = IPoolQuotaKeeperV3(pool.poolQuotaKeeper()); 47 | 48 | // /// update rates & cumulative indexes 49 | // address[] memory tokens = keeper.quotedTokens(); 50 | // uint256 len = tokens.length; 51 | // uint16[] memory rateUpdates = new uint16[](len); 52 | 53 | // unchecked { 54 | // for (uint256 i; i < len; ++i) { 55 | // address token = tokens[i]; 56 | // rateUpdates[i] = rates[token]; 57 | // } 58 | // } 59 | 60 | keeper.updateRates(); 61 | } 62 | 63 | function addQuotaToken(address token, uint16 rate) external configuratorOnly { 64 | rates[token] = rate; 65 | IPoolQuotaKeeperV3 keeper = IPoolQuotaKeeperV3(pool.poolQuotaKeeper()); 66 | keeper.addQuotaToken(token); 67 | } 68 | 69 | function changeQuotaTokenRateParams(address token, uint16 rate) external configuratorOnly { 70 | rates[token] = rate; 71 | } 72 | 73 | function getRates(address[] memory tokens) external view returns (uint16[] memory result) { 74 | uint256 len = tokens.length; 75 | result = new uint16[](len); 76 | unchecked { 77 | for (uint256 i; i < len; ++i) { 78 | address token = tokens[i]; 79 | result[i] = rates[token]; 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /contracts/test/mocks/governance/GearStakingMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import { 7 | IGearStakingV3, MultiVote, EPOCHS_TO_WITHDRAW, VotingContractStatus 8 | } from "../../../interfaces/IGearStakingV3.sol"; 9 | 10 | contract GearStakingMock is IGearStakingV3 { 11 | uint256 public constant version = 3_00; 12 | 13 | uint16 public getCurrentEpoch; 14 | 15 | function setCurrentEpoch(uint16 epoch) external { 16 | getCurrentEpoch = epoch; 17 | } 18 | 19 | function firstEpochTimestamp() external view returns (uint256) {} 20 | 21 | function deposit(uint96 amount, MultiVote[] calldata votes) external {} 22 | 23 | function depositWithPermit( 24 | uint96 amount, 25 | MultiVote[] calldata votes, 26 | uint256 deadline, 27 | uint8 v, 28 | bytes32 r, 29 | bytes32 s 30 | ) external {} 31 | 32 | function multivote(MultiVote[] calldata votes) external {} 33 | 34 | function withdraw(uint96 amount, address to, MultiVote[] calldata votes) external {} 35 | 36 | function claimWithdrawals(address to) external {} 37 | 38 | function migrate(uint96 amount, MultiVote[] calldata votesBefore, MultiVote[] calldata votesAfter) external {} 39 | 40 | function depositOnMigration(uint96 amount, address to, MultiVote[] calldata votes) external {} 41 | 42 | // 43 | // GETTERS 44 | // 45 | 46 | /// @dev GEAR token address 47 | function gear() external view returns (address) {} 48 | 49 | /// @dev The total amount staked by the user in staked GEAR 50 | function balanceOf(address user) external view returns (uint256) {} 51 | 52 | /// @dev The amount available to the user for voting or withdrawal 53 | function availableBalance(address user) external view returns (uint256) {} 54 | 55 | /// @dev Returns the amounts withdrawable now and over the next 4 epochs 56 | function getWithdrawableAmounts(address user) 57 | external 58 | view 59 | returns (uint256 withdrawableNow, uint256[EPOCHS_TO_WITHDRAW] memory withdrawableInEpochs) 60 | {} 61 | 62 | function allowedVotingContract(address) external view returns (VotingContractStatus) {} 63 | 64 | function setVotingContractStatus(address votingContract, VotingContractStatus status) external {} 65 | 66 | function successor() external view returns (address) {} 67 | 68 | function setSuccessor(address newSuccessor) external {} 69 | 70 | function migrator() external view returns (address) {} 71 | 72 | function setMigrator(address newMigrator) external {} 73 | } 74 | -------------------------------------------------------------------------------- /contracts/test/mocks/oracles/PriceFeedMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.10; 5 | 6 | import {PriceFeedType} from "@gearbox-protocol/sdk-gov/contracts/PriceFeedType.sol"; 7 | import {IPriceFeed} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceFeed.sol"; 8 | 9 | enum FlagState { 10 | FALSE, 11 | TRUE, 12 | REVERT 13 | } 14 | 15 | /// @title Price feed mock 16 | /// @notice Used for test purposes only 17 | contract PriceFeedMock is IPriceFeed { 18 | PriceFeedType public constant override priceFeedType = PriceFeedType.CHAINLINK_ORACLE; 19 | 20 | int256 private price; 21 | uint8 public immutable override decimals; 22 | 23 | uint80 internal roundId; 24 | uint256 startedAt; 25 | uint256 updatedAt; 26 | uint80 internal answerInRound; 27 | 28 | FlagState internal _skipPriceCheck; 29 | 30 | bool internal revertOnLatestRound; 31 | 32 | constructor(int256 _price, uint8 _decimals) { 33 | price = _price; 34 | decimals = _decimals; 35 | roundId = 80; 36 | answerInRound = 80; 37 | // set to quite far in the future 38 | startedAt = block.timestamp + 36500 days; 39 | updatedAt = block.timestamp + 36500 days; 40 | } 41 | 42 | function setParams(uint80 _roundId, uint256 _startedAt, uint256 _updatedAt, uint80 _answerInRound) external { 43 | roundId = _roundId; 44 | startedAt = _startedAt; 45 | updatedAt = _updatedAt; 46 | answerInRound = _answerInRound; 47 | 48 | _skipPriceCheck = FlagState.REVERT; 49 | } 50 | 51 | function description() external pure override returns (string memory) { 52 | return "price oracle"; 53 | } 54 | 55 | function version() external pure override returns (uint256) { 56 | return 1; 57 | } 58 | 59 | function setPrice(int256 newPrice) external { 60 | price = newPrice; 61 | } 62 | 63 | function latestRoundData() 64 | external 65 | view 66 | override 67 | returns ( 68 | uint80, // roundId, 69 | int256, // answer, 70 | uint256, // startedAt, 71 | uint256, // updatedAt, 72 | uint80 //answeredInRound 73 | ) 74 | { 75 | if (revertOnLatestRound) revert(); 76 | 77 | return (roundId, price, startedAt, updatedAt, answerInRound); 78 | } 79 | 80 | // function priceFeedType() external view override returns (PriceFeedType) { 81 | // return _priceFeedType; 82 | // } 83 | 84 | function skipPriceCheck() external view override returns (bool) { 85 | return flagState(_skipPriceCheck); 86 | } 87 | 88 | function flagState(FlagState f) internal pure returns (bool value) { 89 | if (f == FlagState.REVERT) revert(); 90 | return f == FlagState.TRUE; 91 | } 92 | 93 | function setSkipPriceCheck(FlagState f) external { 94 | _skipPriceCheck = f; 95 | } 96 | 97 | function setRevertOnLatestRound(bool value) external { 98 | revertOnLatestRound = value; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /contracts/test/mocks/oracles/PriceFeedOnDemandMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | contract PriceFeedOnDemandMock { 7 | function updatePrice(bytes calldata data) external {} 8 | } 9 | -------------------------------------------------------------------------------- /contracts/test/mocks/oracles/PriceOracleMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | //pragma abicoder v1; 6 | 7 | import {IPriceOracleBase} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceOracleBase.sol"; 8 | 9 | // EXCEPTIONS 10 | 11 | import {Test} from "forge-std/Test.sol"; 12 | import "forge-std/console.sol"; 13 | 14 | /// @title Disposable credit accounts factory 15 | contract PriceOracleMock is Test, IPriceOracleBase { 16 | mapping(address => uint256) public priceInUSD; 17 | 18 | uint256 public constant override version = 3_00; 19 | 20 | mapping(address => bool) revertsOnGetPrice; 21 | mapping(address => mapping(bool => address)) priceFeedsInt; 22 | 23 | constructor() { 24 | vm.label(address(this), "PRICE_ORACLE"); 25 | } 26 | 27 | function priceFeeds(address token) public view returns (address) { 28 | return priceFeedsInt[token][false]; 29 | } 30 | 31 | function priceFeedsRaw(address token, bool reserve) public view returns (address) { 32 | return priceFeedsInt[token][reserve]; 33 | } 34 | 35 | function setRevertOnGetPrice(address token, bool value) external { 36 | revertsOnGetPrice[token] = value; 37 | } 38 | 39 | function setPrice(address token, uint256 price) external { 40 | priceInUSD[token] = price; 41 | } 42 | 43 | function addPriceFeed(address token, address priceFeed) external { 44 | priceFeedsInt[token][false] = priceFeed; 45 | } 46 | 47 | /// @dev Converts a quantity of an asset to USD (decimals = 8). 48 | /// @param amount Amount to convert 49 | /// @param token Address of the token to be converted 50 | function convertToUSD(uint256 amount, address token) public view returns (uint256) { 51 | return amount * getPrice(token) / 10 ** 8; 52 | } 53 | 54 | /// @dev Converts a quantity of USD (decimals = 8) to an equivalent amount of an asset 55 | /// @param amount Amount to convert 56 | /// @param token Address of the token converted to 57 | function convertFromUSD(uint256 amount, address token) public view returns (uint256) { 58 | return amount * 10 ** 8 / getPrice(token); 59 | } 60 | 61 | /// @dev Converts one asset into another 62 | /// 63 | /// @param amount Amount to convert 64 | /// @param tokenFrom Address of the token to convert from 65 | /// @param tokenTo Address of the token to convert to 66 | function convert(uint256 amount, address tokenFrom, address tokenTo) external view returns (uint256) { 67 | return convertFromUSD(convertToUSD(amount, tokenFrom), tokenTo); 68 | } 69 | 70 | /// @dev Returns token's price in USD (8 decimals) 71 | /// @param token The token to compute the price for 72 | function getPrice(address token) public view returns (uint256 price) { 73 | price = priceInUSD[token]; 74 | if (price == 0) revert("Price is not set"); 75 | 76 | if (revertsOnGetPrice[token]) { 77 | console.log("Getting price for ", token, " should not be called"); 78 | revert("PriceOracle mock should not be called reverted"); 79 | } 80 | } 81 | 82 | /// @dev Returns the price feed address for the passed token 83 | /// @param token Token to get the price feed for 84 | function priceFeedsOrRevert(address token) external view returns (address priceFeed) { 85 | priceFeed = priceFeedsInt[token][false]; 86 | require(priceFeed != address(0), "Price feed is not set"); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /contracts/test/mocks/token/DegenNFTMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | // EXCEPTIONS 7 | import "../../../interfaces/IExceptions.sol"; 8 | 9 | contract DegenNFTMock { 10 | bool revertOnBurn; 11 | 12 | function burn(address, uint256) external view { 13 | if (revertOnBurn) revert InsufficientBalanceException(); 14 | } 15 | 16 | function setRevertOnBurn(bool _revertOnBurn) external { 17 | revertOnBurn = _revertOnBurn; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test/mocks/token/ERC20ApproveRestricted.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.10; 5 | 6 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 7 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 8 | 9 | contract ERC20ApproveRestrictedRevert is ERC20, Ownable { 10 | constructor() ERC20("", "") {} 11 | 12 | function approve(address user, uint256 amount) public override returns (bool) { 13 | if ((allowance(msg.sender, user) > 0) && (amount != 0)) { 14 | revert("Try to change allowance from non-zero to non-zero"); 15 | } 16 | _approve(msg.sender, user, amount); 17 | return true; 18 | } 19 | } 20 | 21 | contract ERC20ApproveRestrictedFalse is ERC20, Ownable { 22 | constructor() ERC20("", "") {} 23 | 24 | function approve(address user, uint256 amount) public override returns (bool) { 25 | if ((allowance(msg.sender, user) > 0) && (amount != 0)) { 26 | return false; 27 | } 28 | _approve(msg.sender, user, amount); 29 | return true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/test/mocks/token/ERC20Blacklistable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 7 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 8 | 9 | contract ERC20BlacklistableMock is ERC20, Ownable { 10 | uint8 private immutable _decimals; 11 | mapping(address => bool) public isBlacklisted; 12 | mapping(address => bool) public isBlackListed; 13 | 14 | constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { 15 | _decimals = decimals_; 16 | } 17 | 18 | function decimals() public view override returns (uint8) { 19 | return _decimals; 20 | } 21 | 22 | function mint(address to, uint256 amount) external onlyOwner returns (bool) { 23 | _mint(to, amount); 24 | return true; 25 | } 26 | 27 | function transfer(address recipient, uint256 amount) public override returns (bool) { 28 | if (isBlacklisted[msg.sender] || isBlacklisted[recipient]) { 29 | revert("Token transaction with blacklisted address"); 30 | } 31 | 32 | _transfer(_msgSender(), recipient, amount); 33 | 34 | return true; 35 | } 36 | 37 | function transferFrom(address from, address to, uint256 amount) public override returns (bool) { 38 | if (isBlacklisted[from] || isBlacklisted[to]) { 39 | revert("Token transaction with blacklisted address"); 40 | } 41 | 42 | address spender = _msgSender(); 43 | _spendAllowance(from, spender, amount); 44 | _transfer(from, to, amount); 45 | return true; 46 | } 47 | 48 | function setBlacklisted(address account, bool status) external { 49 | isBlacklisted[account] = status; 50 | } 51 | 52 | function setBlackListed(address account, bool status) external { 53 | isBlackListed[account] = status; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /contracts/test/mocks/token/ERC20FeeMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {ERC20Mock} from "../../mocks/token/ERC20Mock.sol"; 7 | import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol"; 8 | 9 | contract ERC20FeeMock is ERC20Mock { 10 | uint256 public basisPointsRate; 11 | uint256 public maximumFee; 12 | 13 | constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20Mock(name_, symbol_, decimals_) {} 14 | 15 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 16 | uint256 fee = _computeFee(amount); 17 | _transfer(_msgSender(), recipient, amount - fee); 18 | if (fee > 0) _transfer(_msgSender(), owner(), fee); 19 | return true; 20 | } 21 | 22 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 23 | _spendAllowance(sender, _msgSender(), amount); 24 | uint256 fee = _computeFee(amount); 25 | if (fee > 0) _transfer(sender, owner(), fee); 26 | _transfer(sender, recipient, amount - fee); 27 | return true; 28 | } 29 | 30 | function _computeFee(uint256 amount) internal view returns (uint256) { 31 | uint256 fee = (amount * basisPointsRate) / PERCENTAGE_FACTOR; 32 | if (fee > maximumFee) { 33 | fee = maximumFee; 34 | } 35 | return fee; 36 | } 37 | 38 | function setMaximumFee(uint256 _fee) external { 39 | maximumFee = _fee; 40 | } 41 | 42 | function setBasisPointsRate(uint256 _rate) external { 43 | require(_rate < PERCENTAGE_FACTOR, "Incorrect fee"); 44 | basisPointsRate = _rate; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/test/mocks/token/ERC20Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.10; 5 | 6 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 7 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 8 | 9 | contract ERC20Mock is ERC20, Ownable { 10 | uint8 private immutable _decimals; 11 | address public minter; 12 | 13 | constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { 14 | _decimals = decimals_; 15 | minter = msg.sender; 16 | // _mint(msg.sender, 1e24); 17 | } 18 | 19 | modifier minterOnly() { 20 | require(msg.sender == minter, "Minter calls only"); 21 | _; 22 | } 23 | 24 | function decimals() public view override returns (uint8) { 25 | return _decimals; 26 | } 27 | 28 | function mint(address to, uint256 amount) external minterOnly returns (bool) { 29 | _mint(to, amount); 30 | return true; 31 | } 32 | 33 | function burnFrom(address to, uint256 amount) external minterOnly returns (bool) { 34 | _burn(to, amount); 35 | return true; 36 | } 37 | 38 | function burn(address to, uint256 amount) external returns (bool) { 39 | _burn(to, amount); 40 | return true; 41 | } 42 | 43 | function set_minter(address _minter) external { 44 | minter = _minter; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/test/mocks/token/ERC20PermitMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; 8 | 9 | contract ERC20PermitMock is ERC20Permit { 10 | bytes32 private constant _PERMIT_TYPEHASH = 11 | keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 12 | 13 | uint8 private _decimals; 14 | 15 | constructor(string memory name, string memory symbol, uint8 decimals_) ERC20(name, symbol) ERC20Permit(name) { 16 | _decimals = decimals_; 17 | } 18 | 19 | function decimals() public view override returns (uint8) { 20 | return _decimals; 21 | } 22 | 23 | function getPermitHash(address owner, address spender, uint256 value, uint256 deadline) 24 | public 25 | view 26 | returns (bytes32 permitHash) 27 | { 28 | bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, nonces(owner), deadline)); 29 | permitHash = _hashTypedDataV4(structHash); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/test/mocks/token/WETHMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.10; 3 | 4 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | contract WETHMock is IERC20 { 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 | function mint(address to, uint256 amount) external { 20 | balanceOf[to] += amount; 21 | } 22 | 23 | receive() external payable { 24 | deposit(); // T:[WM-1] 25 | } 26 | 27 | function deposit() public payable { 28 | balanceOf[msg.sender] += msg.value; // T:[WM-1] 29 | emit Deposit(msg.sender, msg.value); // T:[WM-1] 30 | } 31 | 32 | function withdraw(uint256 wad) public { 33 | require(balanceOf[msg.sender] >= wad); // T:[WM-2] 34 | balanceOf[msg.sender] -= wad; // T:[WM-2] 35 | payable(msg.sender).transfer(wad); // T:[WM-3] 36 | emit Withdrawal(msg.sender, wad); // T:[WM-4] 37 | } 38 | 39 | function totalSupply() public view returns (uint256) { 40 | return address(this).balance; // T:[WM-1, 2] 41 | } 42 | 43 | function approve(address guy, uint256 wad) public returns (bool) { 44 | allowance[msg.sender][guy] = wad; // T:[WM-3] 45 | emit Approval(msg.sender, guy, wad); // T:[WM-3] 46 | return true; 47 | } 48 | 49 | function transfer(address dst, uint256 wad) public returns (bool) { 50 | return transferFrom(msg.sender, dst, wad); // T:[WM-4,5,6] 51 | } 52 | 53 | function transferFrom(address src, address dst, uint256 wad) public returns (bool) { 54 | require(balanceOf[src] >= wad); // T:[WM-4] 55 | 56 | if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { 57 | require(allowance[src][msg.sender] >= wad); // T:[WM-4] 58 | allowance[src][msg.sender] -= wad; // T:[WM-7] 59 | } 60 | 61 | balanceOf[src] -= wad; // T:[WM-5] 62 | balanceOf[dst] += wad; // T:[WM-5] 63 | 64 | emit Transfer(src, dst, wad); // T:[WM-6] 65 | 66 | return true; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/test/suites/CreditManagerFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import "@openzeppelin/contracts/utils/Create2.sol"; 7 | 8 | import {CreditManagerV3} from "../../credit/CreditManagerV3.sol"; 9 | import {CreditFacadeV3} from "../../credit/CreditFacadeV3.sol"; 10 | import {CreditConfiguratorV3, CreditManagerOpts} from "../../credit/CreditConfiguratorV3.sol"; 11 | 12 | /// @title CreditManagerFactory 13 | /// @notice Deploys 3 core interdependent contracts: CreditManage, CreditFacadeV3 and CredigConfigurator 14 | /// and setup them by following options 15 | contract CreditManagerFactory { 16 | CreditManagerV3 public creditManager; 17 | CreditFacadeV3 public creditFacade; 18 | CreditConfiguratorV3 public creditConfigurator; 19 | 20 | constructor(address _ap, address _pool, CreditManagerOpts memory opts, bytes32 salt) { 21 | creditManager = new CreditManagerV3(_ap, _pool, opts.name); 22 | creditFacade = new CreditFacadeV3( 23 | address(creditManager), 24 | opts.degenNFT, 25 | opts.expirable 26 | ); 27 | 28 | bytes memory configuratorByteCode = 29 | abi.encodePacked(type(CreditConfiguratorV3).creationCode, abi.encode(creditManager, creditFacade, opts)); 30 | 31 | creditConfigurator = CreditConfiguratorV3(Create2.computeAddress(salt, keccak256(configuratorByteCode))); 32 | 33 | creditManager.setCreditConfigurator(address(creditConfigurator)); 34 | 35 | Create2.deploy(0, salt, configuratorByteCode); 36 | 37 | require(address(creditConfigurator.creditManager()) == address(creditManager), "Incorrect CM"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/test/suites/GenesisFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | import {AddressProviderV3} from "../../core/AddressProviderV3.sol"; 9 | import {ContractsRegister} from "@gearbox-protocol/core-v2/contracts/core/ContractsRegister.sol"; 10 | import {ACL} from "@gearbox-protocol/core-v2/contracts/core/ACL.sol"; 11 | import {AccountFactory} from "@gearbox-protocol/core-v2/contracts/core/AccountFactory.sol"; 12 | import {AccountFactoryV3} from "../../core/AccountFactoryV3.sol"; 13 | import {GearStakingV3} from "../../governance/GearStakingV3.sol"; 14 | import {PriceFeedConfig} from "../interfaces/ICreditConfig.sol"; 15 | 16 | import "../../interfaces/IAddressProviderV3.sol"; 17 | import {BotListV3} from "../../core/BotListV3.sol"; 18 | import {PriceOracleV3} from "../../core/PriceOracleV3.sol"; 19 | import {GearToken} from "@gearbox-protocol/core-v2/contracts/tokens/GearToken.sol"; 20 | 21 | contract GenesisFactory is Ownable { 22 | AddressProviderV3 public addressProvider; 23 | ACL public acl; 24 | PriceOracleV3 public priceOracle; 25 | 26 | constructor(address wethToken, address treasury, uint256 accountFactoryVer) { 27 | acl = new ACL(); 28 | addressProvider = new AddressProviderV3(address(acl)); 29 | addressProvider.setAddress(AP_WETH_TOKEN, wethToken, false); 30 | addressProvider.setAddress(AP_TREASURY, treasury, false); 31 | 32 | ContractsRegister contractsRegister = new ContractsRegister(address(addressProvider)); 33 | addressProvider.setAddress(AP_CONTRACTS_REGISTER, address(contractsRegister), false); 34 | 35 | priceOracle = new PriceOracleV3(address(addressProvider)); 36 | addressProvider.setAddress(AP_PRICE_ORACLE, address(priceOracle), true); 37 | 38 | address accountFactory; 39 | if (accountFactoryVer == 1) { 40 | AccountFactory af = new AccountFactory(address(addressProvider)); 41 | af.addCreditAccount(); 42 | af.addCreditAccount(); 43 | 44 | accountFactory = address(af); 45 | } else { 46 | accountFactory = address(new AccountFactoryV3(address(addressProvider))); 47 | } 48 | 49 | addressProvider.setAddress(AP_ACCOUNT_FACTORY, accountFactory, false); 50 | 51 | BotListV3 botList = new BotListV3(address(addressProvider)); 52 | addressProvider.setAddress(AP_BOT_LIST, address(botList), true); 53 | 54 | GearToken gearToken = new GearToken(address(this)); 55 | addressProvider.setAddress(AP_GEAR_TOKEN, address(gearToken), false); 56 | 57 | GearStakingV3 gearStaking = new GearStakingV3(address(addressProvider), 1); 58 | addressProvider.setAddress(AP_GEAR_STAKING, address(gearStaking), true); 59 | 60 | gearToken.transferOwnership(msg.sender); 61 | acl.transferOwnership(msg.sender); 62 | } 63 | 64 | function addPriceFeeds(PriceFeedConfig[] memory priceFeeds) external onlyOwner { 65 | uint256 len = priceFeeds.length; 66 | for (uint256 i; i < len; ++i) { 67 | priceOracle.setPriceFeed( 68 | priceFeeds[i].token, priceFeeds[i].priceFeed, priceFeeds[i].stalenessPeriod, priceFeeds[i].trusted 69 | ); 70 | } 71 | acl.transferOwnership(msg.sender); 72 | } 73 | 74 | function claimACLOwnership() external onlyOwner { 75 | acl.claimOwnership(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/test/suites/PoolFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import "../../interfaces/IAddressProviderV3.sol"; 7 | import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; 8 | 9 | import {PoolV3} from "../../pool/PoolV3.sol"; 10 | import {LinearInterestRateModelV3} from "../../pool/LinearInterestRateModelV3.sol"; 11 | 12 | import {GaugeV3} from "../../governance/GaugeV3.sol"; 13 | import {PoolQuotaKeeperV3} from "../../pool/PoolQuotaKeeperV3.sol"; 14 | import { 15 | IPoolV3DeployConfig, LinearIRMV3DeployParams, GaugeRate, PoolQuotaLimit 16 | } from "../interfaces/ICreditConfig.sol"; 17 | import {TokensTestSuite} from "./TokensTestSuite.sol"; 18 | 19 | import "../lib/constants.sol"; 20 | 21 | contract PoolFactory is Test { 22 | PoolV3 public pool; 23 | PoolQuotaKeeperV3 public poolQuotaKeeper; 24 | GaugeV3 public gauge; 25 | 26 | constructor( 27 | address addressProvider, 28 | IPoolV3DeployConfig config, 29 | address underlying, 30 | bool supportQuotas, 31 | TokensTestSuite tokensTestSuite 32 | ) { 33 | // uint16 U_1, 34 | // uint16 U_2, 35 | // uint16 R_base, 36 | // uint16 R_slope1, 37 | // uint16 R_slope2, 38 | // uint16 R_slope3, 39 | // bool _isBorrowingMoreU2Forbidden 40 | LinearIRMV3DeployParams memory irmParams = config.irm(); 41 | LinearInterestRateModelV3 irm = new LinearInterestRateModelV3( 42 | irmParams.U_1, 43 | irmParams.U_2, 44 | irmParams.R_base, 45 | irmParams.R_slope1, 46 | irmParams.R_slope2, 47 | irmParams.R_slope3, 48 | irmParams._isBorrowingMoreU2Forbidden 49 | ); 50 | 51 | // address addressProvider_, 52 | // address underlyingToken_, 53 | // address interestRateModel_, 54 | // uint256 totalDebtLimit_, 55 | // string memory namePrefix_, 56 | // string memory symbolPrefix_ 57 | pool = new PoolV3({ 58 | addressProvider_: addressProvider, 59 | underlyingToken_: underlying, 60 | interestRateModel_: address(irm), 61 | totalDebtLimit_: type(uint256).max, 62 | name_: config.name(), 63 | symbol_: config.symbol() 64 | } ); 65 | 66 | if (supportQuotas) { 67 | address gearStaking = IAddressProviderV3(addressProvider).getAddressOrRevert(AP_GEAR_STAKING, 3_00); 68 | 69 | gauge = new GaugeV3(address(pool), gearStaking); 70 | vm.prank(CONFIGURATOR); 71 | gauge.setFrozenEpoch(false); 72 | 73 | vm.label(address(gauge), string.concat("GaugeV3-", config.symbol())); 74 | 75 | poolQuotaKeeper = new PoolQuotaKeeperV3(payable(address(pool))); 76 | 77 | vm.prank(CONFIGURATOR); 78 | poolQuotaKeeper.setGauge(address(gauge)); 79 | 80 | vm.prank(CONFIGURATOR); 81 | pool.setPoolQuotaKeeper(address(poolQuotaKeeper)); 82 | 83 | vm.label(address(poolQuotaKeeper), string.concat("PoolQuotaKeeperV3-", config.symbol())); 84 | 85 | GaugeRate[] memory gaugeRates = config.gaugeRates(); 86 | 87 | uint256 len = gaugeRates.length; 88 | 89 | unchecked { 90 | for (uint256 i; i < len; ++i) { 91 | GaugeRate memory gaugeRate = gaugeRates[i]; 92 | address token = tokensTestSuite.addressOf(gaugeRate.token); 93 | 94 | vm.prank(CONFIGURATOR); 95 | gauge.addQuotaToken(token, gaugeRate.minRate, gaugeRate.maxRate); 96 | } 97 | } 98 | 99 | PoolQuotaLimit[] memory quotaLimits = config.quotaLimits(); 100 | len = quotaLimits.length; 101 | 102 | unchecked { 103 | for (uint256 i; i < len; ++i) { 104 | address token = tokensTestSuite.addressOf(quotaLimits[i].token); 105 | 106 | vm.startPrank(CONFIGURATOR); 107 | poolQuotaKeeper.setTokenLimit(token, quotaLimits[i].limit); 108 | poolQuotaKeeper.setTokenQuotaIncreaseFee(token, quotaLimits[i].quotaIncreaseFee); 109 | vm.stopPrank(); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /contracts/test/suites/TokensTestSuiteHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 8 | 9 | import {IWETH} from "@gearbox-protocol/core-v2/contracts/interfaces/external/IWETH.sol"; 10 | 11 | import {ITokenTestSuite} from "../interfaces/ITokenTestSuite.sol"; 12 | 13 | // MOCKS 14 | import {ERC20Mock} from "../mocks/token/ERC20Mock.sol"; 15 | 16 | import {Test} from "forge-std/Test.sol"; 17 | 18 | contract TokensTestSuiteHelper is Test, ITokenTestSuite { 19 | using SafeERC20 for IERC20; 20 | 21 | uint256 chainId; 22 | address public wethToken; 23 | 24 | function topUpWETH() public payable override { 25 | IWETH(wethToken).deposit{value: msg.value}(); 26 | } 27 | 28 | function topUpWETH(address onBehalfOf, uint256 value) public override { 29 | vm.prank(onBehalfOf); 30 | IWETH(wethToken).deposit{value: value}(); 31 | } 32 | 33 | function mint(address token, address to, uint256 amount) public virtual override { 34 | if (token == wethToken) { 35 | vm.deal(address(this), amount); 36 | IWETH(wethToken).deposit{value: amount}(); 37 | IERC20(token).transfer(to, amount); 38 | } else { 39 | // ERC20Mock(token).mint(to, amount); 40 | if (chainId == 1337 || chainId == 31337) ERC20Mock(token).mint(to, amount); 41 | // Live test case 42 | else deal(token, to, amount, false); 43 | } 44 | } 45 | 46 | function balanceOf(address token, address holder) public view override returns (uint256 balance) { 47 | balance = IERC20(token).balanceOf(holder); 48 | } 49 | 50 | function approve(address token, address holder, address targetContract) public override { 51 | approve(token, holder, targetContract, type(uint256).max); 52 | } 53 | 54 | function approve(address token, address holder, address targetContract, uint256 amount) public override { 55 | vm.startPrank(holder); 56 | IERC20(token).forceApprove(targetContract, amount); 57 | vm.stopPrank(); 58 | } 59 | 60 | function burn(address token, address from, uint256 amount) public override { 61 | ERC20Mock(token).burn(from, amount); 62 | } 63 | 64 | receive() external payable {} 65 | } 66 | -------------------------------------------------------------------------------- /contracts/test/suites/WETHMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.10; 3 | 4 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | contract WETHMock is IERC20 { 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 | function mint(address to, uint256 amount) external { 20 | balanceOf[to] += amount; 21 | } 22 | 23 | receive() external payable { 24 | deposit(); // T:[WM-1] 25 | } 26 | 27 | function deposit() public payable { 28 | balanceOf[msg.sender] += msg.value; // T:[WM-1] 29 | emit Deposit(msg.sender, msg.value); // T:[WM-1] 30 | } 31 | 32 | function withdraw(uint256 wad) public { 33 | require(balanceOf[msg.sender] >= wad); // T:[WM-2] 34 | balanceOf[msg.sender] -= wad; // T:[WM-2] 35 | payable(msg.sender).transfer(wad); // T:[WM-3] 36 | emit Withdrawal(msg.sender, wad); // T:[WM-4] 37 | } 38 | 39 | function totalSupply() public view returns (uint256) { 40 | return address(this).balance; // T:[WM-1, 2] 41 | } 42 | 43 | function approve(address guy, uint256 wad) public returns (bool) { 44 | allowance[msg.sender][guy] = wad; // T:[WM-3] 45 | emit Approval(msg.sender, guy, wad); // T:[WM-3] 46 | return true; 47 | } 48 | 49 | function transfer(address dst, uint256 wad) public returns (bool) { 50 | return transferFrom(msg.sender, dst, wad); // T:[WM-4,5,6] 51 | } 52 | 53 | function transferFrom(address src, address dst, uint256 wad) public returns (bool) { 54 | require(balanceOf[src] >= wad); // T:[WM-4] 55 | 56 | if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { 57 | require(allowance[src][msg.sender] >= wad); // T:[WM-4] 58 | allowance[src][msg.sender] -= wad; // T:[WM-7] 59 | } 60 | 61 | balanceOf[src] -= wad; // T:[WM-5] 62 | balanceOf[dst] += wad; // T:[WM-5] 63 | 64 | emit Transfer(src, dst, wad); // T:[WM-6] 65 | 66 | return true; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/test/unit/core/AccountFactoryV3Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {AccountFactoryV3, FactoryParams, QueuedAccount} from "../../../core/AccountFactoryV3.sol"; 7 | 8 | contract AccountFactoryV3Harness is AccountFactoryV3 { 9 | constructor(address addressProvider) AccountFactoryV3(addressProvider) {} 10 | 11 | function queuedAccounts(address creditManager, uint256 index) external view returns (QueuedAccount memory) { 12 | return _queuedAccounts[creditManager][index]; 13 | } 14 | 15 | function setQueuedAccount(address creditManager, uint256 index, address creditAccount, uint40 reusableAfter) 16 | external 17 | { 18 | _queuedAccounts[creditManager][index] = QueuedAccount(creditAccount, reusableAfter); 19 | } 20 | 21 | function factoryParams(address creditManager) external view returns (FactoryParams memory) { 22 | return _factoryParams[creditManager]; 23 | } 24 | 25 | function setFactoryParams(address creditManager, address masterCreditAccount, uint40 head, uint40 tail) external { 26 | _factoryParams[creditManager] = FactoryParams(masterCreditAccount, head, tail); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/test/unit/core/PriceOracleV3Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {PriceOracleV3, PriceFeedParams} from "../../../core/PriceOracleV3.sol"; 7 | 8 | contract PriceOracleV3Harness is PriceOracleV3 { 9 | constructor(address addressProvider) PriceOracleV3(addressProvider) {} 10 | 11 | function getTokenReserveKey(address token) external pure returns (address) { 12 | return _getTokenReserveKey(token); 13 | } 14 | 15 | function getPriceFeedParams(address token) external view returns (PriceFeedParams memory) { 16 | (address priceFeed, uint32 stalenessPeriod, bool skipCheck, uint8 decimals, bool useReserve, bool trusted) = 17 | _getPriceFeedParams(token); 18 | return PriceFeedParams(priceFeed, stalenessPeriod, skipCheck, decimals, useReserve, trusted); 19 | } 20 | 21 | function getReservePriceFeedParams(address token) external view returns (PriceFeedParams memory) { 22 | (address priceFeed, uint32 stalenessPeriod, bool skipCheck, uint8 decimals, bool useReserve, bool trusted) = 23 | _getPriceFeedParams(_getTokenReserveKey(token)); 24 | return PriceFeedParams(priceFeed, stalenessPeriod, skipCheck, decimals, useReserve, trusted); 25 | } 26 | 27 | function getPrice(address priceFeed, uint32 stalenessPeriod, bool skipCheck, uint8 decimals) 28 | external 29 | view 30 | returns (uint256 price, uint256 scale) 31 | { 32 | return _getPrice(priceFeed, stalenessPeriod, skipCheck, decimals); 33 | } 34 | 35 | function hackPriceFeedParams(address token, PriceFeedParams memory params) external { 36 | _priceFeedsParams[token] = params; 37 | } 38 | 39 | function hackReservePriceFeedParams(address token, PriceFeedParams memory params) external { 40 | _priceFeedsParams[_getTokenReserveKey(token)] = params; 41 | } 42 | 43 | function validateToken(address token) external view returns (uint8 decimals) { 44 | return _validateToken(token); 45 | } 46 | 47 | function validatePriceFeed(address priceFeed, uint32 stalenessPeriod) external view returns (bool skipCheck) { 48 | return _validatePriceFeed(priceFeed, stalenessPeriod); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test/unit/credit/CreditAccountV3.unit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | 8 | import {CreditAccountV3} from "../../../credit/CreditAccountV3.sol"; 9 | import {CallerNotAccountFactoryException, CallerNotCreditManagerException} from "../../../interfaces/IExceptions.sol"; 10 | 11 | import {TestHelper} from "../../lib/helper.sol"; 12 | 13 | /// @title Credit account V3 unit test 14 | /// @notice U:[CA]: Unit tests for credit account 15 | contract CreditAccountV3UnitTest is TestHelper { 16 | CreditAccountV3 creditAccount; 17 | 18 | address factory; 19 | address creditManager; 20 | 21 | function setUp() public { 22 | factory = makeAddr("ACCOUNT_FACTORY"); 23 | creditManager = makeAddr("CREDIT_MANAGER"); 24 | 25 | vm.prank(factory); 26 | creditAccount = new CreditAccountV3(creditManager); 27 | } 28 | 29 | /// @notice U:[CA-1]: Constructor sets correct values 30 | function test_U_CA_01_constructor_sets_correct_values(address factory_, address creditManager_) public { 31 | vm.assume(factory_ != creditManager_); 32 | 33 | vm.prank(factory_); 34 | CreditAccountV3 creditAccount_ = new CreditAccountV3(creditManager_); 35 | 36 | assertEq(creditAccount_.factory(), factory_, "Incorrect factory"); 37 | assertEq(creditAccount_.creditManager(), creditManager_, "Incorrect creditManager"); 38 | } 39 | 40 | /// @notice U:[CA-2]: External functions have correct access 41 | function test_U_CA_02_external_functions_have_correct_access(address caller) public { 42 | vm.startPrank(caller); 43 | if (caller != creditManager) { 44 | vm.expectRevert(CallerNotCreditManagerException.selector); 45 | creditAccount.safeTransfer({token: address(0), to: address(0), amount: 0}); 46 | } 47 | if (caller != creditManager) { 48 | vm.expectRevert(CallerNotCreditManagerException.selector); 49 | creditAccount.execute({target: address(0), data: bytes("")}); 50 | } 51 | if (caller != factory) { 52 | vm.expectRevert(CallerNotAccountFactoryException.selector); 53 | creditAccount.rescue({target: address(0), data: bytes("")}); 54 | } 55 | vm.stopPrank(); 56 | } 57 | 58 | /// @notice U:[CA-3]: `safeTransfer` works correctly 59 | function test_U_CA_03_safeTransfer_works_correctly(address token, address to, uint256 amount) public { 60 | vm.assume(token != address(vm)); // just brilliant 61 | vm.mockCall(token, abi.encodeCall(IERC20.transfer, (to, amount)), bytes("")); 62 | vm.expectCall(token, abi.encodeCall(IERC20.transfer, (to, amount))); 63 | vm.prank(creditManager); 64 | creditAccount.safeTransfer({token: token, to: to, amount: amount}); 65 | } 66 | 67 | /// @notice U:[CA-4]: `execute` works correctly 68 | function test_U_CA_04_execute_works_correctly(address target, bytes memory data, bytes memory expResult) public { 69 | vm.assume(target != address(vm)); 70 | vm.mockCall(target, data, expResult); 71 | vm.expectCall(target, data); 72 | vm.prank(creditManager); 73 | bytes memory result = creditAccount.execute(target, data); 74 | assertEq(result, expResult, "Incorrect result"); 75 | } 76 | 77 | /// @notice U:[CA-5]: `rescue` works correctly 78 | function test_U_CA_05_rescue_works_correctly(address target, bytes memory data) public { 79 | vm.assume(target != address(vm)); 80 | vm.mockCall(target, data, bytes("")); 81 | vm.expectCall(target, data); 82 | vm.prank(factory); 83 | creditAccount.rescue(target, data); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /contracts/test/unit/credit/CreditFacadeV3Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import "../../../interfaces/ICreditFacadeV3.sol"; 7 | import {CreditFacadeV3} from "../../../credit/CreditFacadeV3.sol"; 8 | import {ManageDebtAction} from "../../../interfaces/ICreditManagerV3.sol"; 9 | import {BalanceWithMask} from "../../../libraries/BalancesLogic.sol"; 10 | 11 | contract CreditFacadeV3Harness is CreditFacadeV3 { 12 | constructor(address _creditManager, address _degenNFT, bool _expirable) 13 | CreditFacadeV3(_creditManager, _degenNFT, _expirable) 14 | {} 15 | 16 | function setReentrancy(uint8 _status) external { 17 | _reentrancyStatus = _status; 18 | } 19 | 20 | function setCumulativeLoss(uint128 newLoss) external { 21 | lossParams.currentCumulativeLoss = newLoss; 22 | } 23 | 24 | function multicallInt(address creditAccount, MultiCall[] calldata calls, uint256 enabledTokensMask, uint256 flags) 25 | external 26 | returns (FullCheckParams memory fullCheckParams) 27 | { 28 | return _multicall(creditAccount, calls, enabledTokensMask, flags, 0); 29 | } 30 | 31 | function applyPriceOnDemandInt(MultiCall[] calldata calls) external returns (uint256 remainingCalls) { 32 | return _applyOnDemandPriceUpdates(calls); 33 | } 34 | 35 | function fullCollateralCheckInt( 36 | address creditAccount, 37 | uint256 enabledTokensMaskBefore, 38 | FullCheckParams memory fullCheckParams, 39 | BalanceWithMask[] memory forbiddenBalances, 40 | uint256 forbiddenTokensMask 41 | ) external { 42 | _fullCollateralCheck( 43 | creditAccount, enabledTokensMaskBefore, fullCheckParams, forbiddenBalances, forbiddenTokensMask 44 | ); 45 | } 46 | 47 | function revertIfNoPermission(uint256 flags, uint256 permission) external pure { 48 | _revertIfNoPermission(flags, permission); 49 | } 50 | 51 | function revertIfOutOfBorrowingLimit(uint256 amount) external { 52 | _revertIfOutOfBorrowingLimit(amount); 53 | } 54 | 55 | function setLastBlockBorrowed(uint64 _lastBlockBorrowed) external { 56 | lastBlockBorrowed = _lastBlockBorrowed; 57 | } 58 | 59 | function setTotalBorrowedInBlock(uint128 _totalBorrowedInBlock) external { 60 | totalBorrowedInBlock = _totalBorrowedInBlock; 61 | } 62 | 63 | function lastBlockBorrowedInt() external view returns (uint64) { 64 | return lastBlockBorrowed; 65 | } 66 | 67 | function totalBorrowedInBlockInt() external view returns (uint128) { 68 | return totalBorrowedInBlock; 69 | } 70 | 71 | function revertIfOutOfDebtLimits(uint256 debt) external view { 72 | _revertIfOutOfDebtLimits(debt); 73 | } 74 | 75 | function isExpired() external view returns (bool) { 76 | return _isExpired(); 77 | } 78 | 79 | function setCurrentCumulativeLoss(uint128 _currentCumulativeLoss) external { 80 | lossParams.currentCumulativeLoss = _currentCumulativeLoss; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /contracts/test/unit/governance/GaugeV3Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | pragma abicoder v1; 6 | 7 | // INTERFACES 8 | import {GaugeV3, QuotaRateParams, UserVotes} from "../../../governance/GaugeV3.sol"; 9 | 10 | contract GaugeV3Harness is GaugeV3 { 11 | constructor(address _pool, address _gearStaking) GaugeV3(_pool, _gearStaking) {} 12 | 13 | function setQuotaRateParams( 14 | address token, 15 | uint16 minRate, 16 | uint16 maxRate, 17 | uint96 totalVotesLpSide, 18 | uint96 totalVotesCaSide 19 | ) external { 20 | quotaRateParams[token] = QuotaRateParams({ 21 | minRate: minRate, 22 | maxRate: maxRate, 23 | totalVotesLpSide: totalVotesLpSide, 24 | totalVotesCaSide: totalVotesCaSide 25 | }); 26 | } 27 | 28 | function setUserTokenVotes(address user, address token, uint96 votesLpSide, uint96 votesCaSide) external { 29 | userTokenVotes[user][token] = UserVotes({votesLpSide: votesLpSide, votesCaSide: votesCaSide}); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/test/unit/governance/PolicyManagerV3Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {PolicyManagerV3, Policy} from "../../../governance/PolicyManagerV3.sol"; 7 | 8 | contract PolicyManagerV3Harness is PolicyManagerV3 { 9 | constructor(address _addressProvider) PolicyManagerV3(_addressProvider) {} 10 | 11 | function checkPolicy(address contractAddress, string memory paramName, uint256 oldValue, uint256 newValue) 12 | external 13 | returns (bool) 14 | { 15 | return _checkPolicy(contractAddress, paramName, oldValue, newValue); 16 | } 17 | 18 | function checkPolicy(bytes32 policyHash, uint256 oldValue, uint256 newValue) external returns (bool) { 19 | return _checkPolicy(policyHash, oldValue, newValue); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/test/unit/libraries/BitMask.unit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IncorrectParameterException} from "../../../interfaces/IExceptions.sol"; 7 | import {BitMask} from "../../../libraries/BitMask.sol"; 8 | 9 | import {TestHelper} from "../../lib/helper.sol"; 10 | 11 | /// @title BitMask logic test 12 | /// @notice U:[BM]: Unit tests for bit mask library 13 | contract BitMaskUnitTest is TestHelper { 14 | using BitMask for uint256; 15 | 16 | /// @notice U:[BM-1]: `calcIndex` reverts for zero value 17 | function test_U_BM_01_calcIndex_reverts_for_zero_value() public { 18 | vm.expectRevert(IncorrectParameterException.selector); 19 | uint256(0).calcIndex(); 20 | } 21 | 22 | /// @notice U:[BM-2]: `calcIndex` works correctly 23 | function test_U_BM_02_calcIndex_works_correctly() public { 24 | for (uint256 i = 0; i < 256; ++i) { 25 | uint256 mask = 1 << i; 26 | assertEq(mask.calcIndex(), i, "Incorrect index"); 27 | } 28 | } 29 | 30 | /// @notice U:[BM-3]: `calcEnabledTokens` works correctly 31 | function test_U_BM_03_calcEnabledTokens_works_correctly(uint8 bitsToEnable, uint256 randomValue) public { 32 | uint256 bitMask; 33 | 34 | for (uint256 i; i < bitsToEnable;) { 35 | randomValue = uint256(keccak256(abi.encodePacked(randomValue))); 36 | uint256 randMask = 1 << uint8(randomValue % 255); 37 | if (randMask & bitMask == 0) { 38 | bitMask |= randMask; 39 | ++i; 40 | } 41 | } 42 | 43 | assertEq(bitMask.calcEnabledTokens(), bitsToEnable, "Incorrect bits computation"); 44 | } 45 | 46 | /// @notice U:[BM-4]: `enable` & `disable` works correctly 47 | function test_U_BM_04_enable_and_disable_works_correctly(uint8 bit) public { 48 | uint256 mask; 49 | mask = mask.enable(1 << bit); 50 | assertEq(mask, 1 << bit, "Enable doesn't work"); 51 | 52 | mask = mask.disable(1 << bit); 53 | assertEq(mask, 0, "Disable doesn't work"); 54 | } 55 | 56 | /// @notice U:[BM-5]: `enableDisable` works correctly 57 | function test_U_BM_05_enableDisable_works_correctly(uint8 bit) public { 58 | uint256 mask; 59 | 60 | mask = mask.enableDisable(1 << bit, 0); 61 | assertEq(mask, 1 << bit, "Enable doesn't work"); 62 | 63 | mask = mask.enableDisable(0, 1 << bit); 64 | assertEq(mask, 0, "Disable doesn't work"); 65 | } 66 | 67 | /// @notice U:[BM-6]: `enable` & `disable` works correctly 68 | function test_U_BM_06_enable_and_disable_works_correctly(uint8 bit) public { 69 | uint256 mask; 70 | mask = mask.enable(1 << bit, 0); 71 | assertEq(mask, 0, "Enable doesn't work"); 72 | 73 | mask = mask.enable(1 << bit, 1 << bit); 74 | assertEq(mask, 1 << bit, "Enable doesn't work"); 75 | 76 | mask = mask.disable(1 << bit, 0); 77 | assertEq(mask, 1 << bit, "Disable doesn't work"); 78 | 79 | mask = mask.disable(1 << bit, 1 << bit); 80 | assertEq(mask, 0, "Disable doesn't work"); 81 | } 82 | 83 | /// @notice U:[BM-7]: `enableWithSkip` works correctly 84 | function test_U_BM_07_enableWithSkip_works_correctly(uint8 bit) public { 85 | uint256 mask; 86 | 87 | mask = mask.enableDisable(1 << bit, 0, 0); 88 | assertEq(mask, 0, "Enable doesn't work"); 89 | 90 | mask = mask.enableDisable(1 << bit, 0, 1 << bit); 91 | assertEq(mask, 1 << bit, "Enable doesn't work"); 92 | 93 | mask = mask.enableDisable(0, 1 << bit, 0); 94 | assertEq(mask, 1 << bit, "Disable doesn't work"); 95 | 96 | mask = mask.enableDisable(0, 1 << bit, 1 << bit); 97 | assertEq(mask, 0, "Disable doesn't work"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /contracts/test/unit/libraries/CreditAccountHelper.unit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {CreditAccountHelper} from "../../../libraries/CreditAccountHelper.sol"; 7 | import {ICreditAccountBase} from "../../../interfaces/ICreditAccountV3.sol"; 8 | import {CreditAccountV3} from "../../../credit/CreditAccountV3.sol"; 9 | 10 | import {ERC20ApproveRestrictedRevert, ERC20ApproveRestrictedFalse} from "../../mocks/token/ERC20ApproveRestricted.sol"; 11 | 12 | import {TokensTestSuite} from "../../suites/TokensTestSuite.sol"; 13 | import "@gearbox-protocol/sdk-gov/contracts/Tokens.sol"; 14 | import {TestHelper} from "../../lib/helper.sol"; 15 | import {BalanceHelper} from "../../helpers/BalanceHelper.sol"; 16 | 17 | import "../../lib/constants.sol"; 18 | 19 | /// @title CreditAccountHelper logic test 20 | /// @notice U:[CAH]: Unit tests for credit account helper 21 | contract CreditAccountHelperUnitTest is TestHelper, BalanceHelper { 22 | using CreditAccountHelper for ICreditAccountBase; 23 | 24 | address creditAccount; 25 | 26 | function setUp() public { 27 | tokenTestSuite = new TokensTestSuite(); 28 | creditAccount = address(new CreditAccountV3(address(this))); 29 | } 30 | 31 | /// @notice U:[CAH-1]: approveCreditAccount approves with desired allowance 32 | function test_U_CAH_01_safeApprove_approves_with_desired_allowance() public { 33 | // Case, when current allowance > Allowance_THRESHOLD 34 | tokenTestSuite.approve(TOKEN_DAI, creditAccount, DUMB_ADDRESS, 200); 35 | 36 | address dai = tokenTestSuite.addressOf(TOKEN_DAI); 37 | 38 | ICreditAccountBase(creditAccount).safeApprove(dai, DUMB_ADDRESS, DAI_EXCHANGE_AMOUNT); 39 | 40 | expectAllowance(TOKEN_DAI, creditAccount, DUMB_ADDRESS, DAI_EXCHANGE_AMOUNT); 41 | } 42 | 43 | /// @dev U:[CAH-2]: approveCreditAccount works for ERC20 that revert if allowance > 0 before approve 44 | function test_U_CAH_02_safeApprove_works_for_ERC20_with_approve_restrictions() public { 45 | address approveRevertToken = address(new ERC20ApproveRestrictedRevert()); 46 | 47 | ICreditAccountBase(creditAccount).safeApprove(approveRevertToken, DUMB_ADDRESS, DAI_EXCHANGE_AMOUNT); 48 | 49 | ICreditAccountBase(creditAccount).safeApprove(approveRevertToken, DUMB_ADDRESS, 2 * DAI_EXCHANGE_AMOUNT); 50 | 51 | expectAllowance(approveRevertToken, creditAccount, DUMB_ADDRESS, 2 * DAI_EXCHANGE_AMOUNT); 52 | 53 | address approveFalseToken = address(new ERC20ApproveRestrictedFalse()); 54 | 55 | ICreditAccountBase(creditAccount).safeApprove(approveFalseToken, DUMB_ADDRESS, DAI_EXCHANGE_AMOUNT); 56 | 57 | ICreditAccountBase(creditAccount).safeApprove(approveFalseToken, DUMB_ADDRESS, 2 * DAI_EXCHANGE_AMOUNT); 58 | 59 | expectAllowance(approveFalseToken, creditAccount, DUMB_ADDRESS, 2 * DAI_EXCHANGE_AMOUNT); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /contracts/test/unit/libraries/UnsafeERC20.unit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | 8 | import {UnsafeERC20} from "../../../libraries/UnsafeERC20.sol"; 9 | 10 | import {TestHelper} from "../../lib/helper.sol"; 11 | 12 | /// @title UnsafeERC20 library unit test 13 | /// @notice U:[UE]: Unit tests for UnsafeERC20 library 14 | contract UnsafeERC20UnitTest is TestHelper { 15 | using UnsafeERC20 for IERC20; 16 | 17 | struct UnsafeTransferTestCase { 18 | string name; 19 | bytes transferOutput; 20 | bool transferReverts; 21 | bool expectedResult; 22 | } 23 | 24 | /// @notice U:[UE-1]: `unsafeTransfer` works correctly 25 | function test_U_UE_01_unsafeTransfer_works_correctly() public { 26 | UnsafeTransferTestCase[4] memory cases = [ 27 | UnsafeTransferTestCase({ 28 | name: "function reverts", 29 | transferOutput: bytes(""), 30 | transferReverts: true, 31 | expectedResult: false 32 | }), 33 | UnsafeTransferTestCase({ 34 | name: "function returns false", 35 | transferOutput: abi.encode(false), 36 | transferReverts: false, 37 | expectedResult: false 38 | }), 39 | UnsafeTransferTestCase({ 40 | name: "function returns true", 41 | transferOutput: abi.encode(true), 42 | transferReverts: false, 43 | expectedResult: true 44 | }), 45 | UnsafeTransferTestCase({ 46 | name: "function returns nothing", 47 | transferOutput: bytes(""), 48 | transferReverts: false, 49 | expectedResult: true 50 | }) 51 | ]; 52 | 53 | address token = makeAddr("TOKEN"); 54 | address to = makeAddr("TO"); 55 | uint256 amount = 1 ether; 56 | bytes memory transferCallData = abi.encodeCall(IERC20.transfer, (to, amount)); 57 | 58 | for (uint256 i; i < cases.length; ++i) { 59 | if (cases[i].transferReverts) { 60 | vm.mockCallRevert(token, transferCallData, cases[i].transferOutput); 61 | } else { 62 | vm.mockCall(token, transferCallData, cases[i].transferOutput); 63 | } 64 | 65 | vm.expectCall(token, transferCallData); 66 | 67 | bool result = IERC20(token).unsafeTransfer(to, amount); 68 | assertEq(result, cases[i].expectedResult, _testCaseErr(cases[i].name, "Incorrect result")); 69 | } 70 | } 71 | 72 | /// @notice U:[UE-2]: `unsafeTransferFrom` works correctly 73 | function test_U_UE_02_unsafeTransferFrom_works_correctly() public { 74 | UnsafeTransferTestCase[4] memory cases = [ 75 | UnsafeTransferTestCase({ 76 | name: "function reverts", 77 | transferOutput: bytes(""), 78 | transferReverts: true, 79 | expectedResult: false 80 | }), 81 | UnsafeTransferTestCase({ 82 | name: "function returns false", 83 | transferOutput: abi.encode(false), 84 | transferReverts: false, 85 | expectedResult: false 86 | }), 87 | UnsafeTransferTestCase({ 88 | name: "function returns true", 89 | transferOutput: abi.encode(true), 90 | transferReverts: false, 91 | expectedResult: true 92 | }), 93 | UnsafeTransferTestCase({ 94 | name: "function returns nothing", 95 | transferOutput: bytes(""), 96 | transferReverts: false, 97 | expectedResult: true 98 | }) 99 | ]; 100 | 101 | address token = makeAddr("TOKEN"); 102 | address from = makeAddr("FROM"); 103 | address to = makeAddr("TO"); 104 | uint256 amount = 1 ether; 105 | bytes memory transferFromCallData = abi.encodeCall(IERC20.transferFrom, (from, to, amount)); 106 | 107 | for (uint256 i; i < cases.length; ++i) { 108 | if (cases[i].transferReverts) { 109 | vm.mockCallRevert(token, transferFromCallData, cases[i].transferOutput); 110 | } else { 111 | vm.mockCall(token, transferFromCallData, cases[i].transferOutput); 112 | } 113 | 114 | vm.expectCall(token, transferFromCallData); 115 | 116 | bool result = IERC20(token).unsafeTransferFrom(from, to, amount); 117 | assertEq(result, cases[i].expectedResult, _testCaseErr(cases[i].name, "Incorrect result")); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /contracts/test/unit/pool/PoolV3Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {PoolV3} from "../../../pool/PoolV3.sol"; 7 | import {ENTERED, NOT_ENTERED} from "../../../traits/ReentrancyGuardTrait.sol"; 8 | import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol"; 9 | 10 | contract PoolV3Harness is PoolV3 { 11 | uint16 _transferFee; 12 | 13 | constructor( 14 | address addressProvider_, 15 | address underlyingToken_, 16 | address interestRateModel_, 17 | uint256 totalDebtLimit_, 18 | string memory name_, 19 | string memory symbol_ 20 | ) PoolV3(addressProvider_, underlyingToken_, interestRateModel_, totalDebtLimit_, name_, symbol_) {} 21 | 22 | // ------- // 23 | // GENERAL // 24 | // ------- // 25 | 26 | function hackReentrancyStatus(bool entered) external { 27 | _reentrancyStatus = entered ? ENTERED : NOT_ENTERED; 28 | } 29 | 30 | function hackExpectedLiquidityLU(uint256 value) external { 31 | _expectedLiquidityLU = uint128(value); 32 | } 33 | 34 | // --------- // 35 | // BORROWING // 36 | // --------- // 37 | 38 | function hackTotalBorrowed(uint256 value) external { 39 | _totalDebt.borrowed = uint128(value); 40 | } 41 | 42 | function hackCreditManagerBorrowed(address creditManager, uint256 value) external { 43 | _creditManagerDebt[creditManager].borrowed = uint128(value); 44 | } 45 | 46 | // ------------- // 47 | // INTEREST RATE // 48 | // ------------- // 49 | 50 | function hackBaseInterestRate(uint256 value) external { 51 | _baseInterestRate = uint128(value); 52 | lastBaseInterestUpdate = uint40(block.timestamp); 53 | } 54 | 55 | function hackBaseInterestIndexLU(uint256 value) external { 56 | _baseInterestIndexLU = uint128(value); 57 | } 58 | 59 | function calcBaseInterestAccrued() external view returns (uint256) { 60 | return _calcBaseInterestAccrued(); 61 | } 62 | 63 | function updateBaseInterest( 64 | int256 expectedLiquidityDelta, 65 | int256 availableLiquidityDelta, 66 | bool checkOptimalBorrowing 67 | ) external { 68 | _updateBaseInterest(expectedLiquidityDelta, availableLiquidityDelta, checkOptimalBorrowing); 69 | } 70 | 71 | // ------ // 72 | // QUOTAS // 73 | // ------ // 74 | 75 | function hackQuotaRevenue(uint256 value) external { 76 | _quotaRevenue = uint96(value); 77 | lastQuotaRevenueUpdate = uint40(block.timestamp); 78 | } 79 | 80 | function calcQuotaRevenueAccrued() external view returns (uint256) { 81 | return _calcQuotaRevenueAccrued(); 82 | } 83 | 84 | // ------------- // 85 | // TRANSFER FEES // 86 | // ------------- // 87 | 88 | function hackTransferFee(uint256 value) external { 89 | _transferFee = uint16(value); 90 | } 91 | 92 | function _amountWithFee(uint256 amount) internal view override returns (uint256) { 93 | return amount * PERCENTAGE_FACTOR / (PERCENTAGE_FACTOR - _transferFee); 94 | } 95 | 96 | function _amountMinusFee(uint256 amount) internal view override returns (uint256) { 97 | return amount * (PERCENTAGE_FACTOR - _transferFee) / PERCENTAGE_FACTOR; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /contracts/test/unit/traits/USDT_Transfer.unit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {USDT_Transfer} from "../../../traits/USDT_Transfer.sol"; 7 | import {TestHelper} from "../../lib/helper.sol"; 8 | 9 | contract USDT_TransferUnitTest is USDT_Transfer, TestHelper { 10 | uint256 constant SCALE = 10 ** 18; 11 | 12 | uint256 public basisPointsRate; 13 | uint256 public maximumFee; 14 | 15 | constructor() USDT_Transfer(address(this)) {} 16 | 17 | /// @notice U:[UTT-1]: `amountUSDTWithFee` and `amountUSDTMinusFee` work correctly 18 | /// forge-config: default.fuzz.runs = 50000 19 | function testFuzz_U_UTT_01_amountUSDTWithFee_amountUSDTMinusFee_work_correctly( 20 | uint256 amount, 21 | uint256 feeRate, 22 | uint256 maxFee 23 | ) public { 24 | amount = bound(amount, 0, 10 ** 10) * SCALE; // up to 10B USDT 25 | basisPointsRate = bound(feeRate, 0, 100); // up to 1% 26 | maximumFee = bound(maxFee, 0, 1000) * SCALE; // up to 1000 USDT 27 | 28 | // direction checks 29 | assertGe(_amountUSDTWithFee(amount), amount, "amountWithFee less than amount"); 30 | assertLe(_amountUSDTMinusFee(amount), amount, "amountMinusFee greater than amount"); 31 | 32 | // maximum fee checks 33 | assertLe(_amountUSDTWithFee(amount), amount + maximumFee, "amountWithFee fee greater than maximum"); 34 | assertGe(_amountUSDTMinusFee(amount) + maximumFee, amount, "amountMinusFee fee greater than maximum"); 35 | 36 | // inversion checks 37 | assertEq(_amountUSDTMinusFee(_amountUSDTWithFee(amount)), amount, "amountMinusFee not inverse of amountWithFee"); 38 | assertEq(_amountUSDTWithFee(_amountUSDTMinusFee(amount)), amount, "amountWithFee not inverse of amountMinusFee"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/traits/ACLNonReentrantTrait.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; 7 | 8 | import {IACL} from "@gearbox-protocol/core-v2/contracts/interfaces/IACL.sol"; 9 | import { 10 | CallerNotControllerException, 11 | CallerNotPausableAdminException, 12 | CallerNotUnpausableAdminException 13 | } from "../interfaces/IExceptions.sol"; 14 | 15 | import {ACLTrait} from "./ACLTrait.sol"; 16 | import {ReentrancyGuardTrait} from "./ReentrancyGuardTrait.sol"; 17 | 18 | /// @title ACL non-reentrant trait 19 | /// @notice Extended version of `ACLTrait` that implements pausable functionality, 20 | /// reentrancy protection and external controller role 21 | abstract contract ACLNonReentrantTrait is ACLTrait, Pausable, ReentrancyGuardTrait { 22 | /// @notice Emitted when new external controller is set 23 | event NewController(address indexed newController); 24 | 25 | /// @notice External controller address 26 | address public controller; 27 | 28 | /// @dev Ensures that function caller is external controller or configurator 29 | modifier controllerOnly() { 30 | _ensureCallerIsControllerOrConfigurator(); 31 | _; 32 | } 33 | 34 | /// @dev Reverts if the caller is not controller or configurator 35 | /// @dev Used to cut contract size on modifiers 36 | function _ensureCallerIsControllerOrConfigurator() internal view { 37 | if (msg.sender != controller && !_isConfigurator({account: msg.sender})) { 38 | revert CallerNotControllerException(); 39 | } 40 | } 41 | 42 | /// @dev Ensures that function caller has pausable admin role 43 | modifier pausableAdminsOnly() { 44 | _ensureCallerIsPausableAdmin(); 45 | _; 46 | } 47 | 48 | /// @dev Reverts if the caller is not pausable admin 49 | /// @dev Used to cut contract size on modifiers 50 | function _ensureCallerIsPausableAdmin() internal view { 51 | if (!_isPausableAdmin({account: msg.sender})) { 52 | revert CallerNotPausableAdminException(); 53 | } 54 | } 55 | 56 | /// @dev Ensures that function caller has unpausable admin role 57 | modifier unpausableAdminsOnly() { 58 | _ensureCallerIsUnpausableAdmin(); 59 | _; 60 | } 61 | 62 | /// @dev Reverts if the caller is not unpausable admin 63 | /// @dev Used to cut contract size on modifiers 64 | function _ensureCallerIsUnpausableAdmin() internal view { 65 | if (!_isUnpausableAdmin({account: msg.sender})) { 66 | revert CallerNotUnpausableAdminException(); 67 | } 68 | } 69 | 70 | /// @notice Constructor 71 | /// @param addressProvider Address provider contract address 72 | constructor(address addressProvider) ACLTrait(addressProvider) { 73 | controller = IACL(acl).owner(); 74 | } 75 | 76 | /// @notice Pauses contract, can only be called by an account with pausable admin role 77 | function pause() external virtual pausableAdminsOnly { 78 | _pause(); 79 | } 80 | 81 | /// @notice Unpauses contract, can only be called by an account with unpausable admin role 82 | function unpause() external virtual unpausableAdminsOnly { 83 | _unpause(); 84 | } 85 | 86 | /// @notice Sets new external controller, can only be called by configurator 87 | function setController(address newController) external configuratorOnly { 88 | if (controller == newController) return; 89 | controller = newController; 90 | emit NewController(newController); 91 | } 92 | 93 | /// @dev Checks whether given account has pausable admin role 94 | function _isPausableAdmin(address account) internal view returns (bool) { 95 | return IACL(acl).isPausableAdmin(account); 96 | } 97 | 98 | /// @dev Checks whether given account has unpausable admin role 99 | function _isUnpausableAdmin(address account) internal view returns (bool) { 100 | return IACL(acl).isUnpausableAdmin(account); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /contracts/traits/ACLTrait.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IACL} from "@gearbox-protocol/core-v2/contracts/interfaces/IACL.sol"; 7 | 8 | import {AP_ACL, IAddressProviderV3, NO_VERSION_CONTROL} from "../interfaces/IAddressProviderV3.sol"; 9 | import {CallerNotConfiguratorException} from "../interfaces/IExceptions.sol"; 10 | 11 | import {SanityCheckTrait} from "./SanityCheckTrait.sol"; 12 | 13 | /// @title ACL trait 14 | /// @notice Utility class for ACL (access-control list) consumers 15 | abstract contract ACLTrait is SanityCheckTrait { 16 | /// @notice ACL contract address 17 | address public immutable acl; 18 | 19 | /// @notice Constructor 20 | /// @param addressProvider Address provider contract address 21 | constructor(address addressProvider) nonZeroAddress(addressProvider) { 22 | acl = IAddressProviderV3(addressProvider).getAddressOrRevert(AP_ACL, NO_VERSION_CONTROL); 23 | } 24 | 25 | /// @dev Ensures that function caller has configurator role 26 | modifier configuratorOnly() { 27 | _ensureCallerIsConfigurator(); 28 | _; 29 | } 30 | 31 | /// @dev Reverts if the caller is not the configurator 32 | /// @dev Used to cut contract size on modifiers 33 | function _ensureCallerIsConfigurator() internal view { 34 | if (!_isConfigurator({account: msg.sender})) { 35 | revert CallerNotConfiguratorException(); 36 | } 37 | } 38 | 39 | /// @dev Checks whether given account has configurator role 40 | function _isConfigurator(address account) internal view returns (bool) { 41 | return IACL(acl).isConfigurator(account); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/traits/ContractsRegisterTrait.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {IContractsRegister} from "@gearbox-protocol/core-v2/contracts/interfaces/IContractsRegister.sol"; 7 | 8 | import {AP_CONTRACTS_REGISTER, IAddressProviderV3, NO_VERSION_CONTROL} from "../interfaces/IAddressProviderV3.sol"; 9 | import {RegisteredCreditManagerOnlyException, RegisteredPoolOnlyException} from "../interfaces/IExceptions.sol"; 10 | 11 | import {SanityCheckTrait} from "./SanityCheckTrait.sol"; 12 | 13 | /// @title Contracts register trait 14 | /// @notice Trait that simplifies validation of pools and credit managers 15 | abstract contract ContractsRegisterTrait is SanityCheckTrait { 16 | /// @notice Contracts register contract address 17 | address public immutable contractsRegister; 18 | 19 | /// @dev Ensures that given address is a registered credit manager 20 | modifier registeredPoolOnly(address addr) { 21 | _ensureRegisteredPool(addr); 22 | _; 23 | } 24 | 25 | /// @dev Ensures that given address is a registered pool 26 | modifier registeredCreditManagerOnly(address addr) { 27 | _ensureRegisteredCreditManager(addr); 28 | _; 29 | } 30 | 31 | /// @notice Constructor 32 | /// @param addressProvider Address provider contract address 33 | constructor(address addressProvider) nonZeroAddress(addressProvider) { 34 | contractsRegister = 35 | IAddressProviderV3(addressProvider).getAddressOrRevert(AP_CONTRACTS_REGISTER, NO_VERSION_CONTROL); 36 | } 37 | 38 | /// @dev Ensures that given address is a registered pool 39 | function _ensureRegisteredPool(address addr) internal view { 40 | if (!_isRegisteredPool(addr)) revert RegisteredPoolOnlyException(); 41 | } 42 | 43 | /// @dev Ensures that given address is a registered credit manager 44 | function _ensureRegisteredCreditManager(address addr) internal view { 45 | if (!_isRegisteredCreditManager(addr)) revert RegisteredCreditManagerOnlyException(); 46 | } 47 | 48 | /// @dev Whether given address is a registered pool 49 | function _isRegisteredPool(address addr) internal view returns (bool) { 50 | return IContractsRegister(contractsRegister).isPool(addr); 51 | } 52 | 53 | /// @dev Whether given address is a registered credit manager 54 | function _isRegisteredCreditManager(address addr) internal view returns (bool) { 55 | return IContractsRegister(contractsRegister).isCreditManager(addr); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/traits/PriceFeedValidationTrait.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {Address} from "@openzeppelin/contracts/utils/Address.sol"; 7 | 8 | import { 9 | AddressIsNotContractException, 10 | IncorrectParameterException, 11 | IncorrectPriceException, 12 | IncorrectPriceFeedException, 13 | PriceFeedDoesNotExistException, 14 | StalePriceException 15 | } from "../interfaces/IExceptions.sol"; 16 | import {IPriceFeed, IUpdatablePriceFeed} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceFeed.sol"; 17 | 18 | /// @title Price feed validation trait 19 | abstract contract PriceFeedValidationTrait { 20 | using Address for address; 21 | 22 | /// @dev Ensures that price is positive and not stale 23 | function _checkAnswer(int256 price, uint256 updatedAt, uint32 stalenessPeriod) internal view { 24 | if (price <= 0) revert IncorrectPriceException(); 25 | if (block.timestamp >= updatedAt + stalenessPeriod) revert StalePriceException(); 26 | } 27 | 28 | /// @dev Valites that `priceFeed` is a contract that adheres to Chainlink interface and passes sanity checks 29 | /// @dev Some price feeds return stale prices unless updated right before querying their answer, which causes 30 | /// issues during deployment and configuration, so for such price feeds staleness check is skipped, and 31 | /// special care must be taken to ensure all parameters are in tune. 32 | function _validatePriceFeed(address priceFeed, uint32 stalenessPeriod) internal view returns (bool skipCheck) { 33 | if (!priceFeed.isContract()) revert AddressIsNotContractException(priceFeed); // U:[PO-5] 34 | 35 | try IPriceFeed(priceFeed).decimals() returns (uint8 _decimals) { 36 | if (_decimals != 8) revert IncorrectPriceFeedException(); // U:[PO-5] 37 | } catch { 38 | revert IncorrectPriceFeedException(); // U:[PO-5] 39 | } 40 | 41 | try IPriceFeed(priceFeed).skipPriceCheck() returns (bool _skipCheck) { 42 | skipCheck = _skipCheck; // U:[PO-5] 43 | } catch {} 44 | 45 | try IPriceFeed(priceFeed).latestRoundData() returns (uint80, int256 answer, uint256, uint256 updatedAt, uint80) 46 | { 47 | if (skipCheck) { 48 | if (stalenessPeriod != 0) revert IncorrectParameterException(); // U:[PO-5] 49 | } else { 50 | if (stalenessPeriod == 0) revert IncorrectParameterException(); // U:[PO-5] 51 | 52 | bool updatable; 53 | try IUpdatablePriceFeed(priceFeed).updatable() returns (bool _updatable) { 54 | updatable = _updatable; 55 | } catch {} 56 | if (!updatable) _checkAnswer(answer, updatedAt, stalenessPeriod); // U:[PO-5] 57 | } 58 | } catch { 59 | revert IncorrectPriceFeedException(); // U:[PO-5] 60 | } 61 | } 62 | 63 | /// @dev Returns answer from a price feed with optional sanity and staleness checks 64 | function _getValidatedPrice(address priceFeed, uint32 stalenessPeriod, bool skipCheck) 65 | internal 66 | view 67 | returns (int256 answer) 68 | { 69 | uint256 updatedAt; 70 | (, answer,, updatedAt,) = IPriceFeed(priceFeed).latestRoundData(); // U:[PO-1] 71 | if (!skipCheck) _checkAnswer(answer, updatedAt, stalenessPeriod); // U:[PO-1] 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contracts/traits/ReentrancyGuardTrait.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | uint8 constant NOT_ENTERED = 1; 7 | uint8 constant ENTERED = 2; 8 | 9 | /// @title Reentrancy guard trait 10 | /// @notice Same as OpenZeppelin's `ReentrancyGuard` but only uses 1 byte of storage instead of 32 11 | abstract contract ReentrancyGuardTrait { 12 | uint8 internal _reentrancyStatus = NOT_ENTERED; 13 | 14 | /// @dev Prevents a contract from calling itself, directly or indirectly. 15 | /// Calling a `nonReentrant` function from another `nonReentrant` 16 | /// function is not supported. It is possible to prevent this from happening 17 | /// by making the `nonReentrant` function external, and making it call a 18 | /// `private` function that does the actual work. 19 | modifier nonReentrant() { 20 | // On the first call to nonReentrant, _notEntered will be true 21 | _ensureNotEntered(); 22 | 23 | // Any calls to nonReentrant after this point will fail 24 | _reentrancyStatus = ENTERED; 25 | 26 | _; 27 | 28 | // By storing the original value once again, a refund is triggered (see 29 | // https://eips.ethereum.org/EIPS/eip-2200) 30 | _reentrancyStatus = NOT_ENTERED; 31 | } 32 | 33 | /// @dev Reverts if the contract is currently entered 34 | /// @dev Used to cut contract size on modifiers 35 | function _ensureNotEntered() internal view { 36 | require(_reentrancyStatus != ENTERED, "ReentrancyGuard: reentrant call"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/traits/SanityCheckTrait.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {ZeroAddressException} from "../interfaces/IExceptions.sol"; 7 | 8 | /// @title Sanity check trait 9 | abstract contract SanityCheckTrait { 10 | /// @dev Ensures that passed address is non-zero 11 | modifier nonZeroAddress(address addr) { 12 | _revertIfZeroAddress(addr); 13 | _; 14 | } 15 | 16 | /// @dev Reverts if address is zero 17 | function _revertIfZeroAddress(address addr) private pure { 18 | if (addr == address(0)) revert ZeroAddressException(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/traits/USDT_Transfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols 3 | // (c) Gearbox Foundation, 2023. 4 | pragma solidity ^0.8.17; 5 | 6 | import {USDTFees} from "../libraries/USDTFees.sol"; 7 | import {IUSDT} from "../interfaces/external/IUSDT.sol"; 8 | 9 | /// @title USDT transfer 10 | /// @notice Trait that allows to calculate amounts adjusted for transfer fees 11 | contract USDT_Transfer { 12 | using USDTFees for uint256; 13 | 14 | /// @dev USDT token address 15 | address private immutable usdt; 16 | 17 | constructor(address _usdt) { 18 | usdt = _usdt; 19 | } 20 | 21 | /// @dev Computes amount of USDT that should be sent to receive `amount` 22 | function _amountUSDTWithFee(uint256 amount) internal view virtual returns (uint256) { 23 | uint256 basisPointsRate = _basisPointsRate(); // U:[UTT_01] 24 | if (basisPointsRate == 0) return amount; 25 | return amount.amountUSDTWithFee({basisPointsRate: basisPointsRate, maximumFee: _maximumFee()}); // U:[UTT_01] 26 | } 27 | 28 | /// @dev Computes amount of USDT that would be received if `amount` is sent 29 | function _amountUSDTMinusFee(uint256 amount) internal view virtual returns (uint256) { 30 | uint256 basisPointsRate = _basisPointsRate(); // U:[UTT_01] 31 | if (basisPointsRate == 0) return amount; 32 | return amount.amountUSDTMinusFee({basisPointsRate: basisPointsRate, maximumFee: _maximumFee()}); // U:[UTT_01] 33 | } 34 | 35 | /// @dev Returns fee rate in bps 36 | function _basisPointsRate() internal view returns (uint256) { 37 | return IUSDT(usdt).basisPointsRate(); 38 | } 39 | 40 | /// @dev Returns maximum absolute fee 41 | function _maximumFee() internal view returns (uint256) { 42 | return IUSDT(usdt).maximumFee(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | libs = ['lib'] 3 | out = 'forge-out' 4 | solc_version = '0.8.17' 5 | evm_version = 'shanghai' 6 | src = 'contracts' 7 | optimizer_runs = 12000 8 | 9 | # See more config options https://github.com/gakonst/foundry/tree/master/config 10 | block_number = 120000 11 | block_timestamp = 16400000 12 | gas_limit = 9223372036854775807 # the gas limit in tests 13 | block_base_fee_per_gas = 100 14 | fs_permissions = [{ access = "read-write", path = "./"}] 15 | 16 | [fuzz] 17 | max_test_rejects = 200000 18 | 19 | [invariant] 20 | fail_on_revert = true 21 | runs = 200 22 | depth = 10 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gearbox-protocol/core-v3", 3 | "description": "Core smart contracts of Gearbox V3", 4 | "version": "1.0.1", 5 | "homepage": "https://gearbox.fi", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "files": [ 9 | "contracts", 10 | "scripts", 11 | "types", 12 | "dist" 13 | ], 14 | "keywords": [ 15 | "gearbox" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/Gearbox-protocol/core-v3" 20 | }, 21 | "license": "BUSL-1.1", 22 | "scripts": { 23 | "prepare": "husky install", 24 | "prettier": "forge fmt", 25 | "prettier:ci": "forge fmt", 26 | "forge-install": "forge install --no-commit foundry-rs/forge-std" 27 | }, 28 | "devDependencies": { 29 | "@1inch/solidity-utils": "2.4.0", 30 | "@chainlink/contracts": "^0.6.1", 31 | "@commitlint/cli": "^17.6.3", 32 | "@commitlint/config-conventional": "17.6.0", 33 | "@gearbox-protocol/core-v2": "1.19.0-base.16", 34 | "@gearbox-protocol/prettier-config": "^1.5.0", 35 | "@gearbox-protocol/sdk-gov": "^2.28.0", 36 | "@openzeppelin/contracts": "^4.9.3", 37 | "husky": "^8.0.3", 38 | "lint-staged": "^13.0.3", 39 | "prettier": "^2.7.1", 40 | "prettier-plugin-solidity": "^1.0.0-beta.24", 41 | "rimraf": "^5.0.1" 42 | }, 43 | "prettier": "@gearbox-protocol/prettier-config", 44 | "lint-staged": { 45 | "*.sol": "forge fmt", 46 | "*.{json,md}": "prettier --write" 47 | }, 48 | "dependencies": {} 49 | } 50 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=lib/forge-std/lib/ds-test/src/ 2 | forge-std/=lib/forge-std/src/ 3 | @chainlink/=node_modules/@chainlink/ 4 | @openzeppelin/=node_modules/@openzeppelin/ 5 | @gearbox-protocol=node_modules/@gearbox-protocol/ 6 | @1inch=node_modules/@1inch/ --------------------------------------------------------------------------------