Skip to content

Commit

Permalink
feat: Add global pause (SC-4215) (#32)
Browse files Browse the repository at this point in the history
* feat: add global pause

* fix: only `acceptNewTerms`, `claim`, `triggerDefault` paused

- and get globals from pool

* feat: added pause checks and pull funds

* fix: added acl for pull funds

* fix: update tests

* chore: update comments

Co-authored-by: Michael De Luca <[email protected]>
Co-authored-by: JG Carvalho <[email protected]>
  • Loading branch information
3 people authored Dec 1, 2021
1 parent 7bd18c1 commit 64d4fd4
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 117 deletions.
162 changes: 88 additions & 74 deletions contracts/DebtLocker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ import { DebtLockerStorage } from "./DebtLockerStorage.sol";
/// @title DebtLocker interacts with Loans on behalf of PoolV1
contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied {

/*****************/
/*** Modifiers ***/
/*****************/

modifier whenProtocolNotPaused() {
require(!IMapleGlobalsLike(_getGlobals()).protocolPaused(), "DL:PROTOCOL_PAUSED");
_;
}

/********************************/
/*** Administrative Functions ***/
/********************************/
Expand All @@ -41,7 +50,7 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied {
/*** Pool Delegate Functions ***/
/*******************************/

function acceptNewTerms(address refinancer_, bytes[] calldata calls_, uint256 amount_) override external {
function acceptNewTerms(address refinancer_, bytes[] calldata calls_, uint256 amount_) external override whenProtocolNotPaused {
require(msg.sender == _getPoolDelegate(), "DL:ANT:NOT_PD");

IMapleLoanLike loan_ = IMapleLoanLike(_loan);
Expand All @@ -60,33 +69,40 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied {
_principalRemainingAtLastClaim = loan_.principal();
}

function claim() external override returns (uint256[7] memory details_) {
function claim() external override whenProtocolNotPaused returns (uint256[7] memory details_) {
require(msg.sender == _pool, "DL:C:NOT_POOL");

return _repossessed ? _handleClaimOfRepossessed() : _handleClaim();
}

function setAllowedSlippage(uint256 allowedSlippage_) external override {
// TODO: Discuss pros/cons of pause on this function
function pullFundsFromLiquidator(address token_, address destination_, uint256 amount_) external override {
require(msg.sender == _getPoolDelegate(), "DL:SA:NOT_PD");

Liquidator(_liquidator).pullFunds( token_, destination_, amount_);
}

function setAllowedSlippage(uint256 allowedSlippage_) external override whenProtocolNotPaused {
require(msg.sender == _getPoolDelegate(), "DL:SAS:NOT_PD");

emit AllowedSlippageSet(_allowedSlippage = allowedSlippage_);
}

function setAuctioneer(address auctioneer_) external override {
function setAuctioneer(address auctioneer_) external override whenProtocolNotPaused {
require(msg.sender == _getPoolDelegate(), "DL:SA:NOT_PD");

emit AuctioneerSet(auctioneer_);

Liquidator(_liquidator).setAuctioneer(auctioneer_);
}

function setFundsToCapture(uint256 amount_) override external {
function setFundsToCapture(uint256 amount_) override external whenProtocolNotPaused {
require(msg.sender == _getPoolDelegate(), "DL:SFTC:NOT_PD");

emit FundsToCaptureSet(_fundsToCapture = amount_);
}

function setMinRatio(uint256 minRatio_) external override {
function setMinRatio(uint256 minRatio_) external override whenProtocolNotPaused {
require(msg.sender == _getPoolDelegate(), "DL:SMR:NOT_PD");

emit MinRatioSet(_minRatio = minRatio_);
Expand All @@ -101,9 +117,7 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied {
emit LiquidationStopped();
}

// TODO: Consider adding pullFunds function, calling liquidator.pullFunds()

function triggerDefault() external override {
function triggerDefault() external override whenProtocolNotPaused {
require(msg.sender == _pool, "DL:TD:NOT_POOL");

require(
Expand Down Expand Up @@ -136,6 +150,70 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied {
);
}

/**************************/
/*** Internal Functions ***/
/**************************/

function _handleClaim() internal returns (uint256[7] memory details_) {
// Get loan state variables needed
uint256 claimableFunds = IMapleLoanLike(_loan).claimableFunds();

require(claimableFunds > uint256(0), "DL:HC:NOTHING_TO_CLAIM");

// Send funds to pool
IMapleLoanLike(_loan).claimFunds(claimableFunds, _pool);

uint256 currentPrincipalRemaining = IMapleLoanLike(_loan).principal();

// Determine how much of `claimableFunds` is principal
uint256 principalPortion = _principalRemainingAtLastClaim - currentPrincipalRemaining;

// Update state variables
_principalRemainingAtLastClaim = currentPrincipalRemaining;

// Set return values
// Note: All fees get deducted and transferred during `loan.fundLoan()` that omits the need to
// return the fees distribution to the pool.
details_[0] = claimableFunds;
details_[1] = claimableFunds - principalPortion;
details_[2] = principalPortion;

uint256 amountOfFundsToCapture = _fundsToCapture;

if (amountOfFundsToCapture > uint256(0)) {
details_[0] += amountOfFundsToCapture;
details_[2] += amountOfFundsToCapture;

_fundsToCapture = uint256(0);

require(ERC20Helper.transfer(IMapleLoanLike(_loan).fundsAsset(), _pool, amountOfFundsToCapture), "DL:HC:CAPTURE_FAILED");
}
}

function _handleClaimOfRepossessed() internal returns (uint256[7] memory details_) {
require(!_isLiquidationActive(), "DL:HCOR:LIQ_NOT_FINISHED");

address fundsAsset = IMapleLoanLike(_loan).fundsAsset();
uint256 principalToCover = _principalRemainingAtLastClaim; // Principal remaining at time of liquidation
uint256 fundsCaptured = _fundsToCapture;

// Funds recovered from liquidation and any unclaimed previous payment amounts
uint256 recoveredFunds = IERC20Like(fundsAsset).balanceOf(address(this)) - fundsCaptured;

// If `recoveredFunds` is greater than `principalToCover`, the remaining amount is treated as interest in the context of the pool.
// If `recoveredFunds` is less than `principalToCover`, the difference is registered as a shortfall.
details_[0] = recoveredFunds + fundsCaptured;
details_[1] = recoveredFunds > principalToCover ? recoveredFunds - principalToCover : 0;
details_[2] = fundsCaptured;
details_[5] = recoveredFunds > principalToCover ? principalToCover : recoveredFunds;
details_[6] = principalToCover > recoveredFunds ? principalToCover - recoveredFunds : 0;

_fundsToCapture = uint256(0);
_repossessed = false;

require(ERC20Helper.transfer(fundsAsset, _pool, recoveredFunds + fundsCaptured), "DL:HCOR:TRANSFER");
}

/**********************/
/*** View Functions ***/
/**********************/
Expand All @@ -156,7 +234,7 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied {
return _fundsToCapture;
}

function getExpectedAmount(uint256 swapAmount_) external view override returns (uint256 returnAmount_) {
function getExpectedAmount(uint256 swapAmount_) external view override whenProtocolNotPaused returns (uint256 returnAmount_) {
address collateralAsset = IMapleLoanLike(_loan).collateralAsset();
address fundsAsset = IMapleLoanLike(_loan).fundsAsset();

Expand Down Expand Up @@ -218,70 +296,6 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied {
return IMapleGlobalsLike(_getGlobals()).treasuryFee();
}

/**************************/
/*** Internal Functions ***/
/**************************/

function _handleClaim() internal returns (uint256[7] memory details_) {
// Get loan state variables needed
uint256 claimableFunds = IMapleLoanLike(_loan).claimableFunds();

require(claimableFunds > uint256(0), "DL:HC:NOTHING_TO_CLAIM");

// Send funds to pool
IMapleLoanLike(_loan).claimFunds(claimableFunds, _pool);

uint256 currentPrincipalRemaining = IMapleLoanLike(_loan).principal();

// Determine how much of `claimableFunds` is principal
uint256 principalPortion = _principalRemainingAtLastClaim - currentPrincipalRemaining;

// Update state variables
_principalRemainingAtLastClaim = currentPrincipalRemaining;

// Set return values
// Note: All fees get deducted and transferred during `loan.fundLoan()` that omits the need to
// return the fees distribution to the pool.
details_[0] = claimableFunds;
details_[1] = claimableFunds - principalPortion;
details_[2] = principalPortion;

uint256 amountOfFundsToCapture = _fundsToCapture;

if (amountOfFundsToCapture > uint256(0)) {
details_[0] += amountOfFundsToCapture;
details_[2] += amountOfFundsToCapture;

_fundsToCapture = uint256(0);

require(ERC20Helper.transfer(IMapleLoanLike(_loan).fundsAsset(), _pool, amountOfFundsToCapture), "DL:HC:CAPTURE_FAILED");
}
}

function _handleClaimOfRepossessed() internal returns (uint256[7] memory details_) {
require(!_isLiquidationActive(), "DL:HCOR:LIQ_NOT_FINISHED");

address fundsAsset = IMapleLoanLike(_loan).fundsAsset();
uint256 principalToCover = _principalRemainingAtLastClaim; // Principal remaining at time of liquidation
uint256 fundsCaptured = _fundsToCapture;

// Funds recovered from liquidation and any unclaimed previous payment amounts
uint256 recoveredFunds = IERC20Like(fundsAsset).balanceOf(address(this)) - fundsCaptured;

// If `recoveredFunds` is greater than `principalToCover`, the remaining amount is treated as interest in the context of the pool.
// If `recoveredFunds` is less than `principalToCover`, the difference is registered as a shortfall.
details_[0] = recoveredFunds + fundsCaptured;
details_[1] = recoveredFunds > principalToCover ? recoveredFunds - principalToCover : 0;
details_[2] = fundsCaptured;
details_[5] = recoveredFunds > principalToCover ? principalToCover : recoveredFunds;
details_[6] = principalToCover > recoveredFunds ? principalToCover - recoveredFunds : 0;

_fundsToCapture = uint256(0);
_repossessed = false;

require(ERC20Helper.transfer(fundsAsset, _pool, recoveredFunds + fundsCaptured), "DL:HCOR:TRANSFER");
}

/*******************************/
/*** Internal View Functions ***/
/*******************************/
Expand Down
8 changes: 8 additions & 0 deletions contracts/interfaces/IDebtLocker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ interface IDebtLocker is IMapleProxied {
*/
function mapleTreasury() external view returns (address mapleTreasury_);

/**
* @dev Allows the poolDelegate to pull some funds from liquidator contract
* @param token_ The token address of the funds.
* @param destination_ The destination address of captured funds.
* @param amount_ The amount to pull.
*/
function pullFundsFromLiquidator(address token_, address destination_, uint256 amount_) external;

/**
* @dev Returns the annualized establishment fee that will go to the Maple Treasury.
*/
Expand Down
2 changes: 2 additions & 0 deletions contracts/interfaces/Interfaces.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ interface IMapleGlobalsLike {

function mapleTreasury() external view returns (address mapleTreasury_);

function protocolPaused() external view returns (bool protocolPaused_);

function treasuryFee() external view returns (uint256 treasuryFee_);

}
Expand Down
Loading

0 comments on commit 64d4fd4

Please sign in to comment.