Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fixes after secondary Spearbit review #290

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions contracts/credit/CreditConfiguratorV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,8 @@ contract CreditConfiguratorV3 is ICreditConfiguratorV3, ControlledTrait, SanityC
(uint128 minDebt, uint128 maxDebt) = prevCreditFacade.debtLimits();
_setLimits({minDebt: minDebt, maxDebt: maxDebt}); // I:[CC-22]

_setLossLiquidator(prevCreditFacade.lossLiquidator()); // I:[CC-22]
address lossLiquidator = prevCreditFacade.lossLiquidator();
if (lossLiquidator != address(0)) _setLossLiquidator(lossLiquidator); // I:[CC-22]
Comment on lines -517 to +518
Copy link

@Saw-mon-and-Natalie Saw-mon-and-Natalie Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note that migrateParams needs to be false when using a newer version of CC with an old version of CM since the old CFs connected to old CMs don't have the lossLiquidator() endpoint.

So the params and some other configurations might need to be set manually.


_migrateEmergencyLiquidators(prevCreditFacade); // I:[CC-22C]

Expand Down Expand Up @@ -646,11 +647,12 @@ contract CreditConfiguratorV3 is ICreditConfiguratorV3, ControlledTrait, SanityC
}

/// @notice Sets the new loss liquidator which can enforce policies on how liquidations with loss are performed
/// @param newLossLiquidator New loss liquidator, must be a contract or zero address
/// @param newLossLiquidator New loss liquidator, must be a contract
function setLossLiquidator(address newLossLiquidator)
external
override
configuratorOnly // I:[CC-2]
nonZeroAddress(newLossLiquidator) // I:[CC-26]
{
_setLossLiquidator(newLossLiquidator); // I:[CC-26]
}
Expand Down
17 changes: 10 additions & 7 deletions contracts/credit/CreditFacadeV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,7 @@ contract CreditFacadeV3 is ICreditFacadeV3, Pausable, ACLTrait, ReentrancyGuardT
if (reportedLoss != 0) {
maxDebtPerBlockMultiplier = 0; // U:[FA-17]

address lossLiquidator_ = lossLiquidator;
if (lossLiquidator_ != address(0) && msg.sender != lossLiquidator_) {
if (msg.sender != lossLiquidator) {
revert CallerNotLossLiquidatorException(); // U:[FA-17]
}
}
Expand All @@ -376,7 +375,7 @@ contract CreditFacadeV3 is ICreditFacadeV3, Pausable, ACLTrait, ReentrancyGuardT
/// @dev Reverts if `token` is underlying or if `token` is a phantom token and its `depositedToken` is underlying
/// @dev If `token` is a phantom token, it's withdrawn first, and its `depositedToken` is then sent to the liquidator.
/// Both `seizedAmount` and `minSeizedAmount` refer to `depositedToken` in this case.
/// @dev Like in full liquidations, liquidator can seize non-enabled tokens from the credit account, altough here
/// @dev Like in full liquidations, liquidator can seize non-enabled tokens from the credit account, although here
/// they are actually used to repay debt. Unclaimed rewards are safe since adapter calls are not allowed.
function partiallyLiquidateCreditAccount(
address creditAccount,
Expand Down Expand Up @@ -802,8 +801,10 @@ contract CreditFacadeV3 is ICreditFacadeV3, Pausable, ACLTrait, ReentrancyGuardT
{
if (adapter == address(0) || target == address(0)) revert TargetContractNotAllowedException(); // U:[FA-38]

if (flags & EXTERNAL_CONTRACT_WAS_CALLED_FLAG == 0) _setActiveCreditAccount(creditAccount); // U:[FA-38]
flags |= EXTERNAL_CONTRACT_WAS_CALLED_FLAG;
if (flags & EXTERNAL_CONTRACT_WAS_CALLED_FLAG == 0) {
_setActiveCreditAccount(creditAccount); // U:[FA-38]
flags |= EXTERNAL_CONTRACT_WAS_CALLED_FLAG; // U:[FA-38]
}

bool useSafePrices = abi.decode(adapter.functionCall(callData), (bool)); // U:[FA-38]
if (useSafePrices) flags |= REVERT_ON_FORBIDDEN_TOKENS_FLAG | USE_SAFE_PRICES_FLAG; // U:[FA-38,45]
Expand Down Expand Up @@ -857,13 +858,13 @@ contract CreditFacadeV3 is ICreditFacadeV3, Pausable, ACLTrait, ReentrancyGuardT
/// @notice Sets the new loss liquidator
/// @param newLossLiquidator New loss liquidator
/// @dev Reverts if caller is not credit configurator
/// @dev Reverts if `newLossLiquidator` is not a contract, unless it's zero address
/// @dev Reverts if `newLossLiquidator` is not a contract
function setLossLiquidator(address newLossLiquidator)
external
override
creditConfiguratorOnly // U:[FA-6]
{
if (newLossLiquidator != address(0) && newLossLiquidator.code.length == 0) {
if (newLossLiquidator.code.length == 0) {
revert AddressIsNotContractException(newLossLiquidator); // U:[FA-51]
}
lossLiquidator = newLossLiquidator; // U:[FA-51]
Expand Down Expand Up @@ -902,6 +903,8 @@ contract CreditFacadeV3 is ICreditFacadeV3, Pausable, ACLTrait, ReentrancyGuardT
}

/// @notice Pauses contract, can only be called by an account with pausable admin role
/// @dev Pause blocks all user entrypoints to the contract.
/// Liquidations remain open only to emergency and loss liquidators.
/// @dev Reverts if contract is already paused
function pause() external override pausableAdminsOnly {
_pause();
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/ICreditFacadeV3Multicall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ interface ICreditFacadeV3Multicall {
/// @dev Withdrawals are prohibited in multicalls if there are forbidden tokens enabled as collateral on the account
/// @dev Withdrawals activate safe pricing (min of main and reserve feeds) in collateral check
/// @dev If `token` is a phantom token, it's withdrawn first, and its `depositedToken` is then sent to the recipient.
/// No slippage prevention mechanism is provided as withdrawals are assumed to happen at non-manipulatable rate.
/// Although an adapter call is made in process, permission for external calls is not required.
function withdrawCollateral(address token, uint256 amount, address to) external;

Expand Down
2 changes: 0 additions & 2 deletions contracts/interfaces/IGaugeV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ interface IGaugeV3Events {

/// @title Gauge V3 interface
interface IGaugeV3 is IVotingContract, IRateKeeper, IControlledTrait, IGaugeV3Events {
function voter() external view returns (address);

function updateEpoch() external;

function epochLastUpdate() external view returns (uint16);
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/base/IVotingContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {IVersion} from "./IVersion.sol";
/// @notice Generic interface for a contract that can be voted for in `GearStakingV3` contract
/// @dev `vote` and `unvote` must implement votes accounting since it's not performed on the staking contract side
interface IVotingContract is IVersion {
function voter() external view returns (address);
function vote(address user, uint96 votes, bytes calldata extraData) external;
function unvote(address user, uint96 votes, bytes calldata extraData) external;
}
1 change: 1 addition & 0 deletions contracts/pool/GaugeV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ contract GaugeV3 is IGaugeV3, ControlledTrait, SanityCheckTrait {
uint256 public constant override version = 3_10;

/// @notice Contract type
/// @dev "RK" stands for "rate keeper"
bytes32 public constant override contractType = "RK_GAUGE";

/// @notice Address of the pool this gauge is connected to
Expand Down
5 changes: 4 additions & 1 deletion contracts/pool/PoolV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ contract PoolV3 is
ERC4626,
ERC20Permit,
Pausable,
ReentrancyGuardTrait,
SanityCheckTrait,
ControlledTrait,
ContractsRegisterTrait,
ReentrancyGuardTrait,
IPoolV3
{
using Math for uint256;
Expand Down Expand Up @@ -758,6 +758,9 @@ contract PoolV3 is
}

/// @notice Pauses contract, can only be called by an account with pausable admin role
/// @dev Pause only blocks deposits, withdrawals and transfers.
/// Borrowing and repayment can be paused on the credit side but are not blocked here
/// to allow emergency liquidations to proceed.
/// @dev Reverts if contract is already paused
function pause() external override pausableAdminsOnly {
_pause();
Expand Down
1 change: 1 addition & 0 deletions contracts/pool/TumblerV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ contract TumblerV3 is ITumblerV3, ControlledTrait, SanityCheckTrait {
uint256 public constant override version = 3_10;

/// @notice Contract type
/// @dev "RK" stands for "rate keeper"
bytes32 public constant override contractType = "RK_TUMBLER";

/// @notice Pool whose quota rates are set by this contract
Expand Down
7 changes: 6 additions & 1 deletion contracts/test/helpers/IntegrationTestHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,10 @@ contract IntegrationTestHelper is TestHelper, BalanceHelper, ConfigManager {
cmParams.feeLiquidationExpired,
cmParams.liquidationPremiumExpired
);

vm.etch(LIQUIDATOR, "DUMMY_CODE");
creditConfigurator.setLossLiquidator(LIQUIDATOR);

vm.stopPrank();
vm.roll(block.number + 1);

Expand Down Expand Up @@ -568,13 +572,14 @@ contract IntegrationTestHelper is TestHelper, BalanceHelper, ConfigManager {
);
}

function _makeAccountsLiquitable() internal {
function _makeAccountsLiquidatable() internal {
vm.startPrank(CONFIGURATOR);
uint256 idx = creditManager.collateralTokensCount() - 1;
while (idx != 0) {
address token = creditManager.getTokenByMask(1 << (idx--));
creditConfigurator.setLiquidationThreshold(token, 0);
}
// FIXME: setting liquidation premium to 90% is not the cleanest way to make account liquidatable
creditConfigurator.setFees(200, 9000, 100, 9500);
vm.stopPrank();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,10 @@ contract CreditConfiguratorIntegrationTest is IntegrationTestHelper, ICreditConf

/// @dev I:[CC-26]: setLossLiquidator works correctly
function test_I_CC_26_setLossLiquidator_works_correctly() public creditTest {
vm.expectRevert(ZeroAddressException.selector);
vm.prank(CONFIGURATOR);
creditConfigurator.setLossLiquidator(address(0));

address liquidator = makeAddr("LOSS_LIQUIDATOR");
vm.etch(liquidator, "DUMMY_CODE");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract LiquidateCreditAccountIntegrationTest is IntegrationTestHelper, ICredit
MultiCall({target: address(adapterMock), callData: abi.encodeCall(AdapterMock.dumbCall, ())})
);

_makeAccountsLiquitable();
_makeAccountsLiquidatable();

// EXPECTED STACK TRACE & EVENTS

Expand Down Expand Up @@ -111,7 +111,7 @@ contract LiquidateCreditAccountIntegrationTest is IntegrationTestHelper, ICredit
MultiCall({target: address(adapterMock), callData: abi.encodeCall(AdapterMock.dumbCall, ())})
);

_makeAccountsLiquitable();
_makeAccountsLiquidatable();

vm.prank(LIQUIDATOR);
creditFacade.liquidateCreditAccount(creditAccount, FRIEND, calls);
Expand All @@ -137,7 +137,7 @@ contract LiquidateCreditAccountIntegrationTest is IntegrationTestHelper, ICredit

(address creditAccount,) = _openTestCreditAccount();

_makeAccountsLiquitable();
_makeAccountsLiquidatable();
vm.expectRevert(abi.encodeWithSelector(NoPermissionException.selector, INCREASE_DEBT_PERMISSION));

vm.prank(LIQUIDATOR);
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/integration/credit/Quotas.int.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ contract QuotasIntegrationTest is IntegrationTestHelper, ICreditManagerV3Events

address[2] memory quotedTokens = [tokenTestSuite.addressOf(Tokens.USDT), tokenTestSuite.addressOf(Tokens.LINK)];

vm.prank(USER);
vm.prank(LIQUIDATOR);
creditFacade.liquidateCreditAccount(creditAccount, FRIEND, new MultiCall[](0));

for (uint256 i = 0; i < quotedTokens.length; ++i) {
Expand Down
22 changes: 7 additions & 15 deletions contracts/test/unit/credit/CreditFacadeV3.unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -940,25 +940,21 @@ contract CreditFacadeV3UnitTest is TestHelper, BalanceHelper, ICreditFacadeV3Eve

creditManagerMock.setLiquidateCreditAccountReturns(0, 100);

// loss forbids borrowing, anyone can call
// only the loss liquidator can call
vm.expectRevert(CallerNotLossLiquidatorException.selector);
vm.prank(FRIEND);
uint256 loss =
creditFacade.liquidateCreditAccount({creditAccount: creditAccount, to: FRIEND, calls: new MultiCall[](0)});
assertEq(loss, 100, "Incorrect loss");
assertEq(creditFacade.maxDebtPerBlockMultiplier(), 0, "Borrowing not forbidden");
creditFacade.liquidateCreditAccount({creditAccount: creditAccount, to: FRIEND, calls: new MultiCall[](0)});

vm.etch(LIQUIDATOR, "CODE");
vm.prank(CONFIGURATOR);
creditFacade.setLossLiquidator(LIQUIDATOR);

// only the loss liquidator can call
vm.expectRevert(CallerNotLossLiquidatorException.selector);
vm.prank(FRIEND);
creditFacade.liquidateCreditAccount({creditAccount: creditAccount, to: FRIEND, calls: new MultiCall[](0)});

// loss forbids borrowing
vm.prank(LIQUIDATOR);
creditFacade.liquidateCreditAccount({creditAccount: creditAccount, to: FRIEND, calls: new MultiCall[](0)});
uint256 loss =
creditFacade.liquidateCreditAccount({creditAccount: creditAccount, to: FRIEND, calls: new MultiCall[](0)});
assertEq(loss, 100, "Incorrect loss");
assertEq(creditFacade.maxDebtPerBlockMultiplier(), 0, "Borrowing not forbidden");
}

//
Expand Down Expand Up @@ -2236,10 +2232,6 @@ contract CreditFacadeV3UnitTest is TestHelper, BalanceHelper, ICreditFacadeV3Eve
creditFacade.setLossLiquidator(liquidator);

assertEq(creditFacade.lossLiquidator(), liquidator, "Loss liquidator not set");

vm.prank(CONFIGURATOR);
creditFacade.setLossLiquidator(address(0));
assertEq(creditFacade.lossLiquidator(), address(0), "Loss liquidator not unset");
}

/// @dev U:[FA-52]: setTokenAllowance works properly
Expand Down
Loading