Skip to content

Commit

Permalink
fix: withdrawals blacklist-related fix
Browse files Browse the repository at this point in the history
  • Loading branch information
lekhovitsky committed May 2, 2023
1 parent fb9c724 commit 328a266
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 33 deletions.
4 changes: 2 additions & 2 deletions contracts/credit/CreditFacadeV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,8 @@ contract CreditFacadeV3 is ICreditFacade, ACLNonReentrantTrait, IERC20HelperTrai
}

function _withdraw(address creditAccount, bytes memory callData) internal returns (uint256 tokensToDisable) {
(address to, address token, uint256 amount) = abi.decode(callData, (address, address, uint256));
tokensToDisable = creditManager.withdraw(creditAccount, to, token, amount);
(address token, uint256 amount) = abi.decode(callData, (address, uint256));
tokensToDisable = creditManager.withdraw(creditAccount, token, amount);
}

/// @dev Adds expected deltas to current balances on a Credit account and returns the result
Expand Down
5 changes: 3 additions & 2 deletions contracts/credit/CreditManagerV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1537,20 +1537,21 @@ contract CreditManagerV3 is ICreditManagerV3, SanityCheckTrait, ReentrancyGuard,
}
}

function withdraw(address creditAccount, address to, address token, uint256 amount)
function withdraw(address creditAccount, address token, uint256 amount)
external
override
creditFacadeOnly
returns (uint256 tokensToDisable)
{
uint256 tokenMask = getTokenMaskOrRevert(token);
address borrower = creditAccountInfo[creditAccount].borrower;

uint256 balanceBefore = _balanceOf(token, address(withdrawalManager));
_creditAccountSafeTransfer(creditAccount, token, address(withdrawalManager), amount);
amount = _balanceOf(token, address(withdrawalManager)) - balanceBefore;

if (amount > 1) {
withdrawalManager.addScheduledWithdrawal(creditAccount, token, to, amount, tokenMask.calcIndex());
withdrawalManager.addScheduledWithdrawal(creditAccount, borrower, token, amount, tokenMask.calcIndex());
_enableWithdrawalFlag(creditAccount);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/ICreditFacadeMulticall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ interface ICreditFacadeMulticall {
/// Cannot be lower than PERCENTAGE_FACTOR.
function setFullCheckParams(uint256[] memory collateralHints, uint16 minHealthFactor) external;

function withdraw(address to, address token, uint256 amount) external;
function withdraw(address token, uint256 amount) external;

function revokeAdapterAllowances(RevocationPair[] calldata revocations) external;
}
2 changes: 1 addition & 1 deletion contracts/interfaces/ICreditManagerV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ interface ICreditManagerV3 is ICreditManagerV3Events, IVersion {
/// @dev Withdrawal manager
function withdrawalManager() external view returns (IWithdrawalManager);

function withdraw(address creditAccount, address to, address token, uint256 amount)
function withdraw(address creditAccount, address token, uint256 amount)
external
returns (uint256 tokensToDisable);

Expand Down
24 changes: 14 additions & 10 deletions contracts/interfaces/IWithdrawalManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.

/// @notice Scheduled withdrawal data
/// @param tokenIndex Collateral index of withdrawn token in account's credit manager
/// @param to Withdrawal recipient
/// @param borrower Account owner that should claim tokens
/// @param maturity Timestamp after which withdrawal can be claimed
/// @param amount Amount to withdraw
/// @dev Keeping token index instead of mask allows to pack struct into 2 slots
struct ScheduledWithdrawal {
uint8 tokenIndex;
address to;
address borrower;
uint40 maturity;
uint256 amount;
}
Expand All @@ -34,12 +34,12 @@ interface IWithdrawalManagerEvents {

/// @notice Emitted when new scheduled withdrawal is added
/// @param creditAccount Account to withdraw from
/// @param borrower Account owner that should claim tokens
/// @param token Token to withdraw
/// @param to Withdrawal recipient
/// @param amount Amount to withdraw
/// @param maturity Timestamp after which withdrawal can be claimed
event AddScheduledWithdrawal(
address indexed creditAccount, address indexed token, address to, uint256 amount, uint40 maturity
address indexed creditAccount, address indexed borrower, address indexed token, uint256 amount, uint40 maturity
);

/// @notice Emitted when scheduled withdrawal is cancelled
Expand All @@ -51,9 +51,8 @@ interface IWithdrawalManagerEvents {
/// @notice Emitted when scheduled withdrawal is claimed
/// @param creditAccount Account withdrawal was made from
/// @param token Token claimed
/// @param to Token recipient
/// @param amount Amount claimed
event ClaimScheduledWithdrawal(address indexed creditAccount, address indexed token, address to, uint256 amount);
event ClaimScheduledWithdrawal(address indexed creditAccount, address indexed token, uint256 amount);

/// @notice Emitted when new scheduled withdrawal delay is set by configurator
/// @param delay New delay for scheduled withdrawals
Expand Down Expand Up @@ -119,15 +118,20 @@ interface IWithdrawalManager is IWithdrawalManagerEvents, IVersion {
/// @notice Schedules withdrawal of given token from the credit account,
/// might claim a mature withdrawal first if it's needed to free the slot
/// @param creditAccount Account to withdraw from
/// @param borrower Account owner that should claim tokens
/// @param token Token to withdraw
/// @param to Withdrawal recipient
/// @param amount Amount to withdraw
/// @param tokenIndex Collateral index of withdrawn token in account's credit manager
/// @custom:expects `amount` is greater than 1
/// @custom:expects Credit manager transferred `amount` of `token` to this contract prior to calling this function
/// @custom:expects Credit manager is not in emergency mode
function addScheduledWithdrawal(address creditAccount, address token, address to, uint256 amount, uint8 tokenIndex)
external;
function addScheduledWithdrawal(
address creditAccount,
address borrower,
address token,
uint256 amount,
uint8 tokenIndex
) external;

/// @notice Cancels scheduled withdrawals from the credit account
/// - Under normal operation, cancels immature withdrawals and claims mature ones
Expand All @@ -140,7 +144,7 @@ interface IWithdrawalManager is IWithdrawalManagerEvents, IVersion {
external
returns (uint256 tokensToEnable);

/// @notice Claims scheduled withdrawals from the credit account
/// @notice Claims scheduled withdrawals from the credit account by turning them into immediate withdrawals
/// - Under normal operation, claims all mature withdrawals
/// - In emergency mode, claiming is disabled so it reverts
/// @param creditManager Manager the account is connected to
Expand Down
38 changes: 21 additions & 17 deletions contracts/support/WithdrawalManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {ACLTrait} from "../traits/ACLTrait.sol";
/// - Scheduled withdrawals can be claimed after a certain delay, and exist to support partial withdrawals
/// from credit accounts. One credit account can have up to two immature withdrawals at the same time.
/// Additional rules for scheduled withdrawals:
/// + if account is closed, both mature and immature withdrawals are claimed
/// + if account is closed, both mature and immature withdrawals are claimed (turned into immediate withdrawals)
/// + if account is liquidated, immature withdrawals are cancelled and mature ones are claimed
/// + if account is liquidated in emergency mode, both mature and immature withdrawals are cancelled
/// + in emergency mode, claiming is disabled
Expand Down Expand Up @@ -74,8 +74,7 @@ contract WithdrawalManager is IWithdrawalManager, ACLTrait {
override
creditManagerOnly
{
immediateWithdrawals[account][token] += amount;
emit AddImmediateWithdrawal(account, token, amount);
_addImmediateWithdrawal(account, token, amount);
}

/// @inheritdoc IWithdrawalManager
Expand All @@ -90,6 +89,12 @@ contract WithdrawalManager is IWithdrawalManager, ACLTrait {
emit ClaimImmediateWithdrawal(msg.sender, token, to, amount);
}

/// @dev Increases account's immediately withdrawable balance of token
function _addImmediateWithdrawal(address account, address token, uint256 amount) internal {
immediateWithdrawals[account][token] += amount;
emit AddImmediateWithdrawal(account, token, amount);
}

/// --------------------- ///
/// SCHEDULED WITHDRAWALS ///
/// --------------------- ///
Expand Down Expand Up @@ -140,20 +145,22 @@ contract WithdrawalManager is IWithdrawalManager, ACLTrait {
}

/// @inheritdoc IWithdrawalManager
function addScheduledWithdrawal(address creditAccount, address token, address to, uint256 amount, uint8 tokenIndex)
external
override
creditManagerOnly
{
function addScheduledWithdrawal(
address creditAccount,
address borrower,
address token,
uint256 amount,
uint8 tokenIndex
) external override creditManagerOnly {
ScheduledWithdrawal[2] memory withdrawals = _scheduled[msg.sender][creditAccount];
(bool found, bool claim, uint8 slot) = withdrawals.findFreeSlot();
if (!found) revert NoFreeWithdrawalSlotsException();
if (claim) _executeWithdrawal(msg.sender, creditAccount, withdrawals[slot], true);

uint40 maturity = uint40(block.timestamp) + delay;
_scheduled[msg.sender][creditAccount][slot] =
ScheduledWithdrawal({tokenIndex: tokenIndex, to: to, maturity: maturity, amount: amount});
emit AddScheduledWithdrawal(creditAccount, token, to, amount, maturity);
ScheduledWithdrawal({tokenIndex: tokenIndex, borrower: borrower, maturity: maturity, amount: amount});
emit AddScheduledWithdrawal(creditAccount, borrower, token, amount, maturity);
}

/// @inheritdoc IWithdrawalManager
Expand Down Expand Up @@ -207,16 +214,13 @@ contract WithdrawalManager is IWithdrawalManager, ACLTrait {
(uint256 tokenMask, uint256 amount) = withdrawal.tokenMaskAndAmount();
(address token,) = ICreditManagerV3(creditManager).collateralTokensByMask(tokenMask);

address to = isClaim ? withdrawal.to : creditAccount;
// FIXME: this might fail if `to` is blacklisted in `token`
// this might cause issues during non-emergency liquidations
IERC20(token).safeTransfer(to, amount);

if (isClaim) {
emit ClaimScheduledWithdrawal(creditAccount, token, to, amount);
emit ClaimScheduledWithdrawal(creditAccount, token, amount);
_addImmediateWithdrawal(withdrawal.borrower, token, amount);
} else {
tokensToEnable = tokenMask;
emit CancelScheduledWithdrawal(creditAccount, token, amount);
IERC20(token).safeTransfer(creditAccount, amount);
tokensToEnable = tokenMask;
}

withdrawal.clear();
Expand Down

0 comments on commit 328a266

Please sign in to comment.