Skip to content

Commit

Permalink
contracts/: support supply cap
Browse files Browse the repository at this point in the history
* Add supply cap check to mintAllowed.
* Use internal cash for getCashPrior.
* Add gulp to absorb excess cash into reserves.
  • Loading branch information
bun919tw committed Jan 19, 2021
1 parent c5fcc34 commit 2b66163
Show file tree
Hide file tree
Showing 7 changed files with 1,443 additions and 7 deletions.
30 changes: 27 additions & 3 deletions contracts/CErc20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,36 @@ contract CErc20 is CToken, CErc20Interface {
return _addReservesInternal(addAmount);
}

/**
* @notice Absorb excess cash into reserves.
*/
function gulp() external {
uint256 cashOnChain = getCashOnChain();
uint256 cashPrior = getCashPrior();

uint excessCash = sub_(cashOnChain, cashPrior);
totalReserves = add_(totalReserves, excessCash);
internalCash = cashOnChain;
}

/*** Safe Token ***/

/**
* @notice Gets balance of this contract in terms of the underlying
* @notice Gets internal balance of this contract in terms of the underlying.
* It excludes balance from direct transfer.
* @dev This excludes the value of the current message, if any
* @return The quantity of underlying tokens owned by this contract
*/
function getCashPrior() internal view returns (uint) {
return internalCash;
}

/**
* @notice Gets total balance of this contract in terms of the underlying
* @dev This excludes the value of the current message, if any
* @return The quantity of underlying tokens owned by this contract
*/
function getCashOnChain() internal view returns (uint) {
EIP20Interface token = EIP20Interface(underlying);
return token.balanceOf(address(this));
}
Expand Down Expand Up @@ -162,8 +184,9 @@ contract CErc20 is CToken, CErc20Interface {

// Calculate the amount that was *actually* transferred
uint balanceAfter = EIP20Interface(underlying).balanceOf(address(this));
require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW");
return balanceAfter - balanceBefore; // underflow already checked above, just subtract
uint transferredIn = sub_(balanceAfter, balanceBefore);
internalCash = add_(internalCash, transferredIn);
return transferredIn;
}

/**
Expand Down Expand Up @@ -194,5 +217,6 @@ contract CErc20 is CToken, CErc20Interface {
}
}
require(success, "TOKEN_TRANSFER_OUT_FAILED");
internalCash = sub_(internalCash, amount);
}
}
3 changes: 3 additions & 0 deletions contracts/CErc20Delegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ contract CErc20Delegate is CErc20, CDelegateInterface {
}

require(msg.sender == admin, "only the admin may call _becomeImplementation");

// Set internal cash when becoming implementation
internalCash = getCashOnChain();
}

/**
Expand Down
8 changes: 8 additions & 0 deletions contracts/CErc20Delegator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac
return abi.decode(data, (uint));
}

/**
* @notice Gulps excess contract cash to reserves
* @dev This function calculates excess ERC20 gained from a ERC20.transfer() call and adds the excess to reserves.
*/
function gulp() external {
delegateToImplementation(abi.encodeWithSignature("gulp()"));
}

/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
Expand Down
7 changes: 6 additions & 1 deletion contracts/CTokenInterfaces.sol
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ contract CErc20Storage {
* @notice Underlying asset for this CToken
*/
address public underlying;

/**
* @notice Internal cash counter for this CToken. Should equal underlying.balanceOf(address(this)) for CERC20.
*/
uint256 public internalCash;
}

contract CErc20Interface is CErc20Storage {
Expand All @@ -258,7 +263,7 @@ contract CErc20Interface is CErc20Storage {
function repayBorrow(uint repayAmount) external returns (uint);
function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint);
function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint);

function gulp() external;

/*** Admin Functions ***/

