Skip to content

Commit

Permalink
Merge pull request #193 from Gearbox-protocol/fix--better-quota-accou…
Browse files Browse the repository at this point in the history
…nting

fix: test setup + better quotas accounting
  • Loading branch information
0xmikko authored Mar 28, 2024
2 parents 4af72c2 + 3c798fb commit b2628d7
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 6 deletions.
6 changes: 4 additions & 2 deletions contracts/credit/CreditFacadeV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ contract CreditFacadeV3 is ICreditFacadeV3, ACLNonReentrantTrait {
using SafeERC20 for IERC20;

/// @notice Contract version
uint256 public constant override version = 3_00;
uint256 public constant override version = 3_01;

/// @notice Maximum quota size, as a multiple of `maxDebt`
uint256 public constant override maxQuotaMultiplier = 2;
Expand Down Expand Up @@ -824,7 +824,9 @@ contract CreditFacadeV3 is ICreditFacadeV3, ACLNonReentrantTrait {
(tokensToEnable, tokensToDisable) = ICreditManagerV3(creditManager).updateQuota({
creditAccount: creditAccount,
token: token,
quotaChange: quotaChange,
quotaChange: quotaChange != type(int96).min
? quotaChange / int96(uint96(PERCENTAGE_FACTOR)) * int96(uint96(PERCENTAGE_FACTOR))
: quotaChange,
minQuota: minQuota,
maxQuota: uint96(Math.min(type(uint96).max, maxQuotaMultiplier * debtLimits.maxDebt))
}); // U:[FA-34]
Expand Down
4 changes: 2 additions & 2 deletions contracts/test/integration/credit/Quotas.int.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,8 @@ contract QuotasIntegrationTest is IntegrationTestHelper, ICreditManagerV3Events

(uint16 feeInterest,,,,) = creditManager.fees();

quotaLink = quotaLink > uint96(100_000 * WAD) ? uint96(100_000 * WAD) : quotaLink;
quotaUsdt = quotaUsdt > uint96(100_000 * WAD) ? uint96(100_000 * WAD) : quotaUsdt;
quotaLink = quotaLink > uint96(100_000 * WAD) ? uint96(100_000 * WAD) : quotaLink / 10_000 * 10_000;
quotaUsdt = quotaUsdt > uint96(100_000 * WAD) ? uint96(100_000 * WAD) : quotaUsdt / 10_000 * 10_000;

uint256 expectedTotalDebt = (borrowedAmount * cumulativeIndexAtClose) / cumulativeIndexLastUpdate;
expectedTotalDebt += (quotaLink * 1000) / PERCENTAGE_FACTOR;
Expand Down
116 changes: 116 additions & 0 deletions contracts/test/invaritants/TargetAttacker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// // SPDX-License-Identifier: UNLICENSED
// // Gearbox Protocol. Generalized leverage for DeFi protocols
// // (c) Gearbox Foundation, 2023.
// pragma solidity ^0.8.17;

// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
// import {ICreditManagerV3, CollateralCalcTask, CollateralDebtData} from "../../interfaces/ICreditManagerV3.sol";
// import {IPriceOracleV3} from "../../interfaces/IPriceOracleV3.sol";
// import {ITokenTestSuite} from "../interfaces/ITokenTestSuite.sol";
// import {PriceFeedMock} from "../mocks/oracles/PriceFeedMock.sol";
// import {Random} from "./Random.sol";

// /// @title Target Hacker
// /// This contract simulates different technics to hack the system by provided seed

// contract TargetAttacker is Random {
// using Math for uint256;

// ICreditManagerV3 creditManager;
// IPriceOracleV3 priceOracle;
// ITokenTestSuite tokenTestSuite;
// address creditAccount;

// constructor(address _creditManager, address _priceOracle, address _tokenTestSuite) {
// creditManager = ICreditManagerV3(_creditManager);
// priceOracle = IPriceOracleV3(_priceOracle);
// tokenTestSuite = ITokenTestSuite(_tokenTestSuite);
// }

// // Act function tests different scenarios related to any action
// // which could potential attacker use. Calling internal contracts
// // depositing funds into pools, withdrawing, liquidating, etc.

// // it also could update prices for updatable price oracles

// function act(uint256 _seed) external {
// setSeed(_seed);
// creditAccount = msg.sender;

// function ()[3] memory fnActions = [_stealTokens, _changeTokenPrice, _swapTokens];

// fnActions[getRandomInRange(fnActions.length)]();
// }

// function _changeTokenPrice() internal {
// uint256 cTokensQty = creditManager.collateralTokensCount();
// uint256 mask = 1 << getRandomInRange(cTokensQty);
// (address token,) = creditManager.collateralTokenByMask(mask);

// address priceFeed = IPriceOracleV3(priceOracle).priceFeeds(token);

// (, int256 price,,,) = PriceFeedMock(priceFeed).latestRoundData();

// uint256 sign = getRandomInRange(2);
// uint256 deltaPct = getRandomInRange(500);

// int256 newPrice =
// sign == 1 ? price * (10000 + int256(deltaPct)) / 10000 : price * (10000 - int256(deltaPct)) / 10000;

// PriceFeedMock(priceFeed).setPrice(newPrice);
// }

// function _swapTokens() internal {
// uint256 cTokensQty = creditManager.collateralTokensCount();
// uint256 mask0 = 1 << getRandomInRange(cTokensQty);
// uint256 mask1 = 1 << getRandomInRange(cTokensQty);

// (address tokenIn,) = creditManager.collateralTokenByMask(mask0);
// (address tokenOut,) = creditManager.collateralTokenByMask(mask1);

// uint256 balance = IERC20(tokenIn).balanceOf(creditAccount);

// uint256 tokenInAmount = getRandomInRange(balance);

// uint256 tokenInEq = priceOracle.convert(tokenInAmount, tokenIn, tokenOut);

// IERC20(tokenIn).transferFrom(creditAccount, address(this), tokenInAmount);
// tokenTestSuite.mint(tokenOut, creditAccount, getRandomInRange(tokenInEq));
// }

// function _stealTokens() internal {
// uint256 cTokensQty = creditManager.collateralTokensCount();
// uint256 mask = 1 << getRandomInRange(cTokensQty);
// (tokenIn,) = creditManager.collateralTokenByMask(mask);
// uint256 balance = IERC20(tokenIn).balanceOf(creditAccount);
// IERC20(tokenIn).transferFrom(creditAccount, address(this), getRandomInRange(balance));
// }

// /// Swaps token with some deviation from oracle price

// function _swap() internal {
// uint256 cTokensQty = creditManager.collateralTokensCount();

// (tokenIn,) = creditManager.collateralTokenByMask(1 << getRandomInRange(cTokensQty));
// uint256 balance = IERC20(tokenIn).balanceOf(creditAccount);
// uint256 amount = getRandomInRange(balance);
// IERC20(tokenIn).transferFrom(creditAccount, address(this), amount);

// (tokenOut,) = creditManager.collateralTokenByMask(1 << getRandomInRange(cTokensQty));

// uint256 amountOut = (priceOracle.convert(amount, tokenIn, tokenOut) * (120 - getRandomInRange(40))) / 100;
// amountOut = Math.min(amountOut, IERC20(tokenOut).balanceOf(address(this)));
// IERC20(tokenOut).transfer(creditAccount, amountOut);
// }

// function _deposit() internal {
// uint256 amount = getRandomInRange95(pool.availableLiquidity());
// pool.deposit(amount, address(this));
// }

// function _withdraw() internal {
// uint256 amount = getRandomInRange95(pool.balanceOf(address(this)));
// pool.withdraw(amount, address(this), address(this));
// }
// }
4 changes: 2 additions & 2 deletions contracts/test/unit/credit/CreditFacadeV3.unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1515,15 +1515,15 @@ contract CreditFacadeV3UnitTest is TestHelper, BalanceHelper, ICreditFacadeV3Eve
uint256 maskToEnable = 1 << 4;
uint256 maskToDisable = 1 << 7;

int96 change = -990;
int96 change = -19900;

creditManagerMock.setUpdateQuota({tokensToEnable: maskToEnable, tokensToDisable: maskToDisable});

vm.expectCall(
address(creditManagerMock),
abi.encodeCall(
ICreditManagerV3.updateQuota,
(creditAccount, link, change, 0, uint96(maxDebt * creditFacade.maxQuotaMultiplier()))
(creditAccount, link, change / 10_000 * 10_000, 0, uint96(maxDebt * creditFacade.maxQuotaMultiplier()))
)
);

Expand Down

0 comments on commit b2628d7

Please sign in to comment.