From 84821e32f61ddd73722e6e91f63303b9748e243b Mon Sep 17 00:00:00 2001 From: Van0k Date: Mon, 13 Mar 2023 11:01:29 +0400 Subject: [PATCH 01/12] fix: add DataCompressor --- contracts/core/DataCompressor.sol | 433 ++++++++++++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 contracts/core/DataCompressor.sol diff --git a/contracts/core/DataCompressor.sol b/contracts/core/DataCompressor.sol new file mode 100644 index 00000000..62f63bbc --- /dev/null +++ b/contracts/core/DataCompressor.sol @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: BUSL-1.1 +// Gearbox Protocol. Generalized leverage for DeFi protocols +// (c) Gearbox Holdings, 2022 +pragma solidity ^0.8.10; +pragma experimental ABIEncoderV2; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/PercentageMath.sol"; + +import {IDataCompressor} from "@gearbox-protocol/core-v2/contracts/interfaces/IDataCompressor.sol"; +import {ICreditManager} from "@gearbox-protocol/core-v2/contracts/interfaces/V1/ICreditManager.sol"; +import {ICreditManagerV2} from "../interfaces/ICreditManagerV2.sol"; +import {ICreditFacade} from "../interfaces/ICreditFacade.sol"; +import {ICreditFilter} from "@gearbox-protocol/core-v2/contracts/interfaces/V1/ICreditFilter.sol"; +import {ICreditConfigurator} from "../interfaces/ICreditConfigurator.sol"; +import {ICreditAccount} from "@gearbox-protocol/core-v2/contracts/interfaces/ICreditAccount.sol"; +import {IPoolService} from "@gearbox-protocol/core-v2/contracts/interfaces/IPoolService.sol"; +import {IPool4626} from "../interfaces/IPool4626.sol"; + +import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; + +import {AddressProvider} from "@gearbox-protocol/core-v2/contracts/core/AddressProvider.sol"; +import {ContractsRegister} from "@gearbox-protocol/core-v2/contracts/core/ContractsRegister.sol"; + +import { + CreditAccountData, + CreditManagerData, + PoolData, + TokenInfo, + TokenBalance, + ContractAdapter +} from "@gearbox-protocol/core-v2/contracts/libraries/Types.sol"; + +// EXCEPTIONS +import {ZeroAddressException} from "../interfaces/IErrors.sol"; + +/// @title Data compressor +/// @notice Collects data from various contracts for use in the dApp +/// Do not use for data from any onchain activities +contract DataCompressor is IDataCompressor { + /// @dev Address of the AddressProvider + AddressProvider public immutable addressProvider; + + /// @dev Address of the ContractsRegister + ContractsRegister public immutable contractsRegister; + + /// @dev Address of WETH + address public immutable WETHToken; + + // Contract version + uint256 public constant version = 2; + + /// @dev Prevents function usage for target contracts that are not Gearbox pools + modifier targetIsRegisteredPool(address pool) { + if (!contractsRegister.isPool(pool)) revert NotPoolException(); // T:[WG-1] + _; + } + + /// @dev Prevents function usage for target contracts that are not Gearbox Credit Managers + modifier targetIsRegisteredCreditManager(address creditManager) { + if (!contractsRegister.isCreditManager(creditManager)) { + revert NotCreditManagerException(); + } // T:[WG-3] + _; + } + + constructor(address _addressProvider) { + if (_addressProvider == address(0)) revert ZeroAddressException(); + + addressProvider = AddressProvider(_addressProvider); + contractsRegister = ContractsRegister(addressProvider.getContractsRegister()); + WETHToken = addressProvider.getWethToken(); + } + + /// @dev Returns CreditAccountData for all opened accounts for particular borrower + /// @param borrower Borrower address + function getCreditAccountList(address borrower) external view returns (CreditAccountData[] memory result) { + // Counts how many opened accounts a borrower has + uint256 count; + uint256 creditManagersLength = contractsRegister.getCreditManagersCount(); + + for (uint256 i = 0; i < creditManagersLength;) { + unchecked { + address creditManager = contractsRegister.creditManagers(i); + if (hasOpenedCreditAccount(creditManager, borrower)) { + ++count; + } + ++i; + } + } + + result = new CreditAccountData[](count); + + // Get data & fill the array + count = 0; + for (uint256 i = 0; i < creditManagersLength;) { + address creditManager = contractsRegister.creditManagers(i); + unchecked { + if (hasOpenedCreditAccount(creditManager, borrower)) { + result[count] = getCreditAccountData(creditManager, borrower); + + count++; + } + + ++i; + } + } + } + + /// @dev Returns whether the borrower has an open credit account with the credit manager + /// @param _creditManager Credit manager to check + /// @param borrower Borrower to check + function hasOpenedCreditAccount(address _creditManager, address borrower) + public + view + targetIsRegisteredCreditManager(_creditManager) + returns (bool) + { + return _hasOpenedCreditAccount(_creditManager, borrower); + } + + /// @dev Returns CreditAccountData for a particular Credit Account account, based on creditManager and borrower + /// @param _creditManager Credit manager address + /// @param borrower Borrower address + function getCreditAccountData(address _creditManager, address borrower) + public + view + returns (CreditAccountData memory result) + { + ( + uint8 ver, + ICreditManager creditManager, + ICreditFilter creditFilter, + ICreditManagerV2 creditManagerV2, + ICreditFacade creditFacade, + ) = getCreditContracts(_creditManager); + + address creditAccount = (ver == 1) + ? creditManager.getCreditAccountOrRevert(borrower) + : creditManagerV2.getCreditAccountOrRevert(borrower); + + result.version = ver; + + result.borrower = borrower; + result.creditManager = _creditManager; + result.addr = creditAccount; + + if (ver == 1) { + result.underlying = creditManager.underlyingToken(); + result.totalValue = creditFilter.calcTotalValue(creditAccount); + result.healthFactor = creditFilter.calcCreditAccountHealthFactor(creditAccount); + + try ICreditManager(creditManager).calcRepayAmount(borrower, false) returns (uint256 value) { + result.repayAmount = value; + } catch {} + + try ICreditManager(creditManager).calcRepayAmount(borrower, true) returns (uint256 value) { + result.liquidationAmount = value; + } catch {} + + try ICreditManager(creditManager)._calcClosePayments(creditAccount, result.totalValue, false) returns ( + uint256, uint256, uint256 remainingFunds, uint256, uint256 + ) { + result.canBeClosed = remainingFunds > 0; + } catch {} + + result.borrowedAmount = ICreditAccount(creditAccount).borrowedAmount(); + + result.borrowedAmountPlusInterest = creditFilter.calcCreditAccountAccruedInterest(creditAccount); + } else { + result.underlying = creditManagerV2.underlying(); + (result.totalValue,) = creditFacade.calcTotalValue(creditAccount); + result.healthFactor = creditFacade.calcCreditAccountHealthFactor(creditAccount); + + (result.borrowedAmount, result.borrowedAmountPlusInterest, result.borrowedAmountPlusInterestAndFees) = + creditManagerV2.calcCreditAccountAccruedInterest(creditAccount); + } + + address pool = address((ver == 1) ? creditManager.poolService() : creditManagerV2.pool()); + result.borrowRate = IPoolService(pool).borrowAPY_RAY(); + + uint256 collateralTokenCount = + (ver == 1) ? creditFilter.allowedTokensCount() : creditManagerV2.collateralTokensCount(); + + result.enabledTokenMask = + (ver == 1) ? creditFilter.enabledTokens(creditAccount) : creditManagerV2.enabledTokensMap(creditAccount); + + result.balances = new TokenBalance[](collateralTokenCount); + for (uint256 i = 0; i < collateralTokenCount;) { + unchecked { + TokenBalance memory balance; + uint256 tokenMask = 1 << i; + if (ver == 1) { + (balance.token, balance.balance,,) = creditFilter.getCreditAccountTokenById(creditAccount, i); + balance.isAllowed = creditFilter.isTokenAllowed(balance.token); + } else { + (balance.token,) = creditManagerV2.collateralTokens(i); + balance.balance = IERC20(balance.token).balanceOf(creditAccount); + balance.isAllowed = creditFacade.isTokenAllowed(balance.token); + } + balance.isEnabled = tokenMask & result.enabledTokenMask == 0 ? false : true; + + result.balances[i] = balance; + + ++i; + } + } + + result.cumulativeIndexAtOpen = ICreditAccount(creditAccount).cumulativeIndexAtOpen(); + + result.since = ICreditAccount(creditAccount).since(); + } + + /// @dev Returns CreditManagerData for all Credit Managers + function getCreditManagersList() external view returns (CreditManagerData[] memory result) { + uint256 creditManagersCount = contractsRegister.getCreditManagersCount(); + + result = new CreditManagerData[](creditManagersCount); + + for (uint256 i = 0; i < creditManagersCount;) { + address creditManager = contractsRegister.creditManagers(i); + result[i] = getCreditManagerData(creditManager); + unchecked { + ++i; + } + } + } + + /// @dev Returns CreditManagerData for a particular _creditManager + /// @param _creditManager CreditManager address + function getCreditManagerData(address _creditManager) public view returns (CreditManagerData memory result) { + ( + uint8 ver, + ICreditManager creditManager, + ICreditFilter creditFilter, + ICreditManagerV2 creditManagerV2, + ICreditFacade creditFacade, + ICreditConfigurator creditConfigurator + ) = getCreditContracts(_creditManager); + + result.addr = _creditManager; + result.version = ver; + + result.underlying = (ver == 1) ? creditManager.underlyingToken() : creditManagerV2.underlying(); + result.isWETH = result.underlying == WETHToken; + + { + IPoolService pool = IPoolService((ver == 1) ? creditManager.poolService() : creditManagerV2.pool()); + result.pool = address(pool); + result.canBorrow = pool.creditManagersCanBorrow(_creditManager); + result.borrowRate = pool.borrowAPY_RAY(); + result.availableLiquidity = pool.availableLiquidity(); + } + + if (ver == 1) { + result.minAmount = creditManager.minAmount(); + result.maxAmount = creditManager.maxAmount(); + } else { + (result.minAmount, result.maxAmount) = creditFacade.limits(); + } + { + uint256 collateralTokenCount = + (ver == 1) ? creditFilter.allowedTokensCount() : creditManagerV2.collateralTokensCount(); + + result.collateralTokens = new address[](collateralTokenCount); + result.liquidationThresholds = new uint256[](collateralTokenCount); + unchecked { + for (uint256 i = 0; i < collateralTokenCount; ++i) { + if (ver == 1) { + address token = creditFilter.allowedTokens(i); + result.collateralTokens[i] = token; + result.liquidationThresholds[i] = creditFilter.liquidationThresholds(token); + } else { + (result.collateralTokens[i], result.liquidationThresholds[i]) = + creditManagerV2.collateralTokens(i); + } + } + } + } + if (ver == 1) { + uint256 allowedContractsCount = creditFilter.allowedContractsCount(); + + result.adapters = new ContractAdapter[](allowedContractsCount); + for (uint256 i = 0; i < allowedContractsCount;) { + address allowedContract = creditFilter.allowedContracts(i); + + result.adapters[i] = ContractAdapter({ + allowedContract: allowedContract, + adapter: creditFilter.contractToAdapter(allowedContract) + }); + unchecked { + ++i; + } + } + } else { + address[] memory allowedContracts = creditConfigurator.allowedContracts(); + uint256 len = allowedContracts.length; + result.adapters = new ContractAdapter[](len); + for (uint256 i = 0; i < len;) { + address allowedContract = allowedContracts[i]; + + result.adapters[i] = ContractAdapter({ + allowedContract: allowedContract, + adapter: creditManagerV2.contractToAdapter(allowedContract) + }); + unchecked { + ++i; + } + } + } + + if (ver == 1) { + // VERSION 1 SPECIFIC FIELDS + result.maxLeverageFactor = ICreditManager(creditManager).maxLeverageFactor(); + result.maxEnabledTokensLength = 255; + result.feeInterest = uint16(creditManager.feeInterest()); + result.feeLiquidation = uint16(creditManager.feeLiquidation()); + result.liquidationDiscount = uint16(creditManager.feeLiquidation()); + } else { + // VERSION 2 SPECIFIC FIELDS + result.creditFacade = address(creditFacade); + result.creditConfigurator = creditManagerV2.creditConfigurator(); + result.degenNFT = creditFacade.degenNFT(); + (, result.isIncreaseDebtForbidden,) = creditFacade.params(); // V2 only: true if increasing debt is forbidden + result.forbiddenTokenMask = creditManagerV2.forbiddenTokenMask(); // V2 only: mask which forbids some particular tokens + result.maxEnabledTokensLength = creditManagerV2.maxAllowedEnabledTokenLength(); // V2 only: a limit on enabled tokens imposed for security + { + ( + result.feeInterest, + result.feeLiquidation, + result.liquidationDiscount, + result.feeLiquidationExpired, + result.liquidationDiscountExpired + ) = creditManagerV2.fees(); + } + } + } + + /// @dev Returns PoolData for a particular pool + /// @param _pool Pool address + function getPoolData(address _pool) public view targetIsRegisteredPool(_pool) returns (PoolData memory result) { + IPoolService pool = IPoolService(_pool); + result.version = uint8(pool.version()); + + result.addr = _pool; + result.expectedLiquidity = pool.expectedLiquidity(); + result.expectedLiquidityLimit = pool.expectedLiquidityLimit(); + result.availableLiquidity = pool.availableLiquidity(); + result.totalBorrowed = pool.totalBorrowed(); + result.dieselRate_RAY = pool.getDieselRate_RAY(); + result.linearCumulativeIndex = pool.calcLinearCumulative_RAY(); + result.borrowAPY_RAY = pool.borrowAPY_RAY(); + result.underlying = pool.underlyingToken(); + result.dieselToken = (result.version > 1) ? _pool : pool.dieselToken(); + result.dieselRate_RAY = pool.getDieselRate_RAY(); + result.withdrawFee = pool.withdrawFee(); + result.isWETH = result.underlying == WETHToken; + result.timestampLU = pool._timestampLU(); + result.cumulativeIndex_RAY = pool._cumulativeIndex_RAY(); + + uint256 dieselSupply = IERC20(result.dieselToken).totalSupply(); + + uint256 totalLP = + (result.version > 1) ? IPool4626(_pool).convertToAssets(dieselSupply) : pool.fromDiesel(dieselSupply); + + result.depositAPY_RAY = totalLP == 0 + ? result.borrowAPY_RAY + : (result.borrowAPY_RAY * result.totalBorrowed) * (PERCENTAGE_FACTOR - result.withdrawFee) / totalLP + / PERCENTAGE_FACTOR; + + return result; + } + + /// @dev Returns PoolData for all registered pools + function getPoolsList() external view returns (PoolData[] memory result) { + uint256 poolsLength = contractsRegister.getPoolsCount(); + + result = new PoolData[](poolsLength); + + for (uint256 i = 0; i < poolsLength;) { + address pool = contractsRegister.pools(i); + result[i] = getPoolData(pool); + unchecked { + ++i; + } + } + } + + /// @dev Returns the adapter address for a particular creditManager and targetContract + function getAdapter(address _creditManager, address _allowedContract) + external + view + targetIsRegisteredCreditManager(_creditManager) + returns (address adapter) + { + (uint8 ver,, ICreditFilter creditFilter, ICreditManagerV2 creditManagerV2,,) = + getCreditContracts(_creditManager); + + adapter = (ver == 1) + ? creditFilter.contractToAdapter(_allowedContract) + : creditManagerV2.contractToAdapter(_allowedContract); + } + + /// @dev Internal implementation for hasOpenedCreditAccount + function _hasOpenedCreditAccount(address creditManager, address borrower) internal view returns (bool) { + return ICreditManagerV2(creditManager).creditAccounts(borrower) != address(0); + } + + /// @dev Retrieves all relevant credit contracts for a particular Credit Manager + function getCreditContracts(address _creditManager) + internal + view + targetIsRegisteredCreditManager(_creditManager) + returns ( + uint8 ver, + ICreditManager creditManager, + ICreditFilter creditFilter, + ICreditManagerV2 creditManagerV2, + ICreditFacade creditFacade, + ICreditConfigurator creditConfigurator + ) + { + ver = uint8(IVersion(_creditManager).version()); + if (ver == 1) { + creditManager = ICreditManager(_creditManager); + creditFilter = ICreditFilter(creditManager.creditFilter()); + } else { + creditManagerV2 = ICreditManagerV2(_creditManager); + creditFacade = ICreditFacade(creditManagerV2.creditFacade()); + creditConfigurator = ICreditConfigurator(creditManagerV2.creditConfigurator()); + } + } +} From a22e77f037365d1f52790e88d80da39b1bc751eb Mon Sep 17 00:00:00 2001 From: Van0k Date: Mon, 13 Mar 2023 11:18:43 +0400 Subject: [PATCH 02/12] feat: update versions --- contracts/core/DataCompressor.sol | 2 +- contracts/credit/CreditConfigurator.sol | 2 +- contracts/credit/CreditFacade.sol | 2 +- contracts/credit/CreditManager.sol | 2 +- contracts/interfaces/IBlacklistHelper.sol | 4 +++- contracts/interfaces/IBotList.sol | 4 +++- contracts/interfaces/IGauge.sol | 4 +++- contracts/interfaces/IGearStaking.sol | 4 +++- contracts/pool/Gauge.sol | 3 +++ contracts/pool/Pool4626.sol | 28 +++++++++++------------ contracts/pool/PoolQuotaKeeper.sol | 2 +- contracts/support/BlacklistHelper.sol | 3 +++ contracts/support/BotList.sol | 3 +++ contracts/support/GearStaking.sol | 3 +++ 14 files changed, 43 insertions(+), 23 deletions(-) diff --git a/contracts/core/DataCompressor.sol b/contracts/core/DataCompressor.sol index 62f63bbc..ec007611 100644 --- a/contracts/core/DataCompressor.sol +++ b/contracts/core/DataCompressor.sol @@ -48,7 +48,7 @@ contract DataCompressor is IDataCompressor { address public immutable WETHToken; // Contract version - uint256 public constant version = 2; + uint256 public constant version = 3_00; /// @dev Prevents function usage for target contracts that are not Gearbox pools modifier targetIsRegisteredPool(address pool) { diff --git a/contracts/credit/CreditConfigurator.sol b/contracts/credit/CreditConfigurator.sol index 55c64f8e..9ae5877a 100644 --- a/contracts/credit/CreditConfigurator.sol +++ b/contracts/credit/CreditConfigurator.sol @@ -66,7 +66,7 @@ contract CreditConfigurator is ICreditConfigurator, ACLNonReentrantTrait { EnumerableSet.AddressSet private allowedContractsSet; /// @dev Contract version - uint256 public constant version = 2; + uint256 public constant version = 3_00; /// @dev Constructor has a special role in credit management deployment /// This is where the initial configuration is performed. diff --git a/contracts/credit/CreditFacade.sol b/contracts/credit/CreditFacade.sol index 2890b601..350bd3f4 100644 --- a/contracts/credit/CreditFacade.sol +++ b/contracts/credit/CreditFacade.sol @@ -97,7 +97,7 @@ contract CreditFacade is ICreditFacade, ReentrancyGuard { uint256 internal totalBorrowedInBlock; /// @dev Contract version - uint256 public constant override version = 2; + uint256 public constant override version = 3_00; /// @dev Restricts actions for users with opened credit accounts only modifier creditConfiguratorOnly() { diff --git a/contracts/credit/CreditManager.sol b/contracts/credit/CreditManager.sol index a8e4427d..bd2f43d2 100644 --- a/contracts/credit/CreditManager.sol +++ b/contracts/credit/CreditManager.sol @@ -155,7 +155,7 @@ contract CreditManager is ICreditManagerV2, ACLNonReentrantTrait { mapping(address => uint256) public cumulativeQuotaInterest; /// @dev contract version - uint256 public constant override version = 2_10; + uint256 public constant override version = 3_00; // // MODIFIERS diff --git a/contracts/interfaces/IBlacklistHelper.sol b/contracts/interfaces/IBlacklistHelper.sol index 0d6fd247..6e25ca50 100644 --- a/contracts/interfaces/IBlacklistHelper.sol +++ b/contracts/interfaces/IBlacklistHelper.sol @@ -3,6 +3,8 @@ // (c) Gearbox Holdings, 2022 pragma solidity ^0.8.10; +import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; + interface IBlacklistHelperEvents { /// @dev Emitted when a borrower's claimable balance is increased event ClaimableAdded(address indexed underlying, address indexed holder, uint256 amount); @@ -28,7 +30,7 @@ interface IBlacklistHelperExceptions { error NothingToClaimException(); } -interface IBlacklistHelper is IBlacklistHelperEvents, IBlacklistHelperExceptions { +interface IBlacklistHelper is IBlacklistHelperEvents, IBlacklistHelperExceptions, IVersion { /// @dev Returns whether the account is blacklisted for a particular underlying function isBlacklisted(address underlying, address account) external view returns (bool); diff --git a/contracts/interfaces/IBotList.sol b/contracts/interfaces/IBotList.sol index 26297842..f751c2b8 100644 --- a/contracts/interfaces/IBotList.sol +++ b/contracts/interfaces/IBotList.sol @@ -3,6 +3,8 @@ // (c) Gearbox Holdings, 2022 pragma solidity ^0.8.10; +import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; + struct BotFunding { uint72 remainingFunds; uint72 maxWeeklyAllowance; @@ -39,7 +41,7 @@ interface IBotListEvents { } /// @title IBotList -interface IBotList is IBotListEvents, IBotListExceptions { +interface IBotList is IBotListEvents, IBotListExceptions, IVersion { /// @dev Sets approval from msg.sender to bot function setBotStatus(address bot, bool status) external; diff --git a/contracts/interfaces/IGauge.sol b/contracts/interfaces/IGauge.sol index 21216ef4..cc2168aa 100644 --- a/contracts/interfaces/IGauge.sol +++ b/contracts/interfaces/IGauge.sol @@ -3,6 +3,8 @@ // (c) Gearbox Holdings, 2022 pragma solidity ^0.8.10; +import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; + struct QuotaRateParams { uint16 minRiskRate; uint16 maxRate; @@ -44,7 +46,7 @@ interface IGaugeEvents { /// @title IGauge -interface IGauge is IGaugeEvents, IGaugeExceptions { +interface IGauge is IGaugeEvents, IGaugeExceptions, IVersion { /// @dev Rolls the new epoch and updates all quota rates function updateEpoch() external; diff --git a/contracts/interfaces/IGearStaking.sol b/contracts/interfaces/IGearStaking.sol index 42d847dc..55e2de66 100644 --- a/contracts/interfaces/IGearStaking.sol +++ b/contracts/interfaces/IGearStaking.sol @@ -3,6 +3,8 @@ // (c) Gearbox Holdings, 2022 pragma solidity ^0.8.17; +import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; + enum VotingContractStatus { NOT_ALLOWED, ALLOWED, @@ -42,7 +44,7 @@ interface IGearStakingEvents { event GearWithdrawalClaimed(address indexed user, address to, uint256 amount); } -interface IGearStaking is IGearStakingEvents, IGearStakingExceptions { +interface IGearStaking is IGearStakingEvents, IGearStakingExceptions, IVersion { /// @dev Returns the current global voting epoch function getCurrentEpoch() external view returns (uint16); diff --git a/contracts/pool/Gauge.sol b/contracts/pool/Gauge.sol index 6fdd53d7..d4427664 100644 --- a/contracts/pool/Gauge.sol +++ b/contracts/pool/Gauge.sol @@ -50,6 +50,9 @@ contract Gauge is IGauge, ACLNonReentrantTrait { /// @dev Epoch when the gauge was last updated uint16 public epochLU; + /// @dev Contract version + uint256 public constant version = 3_00; + // // CONSTRUCTOR // diff --git a/contracts/pool/Pool4626.sol b/contracts/pool/Pool4626.sol index d2ac0f0c..118fd8bd 100644 --- a/contracts/pool/Pool4626.sol +++ b/contracts/pool/Pool4626.sol @@ -57,7 +57,7 @@ contract Pool4626 is ERC4626, IPool4626, ACLNonReentrantTrait { bool public immutable supportsQuotas; /// @dev Contract version - uint256 public constant override version = 2_10; + uint256 public constant override version = 3_00; // [SLOT #1] @@ -182,7 +182,7 @@ contract Pool4626 is ERC4626, IPool4626, ACLNonReentrantTrait { /// @dev See {IERC4626-deposit}. function deposit(uint256 assets, address receiver) public - override (ERC4626, IERC4626) + override(ERC4626, IERC4626) whenNotPaused // F:[P4-4] nonReentrant nonZeroAddress(receiver) @@ -212,7 +212,7 @@ contract Pool4626 is ERC4626, IPool4626, ACLNonReentrantTrait { /// In this case, the shares will be minted without requiring any assets to be deposited. function mint(uint256 shares, address receiver) public - override (ERC4626, IERC4626) + override(ERC4626, IERC4626) whenNotPaused // F:[P4-4] nonReentrant nonZeroAddress(receiver) @@ -246,7 +246,7 @@ contract Pool4626 is ERC4626, IPool4626, ACLNonReentrantTrait { /// @dev See {IERC4626-withdraw}. function withdraw(uint256 assets, address receiver, address owner) public - override (ERC4626, IERC4626) + override(ERC4626, IERC4626) whenNotPaused // F:[P4-4] nonReentrant nonZeroAddress(receiver) @@ -260,7 +260,7 @@ contract Pool4626 is ERC4626, IPool4626, ACLNonReentrantTrait { /// @dev See {IERC4626-redeem}. function redeem(uint256 shares, address receiver, address owner) public - override (ERC4626, IERC4626) + override(ERC4626, IERC4626) whenNotPaused // F:[P4-4] nonReentrant nonZeroAddress(receiver) @@ -342,47 +342,47 @@ contract Pool4626 is ERC4626, IPool4626, ACLNonReentrantTrait { } /// @dev See {IERC4626-totalAssets}. - function totalAssets() public view override (ERC4626, IERC4626) returns (uint256 assets) { + function totalAssets() public view override(ERC4626, IERC4626) returns (uint256 assets) { return expectedLiquidity(); } /// @dev See {IERC4626-maxDeposit}. - function maxDeposit(address) public view override (ERC4626, IERC4626) returns (uint256) { + function maxDeposit(address) public view override(ERC4626, IERC4626) returns (uint256) { return (_expectedLiquidityLimit == type(uint128).max) ? type(uint256).max : _amountWithFee(_expectedLiquidityLimit - expectedLiquidity()); } /// @dev See {IERC4626-previewDeposit}. - function previewDeposit(uint256 assets) public view override (ERC4626, IERC4626) returns (uint256) { + function previewDeposit(uint256 assets) public view override(ERC4626, IERC4626) returns (uint256) { return _convertToShares(_amountMinusFee(assets), Math.Rounding.Down); // TODO: add fee parameter } /// @dev See {IERC4626-maxMint}. - function maxMint(address) public view override (ERC4626, IERC4626) returns (uint256) { + function maxMint(address) public view override(ERC4626, IERC4626) returns (uint256) { uint128 limit = _expectedLiquidityLimit; return (limit == type(uint128).max) ? type(uint256).max : previewMint(limit - expectedLiquidity()); } /// @dev See {IERC4626-previewMint}. - function previewMint(uint256 shares) public view override (ERC4626, IERC4626) returns (uint256) { + function previewMint(uint256 shares) public view override(ERC4626, IERC4626) returns (uint256) { return _amountWithFee(_convertToAssets(shares, Math.Rounding.Up)); // We need to round up shares.mulDivUp(totalAssets(), supply); } /// @dev See {IERC4626-maxWithdraw}. - function maxWithdraw(address owner) public view override (ERC4626, IERC4626) returns (uint256) { + function maxWithdraw(address owner) public view override(ERC4626, IERC4626) returns (uint256) { return availableLiquidity().min(previewWithdraw(balanceOf(owner))); } /// @dev See {IERC4626-previewWithdraw}. - function previewWithdraw(uint256 assets) public view override (ERC4626, IERC4626) returns (uint256) { + function previewWithdraw(uint256 assets) public view override(ERC4626, IERC4626) returns (uint256) { return _convertToShares( (_amountWithFee(assets) * PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR - withdrawFee), Math.Rounding.Up ); } /// @dev See {IERC4626-maxRedeem}. - function maxRedeem(address owner) public view override (ERC4626, IERC4626) returns (uint256 shares) { + function maxRedeem(address owner) public view override(ERC4626, IERC4626) returns (uint256 shares) { shares = balanceOf(owner); uint256 assets = _convertToAssets(shares, Math.Rounding.Down); uint256 assetsAvailable = availableLiquidity(); @@ -392,7 +392,7 @@ contract Pool4626 is ERC4626, IPool4626, ACLNonReentrantTrait { } /// @dev See {IERC4626-previewRedeem}. - function previewRedeem(uint256 shares) public view override (ERC4626, IERC4626) returns (uint256 assets) { + function previewRedeem(uint256 shares) public view override(ERC4626, IERC4626) returns (uint256 assets) { assets = _calcDeliveredAsstes(_convertToAssets(shares, Math.Rounding.Down)); } diff --git a/contracts/pool/PoolQuotaKeeper.sol b/contracts/pool/PoolQuotaKeeper.sol index 20173977..4152612e 100644 --- a/contracts/pool/PoolQuotaKeeper.sol +++ b/contracts/pool/PoolQuotaKeeper.sol @@ -72,7 +72,7 @@ contract PoolQuotaKeeper is IPoolQuotaKeeper, ACLNonReentrantTrait { IGauge public gauge; /// @dev Contract version - uint256 public constant override version = 2_10; + uint256 public constant override version = 3_00; /// @dev Reverts if the function is called by non-gauge modifier gaugeOnly() { diff --git a/contracts/support/BlacklistHelper.sol b/contracts/support/BlacklistHelper.sol index c18ea826..2a4ac712 100644 --- a/contracts/support/BlacklistHelper.sol +++ b/contracts/support/BlacklistHelper.sol @@ -36,6 +36,9 @@ contract BlacklistHelper is ACLNonReentrantTrait, IBlacklistHelper { /// @dev mapping from (underlying, account) to amount available to claim mapping(address => mapping(address => uint256)) public claimable; + /// @dev Contract version + uint256 public constant override version = 3_00; + /// @dev Restricts calls to Credit Facades only modifier creditFacadeOnly() { if (!isSupportedCreditFacade[msg.sender]) { diff --git a/contracts/support/BotList.sol b/contracts/support/BotList.sol index 6988bcb1..792087fd 100644 --- a/contracts/support/BotList.sol +++ b/contracts/support/BotList.sol @@ -38,6 +38,9 @@ contract BotList is ACLNonReentrantTrait, IBotList { /// @dev Address of the DAO treasury address public immutable treasury; + /// @dev Contract version + uint256 public constant override version = 3_00; + constructor(address _addressProvider) ACLNonReentrantTrait(_addressProvider) { treasury = IAddressProvider(_addressProvider).getTreasuryContract(); } diff --git a/contracts/support/GearStaking.sol b/contracts/support/GearStaking.sol index 1bee8a69..92dfdce1 100644 --- a/contracts/support/GearStaking.sol +++ b/contracts/support/GearStaking.sol @@ -38,6 +38,9 @@ contract GearStaking is ACLNonReentrantTrait, IGearStaking { /// @dev Timestamp of the first epoch of voting uint256 immutable firstEpochTimestamp; + /// @dev Contract version + uint256 public constant override version = 3_00; + constructor(address _addressProvider, uint256 _firstEpochTimestamp) ACLNonReentrantTrait(_addressProvider) { gear = IERC20(IAddressProvider(_addressProvider).getGearToken()); firstEpochTimestamp = _firstEpochTimestamp; From bebc87c15fe94961af8e00386ae96d0de7c8c25a Mon Sep 17 00:00:00 2001 From: Van0k Date: Mon, 13 Mar 2023 15:35:10 +0400 Subject: [PATCH 03/12] fix: add gauge to poolQuotaKeeper interface --- contracts/interfaces/IPoolQuotaKeeper.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/interfaces/IPoolQuotaKeeper.sol b/contracts/interfaces/IPoolQuotaKeeper.sol index a780db54..bf8e50ec 100644 --- a/contracts/interfaces/IPoolQuotaKeeper.sol +++ b/contracts/interfaces/IPoolQuotaKeeper.sol @@ -3,6 +3,7 @@ // (c) Gearbox Holdings, 2022 pragma solidity ^0.8.10; +import {IGauge} from "./IGauge.sol"; import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; enum QuotaStatusChange { @@ -107,6 +108,9 @@ interface IPoolQuotaKeeper is IPoolQuotaKeeperEvents, IPoolQuotaKeeperExceptions // GETTERS // + /// @dev Returns the gauge address + function gauge() external view returns (IGauge); + /// @dev Returns quota rate in PERCENTAGE FORMAT function getQuotaRate(address) external view returns (uint16); From d51b2f34ff09f27d8f6c4a85a851beed398cad30 Mon Sep 17 00:00:00 2001 From: Van0k Date: Mon, 13 Mar 2023 15:45:12 +0400 Subject: [PATCH 04/12] fix: add voter to gauge interface --- contracts/interfaces/IGauge.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/interfaces/IGauge.sol b/contracts/interfaces/IGauge.sol index cc2168aa..9a996cb8 100644 --- a/contracts/interfaces/IGauge.sol +++ b/contracts/interfaces/IGauge.sol @@ -3,6 +3,7 @@ // (c) Gearbox Holdings, 2022 pragma solidity ^0.8.10; +import {IGearStaking} from "./IGearStaking.sol"; import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol"; struct QuotaRateParams { @@ -47,6 +48,9 @@ interface IGaugeEvents { /// @title IGauge interface IGauge is IGaugeEvents, IGaugeExceptions, IVersion { + /// @dev Returns the main voting contract + function voter() external view returns (IGearStaking); + /// @dev Rolls the new epoch and updates all quota rates function updateEpoch() external; From ffedd019a9683adfc650f42127dfd647eb12c0c7 Mon Sep 17 00:00:00 2001 From: Dmitry Lekhovitsky Date: Tue, 14 Mar 2023 14:27:17 +0200 Subject: [PATCH 05/12] chore: fix remappings --- .vscode/extensions.json | 2 +- .vscode/settings.json | 13 +++---------- foundry.toml | 8 -------- remappings.txt | 5 +++++ 4 files changed, 9 insertions(+), 19 deletions(-) create mode 100644 remappings.txt diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 28e6924b..8bf605de 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { "recommendations": [ "esbenp.prettier-vscode", - "nomicfoundation.hardhat-solidity", + "JuanBlanco.solidity", "dbaeumer.vscode-eslint" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 6a5afde0..6c2e5b5f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[solidity]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "JuanBlanco.solidity" }, "editor.defaultFormatter": "esbenp.prettier-vscode", @@ -11,15 +11,8 @@ "editor.tabSize": 2, "eslint.validate": ["javascript", "typescript"], "files.eol": "\n", - "solidity.formatter": "forge", "solidity.packageDefaultDependenciesContractsDirectory": "contracts", "solidity.packageDefaultDependenciesDirectory": "node_modules", - "solidity.remappings": [ - "@chainlink/=node_modules/@chainlink/", - "@ensdomains/=node_modules/@ensdomains/", - "@openzeppelin/=node_modules/@openzeppelin/", - "ds-test/=lib/ds-test/src/", - "hardhat/=node_modules/hardhat/", - "@gearbox-protocol=node_modules/@gearbox-protocol/" - ] + "solidity.compileUsingRemoteVersion": "v0.8.17", + "solidity.formatter": "forge" } diff --git a/foundry.toml b/foundry.toml index 4fc44d9c..56fa85f6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,14 +1,6 @@ [profile.default] libs = ['lib'] out = 'forge-out' -remappings = [ - 'ds-test/=lib/ds-test/src/', - '@ensdomains/=node_modules/@ensdomains/', - '@openzeppelin/=node_modules/@openzeppelin/', - '@chainlink/=node_modules/@chainlink/', - 'hardhat/=node_modules/hardhat/', - '@gearbox-protocol=node_modules/@gearbox-protocol/' -] solc_version = '0.8.17' src = 'contracts' optimizer_runs = 20000 diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 00000000..5f74bb3f --- /dev/null +++ b/remappings.txt @@ -0,0 +1,5 @@ +ds-test/=lib/forge-std/lib/ds-test/src/ +forge-std/=lib/forge-std/src/ +@chainlink/=node_modules/@chainlink/ +@openzeppelin/=node_modules/@openzeppelin/ +@gearbox-protocol=node_modules/@gearbox-protocol/ From 083d1879105d703315bb3d742da4752fc8f3dc3f Mon Sep 17 00:00:00 2001 From: Dmitry Lekhovitsky Date: Tue, 14 Mar 2023 14:27:58 +0200 Subject: [PATCH 06/12] fix: remove euler adapter and price feed types --- contracts/interfaces/IPriceFeedType.sol | 5 +++-- contracts/interfaces/adapters/IAdapter.sol | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/interfaces/IPriceFeedType.sol b/contracts/interfaces/IPriceFeedType.sol index 3047cf60..b2f00b64 100644 --- a/contracts/interfaces/IPriceFeedType.sol +++ b/contracts/interfaces/IPriceFeedType.sol @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// Gearbox Protocol. Generalized leverage for DeFi protocols +// (c) Gearbox Holdings, 2023 +pragma solidity ^0.8.17; enum PriceFeedType { CHAINLINK_ORACLE, @@ -13,7 +15,6 @@ enum PriceFeedType { BOUNDED_ORACLE, COMPOSITE_ORACLE, AAVE_ORACLE, - EULER_ORACLE, COMPOUND_ORACLE, BALANCER_STABLE_LP_ORACLE, BALANCER_WEIGHTED_LP_ORACLE diff --git a/contracts/interfaces/adapters/IAdapter.sol b/contracts/interfaces/adapters/IAdapter.sol index b06d8c69..93e1ea59 100644 --- a/contracts/interfaces/adapters/IAdapter.sol +++ b/contracts/interfaces/adapters/IAdapter.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // Gearbox Protocol. Generalized leverage for DeFi protocols -// (c) Gearbox Holdings, 2022 +// (c) Gearbox Holdings, 2023 pragma solidity ^0.8.17; import {ICreditManagerV2} from "../ICreditManagerV2.sol"; @@ -25,7 +25,6 @@ enum AdapterType { BALANCER_VAULT, AAVE_V2_LENDING_POOL, AAVE_V2_WRAPPED_ATOKEN, - EULER_V1_ETOKEN, COMPOUND_V2_CERC20, COMPOUND_V2_CETHER } From b6c7ac785e148a329ced071bbf5fa0d84996723c Mon Sep 17 00:00:00 2001 From: Dmitry Lekhovitsky Date: Tue, 14 Mar 2023 14:30:50 +0200 Subject: [PATCH 07/12] feat: add _checkToken to abstract adapter --- contracts/adapters/AbstractAdapter.sol | 44 +++-- contracts/test/adapters/AbstractAdapter.t.sol | 185 ++++++++++-------- contracts/test/mocks/adapters/AdapterMock.sol | 40 ++-- 3 files changed, 148 insertions(+), 121 deletions(-) diff --git a/contracts/adapters/AbstractAdapter.sol b/contracts/adapters/AbstractAdapter.sol index c91bb380..8f37f619 100644 --- a/contracts/adapters/AbstractAdapter.sol +++ b/contracts/adapters/AbstractAdapter.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Gearbox Protocol. Generalized leverage for DeFi protocols -// (c) Gearbox Holdings, 2022 +// (c) Gearbox Holdings, 2023 pragma solidity ^0.8.17; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -51,30 +51,33 @@ abstract contract AbstractAdapter is IAdapter { return creditManager.getCreditAccountOrRevert(_creditFacade()); // F: [AA-4] } - /// @dev Executes an arbitrary call from the Credit Account to the target contract - /// @param callData Data to call the target contract with - /// @return result Call output - function _execute(bytes memory callData) internal returns (bytes memory result) { - return creditManager.executeOrder(targetContract, callData); // F: [AA-6,9] + /// @dev Checks if token is registered as collateral token in the Credit Manager + /// @param token Token to check + /// @return tokenMask Collateral token mask + function _checkToken(address token) internal view returns (uint256 tokenMask) { + tokenMask = creditManager.tokenMasksMap(token); // F: [AA-6] + if (tokenMask == 0) { + revert TokenIsNotInAllowedList(token); // F: [AA-6] + } } /// @dev Approves the target contract to spend given token from the Credit Account /// @param token Token to be approved /// @param amount Amount to be approved function _approveToken(address token, uint256 amount) internal { - creditManager.approveCreditAccount(targetContract, token, amount); // F: [AA-6,10] + creditManager.approveCreditAccount(targetContract, token, amount); // F: [AA-7, AA-8] } /// @dev Enables a token in the Credit Account /// @param token Address of the token to enable function _enableToken(address token) internal { - creditManager.checkAndEnableToken(token); // F: [AA-6,11] + creditManager.checkAndEnableToken(token); // F: [AA-7, AA-9] } /// @dev Disables a token in the Credit Account /// @param token Address of the token to disable function _disableToken(address token) internal { - creditManager.disableToken(token); // F: [AA-6,12] + creditManager.disableToken(token); // F: [AA-7, AA-10] } /// @dev Changes enabled tokens in the Credit Account @@ -84,7 +87,14 @@ abstract contract AbstractAdapter is IAdapter { /// determined in the adapter constructor, thus saving gas by avoiding querying them during execution /// and combining multiple enable/disable operations into a single one function _changeEnabledTokens(uint256 tokensToEnable, uint256 tokensToDisable) internal { - creditManager.changeEnabledTokens(tokensToEnable, tokensToDisable); // F: [AA-6,13] + creditManager.changeEnabledTokens(tokensToEnable, tokensToDisable); // F: [AA-7, AA-11] + } + + /// @dev Executes an arbitrary call from the Credit Account to the target contract + /// @param callData Data to call the target contract with + /// @return result Call output + function _execute(bytes memory callData) internal returns (bytes memory result) { + return creditManager.executeOrder(targetContract, callData); // F: [AA-7, AA-12] } /// @dev Executes a swap operation on the target contract from the Credit Account @@ -99,7 +109,7 @@ abstract contract AbstractAdapter is IAdapter { internal returns (bytes memory result) { - return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn, false); // F: [AA-6,7] + return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn, false); // F: [AA-7, AA-13] } /// @dev Executes a swap operation on the target contract from the Credit Account @@ -114,7 +124,7 @@ abstract contract AbstractAdapter is IAdapter { internal returns (bytes memory result) { - return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn, true); // F: [AA-6,8] + return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn, true); // F: [AA-7, AA-14] } /// @dev Implementation of `_executeSwap...` operations @@ -128,18 +138,18 @@ abstract contract AbstractAdapter is IAdapter { bool allowTokenIn ) private returns (bytes memory result) { if (allowTokenIn) { - _approveToken(tokenIn, type(uint256).max); // F: [AA-8] + _approveToken(tokenIn, type(uint256).max); // F: [AA-14] } - result = _execute(callData); // F: [AA-7,8] + result = _execute(callData); // F: [AA-13, AA-14] if (allowTokenIn) { - _approveToken(tokenIn, 1); // F: [AA-8] + _approveToken(tokenIn, 1); // F: [AA-14] } if (disableTokenIn) { - _disableToken(tokenIn); // F: [AA-7,8] + _disableToken(tokenIn); // F: [AA-13, AA-14] } - _enableToken(tokenOut); // F: [AA-7,8] + _enableToken(tokenOut); // F: [AA-13, AA-14] } } diff --git a/contracts/test/adapters/AbstractAdapter.t.sol b/contracts/test/adapters/AbstractAdapter.t.sol index b0729b5f..227e47a2 100644 --- a/contracts/test/adapters/AbstractAdapter.t.sol +++ b/contracts/test/adapters/AbstractAdapter.t.sol @@ -18,6 +18,7 @@ import {BalanceHelper} from "../helpers/BalanceHelper.sol"; import {CreditFacadeTestHelper} from "../helpers/CreditFacadeTestHelper.sol"; // EXCEPTIONS +import {IAdapterExceptions} from "../../interfaces/adapters/IAdapter.sol"; import {ZeroAddressException} from "../../interfaces/IErrors.sol"; import {ICreditManagerV2Exceptions} from "../../interfaces/ICreditManagerV2.sol"; @@ -132,8 +133,20 @@ contract AbstractAdapterTest is adapterMock.execute(DUMB_CALLDATA); } - /// @dev [AA-6]: AbstractAdapter functions revert if user has no credit account - function test_AA_06_adapter_reverts_if_user_has_no_credit_account() public { + /// @dev [AA-6]: AbstractAdapter _checkToken works correctly + function test_AA_06_checkToken_works_correctly() public { + assertEq( + adapterMock.checkToken(tokenTestSuite.addressOf(Tokens.DAI)), + creditManager.tokenMasksMap(tokenTestSuite.addressOf(Tokens.DAI)) + ); + + address token = address(0xdead); + evm.expectRevert(abi.encodeWithSelector(IAdapterExceptions.TokenIsNotInAllowedList.selector, token)); + adapterMock.checkToken(address(0xdead)); + } + + /// @dev [AA-7]: AbstractAdapter functions revert if user has no credit account + function test_AA_07_adapter_reverts_if_user_has_no_credit_account() public { evm.expectRevert(HasNoOpenedAccountException.selector); adapterMock.creditAccount(); @@ -206,8 +219,88 @@ contract AbstractAdapterTest is } } - /// @dev [AA-7]: _executeSwapNoApprove correctly passes parameters to CreditManager - function test_AA_07_executeSwapNoApprove_correctly_passes_to_credit_manager() public { + /// @dev [AA-8]: _approveToken correctly passes parameters to CreditManager + function test_AA_08_approveToken_correctly_passes_to_credit_manager() public { + _openTestCreditAccount(); + + evm.expectCall( + address(creditManager), + abi.encodeCall(ICreditManagerV2.approveCreditAccount, (address(targetMock), usdc, 10)) + ); + + evm.prank(USER); + creditFacade.multicall( + multicallBuilder( + MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.approveToken, (usdc, 10))}) + ) + ); + } + + /// @dev [AA-9]: _enableToken correctly passes parameters to CreditManager + function test_AA_09_enableToken_correctly_passes_to_credit_manager() public { + _openTestCreditAccount(); + + evm.expectCall(address(creditManager), abi.encodeCall(ICreditManagerV2.checkAndEnableToken, (usdc))); + + evm.prank(USER); + creditFacade.multicall( + multicallBuilder( + MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.enableToken, (usdc))}) + ) + ); + } + + /// @dev [AA-10]: _disableToken correctly passes parameters to CreditManager + function test_AA_10_disableToken_correctly_passes_to_credit_manager() public { + _openTestCreditAccount(); + + evm.expectCall(address(creditManager), abi.encodeCall(ICreditManagerV2.disableToken, (usdc))); + + evm.prank(USER); + creditFacade.multicall( + multicallBuilder( + MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.disableToken, (usdc))}) + ) + ); + } + + /// @dev [AA-11]: _changeEnabledTokens correctly passes parameters to CreditManager + function test_AA_11_changeEnabledTokens_correctly_passes_to_credit_manager() public { + _openTestCreditAccount(); + + evm.expectCall(address(creditManager), abi.encodeCall(ICreditManagerV2.changeEnabledTokens, (1, 2))); + + evm.prank(USER); + creditFacade.multicall( + multicallBuilder( + MultiCall({ + target: address(adapterMock), + callData: abi.encodeCall(adapterMock.changeEnabledTokens, (1, 2)) + }) + ) + ); + } + + /// @dev [AA-12]: _execute correctly passes parameters to CreditManager + function test_AA_12_execute_correctly_passes_to_credit_manager() public { + _openTestCreditAccount(); + + bytes memory DUMB_CALLDATA = abi.encodeWithSignature("hello(string)", "world"); + + evm.expectCall( + address(creditManager), abi.encodeCall(ICreditManagerV2.executeOrder, (address(targetMock), DUMB_CALLDATA)) + ); + + evm.prank(USER); + creditFacade.multicall( + multicallBuilder( + MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.execute, DUMB_CALLDATA)}) + ) + ); + } + + /// @dev [AA-13]: _executeSwapNoApprove correctly passes parameters to CreditManager + function test_AA_13_executeSwapNoApprove_correctly_passes_to_credit_manager() public { _openTestCreditAccount(); bytes memory DUMB_CALLDATA = abi.encodeWithSignature("hello(string)", "world"); @@ -236,8 +329,8 @@ contract AbstractAdapterTest is } } - /// @dev [AA-8]: _executeSwapSafeApprove correctly passes parameters to CreditManager and sets allowance - function test_AA_08_executeSwapSafeApprove_correctly_passes_to_credit_manager() public { + /// @dev [AA-14]: _executeSwapSafeApprove correctly passes parameters to CreditManager and sets allowance + function test_AA_14_executeSwapSafeApprove_correctly_passes_to_credit_manager() public { (address ca,) = _openTestCreditAccount(); bytes memory DUMB_CALLDATA = abi.encodeWithSignature("hello(string)", "world"); @@ -277,84 +370,4 @@ contract AbstractAdapterTest is assertEq(IERC20(usdc).allowance(ca, address(targetMock)), 1, "Incorrect allowance set"); } } - - /// @dev [AA-9]: _execute correctly passes parameters to CreditManager - function test_AA_09_execute_correctly_passes_to_credit_manager() public { - _openTestCreditAccount(); - - bytes memory DUMB_CALLDATA = abi.encodeWithSignature("hello(string)", "world"); - - evm.expectCall( - address(creditManager), abi.encodeCall(ICreditManagerV2.executeOrder, (address(targetMock), DUMB_CALLDATA)) - ); - - evm.prank(USER); - creditFacade.multicall( - multicallBuilder( - MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.execute, DUMB_CALLDATA)}) - ) - ); - } - - /// @dev [AA-10]: _approveToken correctly passes parameters to CreditManager - function test_AA_10_approveToken_correctly_passes_to_credit_manager() public { - _openTestCreditAccount(); - - evm.expectCall( - address(creditManager), - abi.encodeCall(ICreditManagerV2.approveCreditAccount, (address(targetMock), usdc, 10)) - ); - - evm.prank(USER); - creditFacade.multicall( - multicallBuilder( - MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.approveToken, (usdc, 10))}) - ) - ); - } - - /// @dev [AA-11]: _enableToken correctly passes parameters to CreditManager - function test_AA_11_enableToken_correctly_passes_to_credit_manager() public { - _openTestCreditAccount(); - - evm.expectCall(address(creditManager), abi.encodeCall(ICreditManagerV2.checkAndEnableToken, (usdc))); - - evm.prank(USER); - creditFacade.multicall( - multicallBuilder( - MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.enableToken, (usdc))}) - ) - ); - } - - /// @dev [AA-12]: _disableToken correctly passes parameters to CreditManager - function test_AA_12_disableToken_correctly_passes_to_credit_manager() public { - _openTestCreditAccount(); - - evm.expectCall(address(creditManager), abi.encodeCall(ICreditManagerV2.disableToken, (usdc))); - - evm.prank(USER); - creditFacade.multicall( - multicallBuilder( - MultiCall({target: address(adapterMock), callData: abi.encodeCall(adapterMock.disableToken, (usdc))}) - ) - ); - } - - /// @dev [AA-13]: _changeEnabledTokens correctly passes parameters to CreditManager - function test_AA_13_changeEnabledTokens_correctly_passes_to_credit_manager() public { - _openTestCreditAccount(); - - evm.expectCall(address(creditManager), abi.encodeCall(ICreditManagerV2.changeEnabledTokens, (1, 2))); - - evm.prank(USER); - creditFacade.multicall( - multicallBuilder( - MultiCall({ - target: address(adapterMock), - callData: abi.encodeCall(adapterMock.changeEnabledTokens, (1, 2)) - }) - ) - ); - } } diff --git a/contracts/test/mocks/adapters/AdapterMock.sol b/contracts/test/mocks/adapters/AdapterMock.sol index 7b778bda..9ffd13e3 100644 --- a/contracts/test/mocks/adapters/AdapterMock.sol +++ b/contracts/test/mocks/adapters/AdapterMock.sol @@ -24,24 +24,8 @@ contract AdapterMock is AbstractAdapter { return _creditAccount(); } - function executeSwapNoApprove(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) - external - creditFacadeOnly - returns (bytes memory result) - { - return _executeSwapNoApprove(tokenIn, tokenOut, callData, disableTokenIn); - } - - function executeSwapSafeApprove(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) - external - creditFacadeOnly - returns (bytes memory result) - { - return _executeSwapSafeApprove(tokenIn, tokenOut, callData, disableTokenIn); - } - - function execute(bytes memory callData) external creditFacadeOnly returns (bytes memory result) { - result = _execute(callData); + function checkToken(address token) external view returns (uint256 tokenMask) { + return _checkToken(token); } function approveToken(address token, uint256 amount) external creditFacadeOnly { @@ -60,6 +44,26 @@ contract AdapterMock is AbstractAdapter { _changeEnabledTokens(tokensToEnable, tokensToDisable); } + function execute(bytes memory callData) external creditFacadeOnly returns (bytes memory result) { + result = _execute(callData); + } + + function executeSwapNoApprove(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) + external + creditFacadeOnly + returns (bytes memory result) + { + return _executeSwapNoApprove(tokenIn, tokenOut, callData, disableTokenIn); + } + + function executeSwapSafeApprove(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) + external + creditFacadeOnly + returns (bytes memory result) + { + return _executeSwapSafeApprove(tokenIn, tokenOut, callData, disableTokenIn); + } + fallback() external creditFacadeOnly { _execute(msg.data); } From bb5978ee7cf18c6f6daaeb1af5d99cdb6d2629bc Mon Sep 17 00:00:00 2001 From: Van0k Date: Wed, 15 Mar 2023 13:45:40 +0400 Subject: [PATCH 08/12] fix: cm factory support pool4626 --- contracts/factories/CreditManagerFactoryBase.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/factories/CreditManagerFactoryBase.sol b/contracts/factories/CreditManagerFactoryBase.sol index bc2bc7a9..46e7805a 100644 --- a/contracts/factories/CreditManagerFactoryBase.sol +++ b/contracts/factories/CreditManagerFactoryBase.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.10; import {ContractsRegister} from "@gearbox-protocol/core-v2/contracts/core/ContractsRegister.sol"; -import {PoolService} from "@gearbox-protocol/core-v2/contracts/pool/PoolService.sol"; +import {Pool4626} from "../pool/Pool4626.sol"; import {CreditManager} from "../credit/CreditManager.sol"; import {CreditFacade} from "../credit/CreditFacade.sol"; import {CreditConfigurator, CreditManagerOpts} from "../credit/CreditConfigurator.sol"; @@ -23,14 +23,14 @@ contract CreditManagerFactoryBase is ContractUpgrader { CreditManager public creditManager; CreditFacade public creditFacade; CreditConfigurator public creditConfigurator; - PoolService public immutable pool; + Pool4626 public immutable pool; Adapter[] public adapters; constructor(address _pool, CreditManagerOpts memory opts, uint256 salt) - ContractUpgrader(address(PoolService(_pool).addressProvider())) + ContractUpgrader(address(Pool4626(_pool).addressProvider())) { - pool = PoolService(_pool); + pool = Pool4626(_pool); creditManager = new CreditManager(_pool); creditFacade = new CreditFacade( @@ -113,7 +113,7 @@ contract CreditManagerFactoryBase is ContractUpgrader { cr.addCreditManager(address(creditManager)); // T:[PD-2] - pool.connectCreditManager(address(creditManager)); + pool.setCreditManagerLimit(address(creditManager), pool.expectedLiquidityLimit()); _postInstall(); } From 8656121635e0cb30fd926dd289a38086367497b6 Mon Sep 17 00:00:00 2001 From: Dmitry Lekhovitsky Date: Wed, 15 Mar 2023 18:04:12 +0200 Subject: [PATCH 09/12] feat: abstract adapter inherits acl trait --- contracts/adapters/AbstractAdapter.sol | 22 +++++++++++++------ contracts/interfaces/adapters/IAdapter.sol | 4 ++++ contracts/test/adapters/AbstractAdapter.t.sol | 18 ++++++++++----- .../test/credit/CreditConfigurator.t.sol | 5 ++++- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/contracts/adapters/AbstractAdapter.sol b/contracts/adapters/AbstractAdapter.sol index 8f37f619..f0360ff8 100644 --- a/contracts/adapters/AbstractAdapter.sol +++ b/contracts/adapters/AbstractAdapter.sol @@ -3,29 +3,37 @@ // (c) Gearbox Holdings, 2023 pragma solidity ^0.8.17; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - +import {ACLNonReentrantTrait} from "../core/ACLNonReentrantTrait.sol"; import {IAdapter} from "../interfaces/adapters/IAdapter.sol"; +import {IAddressProvider} from "@gearbox-protocol/core-v2/contracts/interfaces/IAddressProvider.sol"; import {ICreditManagerV2} from "../interfaces/ICreditManagerV2.sol"; +import {IPool4626} from "../interfaces/IPool4626.sol"; import {ZeroAddressException} from "../interfaces/IErrors.sol"; /// @title Abstract adapter /// @dev Inheriting adapters MUST use provided internal functions to perform all operations with credit accounts -abstract contract AbstractAdapter is IAdapter { +abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { /// @notice Credit Manager the adapter is connected to ICreditManagerV2 public immutable override creditManager; + + /// @notice Address provider + IAddressProvider public immutable override addressProvider; + /// @notice Address of the contract the adapter is interacting with address public immutable override targetContract; /// @notice Constructor /// @param _creditManager Credit Manager to connect this adapter to /// @param _targetContract Address of the contract this adapter should interact with - constructor(address _creditManager, address _targetContract) { - if (_creditManager == address(0) || _targetContract == address(0)) { - revert ZeroAddressException(); - } // F: [AA-2] + constructor(address _creditManager, address _targetContract) + ACLNonReentrantTrait(IPool4626(ICreditManagerV2(_creditManager).pool()).addressProvider()) + { + if (_targetContract == address(0)) { + revert ZeroAddressException(); // F: [AA-2] + } creditManager = ICreditManagerV2(_creditManager); // F: [AA-1] + addressProvider = IAddressProvider(IPool4626(creditManager.pool()).addressProvider()); // F: [AA-1] targetContract = _targetContract; // F: [AA-1] } diff --git a/contracts/interfaces/adapters/IAdapter.sol b/contracts/interfaces/adapters/IAdapter.sol index 93e1ea59..3babc4b2 100644 --- a/contracts/interfaces/adapters/IAdapter.sol +++ b/contracts/interfaces/adapters/IAdapter.sol @@ -3,6 +3,7 @@ // (c) Gearbox Holdings, 2023 pragma solidity ^0.8.17; +import {IAddressProvider} from "@gearbox-protocol/core-v2/contracts/interfaces/IAddressProvider.sol"; import {ICreditManagerV2} from "../ICreditManagerV2.sol"; enum AdapterType { @@ -45,6 +46,9 @@ interface IAdapter is IAdapterExceptions { /// @notice Address of the contract the adapter is interacting with function targetContract() external view returns (address); + /// @notice Address provider + function addressProvider() external view returns (IAddressProvider); + /// @notice Adapter type function _gearboxAdapterType() external pure returns (AdapterType); diff --git a/contracts/test/adapters/AbstractAdapter.t.sol b/contracts/test/adapters/AbstractAdapter.t.sol index 227e47a2..f4cc2c6d 100644 --- a/contracts/test/adapters/AbstractAdapter.t.sol +++ b/contracts/test/adapters/AbstractAdapter.t.sol @@ -8,10 +8,12 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {AccountFactory} from "@gearbox-protocol/core-v2/contracts/core/AccountFactory.sol"; import {CreditFacade} from "../../credit/CreditFacade.sol"; +import {IAddressProvider} from "@gearbox-protocol/core-v2/contracts/interfaces/IAddressProvider.sol"; import {ICreditAccount} from "@gearbox-protocol/core-v2/contracts/interfaces/ICreditAccount.sol"; import {ICreditFacade, MultiCall} from "@gearbox-protocol/core-v2/contracts/interfaces/ICreditFacade.sol"; import {ICreditManagerV2, ICreditManagerV2Events} from "../../interfaces/ICreditManagerV2.sol"; import {ICreditFacadeEvents, ICreditFacadeExceptions} from "../../interfaces/ICreditFacade.sol"; +import {IPool4626} from "../../interfaces/IPool4626.sol"; import "../lib/constants.sol"; import {BalanceHelper} from "../helpers/BalanceHelper.sol"; @@ -95,18 +97,24 @@ contract AbstractAdapterTest is /// @dev [AA-1]: AbstractAdapter constructor sets correct values function test_AA_01_constructor_sets_correct_values() public { - assertEq(address(adapterMock.creditManager()), address(creditManager), "Incorrect Credit Manager"); + assertEq(address(adapterMock.creditManager()), address(creditManager), "Incorrect credit manager"); + + assertEq( + address(adapterMock.addressProvider()), + IPool4626(creditManager.pool()).addressProvider(), + "Incorrect address provider" + ); assertEq(adapterMock.targetContract(), address(targetMock), "Incorrect target contract"); } - /// @dev [AA-2]: AbstractAdapter constructor reverts when passed a zero-address + /// @dev [AA-2]: AbstractAdapter constructor reverts when passed zero-address as target contract function test_AA_02_constructor_reverts_on_zero_address() public { - evm.expectRevert(ZeroAddressException.selector); - AdapterMock am = new AdapterMock(address(0), address(0)); + evm.expectRevert(); + new AdapterMock(address(0), address(0)); evm.expectRevert(ZeroAddressException.selector); - am = new AdapterMock(address(creditManager), address(0)); + new AdapterMock(address(creditManager), address(0)); } /// @dev [AA-3]: AbstractAdapter uses correct credit facade diff --git a/contracts/test/credit/CreditConfigurator.t.sol b/contracts/test/credit/CreditConfigurator.t.sol index c8eb5325..c1b172dc 100644 --- a/contracts/test/credit/CreditConfigurator.t.sol +++ b/contracts/test/credit/CreditConfigurator.t.sol @@ -92,7 +92,10 @@ contract CreditConfiguratorTest is TARGET_CONTRACT = address(new TargetContractMock()); adapter1 = new AdapterMock(address(creditManager), TARGET_CONTRACT); - adapterDifferentCM = new AdapterMock(address(this), TARGET_CONTRACT); + + adapterDifferentCM = new AdapterMock( + address(new CreditFacadeTestSuite(creditConfig).creditManager()), TARGET_CONTRACT + ); DUMB_COMPARTIBLE_CONTRACT = address(adapter1); } From f47fc94517d839e7a690d095de600e1c0ac0dfe4 Mon Sep 17 00:00:00 2001 From: Dmitry Lekhovitsky Date: Thu, 16 Mar 2023 19:25:23 +0200 Subject: [PATCH 10/12] fix: refactor _executeSwap --- contracts/adapters/AbstractAdapter.sol | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/contracts/adapters/AbstractAdapter.sol b/contracts/adapters/AbstractAdapter.sol index f0360ff8..86964ba5 100644 --- a/contracts/adapters/AbstractAdapter.sol +++ b/contracts/adapters/AbstractAdapter.sol @@ -117,7 +117,7 @@ abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { internal returns (bytes memory result) { - return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn, false); // F: [AA-7, AA-13] + return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn); // F: [AA-7, AA-13] } /// @dev Executes a swap operation on the target contract from the Credit Account @@ -132,29 +132,19 @@ abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { internal returns (bytes memory result) { - return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn, true); // F: [AA-7, AA-14] + _approveToken(tokenIn, type(uint256).max); // F: [AA-14] + result = _executeSwap(tokenIn, tokenOut, callData, disableTokenIn); // F: [AA-7, AA-14] + _approveToken(tokenIn, 1); // F: [AA-14] } /// @dev Implementation of `_executeSwap...` operations /// @dev Kept private as only the internal wrappers are intended to be used /// by inheritors - function _executeSwap( - address tokenIn, - address tokenOut, - bytes memory callData, - bool disableTokenIn, - bool allowTokenIn - ) private returns (bytes memory result) { - if (allowTokenIn) { - _approveToken(tokenIn, type(uint256).max); // F: [AA-14] - } - + function _executeSwap(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) + private + returns (bytes memory result) + { result = _execute(callData); // F: [AA-13, AA-14] - - if (allowTokenIn) { - _approveToken(tokenIn, 1); // F: [AA-14] - } - if (disableTokenIn) { _disableToken(tokenIn); // F: [AA-13, AA-14] } From 6988e65dfc4e397c0c951e9fa69a0d87c88dce1d Mon Sep 17 00:00:00 2001 From: Dmitry Lekhovitsky Date: Sat, 18 Mar 2023 15:18:52 +0200 Subject: [PATCH 11/12] feat: _executeSwapNoApprove reverts on bad tokens --- contracts/adapters/AbstractAdapter.sol | 11 ++++-- contracts/test/adapters/AbstractAdapter.t.sol | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/contracts/adapters/AbstractAdapter.sol b/contracts/adapters/AbstractAdapter.sol index 86964ba5..6d61deed 100644 --- a/contracts/adapters/AbstractAdapter.sol +++ b/contracts/adapters/AbstractAdapter.sol @@ -72,12 +72,14 @@ abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { /// @dev Approves the target contract to spend given token from the Credit Account /// @param token Token to be approved /// @param amount Amount to be approved + /// @dev Reverts if token is not registered as collateral token in the Credit Manager function _approveToken(address token, uint256 amount) internal { creditManager.approveCreditAccount(targetContract, token, amount); // F: [AA-7, AA-8] } /// @dev Enables a token in the Credit Account /// @param token Address of the token to enable + /// @dev Reverts if token is not registered as collateral token in the Credit Manager function _enableToken(address token) internal { creditManager.checkAndEnableToken(token); // F: [AA-7, AA-9] } @@ -113,11 +115,13 @@ abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { /// @param disableTokenIn Whether the input token should be disabled afterwards /// (for operations that spend the entire balance) /// @return result Call output + /// @dev Reverts if tokenIn or tokenOut are not registered as collateral in the Credit Manager function _executeSwapNoApprove(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) internal returns (bytes memory result) { - return _executeSwap(tokenIn, tokenOut, callData, disableTokenIn); // F: [AA-7, AA-13] + _checkToken(tokenIn); // F: [AA-13, AA-15] + result = _executeSwap(tokenIn, tokenOut, callData, disableTokenIn); // F: [AA-7, AA-13] } /// @dev Executes a swap operation on the target contract from the Credit Account @@ -128,11 +132,12 @@ abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { /// @param disableTokenIn Whether the input token should be disabled afterwards /// (for operations that spend the entire balance) /// @return result Call output + /// @dev Reverts if tokenIn or tokenOut are not registered as collateral in the Credit Manager function _executeSwapSafeApprove(address tokenIn, address tokenOut, bytes memory callData, bool disableTokenIn) internal returns (bytes memory result) { - _approveToken(tokenIn, type(uint256).max); // F: [AA-14] + _approveToken(tokenIn, type(uint256).max); // F: [AA-14, AA-15] result = _executeSwap(tokenIn, tokenOut, callData, disableTokenIn); // F: [AA-7, AA-14] _approveToken(tokenIn, 1); // F: [AA-14] } @@ -148,6 +153,6 @@ abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { if (disableTokenIn) { _disableToken(tokenIn); // F: [AA-13, AA-14] } - _enableToken(tokenOut); // F: [AA-13, AA-14] + _enableToken(tokenOut); // F: [AA-13, AA-14, AA-15] } } diff --git a/contracts/test/adapters/AbstractAdapter.t.sol b/contracts/test/adapters/AbstractAdapter.t.sol index f4cc2c6d..501e7e2f 100644 --- a/contracts/test/adapters/AbstractAdapter.t.sol +++ b/contracts/test/adapters/AbstractAdapter.t.sol @@ -378,4 +378,40 @@ contract AbstractAdapterTest is assertEq(IERC20(usdc).allowance(ca, address(targetMock)), 1, "Incorrect allowance set"); } } + + /// @dev [AA-15]: _executeSwapNoApprove reverts if tokenIn or tokenOut are not allowed + function test_AA_15_executeSwap_reverts_if_tokenIn_or_tokenOut_are_not_allowed() public { + _openTestCreditAccount(); + + address TOKEN = address(0xdead); + bytes memory DUMB_CALLDATA = abi.encodeWithSignature("hello(string)", "world"); + + // ti == 0 => bad tokenOut, ti == 1 => bad tokenIn + // sa == 0 => no approve, sa == 1 => safe approve + for (uint256 ti; ti < 2; ++ti) { + for (uint256 sa; sa < 2; ++sa) { + bytes memory callData; + if (sa == 1) { + callData = abi.encodeCall( + adapterMock.executeSwapSafeApprove, + (ti == 1 ? TOKEN : dai, ti == 1 ? dai : TOKEN, DUMB_CALLDATA, false) + ); + } else { + callData = abi.encodeCall( + adapterMock.executeSwapNoApprove, + (ti == 1 ? TOKEN : dai, ti == 1 ? dai : TOKEN, DUMB_CALLDATA, false) + ); + } + + if (sa == 0 && ti == 1) { + evm.expectRevert(abi.encodeWithSelector(IAdapterExceptions.TokenIsNotInAllowedList.selector, TOKEN)); + } else { + evm.expectRevert(TokenNotAllowedException.selector); + } + + evm.prank(USER); + creditFacade.multicall(multicallBuilder(MultiCall({target: address(adapterMock), callData: callData}))); + } + } + } } From d7a27360c2898f8795d6ea144080877b1755c05d Mon Sep 17 00:00:00 2001 From: Dmitry Lekhovitsky Date: Sat, 18 Mar 2023 15:31:36 +0200 Subject: [PATCH 12/12] fix-commit-tag --- contracts/adapters/AbstractAdapter.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/adapters/AbstractAdapter.sol b/contracts/adapters/AbstractAdapter.sol index 6d61deed..541d7540 100644 --- a/contracts/adapters/AbstractAdapter.sol +++ b/contracts/adapters/AbstractAdapter.sol @@ -120,7 +120,7 @@ abstract contract AbstractAdapter is IAdapter, ACLNonReentrantTrait { internal returns (bytes memory result) { - _checkToken(tokenIn); // F: [AA-13, AA-15] + _checkToken(tokenIn); // F: [AA-15] result = _executeSwap(tokenIn, tokenOut, callData, disableTokenIn); // F: [AA-7, AA-13] }