Expand Down
61 changes: 58 additions & 3 deletions contracts/Comptroller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import "./Governance/Comp.sol";
* @title Compound's Comptroller Contract
* @author Compound
*/
contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerErrorReporter, ExponentialNoError {
contract Comptroller is ComptrollerV6Storage, ComptrollerInterface, ComptrollerErrorReporter, ExponentialNoError {
/// @notice Emitted when an admin supports a market
event MarketListed(CToken cToken);

Expand Down Expand Up @@ -61,6 +61,12 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE
/// @notice Emitted when borrow cap guardian is changed
event NewBorrowCapGuardian(address oldBorrowCapGuardian, address newBorrowCapGuardian);

/// @notice Emitted when supply cap for a cToken is changed
event NewSupplyCap(CToken indexed cToken, uint newSupplyCap);

/// @notice Emitted when supply cap guardian is changed
event NewSupplyCapGuardian(address oldSupplyCapGuardian, address newSupplyCapGuardian);

/// @notice Emitted when COMP is granted by admin
event CompGranted(address recipient, uint amount);

Expand Down Expand Up @@ -227,12 +233,24 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE

// Shh - currently unused
minter;
mintAmount;

if (!markets[cToken].isListed) {
return uint(Error.MARKET_NOT_LISTED);
}

uint supplyCap = supplyCaps[cToken];
// Supply cap of 0 corresponds to unlimited supplying
if (supplyCap != 0) {
uint totalCash = CToken(cToken).getCash();
uint totalBorrows = CToken(cToken).totalBorrows();
uint totalReserves = CToken(cToken).totalReserves();
// totalSupplies = totalCash + totalBorrows - totalReserves
uint totalSupplies = sub_(add_(totalCash, totalBorrows), totalReserves);

uint nextTotalSupplies = add_(totalSupplies, mintAmount);
require(nextTotalSupplies < supplyCap, "market supply cap reached");
}

// Keep the flywheel moving
updateCompSupplyIndex(cToken);
distributeSupplierComp(cToken, minter);
Expand Down Expand Up @@ -949,7 +967,7 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE
* @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing.
*/
function _setMarketBorrowCaps(CToken[] calldata cTokens, uint[] calldata newBorrowCaps) external {
require(msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps");
require(msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps");

uint numMarkets = cTokens.length;
uint numBorrowCaps = newBorrowCaps.length;
Expand Down Expand Up @@ -979,6 +997,43 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE
emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian);
}

/**
* @notice Set the given supply caps for the given cToken markets. Supplying that brings total supplys to or above supply cap will revert.
* @dev Admin or supplyCapGuardian function to set the supply caps. A supply cap of 0 corresponds to unlimited supplying.
* @param cTokens The addresses of the markets (tokens) to change the supply caps for
* @param newSupplyCaps The new supply cap values in underlying to be set. A value of 0 corresponds to unlimited supplying.
*/
function _setMarketSupplyCaps(CToken[] calldata cTokens, uint[] calldata newSupplyCaps) external {
require(msg.sender == admin || msg.sender == supplyCapGuardian, "only admin or supply cap guardian can set supply caps");

uint numMarkets = cTokens.length;
uint numSupplyCaps = newSupplyCaps.length;

require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input");

for(uint i = 0; i < numMarkets; i++) {
supplyCaps[address(cTokens[i])] = newSupplyCaps[i];
emit NewSupplyCap(cTokens[i], newSupplyCaps[i]);
}
}

/**
* @notice Admin function to change the Supply Cap Guardian
* @param newSupplyCapGuardian The address of the new Supply Cap Guardian
*/
function _setSupplyCapGuardian(address newSupplyCapGuardian) external {
require(msg.sender == admin, "only admin can set supply cap guardian");

// Save current value for inclusion in log
address oldSupplyCapGuardian = supplyCapGuardian;

// Store supplyCapGuardian with value newSupplyCapGuardian
supplyCapGuardian = newSupplyCapGuardian;

// Emit NewSupplyCapGuardian(OldSupplyCapGuardian, NewSupplyCapGuardian)
emit NewSupplyCapGuardian(oldSupplyCapGuardian, newSupplyCapGuardian);
}

/**
* @notice Admin function to change the Pause Guardian
* @param newPauseGuardian The address of the new Pause Guardian
Expand Down
Loading

0 comments on commit 2b66163

Please sign in to comment.