diff --git a/abi/cCollateralCapDelegator.js b/abi/cCollateralCapDelegator.js new file mode 100644 index 0000000..81aec56 --- /dev/null +++ b/abi/cCollateralCapDelegator.js @@ -0,0 +1,5 @@ +module.exports = [ + 'function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) external', + 'function implementation() view returns (address) ' + + ]; diff --git a/abi/cTokenAdmin.js b/abi/cTokenAdmin.js new file mode 100644 index 0000000..b69f0d9 --- /dev/null +++ b/abi/cTokenAdmin.js @@ -0,0 +1,6 @@ + + +module.exports = [ + 'function _setImplementation(address cToken, address implementation, bool allowResign, bytes calldata becomeImplementationData) external', + ]; + \ No newline at end of file diff --git a/abi/comptroller.js b/abi/comptroller.js index 2cd5343..5c1f959 100644 --- a/abi/comptroller.js +++ b/abi/comptroller.js @@ -2,5 +2,7 @@ module.exports = [ 'function _setAllowlist(address protocol, bool allow) external', 'function _setCreditLimit(address protocol, uint creditLimit) external', 'function _dropInvalidMarket() external', + 'function _setFlashloanPaused(address cToken, bool state) external returns (bool)', + 'function flashloanGuardianPaused(address addr) external view returns (bool)', 'function getAllMarkets() external view returns (address[] memory)' ]; diff --git a/abi/erc20.js b/abi/erc20.js index a6898b0..4cad211 100644 --- a/abi/erc20.js +++ b/abi/erc20.js @@ -1,4 +1,6 @@ module.exports = [ 'function approve(address spender, uint256 amount) external returns (bool success)', + 'function transfer(address to, uint value) external', 'function balanceOf(address account) external view returns (uint)', + ]; diff --git a/abi/unitroller.js b/abi/unitroller.js index 0ebea54..8f9e0fe 100644 --- a/abi/unitroller.js +++ b/abi/unitroller.js @@ -1,4 +1,4 @@ module.exports = [ 'function _setPendingImplementation(address newPendingImplementation) external returns (uint)', - 'function comptrollerImplementation() external view returns (address)' + 'function comptrollerImplementation() external view returns (address)', ]; diff --git a/contracts/CCTokenDelegate.sol b/contracts/CCTokenDelegate.sol index c38cfee..033168f 100644 --- a/contracts/CCTokenDelegate.sol +++ b/contracts/CCTokenDelegate.sol @@ -8,7 +8,13 @@ import "./EIP20Interface.sol"; */ interface IComptroller { function getCompAddress() external view returns (address); - function claimComp(address[] calldata holders, CToken[] calldata cTokens, bool borrowers, bool suppliers) external; + + function claimComp( + address[] calldata holders, + CToken[] calldata cTokens, + bool borrowers, + bool suppliers + ) external; } /** @@ -33,8 +39,8 @@ contract CCTokenDelegate is CCapableErc20Delegate { * @member index The last updated index */ struct RewardState { - uint balance; - uint index; + uint256 balance; + uint256 index; } /** @@ -45,12 +51,12 @@ contract CCTokenDelegate is CCapableErc20Delegate { /** * @notice The index of every Compound's CToken supplier */ - mapping(address => uint) public supplierState; + mapping(address => uint256) public supplierState; /** * @notice The comp amount of every user */ - mapping(address => uint) public compUserAccrued; + mapping(address => uint256) public compUserAccrued; /** * @notice Delegate interface to become the implementation @@ -67,20 +73,20 @@ contract CCTokenDelegate is CCapableErc20Delegate { * @notice Manually claim comp rewards by user * @return The amount of comp rewards user claims */ - function claimComp() public returns (uint) { + function claimComp(address account) public returns (uint256) { harvestComp(); updateSupplyIndex(); - updateSupplierIndex(msg.sender); + updateSupplierIndex(account); - uint compBalance = compUserAccrued[msg.sender]; + uint256 compBalance = compUserAccrued[account]; if (compBalance > 0) { // Transfer user comp and subtract the balance in supplyState - EIP20Interface(comp).transfer(msg.sender, compBalance); + EIP20Interface(comp).transfer(account, compBalance); supplyState.balance = sub_(supplyState.balance, compBalance); // Clear user's comp accrued. - compUserAccrued[msg.sender] = 0; + compUserAccrued[account] = 0; return compBalance; } @@ -97,7 +103,12 @@ contract CCTokenDelegate is CCapableErc20Delegate { * @param tokens The number of tokens to transfer * @return Whether or not the transfer succeeded */ - function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) { + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256) { harvestComp(); updateSupplyIndex(); @@ -113,11 +124,17 @@ contract CCTokenDelegate is CCapableErc20Delegate { * @notice Transfer the underlying to this contract * @param from Address to transfer funds from * @param amount Amount of underlying to transfer + * @param isNative The amount is in native or not * @return The actual amount that is transferred */ - function doTransferIn(address from, uint amount) internal returns (uint) { - uint transferredIn = super.doTransferIn(from, amount); + function doTransferIn( + address from, + uint256 amount, + bool isNative + ) internal returns (uint256) { + uint256 transferredIn = super.doTransferIn(from, amount, isNative); + harvestComp(); updateSupplyIndex(); updateSupplierIndex(from); @@ -128,19 +145,25 @@ contract CCTokenDelegate is CCapableErc20Delegate { * @notice Transfer the underlying from this contract * @param to Address to transfer funds to * @param amount Amount of underlying to transfer + * @param isNative The amount is in native or not */ - function doTransferOut(address payable to, uint amount) internal { + function doTransferOut( + address payable to, + uint256 amount, + bool isNative + ) internal { + harvestComp(); updateSupplyIndex(); updateSupplierIndex(to); - super.doTransferOut(to, amount); + super.doTransferOut(to, amount, isNative); } /*** Internal functions ***/ function harvestComp() internal { address[] memory holders = new address[](1); - holders[0] = msg.sender; + holders[0] = address(this); CToken[] memory cTokens = new CToken[](1); cTokens[0] = CToken(underlying); @@ -149,8 +172,8 @@ contract CCTokenDelegate is CCapableErc20Delegate { } function updateSupplyIndex() internal { - uint compAccrued = sub_(compBalance(), supplyState.balance); - uint supplyTokens = CToken(address(this)).totalSupply(); + uint256 compAccrued = sub_(compBalance(), supplyState.balance); + uint256 supplyTokens = CToken(address(this)).totalSupply(); Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: supplyState.index}), ratio); @@ -164,14 +187,14 @@ contract CCTokenDelegate is CCapableErc20Delegate { Double memory supplierIndex = Double({mantissa: supplierState[supplier]}); Double memory deltaIndex = sub_(supplyIndex, supplierIndex); if (deltaIndex.mantissa > 0) { - uint supplierTokens = CToken(address(this)).balanceOf(supplier); - uint supplierDelta = mul_(supplierTokens, deltaIndex); + uint256 supplierTokens = CToken(address(this)).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); compUserAccrued[supplier] = add_(compUserAccrued[supplier], supplierDelta); supplierState[supplier] = supplyIndex.mantissa; } } - function compBalance() internal view returns (uint) { + function compBalance() internal view returns (uint256) { return EIP20Interface(comp).balanceOf(address(this)); } } diff --git a/contracts/CCapableErc20.sol b/contracts/CCapableErc20.sol index 30871f3..163f475 100644 --- a/contracts/CCapableErc20.sol +++ b/contracts/CCapableErc20.sol @@ -3,11 +3,11 @@ pragma solidity ^0.5.16; import "./CToken.sol"; /** - * @title Compound's CCapableErc20 Contract + * @title Deprecated Cream's CCapableErc20 Contract * @notice CTokens which wrap an EIP-20 underlying - * @author Compound + * @author Cream */ -contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterface { +contract CCapableErc20 is CToken, CCapableErc20Interface { /** * @notice Initialize the new money market * @param underlying_ The address of the underlying asset @@ -18,13 +18,15 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token */ - function initialize(address underlying_, - ComptrollerInterface comptroller_, - InterestRateModel interestRateModel_, - uint initialExchangeRateMantissa_, - string memory name_, - string memory symbol_, - uint8 decimals_) public { + function initialize( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_ + ) public { // CToken initialize does the bulk of the work super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); @@ -41,8 +43,8 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function mint(uint mintAmount) external returns (uint) { - (uint err,) = mintInternal(mintAmount); + function mint(uint256 mintAmount) external returns (uint256) { + (uint256 err, ) = mintInternal(mintAmount, false); return err; } @@ -52,8 +54,8 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeem(uint redeemTokens) external returns (uint) { - return redeemInternal(redeemTokens); + function redeem(uint256 redeemTokens) external returns (uint256) { + return redeemInternal(redeemTokens, false); } /** @@ -62,17 +64,17 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeemUnderlying(uint redeemAmount) external returns (uint) { - return redeemUnderlyingInternal(redeemAmount); + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { + return redeemUnderlyingInternal(redeemAmount, false); } /** - * @notice Sender borrows assets from the protocol to their own address - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function borrow(uint borrowAmount) external returns (uint) { - return borrowInternal(borrowAmount); + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { + return borrowInternal(borrowAmount, false); } /** @@ -80,8 +82,8 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function repayBorrow(uint repayAmount) external returns (uint) { - (uint err,) = repayBorrowInternal(repayAmount); + function repayBorrow(uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowInternal(repayAmount, false); return err; } @@ -91,8 +93,8 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) { - (uint err,) = repayBorrowBehalfInternal(borrower, repayAmount); + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowBehalfInternal(borrower, repayAmount, false); return err; } @@ -104,8 +106,12 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param cTokenCollateral The market in which to seize collateral from the borrower * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) { - (uint err,) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral); + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256) { + (uint256 err, ) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral, false); return err; } @@ -114,22 +120,65 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @param addAmount The amount fo underlying token to add as reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _addReserves(uint addAmount) external returns (uint) { - return _addReservesInternal(addAmount); + function _addReserves(uint256 addAmount) external returns (uint256) { + return _addReservesInternal(addAmount, false); } /** * @notice Absorb excess cash into reserves. */ - function gulp() external { + function gulp() external nonReentrant { uint256 cashOnChain = getCashOnChain(); uint256 cashPrior = getCashPrior(); - uint excessCash = sub_(cashOnChain, cashPrior); + uint256 excessCash = sub_(cashOnChain, cashPrior); totalReserves = add_(totalReserves, excessCash); internalCash = cashOnChain; } + /** + * @notice Flash loan funds to a given account. + * @param receiver The receiver address for the funds + * @param amount The amount of the funds to be loaned + * @param params The other parameters + */ + function flashLoan( + address receiver, + uint256 amount, + bytes calldata params + ) external nonReentrant { + require(amount > 0, "flashLoan amount should be greater than zero"); + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); + + uint256 cashOnChainBefore = getCashOnChain(); + uint256 cashBefore = getCashPrior(); + require(cashBefore >= amount, "INSUFFICIENT_LIQUIDITY"); + + // 1. calculate fee, 1 bips = 1/10000 + uint256 totalFee = div_(mul_(amount, flashFeeBips), 10000); + + // 2. transfer fund to receiver + doTransferOut(address(uint160(receiver)), amount, false); + + // 3. update totalBorrows + totalBorrows = add_(totalBorrows, amount); + + // 4. execute receiver's callback function + IFlashloanReceiver(receiver).executeOperation(msg.sender, underlying, amount, totalFee, params); + + // 5. check balance + uint256 cashOnChainAfter = getCashOnChain(); + require(cashOnChainAfter == add_(cashOnChainBefore, totalFee), "BALANCE_INCONSISTENT"); + + // 6. update reserves and internal cash and totalBorrows + uint256 reservesFee = mul_ScalarTruncate(Exp({mantissa: reserveFactorMantissa}), totalFee); + totalReserves = add_(totalReserves, reservesFee); + internalCash = add_(cashBefore, totalFee); + totalBorrows = sub_(totalBorrows, amount); + + emit Flashloan(receiver, amount, totalFee, reservesFee); + } + /*** Safe Token ***/ /** @@ -138,7 +187,7 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @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) { + function getCashPrior() internal view returns (uint256) { return internalCash; } @@ -147,7 +196,7 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * @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) { + function getCashOnChain() internal view returns (uint256) { EIP20Interface token = EIP20Interface(underlying); return token.balanceOf(address(this)); } @@ -161,30 +210,39 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ - function doTransferIn(address from, uint amount) internal returns (uint) { + function doTransferIn( + address from, + uint256 amount, + bool isNative + ) internal returns (uint256) { + isNative; // unused + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); - uint balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); + uint256 balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); token.transferFrom(from, address(this), amount); bool success; assembly { switch returndatasize() - case 0 { // This is a non-standard ERC-20 - success := not(0) // set success to true - } - case 32 { // This is a compliant ERC-20 - returndatacopy(0, 0, 32) - success := mload(0) // Set `success = returndata` of external call - } - default { // This is an excessively non-compliant ERC-20, revert. - revert(0, 0) - } + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a compliant ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } } require(success, "TOKEN_TRANSFER_IN_FAILED"); // Calculate the amount that was *actually* transferred - uint balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); - uint transferredIn = sub_(balanceAfter, balanceBefore); + uint256 balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); + uint256 transferredIn = sub_(balanceAfter, balanceBefore); internalCash = add_(internalCash, transferredIn); return transferredIn; } @@ -198,25 +256,335 @@ contract CCapableErc20 is CToken, CCapableErc20Interface, CCapableDelegateInterf * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ - function doTransferOut(address payable to, uint amount) internal { + function doTransferOut( + address payable to, + uint256 amount, + bool isNative + ) internal { + isNative; // unused + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); token.transfer(to, amount); bool success; assembly { switch returndatasize() - case 0 { // This is a non-standard ERC-20 - success := not(0) // set success to true - } - case 32 { // This is a complaint ERC-20 - returndatacopy(0, 0, 32) - success := mload(0) // Set `success = returndata` of external call - } - default { // This is an excessively non-compliant ERC-20, revert. - revert(0, 0) - } + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a complaint ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } } require(success, "TOKEN_TRANSFER_OUT_FAILED"); internalCash = sub_(internalCash, amount); } + + /** + * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` + * @dev Called by both `transfer` and `transferFrom` internally + * @param spender The address of the account performing the transfer + * @param src The address of the source account + * @param dst The address of the destination account + * @param tokens The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256) { + /* Fail if transfer not allowed */ + uint256 allowed = comptroller.transferAllowed(address(this), src, dst, tokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed); + } + + /* Do not allow self-transfers */ + if (src == dst) { + return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED); + } + + /* Get the allowance, infinite for the account owner */ + uint256 startingAllowance = 0; + if (spender == src) { + startingAllowance = uint256(-1); + } else { + startingAllowance = transferAllowances[src][spender]; + } + + /* Do the calculations, checking for {under,over}flow */ + accountTokens[src] = sub_(accountTokens[src], tokens); + accountTokens[dst] = add_(accountTokens[dst], tokens); + + /* Eat some of the allowance (if necessary) */ + if (startingAllowance != uint256(-1)) { + transferAllowances[src][spender] = sub_(startingAllowance, tokens); + } + + /* We emit a Transfer event */ + emit Transfer(src, dst, tokens); + + // unused function + // comptroller.transferVerify(address(this), src, dst, tokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Get the account's cToken balances + * @param account The address of the account + */ + function getCTokenBalanceInternal(address account) internal view returns (uint256) { + return accountTokens[account]; + } + + struct MintLocalVars { + uint256 exchangeRateMantissa; + uint256 mintTokens; + uint256 actualMintAmount; + } + + /** + * @notice User supplies assets into the market and receives cTokens in exchange + * @dev Assumes interest has already been accrued up to the current block + * @param minter The address of the account which is supplying the assets + * @param mintAmount The amount of the underlying asset to supply + * @param isNative The amount is in native or not + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. + */ + function mintFresh( + address minter, + uint256 mintAmount, + bool isNative + ) internal returns (uint256, uint256) { + /* Fail if mint not allowed */ + uint256 allowed = comptroller.mintAllowed(address(this), minter, mintAmount); + if (allowed != 0) { + return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0); + } + + /* + * Return if mintAmount is zero. + * Put behind `mintAllowed` for accuring potential COMP rewards. + */ + if (mintAmount == 0) { + return (uint256(Error.NO_ERROR), 0); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0); + } + + MintLocalVars memory vars; + + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We call `doTransferIn` for the minter and the mintAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * `doTransferIn` reverts if anything goes wrong, since we can't be sure if + * side-effects occurred. The function returns the amount actually transferred, + * in case of a fee. On success, the cToken holds an additional `actualMintAmount` + * of cash. + */ + vars.actualMintAmount = doTransferIn(minter, mintAmount, isNative); + + /* + * We get the current exchange rate and calculate the number of cTokens to be minted: + * mintTokens = actualMintAmount / exchangeRate + */ + vars.mintTokens = div_ScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa})); + + /* + * We calculate the new total supply of cTokens and minter token balance, checking for overflow: + * totalSupply = totalSupply + mintTokens + * accountTokens[minter] = accountTokens[minter] + mintTokens + */ + totalSupply = add_(totalSupply, vars.mintTokens); + accountTokens[minter] = add_(accountTokens[minter], vars.mintTokens); + + /* We emit a Mint event, and a Transfer event */ + emit Mint(minter, vars.actualMintAmount, vars.mintTokens); + emit Transfer(address(this), minter, vars.mintTokens); + + /* We call the defense hook */ + // unused function + // comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens); + + return (uint256(Error.NO_ERROR), vars.actualMintAmount); + } + + struct RedeemLocalVars { + uint256 exchangeRateMantissa; + uint256 redeemTokens; + uint256 redeemAmount; + uint256 totalSupplyNew; + uint256 accountTokensNew; + } + + /** + * @notice User redeems cTokens in exchange for the underlying asset + * @dev Assumes interest has already been accrued up to the current block. Only one of redeemTokensIn or redeemAmountIn may be non-zero and it would do nothing if both are zero. + * @param redeemer The address of the account which is redeeming the tokens + * @param redeemTokensIn The number of cTokens to redeem into underlying + * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens + * @param isNative The amount is in native or not + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemFresh( + address payable redeemer, + uint256 redeemTokensIn, + uint256 redeemAmountIn, + bool isNative + ) internal returns (uint256) { + require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); + + RedeemLocalVars memory vars; + + /* exchangeRate = invoke Exchange Rate Stored() */ + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + /* If redeemTokensIn > 0: */ + if (redeemTokensIn > 0) { + /* + * We calculate the exchange rate and the amount of underlying to be redeemed: + * redeemTokens = redeemTokensIn + * redeemAmount = redeemTokensIn x exchangeRateCurrent + */ + vars.redeemTokens = redeemTokensIn; + vars.redeemAmount = mul_ScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn); + } else { + /* + * We get the current exchange rate and calculate the amount to be redeemed: + * redeemTokens = redeemAmountIn / exchangeRate + * redeemAmount = redeemAmountIn + */ + vars.redeemTokens = div_ScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa})); + vars.redeemAmount = redeemAmountIn; + } + + /* Fail if redeem not allowed */ + uint256 allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed); + } + + /* + * Return if redeemTokensIn and redeemAmountIn are zero. + * Put behind `redeemAllowed` for accuring potential COMP rewards. + */ + if (redeemTokensIn == 0 && redeemAmountIn == 0) { + return uint256(Error.NO_ERROR); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK); + } + + /* + * We calculate the new total supply and redeemer balance, checking for underflow: + * totalSupplyNew = totalSupply - redeemTokens + * accountTokensNew = accountTokens[redeemer] - redeemTokens + */ + vars.totalSupplyNew = sub_(totalSupply, vars.redeemTokens); + vars.accountTokensNew = sub_(accountTokens[redeemer], vars.redeemTokens); + + /* Fail gracefully if protocol has insufficient cash */ + if (getCashPrior() < vars.redeemAmount) { + return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We invoke doTransferOut for the redeemer and the redeemAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken has redeemAmount less of cash. + * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. + */ + doTransferOut(redeemer, vars.redeemAmount, isNative); + + /* We write previously calculated values into storage */ + totalSupply = vars.totalSupplyNew; + accountTokens[redeemer] = vars.accountTokensNew; + + /* We emit a Transfer event, and a Redeem event */ + emit Transfer(redeemer, address(this), vars.redeemTokens); + emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens); + + /* We call the defense hook */ + comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. + * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. + * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seizeInternal( + address seizerToken, + address liquidator, + address borrower, + uint256 seizeTokens + ) internal returns (uint256) { + /* Fail if seize not allowed */ + uint256 allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed); + } + + /* + * Return if seizeTokens is zero. + * Put behind `seizeAllowed` for accuring potential COMP rewards. + */ + if (seizeTokens == 0) { + return uint256(Error.NO_ERROR); + } + + /* Fail if borrower = liquidator */ + if (borrower == liquidator) { + return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER); + } + + /* + * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: + * borrowerTokensNew = accountTokens[borrower] - seizeTokens + * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens + */ + accountTokens[borrower] = sub_(accountTokens[borrower], seizeTokens); + accountTokens[liquidator] = add_(accountTokens[liquidator], seizeTokens); + + /* Emit a Transfer event */ + emit Transfer(borrower, liquidator, seizeTokens); + + /* We call the defense hook */ + // unused function + // comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens); + + return uint256(Error.NO_ERROR); + } } diff --git a/contracts/CCollateralCapErc20.sol b/contracts/CCollateralCapErc20.sol new file mode 100644 index 0000000..9b172db --- /dev/null +++ b/contracts/CCollateralCapErc20.sol @@ -0,0 +1,809 @@ +pragma solidity ^0.5.16; + +import "./CToken.sol"; +import "./ERC3156FlashLenderInterface.sol"; +import "./ERC3156FlashBorrowerInterface.sol"; +/** + * @title Cream's CCollateralCapErc20 Contract + * @notice CTokens which wrap an EIP-20 underlying with collateral cap + * @author Cream + */ +contract CCollateralCapErc20 is CToken, CCollateralCapErc20Interface { + /** + * @notice Initialize the new money market + * @param underlying_ The address of the underlying asset + * @param comptroller_ The address of the Comptroller + * @param interestRateModel_ The address of the interest rate model + * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 + * @param name_ ERC-20 name of this token + * @param symbol_ ERC-20 symbol of this token + * @param decimals_ ERC-20 decimal precision of this token + */ + function initialize( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_ + ) public { + // CToken initialize does the bulk of the work + super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); + + // Set underlying and sanity check it + underlying = underlying_; + EIP20Interface(underlying).totalSupply(); + } + + /*** User Interface ***/ + + /** + * @notice Sender supplies assets into the market and receives cTokens in exchange + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param mintAmount The amount of the underlying asset to supply + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function mint(uint256 mintAmount) external returns (uint256) { + (uint256 err, ) = mintInternal(mintAmount, false); + return err; + } + + /** + * @notice Sender redeems cTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemTokens The number of cTokens to redeem into underlying + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeem(uint256 redeemTokens) external returns (uint256) { + return redeemInternal(redeemTokens, false); + } + + /** + * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemAmount The amount of underlying to redeem + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { + return redeemUnderlyingInternal(redeemAmount, false); + } + + /** + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { + return borrowInternal(borrowAmount, false); + } + + /** + * @notice Sender repays their own borrow + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrow(uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowInternal(repayAmount, false); + return err; + } + + /** + * @notice Sender repays a borrow belonging to borrower + * @param borrower the account with the debt being payed off + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowBehalfInternal(borrower, repayAmount, false); + return err; + } + + /** + * @notice The sender liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @param borrower The borrower of this cToken to be liquidated + * @param repayAmount The amount of the underlying borrowed asset to repay + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256) { + (uint256 err, ) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral, false); + return err; + } + + /** + * @notice The sender adds to reserves. + * @param addAmount The amount fo underlying token to add as reserves + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _addReserves(uint256 addAmount) external returns (uint256) { + return _addReservesInternal(addAmount, false); + } + + /** + * @notice Set the given collateral cap for the market. + * @param newCollateralCap New collateral cap for this market. A value of 0 corresponds to no cap. + */ + function _setCollateralCap(uint256 newCollateralCap) external { + require(msg.sender == admin, "only admin can set collateral cap"); + + collateralCap = newCollateralCap; + emit NewCollateralCap(address(this), newCollateralCap); + } + + /** + * @notice Absorb excess cash into reserves. + */ + function gulp() external nonReentrant { + uint256 cashOnChain = getCashOnChain(); + uint256 cashPrior = getCashPrior(); + + uint256 excessCash = sub_(cashOnChain, cashPrior); + totalReserves = add_(totalReserves, excessCash); + internalCash = cashOnChain; + } + + /** + * @notice Get the max flash loan amount + */ + function maxFlashLoan() external view returns (uint256) { + uint256 amount = 0; + if ( + ComptrollerInterfaceExtension(address(comptroller)).flashloanAllowed(address(this), address(0), amount, "") + ) { + amount = getCashPrior(); + } + return amount; + } + + /** + * @notice Get the flash loan fees + * @param amount amount of token to borrow + */ + function flashFee(uint256 amount) external view returns (uint256) { + require( + ComptrollerInterfaceExtension(address(comptroller)).flashloanAllowed(address(this), address(0), amount, ""), + "flashloan is paused" + ); + return div_(mul_(amount, flashFeeBips), 10000); + } + + /** + * @notice Flash loan funds to a given account. + * @param receiver The receiver address for the funds + * @param initiator flash loan initiator + * @param amount The amount of the funds to be loaned + * @param data The other data + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address initiator, + uint256 amount, + bytes calldata data + ) external nonReentrant returns (bool) { + require(amount > 0, "flashLoan amount should be greater than zero"); + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); + require( + ComptrollerInterfaceExtension(address(comptroller)).flashloanAllowed( + address(this), + address(receiver), + amount, + data + ), + "flashloan is paused" + ); + uint256 cashOnChainBefore = getCashOnChain(); + uint256 cashBefore = getCashPrior(); + require(cashBefore >= amount, "INSUFFICIENT_LIQUIDITY"); + + // 1. calculate fee, 1 bips = 1/10000 + uint256 totalFee = this.flashFee(amount); + + // 2. transfer fund to receiver + doTransferOut(address(uint160(address(receiver))), amount, false); + + // 3. update totalBorrows + totalBorrows = add_(totalBorrows, amount); + + // 4. execute receiver's callback function + + require( + receiver.onFlashLoan(initiator, underlying, amount, totalFee, data) == + keccak256("ERC3156FlashBorrowerInterface.onFlashLoan"), + "IERC3156: Callback failed" + ); + + // 5. take amount + fee from receiver, then check balance + uint256 repaymentAmount = add_(amount, totalFee); + doTransferIn(address(receiver), repaymentAmount, false); + + uint256 cashOnChainAfter = getCashOnChain(); + + require(cashOnChainAfter == add_(cashOnChainBefore, totalFee), "BALANCE_INCONSISTENT"); + + // 6. update reserves and internal cash and totalBorrows + uint256 reservesFee = mul_ScalarTruncate(Exp({mantissa: reserveFactorMantissa}), totalFee); + totalReserves = add_(totalReserves, reservesFee); + internalCash = add_(cashBefore, totalFee); + totalBorrows = sub_(totalBorrows, amount); + + emit Flashloan(address(receiver), amount, totalFee, reservesFee); + return true; + } + + /** + * @notice Register account collateral tokens if there is space. + * @param account The account to register + * @dev This function could only be called by comptroller. + * @return The actual registered amount of collateral + */ + function registerCollateral(address account) external returns (uint256) { + // Make sure accountCollateralTokens of `account` is initialized. + initializeAccountCollateralTokens(account); + + require(msg.sender == address(comptroller), "only comptroller may register collateral for user"); + + uint256 amount = sub_(accountTokens[account], accountCollateralTokens[account]); + return increaseUserCollateralInternal(account, amount); + } + + /** + * @notice Unregister account collateral tokens if the account still has enough collateral. + * @dev This function could only be called by comptroller. + * @param account The account to unregister + */ + function unregisterCollateral(address account) external { + // Make sure accountCollateralTokens of `account` is initialized. + initializeAccountCollateralTokens(account); + + require(msg.sender == address(comptroller), "only comptroller may unregister collateral for user"); + + decreaseUserCollateralInternal(account, accountCollateralTokens[account]); + } + + /*** Safe Token ***/ + + /** + * @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 (uint256) { + 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 (uint256) { + EIP20Interface token = EIP20Interface(underlying); + return token.balanceOf(address(this)); + } + + /** + * @notice Initialize the account's collateral tokens. This function should be called in the beginning of every function + * that accesses accountCollateralTokens or accountTokens. + * @param account The account of accountCollateralTokens that needs to be updated + */ + function initializeAccountCollateralTokens(address account) internal { + /** + * If isCollateralTokenInit is false, it means accountCollateralTokens was not initialized yet. + * This case will only happen once and must be the very beginning. accountCollateralTokens is a new structure and its + * initial value should be equal to accountTokens if user has entered the market. However, it's almost impossible to + * check every user's value when the implementation becomes active. Therefore, it must rely on every action which will + * access accountTokens to call this function to check if accountCollateralTokens needed to be initialized. + */ + if (!isCollateralTokenInit[account]) { + if (ComptrollerInterfaceExtension(address(comptroller)).checkMembership(account, CToken(this))) { + accountCollateralTokens[account] = accountTokens[account]; + totalCollateralTokens = add_(totalCollateralTokens, accountTokens[account]); + + emit UserCollateralChanged(account, accountCollateralTokens[account]); + } + isCollateralTokenInit[account] = true; + } + } + + /** + * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case. + * This will revert due to insufficient balance or insufficient allowance. + * This function returns the actual amount received, + * which may be less than `amount` if there is a fee attached to the transfer. + * + * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. + * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca + */ + function doTransferIn( + address from, + uint256 amount, + bool isNative + ) internal returns (uint256) { + isNative; // unused + + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); + uint256 balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); + token.transferFrom(from, address(this), amount); + + bool success; + assembly { + switch returndatasize() + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a compliant ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } + } + require(success, "TOKEN_TRANSFER_IN_FAILED"); + + // Calculate the amount that was *actually* transferred + uint256 balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); + uint256 transferredIn = sub_(balanceAfter, balanceBefore); + internalCash = add_(internalCash, transferredIn); + return transferredIn; + } + + /** + * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory + * error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to + * insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified + * it is >= amount, this should not revert in normal conditions. + * + * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. + * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca + */ + function doTransferOut( + address payable to, + uint256 amount, + bool isNative + ) internal { + isNative; // unused + + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); + token.transfer(to, amount); + + bool success; + assembly { + switch returndatasize() + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a complaint ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } + } + require(success, "TOKEN_TRANSFER_OUT_FAILED"); + internalCash = sub_(internalCash, amount); + } + + /** + * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` + * @dev Called by both `transfer` and `transferFrom` internally + * @param spender The address of the account performing the transfer + * @param src The address of the source account + * @param dst The address of the destination account + * @param tokens The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256) { + // Make sure accountCollateralTokens of `src` and `dst` are initialized. + initializeAccountCollateralTokens(src); + initializeAccountCollateralTokens(dst); + + /** + * For every user, accountTokens must be greater than or equal to accountCollateralTokens. + * The buffer between the two values will be transferred first. + * bufferTokens = accountTokens[src] - accountCollateralTokens[src] + * collateralTokens = tokens - bufferTokens + */ + uint256 bufferTokens = sub_(accountTokens[src], accountCollateralTokens[src]); + uint256 collateralTokens = 0; + if (tokens > bufferTokens) { + collateralTokens = tokens - bufferTokens; + } + + /** + * Since bufferTokens are not collateralized and can be transferred freely, we only check with comptroller + * whether collateralized tokens can be transferred. + */ + uint256 allowed = comptroller.transferAllowed(address(this), src, dst, collateralTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed); + } + + /* Do not allow self-transfers */ + if (src == dst) { + return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED); + } + + /* Get the allowance, infinite for the account owner */ + uint256 startingAllowance = 0; + if (spender == src) { + startingAllowance = uint256(-1); + } else { + startingAllowance = transferAllowances[src][spender]; + } + + /* Do the calculations, checking for {under,over}flow */ + accountTokens[src] = sub_(accountTokens[src], tokens); + accountTokens[dst] = add_(accountTokens[dst], tokens); + if (collateralTokens > 0) { + accountCollateralTokens[src] = sub_(accountCollateralTokens[src], collateralTokens); + accountCollateralTokens[dst] = add_(accountCollateralTokens[dst], collateralTokens); + + emit UserCollateralChanged(src, accountCollateralTokens[src]); + emit UserCollateralChanged(dst, accountCollateralTokens[dst]); + } + + /* Eat some of the allowance (if necessary) */ + if (startingAllowance != uint256(-1)) { + transferAllowances[src][spender] = sub_(startingAllowance, tokens); + } + + /* We emit a Transfer event */ + emit Transfer(src, dst, tokens); + + // unused function + // comptroller.transferVerify(address(this), src, dst, tokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Get the account's cToken balances + * @param account The address of the account + */ + function getCTokenBalanceInternal(address account) internal view returns (uint256) { + if (isCollateralTokenInit[account]) { + return accountCollateralTokens[account]; + } else { + /** + * If the value of accountCollateralTokens was not initialized, we should return the value of accountTokens. + */ + return accountTokens[account]; + } + } + + /** + * @notice Increase user's collateral. Increase as much as we can. + * @param account The address of the account + * @param amount The amount of collateral user wants to increase + * @return The actual increased amount of collateral + */ + function increaseUserCollateralInternal(address account, uint256 amount) internal returns (uint256) { + uint256 totalCollateralTokensNew = add_(totalCollateralTokens, amount); + if (collateralCap == 0 || (collateralCap != 0 && totalCollateralTokensNew <= collateralCap)) { + // 1. If collateral cap is not set, + // 2. If collateral cap is set but has enough space for this user, + // give all the user needs. + totalCollateralTokens = totalCollateralTokensNew; + accountCollateralTokens[account] = add_(accountCollateralTokens[account], amount); + + emit UserCollateralChanged(account, accountCollateralTokens[account]); + return amount; + } else if (collateralCap > totalCollateralTokens) { + // If the collateral cap is set but the remaining cap is not enough for this user, + // give the remaining parts to the user. + uint256 gap = sub_(collateralCap, totalCollateralTokens); + totalCollateralTokens = add_(totalCollateralTokens, gap); + accountCollateralTokens[account] = add_(accountCollateralTokens[account], gap); + + emit UserCollateralChanged(account, accountCollateralTokens[account]); + return gap; + } + return 0; + } + + /** + * @notice Decrease user's collateral. Reject if the amount can't be fully decrease. + * @param account The address of the account + * @param amount The amount of collateral user wants to decrease + */ + function decreaseUserCollateralInternal(address account, uint256 amount) internal { + require(comptroller.redeemAllowed(address(this), account, amount) == 0, "comptroller rejection"); + + /* + * Return if amount is zero. + * Put behind `redeemAllowed` for accuring potential COMP rewards. + */ + if (amount == 0) { + return; + } + + totalCollateralTokens = sub_(totalCollateralTokens, amount); + accountCollateralTokens[account] = sub_(accountCollateralTokens[account], amount); + + emit UserCollateralChanged(account, accountCollateralTokens[account]); + } + + struct MintLocalVars { + uint256 exchangeRateMantissa; + uint256 mintTokens; + uint256 actualMintAmount; + } + + /** + * @notice User supplies assets into the market and receives cTokens in exchange + * @dev Assumes interest has already been accrued up to the current block + * @param minter The address of the account which is supplying the assets + * @param mintAmount The amount of the underlying asset to supply + * @param isNative The amount is in native or not + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. + */ + function mintFresh( + address minter, + uint256 mintAmount, + bool isNative + ) internal returns (uint256, uint256) { + // Make sure accountCollateralTokens of `minter` is initialized. + initializeAccountCollateralTokens(minter); + + /* Fail if mint not allowed */ + uint256 allowed = comptroller.mintAllowed(address(this), minter, mintAmount); + if (allowed != 0) { + return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0); + } + + /* + * Return if mintAmount is zero. + * Put behind `mintAllowed` for accuring potential COMP rewards. + */ + if (mintAmount == 0) { + return (uint256(Error.NO_ERROR), 0); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0); + } + + MintLocalVars memory vars; + + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We call `doTransferIn` for the minter and the mintAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * `doTransferIn` reverts if anything goes wrong, since we can't be sure if + * side-effects occurred. The function returns the amount actually transferred, + * in case of a fee. On success, the cToken holds an additional `actualMintAmount` + * of cash. + */ + vars.actualMintAmount = doTransferIn(minter, mintAmount, isNative); + + /* + * We get the current exchange rate and calculate the number of cTokens to be minted: + * mintTokens = actualMintAmount / exchangeRate + */ + vars.mintTokens = div_ScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa})); + + /* + * We calculate the new total supply of cTokens and minter token balance, checking for overflow: + * totalSupply = totalSupply + mintTokens + * accountTokens[minter] = accountTokens[minter] + mintTokens + */ + totalSupply = add_(totalSupply, vars.mintTokens); + accountTokens[minter] = add_(accountTokens[minter], vars.mintTokens); + + /* + * We only allocate collateral tokens if the minter has entered the market. + */ + if (ComptrollerInterfaceExtension(address(comptroller)).checkMembership(minter, CToken(this))) { + increaseUserCollateralInternal(minter, vars.mintTokens); + } + + /* We emit a Mint event, and a Transfer event */ + emit Mint(minter, vars.actualMintAmount, vars.mintTokens); + emit Transfer(address(this), minter, vars.mintTokens); + + /* We call the defense hook */ + // unused function + // comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens); + + return (uint256(Error.NO_ERROR), vars.actualMintAmount); + } + + struct RedeemLocalVars { + uint256 exchangeRateMantissa; + uint256 redeemTokens; + uint256 redeemAmount; + } + + /** + * @notice User redeems cTokens in exchange for the underlying asset + * @dev Assumes interest has already been accrued up to the current block. Only one of redeemTokensIn or redeemAmountIn may be non-zero and it would do nothing if both are zero. + * @param redeemer The address of the account which is redeeming the tokens + * @param redeemTokensIn The number of cTokens to redeem into underlying + * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens + * @param isNative The amount is in native or not + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemFresh( + address payable redeemer, + uint256 redeemTokensIn, + uint256 redeemAmountIn, + bool isNative + ) internal returns (uint256) { + // Make sure accountCollateralTokens of `redeemer` is initialized. + initializeAccountCollateralTokens(redeemer); + + require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); + + RedeemLocalVars memory vars; + + /* exchangeRate = invoke Exchange Rate Stored() */ + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + /* If redeemTokensIn > 0: */ + if (redeemTokensIn > 0) { + /* + * We calculate the exchange rate and the amount of underlying to be redeemed: + * redeemTokens = redeemTokensIn + * redeemAmount = redeemTokensIn x exchangeRateCurrent + */ + vars.redeemTokens = redeemTokensIn; + vars.redeemAmount = mul_ScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn); + } else { + /* + * We get the current exchange rate and calculate the amount to be redeemed: + * redeemTokens = redeemAmountIn / exchangeRate + * redeemAmount = redeemAmountIn + */ + vars.redeemTokens = div_ScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa})); + vars.redeemAmount = redeemAmountIn; + } + + /** + * For every user, accountTokens must be greater than or equal to accountCollateralTokens. + * The buffer between the two values will be redeemed first. + * bufferTokens = accountTokens[redeemer] - accountCollateralTokens[redeemer] + * collateralTokens = redeemTokens - bufferTokens + */ + uint256 bufferTokens = sub_(accountTokens[redeemer], accountCollateralTokens[redeemer]); + uint256 collateralTokens = 0; + if (vars.redeemTokens > bufferTokens) { + collateralTokens = vars.redeemTokens - bufferTokens; + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK); + } + + /* Fail gracefully if protocol has insufficient cash */ + if (getCashPrior() < vars.redeemAmount) { + return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We invoke doTransferOut for the redeemer and the redeemAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken has redeemAmount less of cash. + * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. + */ + doTransferOut(redeemer, vars.redeemAmount, isNative); + + /* + * We calculate the new total supply and redeemer balance, checking for underflow: + * totalSupplyNew = totalSupply - redeemTokens + * accountTokensNew = accountTokens[redeemer] - redeemTokens + */ + totalSupply = sub_(totalSupply, vars.redeemTokens); + accountTokens[redeemer] = sub_(accountTokens[redeemer], vars.redeemTokens); + + /* + * We only deallocate collateral tokens if the redeemer needs to redeem them. + */ + if (collateralTokens > 0) { + decreaseUserCollateralInternal(redeemer, collateralTokens); + } + + /* We emit a Transfer event, and a Redeem event */ + emit Transfer(redeemer, address(this), vars.redeemTokens); + emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens); + + /* We call the defense hook */ + comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. + * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. + * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seizeInternal( + address seizerToken, + address liquidator, + address borrower, + uint256 seizeTokens + ) internal returns (uint256) { + // Make sure accountCollateralTokens of `liquidator` and `borrower` are initialized. + initializeAccountCollateralTokens(liquidator); + initializeAccountCollateralTokens(borrower); + + /* Fail if seize not allowed */ + uint256 allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed); + } + + /* + * Return if seizeTokens is zero. + * Put behind `seizeAllowed` for accuring potential COMP rewards. + */ + if (seizeTokens == 0) { + return uint256(Error.NO_ERROR); + } + + /* Fail if borrower = liquidator */ + if (borrower == liquidator) { + return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER); + } + + /* + * We calculate the new borrower and liquidator token balances and token collateral balances, failing on underflow/overflow: + * accountTokens[borrower] = accountTokens[borrower] - seizeTokens + * accountTokens[liquidator] = accountTokens[liquidator] + seizeTokens + * accountCollateralTokens[borrower] = accountCollateralTokens[borrower] - seizeTokens + * accountCollateralTokens[liquidator] = accountCollateralTokens[liquidator] + seizeTokens + */ + accountTokens[borrower] = sub_(accountTokens[borrower], seizeTokens); + accountTokens[liquidator] = add_(accountTokens[liquidator], seizeTokens); + accountCollateralTokens[borrower] = sub_(accountCollateralTokens[borrower], seizeTokens); + accountCollateralTokens[liquidator] = add_(accountCollateralTokens[liquidator], seizeTokens); + + /* Emit a Transfer, UserCollateralChanged events */ + emit Transfer(borrower, liquidator, seizeTokens); + emit UserCollateralChanged(borrower, accountCollateralTokens[borrower]); + emit UserCollateralChanged(liquidator, accountCollateralTokens[liquidator]); + + /* We call the defense hook */ + // unused function + // comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens); + + return uint256(Error.NO_ERROR); + } +} diff --git a/contracts/CCollateralCapErc20Delegate.sol b/contracts/CCollateralCapErc20Delegate.sol new file mode 100644 index 0000000..db0e89e --- /dev/null +++ b/contracts/CCollateralCapErc20Delegate.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.5.16; + +import "./CCollateralCapErc20.sol"; + +/** + * @title Cream's CCollateralCapErc20Delegate Contract + * @notice CTokens which wrap an EIP-20 underlying and are delegated to + * @author Cream + */ +contract CCollateralCapErc20Delegate is CCollateralCapErc20 { + /** + * @notice Construct an empty delegate + */ + constructor() public {} + + /** + * @notice Called by the delegator on a delegate to initialize it for duty + * @param data The encoded bytes data for any initialization + */ + function _becomeImplementation(bytes memory data) public { + // Shh -- currently unused + data; + + // Shh -- we don't ever want this hook to be marked pure + if (false) { + implementation = address(0); + } + + require(msg.sender == admin, "only the admin may call _becomeImplementation"); + + // Set internal cash when becoming implementation + internalCash = getCashOnChain(); + + // Set CToken version in comptroller + ComptrollerInterfaceExtension(address(comptroller)).updateCTokenVersion( + address(this), + ComptrollerV2Storage.Version.COLLATERALCAP + ); + } + + /** + * @notice Called by the delegator on a delegate to forfeit its responsibility + */ + function _resignImplementation() public { + // Shh -- we don't ever want this hook to be marked pure + if (false) { + implementation = address(0); + } + + require(msg.sender == admin, "only the admin may call _resignImplementation"); + } +} diff --git a/contracts/CCollateralCapErc20Delegator.sol b/contracts/CCollateralCapErc20Delegator.sol new file mode 100644 index 0000000..e53fe65 --- /dev/null +++ b/contracts/CCollateralCapErc20Delegator.sol @@ -0,0 +1,589 @@ +pragma solidity ^0.5.16; + +import "./CTokenInterfaces.sol"; + +/** + * @title Cream's CCollateralCapErc20Delegator Contract + * @notice CTokens which wrap an EIP-20 underlying and delegate to an implementation + * @author Cream + */ +contract CCollateralCapErc20Delegator is CTokenInterface, CCollateralCapErc20Interface, CDelegatorInterface { + /** + * @notice Construct a new money market + * @param underlying_ The address of the underlying asset + * @param comptroller_ The address of the Comptroller + * @param interestRateModel_ The address of the interest rate model + * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 + * @param name_ ERC-20 name of this token + * @param symbol_ ERC-20 symbol of this token + * @param decimals_ ERC-20 decimal precision of this token + * @param admin_ Address of the administrator of this token + * @param implementation_ The address of the implementation the contract delegates to + * @param becomeImplementationData The encoded args for becomeImplementation + */ + constructor( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_, + address payable admin_, + address implementation_, + bytes memory becomeImplementationData + ) public { + // Creator of the contract is admin during initialization + admin = msg.sender; + + // First delegate gets to initialize the delegator (i.e. storage contract) + delegateTo( + implementation_, + abi.encodeWithSignature( + "initialize(address,address,address,uint256,string,string,uint8)", + underlying_, + comptroller_, + interestRateModel_, + initialExchangeRateMantissa_, + name_, + symbol_, + decimals_ + ) + ); + + // New implementations always get set via the settor (post-initialize) + _setImplementation(implementation_, false, becomeImplementationData); + + // Set the proper admin now that initialization is done + admin = admin_; + } + + /** + * @notice Called by the admin to update the implementation of the delegator + * @param implementation_ The address of the new implementation for delegation + * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation + * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation + */ + function _setImplementation( + address implementation_, + bool allowResign, + bytes memory becomeImplementationData + ) public { + require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin"); + + if (allowResign) { + delegateToImplementation(abi.encodeWithSignature("_resignImplementation()")); + } + + address oldImplementation = implementation; + implementation = implementation_; + + delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData)); + + emit NewImplementation(oldImplementation, implementation); + } + + /** + * @notice Sender supplies assets into the market and receives cTokens in exchange + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param mintAmount The amount of the underlying asset to supply + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function mint(uint256 mintAmount) external returns (uint256) { + mintAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender redeems cTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemTokens The number of cTokens to redeem into underlying + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeem(uint256 redeemTokens) external returns (uint256) { + redeemTokens; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemAmount The amount of underlying to redeem + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { + redeemAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { + borrowAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender repays their own borrow + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrow(uint256 repayAmount) external returns (uint256) { + repayAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender repays a borrow belonging to borrower + * @param borrower the account with the debt being payed off + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256) { + borrower; + repayAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice The sender liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @param borrower The borrower of this cToken to be liquidated + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @param repayAmount The amount of the underlying borrowed asset to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256) { + borrower; + repayAmount; + cTokenCollateral; // Shh + delegateAndReturn(); + } + + /** + * @notice Transfer `amount` tokens from `msg.sender` to `dst` + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transfer(address dst, uint256 amount) external returns (bool) { + dst; + amount; // Shh + delegateAndReturn(); + } + + /** + * @notice Transfer `amount` tokens from `src` to `dst` + * @param src The address of the source account + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool) { + src; + dst; + amount; // Shh + delegateAndReturn(); + } + + /** + * @notice Approve `spender` to transfer up to `amount` from `src` + * @dev This will overwrite the approval amount for `spender` + * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) + * @param spender The address of the account which may transfer tokens + * @param amount The number of tokens that are approved (-1 means infinite) + * @return Whether or not the approval succeeded + */ + function approve(address spender, uint256 amount) external returns (bool) { + spender; + amount; // Shh + delegateAndReturn(); + } + + /** + * @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 { + delegateAndReturn(); + } + + /** + * @notice Flash loan funds to a given account. + * @param receiver The receiver address for the funds + * @param initiator flash loan initiator + * @param amount The amount of the funds to be loaned + * @param data The other data + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address initiator, + uint256 amount, + bytes calldata data + ) external returns (bool) { + receiver; + initiator; + amount; + data; // Shh + delegateAndReturn(); + } + + /** + * @notice Register account collateral tokens if there is space. + * @param account The account to register + * @dev This function could only be called by comptroller. + * @return The actual registered amount of collateral + */ + function registerCollateral(address account) external returns (uint256) { + account; // Shh + delegateAndReturn(); + } + + /** + * @notice Unregister account collateral tokens if the account still has enough collateral. + * @dev This function could only be called by comptroller. + * @param account The account to unregister + */ + function unregisterCollateral(address account) external { + account; // Shh + delegateAndReturn(); + } + + /** + * @notice Get the current allowance from `owner` for `spender` + * @param owner The address of the account which owns the tokens to be spent + * @param spender The address of the account which may transfer tokens + * @return The number of tokens allowed to be spent (-1 means infinite) + */ + function allowance(address owner, address spender) external view returns (uint256) { + owner; + spender; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Get the token balance of the `owner` + * @param owner The address of the account to query + * @return The number of tokens owned by `owner` + */ + function balanceOf(address owner) external view returns (uint256) { + owner; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Get the underlying balance of the `owner` + * @dev This also accrues interest in a transaction + * @param owner The address of the account to query + * @return The amount of underlying owned by `owner` + */ + function balanceOfUnderlying(address owner) external returns (uint256) { + owner; // Shh + delegateAndReturn(); + } + + /** + * @notice Get a snapshot of the account's balances, and the cached exchange rate + * @dev This is used by comptroller to more efficiently perform liquidity checks. + * @param account Address of the account to snapshot + * @return (possible error, token balance, borrow balance, exchange rate mantissa) + */ + function getAccountSnapshot(address account) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ) + { + account; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Returns the current per-block borrow interest rate for this cToken + * @return The borrow interest rate per block, scaled by 1e18 + */ + function borrowRatePerBlock() external view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Returns the current per-block supply interest rate for this cToken + * @return The supply interest rate per block, scaled by 1e18 + */ + function supplyRatePerBlock() external view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Returns the current total borrows plus accrued interest + * @return The total borrows with interest + */ + function totalBorrowsCurrent() external returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex + * @param account The address whose balance should be calculated after updating borrowIndex + * @return The calculated balance + */ + function borrowBalanceCurrent(address account) external returns (uint256) { + account; // Shh + delegateAndReturn(); + } + + /** + * @notice Return the borrow balance of account based on stored data + * @param account The address whose balance should be calculated + * @return The calculated balance + */ + function borrowBalanceStored(address account) public view returns (uint256) { + account; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Accrue interest then return the up-to-date exchange rate + * @return Calculated exchange rate scaled by 1e18 + */ + function exchangeRateCurrent() public returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Calculates the exchange rate from the underlying to the CToken + * @dev This function does not accrue interest before calculating the exchange rate + * @return Calculated exchange rate scaled by 1e18 + */ + function exchangeRateStored() public view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Get cash balance of this cToken in the underlying asset + * @return The quantity of underlying asset owned by this contract + */ + function getCash() external view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Applies accrued interest to total borrows and reserves. + * @dev This calculates interest accrued from the last checkpointed block + * up to the current block and writes new checkpoint to storage. + */ + function accrueInterest() public returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Will fail unless called by another cToken during the process of liquidation. + * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seize( + address liquidator, + address borrower, + uint256 seizeTokens + ) external returns (uint256) { + liquidator; + borrower; + seizeTokens; // Shh + delegateAndReturn(); + } + + /*** Admin Functions ***/ + + /** + * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @param newPendingAdmin New pending admin. + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256) { + newPendingAdmin; // Shh + delegateAndReturn(); + } + + /** + * @notice Sets a new comptroller for the market + * @dev Admin function to set a new comptroller + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setComptroller(ComptrollerInterface newComptroller) public returns (uint256) { + newComptroller; // Shh + delegateAndReturn(); + } + + /** + * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh + * @dev Admin function to accrue interest and set a new reserve factor + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setReserveFactor(uint256 newReserveFactorMantissa) external returns (uint256) { + newReserveFactorMantissa; // Shh + delegateAndReturn(); + } + + /** + * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + * @dev Admin function for pending admin to accept role and update admin + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _acceptAdmin() external returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Accrues interest and adds reserves by transferring from admin + * @param addAmount Amount of reserves to add + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _addReserves(uint256 addAmount) external returns (uint256) { + addAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Accrues interest and reduces reserves by transferring to admin + * @param reduceAmount Amount of reduction to reserves + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _reduceReserves(uint256 reduceAmount) external returns (uint256) { + reduceAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Accrues interest and updates the interest rate model using _setInterestRateModelFresh + * @dev Admin function to accrue interest and update the interest rate model + * @param newInterestRateModel the new interest rate model to use + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256) { + newInterestRateModel; // Shh + delegateAndReturn(); + } + + /** + * @notice Set collateral cap of this market, 0 for no cap + * @param newCollateralCap The new collateral cap + */ + function _setCollateralCap(uint256 newCollateralCap) external { + newCollateralCap; // Shh + delegateAndReturn(); + } + + /** + * @notice Internal method to delegate execution to another contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + * @param callee The contract to delegatecall + * @param data The raw data to delegatecall + * @return The returned bytes from the delegatecall + */ + function delegateTo(address callee, bytes memory data) internal returns (bytes memory) { + (bool success, bytes memory returnData) = callee.delegatecall(data); + assembly { + if eq(success, 0) { + revert(add(returnData, 0x20), returndatasize) + } + } + return returnData; + } + + /** + * @notice Delegates execution to the implementation contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + * @param data The raw data to delegatecall + * @return The returned bytes from the delegatecall + */ + function delegateToImplementation(bytes memory data) public returns (bytes memory) { + return delegateTo(implementation, data); + } + + /** + * @notice Delegates execution to an implementation contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop. + * @param data The raw data to delegatecall + * @return The returned bytes from the delegatecall + */ + function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { + (bool success, bytes memory returnData) = address(this).staticcall( + abi.encodeWithSignature("delegateToImplementation(bytes)", data) + ); + assembly { + if eq(success, 0) { + revert(add(returnData, 0x20), returndatasize) + } + } + return abi.decode(returnData, (bytes)); + } + + function delegateToViewAndReturn() private view returns (bytes memory) { + (bool success, ) = address(this).staticcall( + abi.encodeWithSignature("delegateToImplementation(bytes)", msg.data) + ); + + assembly { + let free_mem_ptr := mload(0x40) + returndatacopy(free_mem_ptr, 0, returndatasize) + + switch success + case 0 { + revert(free_mem_ptr, returndatasize) + } + default { + return(add(free_mem_ptr, 0x40), returndatasize) + } + } + } + + function delegateAndReturn() private returns (bytes memory) { + (bool success, ) = implementation.delegatecall(msg.data); + + assembly { + let free_mem_ptr := mload(0x40) + returndatacopy(free_mem_ptr, 0, returndatasize) + + switch success + case 0 { + revert(free_mem_ptr, returndatasize) + } + default { + return(free_mem_ptr, returndatasize) + } + } + } + + /** + * @notice Delegates execution to an implementation contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + */ + function() external payable { + require(msg.value == 0, "CErc20Delegator:fallback: cannot send value to fallback"); + + // delegate all other functions to current implementation + delegateAndReturn(); + } +} diff --git a/contracts/CErc20.sol b/contracts/CErc20.sol index 3124e79..1a5176a 100644 --- a/contracts/CErc20.sol +++ b/contracts/CErc20.sol @@ -18,13 +18,15 @@ contract CErc20 is CToken, CErc20Interface { * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token */ - function initialize(address underlying_, - ComptrollerInterface comptroller_, - InterestRateModel interestRateModel_, - uint initialExchangeRateMantissa_, - string memory name_, - string memory symbol_, - uint8 decimals_) public { + function initialize( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_ + ) public { // CToken initialize does the bulk of the work super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); @@ -41,8 +43,8 @@ contract CErc20 is CToken, CErc20Interface { * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function mint(uint mintAmount) external returns (uint) { - (uint err,) = mintInternal(mintAmount); + function mint(uint256 mintAmount) external returns (uint256) { + (uint256 err, ) = mintInternal(mintAmount, false); return err; } @@ -52,8 +54,8 @@ contract CErc20 is CToken, CErc20Interface { * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeem(uint redeemTokens) external returns (uint) { - return redeemInternal(redeemTokens); + function redeem(uint256 redeemTokens) external returns (uint256) { + return redeemInternal(redeemTokens, false); } /** @@ -62,17 +64,17 @@ contract CErc20 is CToken, CErc20Interface { * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeemUnderlying(uint redeemAmount) external returns (uint) { - return redeemUnderlyingInternal(redeemAmount); + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { + return redeemUnderlyingInternal(redeemAmount, false); } /** - * @notice Sender borrows assets from the protocol to their own address - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function borrow(uint borrowAmount) external returns (uint) { - return borrowInternal(borrowAmount); + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { + return borrowInternal(borrowAmount, false); } /** @@ -80,8 +82,8 @@ contract CErc20 is CToken, CErc20Interface { * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function repayBorrow(uint repayAmount) external returns (uint) { - (uint err,) = repayBorrowInternal(repayAmount); + function repayBorrow(uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowInternal(repayAmount, false); return err; } @@ -91,8 +93,8 @@ contract CErc20 is CToken, CErc20Interface { * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) { - (uint err,) = repayBorrowBehalfInternal(borrower, repayAmount); + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowBehalfInternal(borrower, repayAmount, false); return err; } @@ -104,8 +106,12 @@ contract CErc20 is CToken, CErc20Interface { * @param cTokenCollateral The market in which to seize collateral from the borrower * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) { - (uint err,) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral); + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256) { + (uint256 err, ) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral, false); return err; } @@ -114,8 +120,8 @@ contract CErc20 is CToken, CErc20Interface { * @param addAmount The amount fo underlying token to add as reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _addReserves(uint addAmount) external returns (uint) { - return _addReservesInternal(addAmount); + function _addReserves(uint256 addAmount) external returns (uint256) { + return _addReservesInternal(addAmount, false); } /*** Safe Token ***/ @@ -125,7 +131,7 @@ contract CErc20 is CToken, CErc20Interface { * @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) { + function getCashPrior() internal view returns (uint256) { EIP20Interface token = EIP20Interface(underlying); return token.balanceOf(address(this)); } @@ -139,29 +145,38 @@ contract CErc20 is CToken, CErc20Interface { * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ - function doTransferIn(address from, uint amount) internal returns (uint) { + function doTransferIn( + address from, + uint256 amount, + bool isNative + ) internal returns (uint256) { + isNative; // unused + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); - uint balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); + uint256 balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); token.transferFrom(from, address(this), amount); bool success; assembly { switch returndatasize() - case 0 { // This is a non-standard ERC-20 - success := not(0) // set success to true - } - case 32 { // This is a compliant ERC-20 - returndatacopy(0, 0, 32) - success := mload(0) // Set `success = returndata` of external call - } - default { // This is an excessively non-compliant ERC-20, revert. - revert(0, 0) - } + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a compliant ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } } require(success, "TOKEN_TRANSFER_IN_FAILED"); // Calculate the amount that was *actually* transferred - uint balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); + uint256 balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); return sub_(balanceAfter, balanceBefore); } @@ -174,24 +189,334 @@ contract CErc20 is CToken, CErc20Interface { * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ - function doTransferOut(address payable to, uint amount) internal { + function doTransferOut( + address payable to, + uint256 amount, + bool isNative + ) internal { + isNative; // unused + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); token.transfer(to, amount); bool success; assembly { switch returndatasize() - case 0 { // This is a non-standard ERC-20 - success := not(0) // set success to true - } - case 32 { // This is a complaint ERC-20 - returndatacopy(0, 0, 32) - success := mload(0) // Set `success = returndata` of external call - } - default { // This is an excessively non-compliant ERC-20, revert. - revert(0, 0) - } + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a complaint ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } } require(success, "TOKEN_TRANSFER_OUT_FAILED"); } + + /** + * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` + * @dev Called by both `transfer` and `transferFrom` internally + * @param spender The address of the account performing the transfer + * @param src The address of the source account + * @param dst The address of the destination account + * @param tokens The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256) { + /* Fail if transfer not allowed */ + uint256 allowed = comptroller.transferAllowed(address(this), src, dst, tokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed); + } + + /* Do not allow self-transfers */ + if (src == dst) { + return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED); + } + + /* Get the allowance, infinite for the account owner */ + uint256 startingAllowance = 0; + if (spender == src) { + startingAllowance = uint256(-1); + } else { + startingAllowance = transferAllowances[src][spender]; + } + + /* Do the calculations, checking for {under,over}flow */ + accountTokens[src] = sub_(accountTokens[src], tokens); + accountTokens[dst] = add_(accountTokens[dst], tokens); + + /* Eat some of the allowance (if necessary) */ + if (startingAllowance != uint256(-1)) { + transferAllowances[src][spender] = sub_(startingAllowance, tokens); + } + + /* We emit a Transfer event */ + emit Transfer(src, dst, tokens); + + // unused function + // comptroller.transferVerify(address(this), src, dst, tokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Get the account's cToken balances + * @param account The address of the account + */ + function getCTokenBalanceInternal(address account) internal view returns (uint256) { + return accountTokens[account]; + } + + struct MintLocalVars { + uint256 exchangeRateMantissa; + uint256 mintTokens; + uint256 actualMintAmount; + } + + /** + * @notice User supplies assets into the market and receives cTokens in exchange + * @dev Assumes interest has already been accrued up to the current block + * @param minter The address of the account which is supplying the assets + * @param mintAmount The amount of the underlying asset to supply + * @param isNative The amount is in native or not + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. + */ + function mintFresh( + address minter, + uint256 mintAmount, + bool isNative + ) internal returns (uint256, uint256) { + /* Fail if mint not allowed */ + uint256 allowed = comptroller.mintAllowed(address(this), minter, mintAmount); + if (allowed != 0) { + return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0); + } + + /* + * Return if mintAmount is zero. + * Put behind `mintAllowed` for accuring potential COMP rewards. + */ + if (mintAmount == 0) { + return (uint256(Error.NO_ERROR), 0); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0); + } + + MintLocalVars memory vars; + + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We call `doTransferIn` for the minter and the mintAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * `doTransferIn` reverts if anything goes wrong, since we can't be sure if + * side-effects occurred. The function returns the amount actually transferred, + * in case of a fee. On success, the cToken holds an additional `actualMintAmount` + * of cash. + */ + vars.actualMintAmount = doTransferIn(minter, mintAmount, isNative); + + /* + * We get the current exchange rate and calculate the number of cTokens to be minted: + * mintTokens = actualMintAmount / exchangeRate + */ + vars.mintTokens = div_ScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa})); + + /* + * We calculate the new total supply of cTokens and minter token balance, checking for overflow: + * totalSupply = totalSupply + mintTokens + * accountTokens[minter] = accountTokens[minter] + mintTokens + */ + totalSupply = add_(totalSupply, vars.mintTokens); + accountTokens[minter] = add_(accountTokens[minter], vars.mintTokens); + + /* We emit a Mint event, and a Transfer event */ + emit Mint(minter, vars.actualMintAmount, vars.mintTokens); + emit Transfer(address(this), minter, vars.mintTokens); + + /* We call the defense hook */ + // unused function + // comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens); + + return (uint256(Error.NO_ERROR), vars.actualMintAmount); + } + + struct RedeemLocalVars { + uint256 exchangeRateMantissa; + uint256 redeemTokens; + uint256 redeemAmount; + uint256 totalSupplyNew; + uint256 accountTokensNew; + } + + /** + * @notice User redeems cTokens in exchange for the underlying asset + * @dev Assumes interest has already been accrued up to the current block. Only one of redeemTokensIn or redeemAmountIn may be non-zero and it would do nothing if both are zero. + * @param redeemer The address of the account which is redeeming the tokens + * @param redeemTokensIn The number of cTokens to redeem into underlying + * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens + * @param isNative The amount is in native or not + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemFresh( + address payable redeemer, + uint256 redeemTokensIn, + uint256 redeemAmountIn, + bool isNative + ) internal returns (uint256) { + require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); + + RedeemLocalVars memory vars; + + /* exchangeRate = invoke Exchange Rate Stored() */ + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + /* If redeemTokensIn > 0: */ + if (redeemTokensIn > 0) { + /* + * We calculate the exchange rate and the amount of underlying to be redeemed: + * redeemTokens = redeemTokensIn + * redeemAmount = redeemTokensIn x exchangeRateCurrent + */ + vars.redeemTokens = redeemTokensIn; + vars.redeemAmount = mul_ScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn); + } else { + /* + * We get the current exchange rate and calculate the amount to be redeemed: + * redeemTokens = redeemAmountIn / exchangeRate + * redeemAmount = redeemAmountIn + */ + vars.redeemTokens = div_ScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa})); + vars.redeemAmount = redeemAmountIn; + } + + /* Fail if redeem not allowed */ + uint256 allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed); + } + + /* + * Return if redeemTokensIn and redeemAmountIn are zero. + * Put behind `redeemAllowed` for accuring potential COMP rewards. + */ + if (redeemTokensIn == 0 && redeemAmountIn == 0) { + return uint256(Error.NO_ERROR); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK); + } + + /* + * We calculate the new total supply and redeemer balance, checking for underflow: + * totalSupplyNew = totalSupply - redeemTokens + * accountTokensNew = accountTokens[redeemer] - redeemTokens + */ + vars.totalSupplyNew = sub_(totalSupply, vars.redeemTokens); + vars.accountTokensNew = sub_(accountTokens[redeemer], vars.redeemTokens); + + /* Fail gracefully if protocol has insufficient cash */ + if (getCashPrior() < vars.redeemAmount) { + return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We invoke doTransferOut for the redeemer and the redeemAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken has redeemAmount less of cash. + * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. + */ + doTransferOut(redeemer, vars.redeemAmount, isNative); + + /* We write previously calculated values into storage */ + totalSupply = vars.totalSupplyNew; + accountTokens[redeemer] = vars.accountTokensNew; + + /* We emit a Transfer event, and a Redeem event */ + emit Transfer(redeemer, address(this), vars.redeemTokens); + emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens); + + /* We call the defense hook */ + comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. + * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. + * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seizeInternal( + address seizerToken, + address liquidator, + address borrower, + uint256 seizeTokens + ) internal returns (uint256) { + /* Fail if seize not allowed */ + uint256 allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed); + } + + /* + * Return if seizeTokens is zero. + * Put behind `seizeAllowed` for accuring potential COMP rewards. + */ + if (seizeTokens == 0) { + return uint256(Error.NO_ERROR); + } + + /* Fail if borrower = liquidator */ + if (borrower == liquidator) { + return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER); + } + + /* + * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: + * borrowerTokensNew = accountTokens[borrower] - seizeTokens + * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens + */ + accountTokens[borrower] = sub_(accountTokens[borrower], seizeTokens); + accountTokens[liquidator] = add_(accountTokens[liquidator], seizeTokens); + + /* Emit a Transfer event */ + emit Transfer(borrower, liquidator, seizeTokens); + + /* We call the defense hook */ + // unused function + // comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens); + + return uint256(Error.NO_ERROR); + } } diff --git a/contracts/CErc20Delegator.sol b/contracts/CErc20Delegator.sol index 6201415..25baefa 100644 --- a/contracts/CErc20Delegator.sol +++ b/contracts/CErc20Delegator.sol @@ -21,28 +21,35 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param implementation_ The address of the implementation the contract delegates to * @param becomeImplementationData The encoded args for becomeImplementation */ - constructor(address underlying_, - ComptrollerInterface comptroller_, - InterestRateModel interestRateModel_, - uint initialExchangeRateMantissa_, - string memory name_, - string memory symbol_, - uint8 decimals_, - address payable admin_, - address implementation_, - bytes memory becomeImplementationData) public { + constructor( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_, + address payable admin_, + address implementation_, + bytes memory becomeImplementationData + ) public { // Creator of the contract is admin during initialization admin = msg.sender; // First delegate gets to initialize the delegator (i.e. storage contract) - delegateTo(implementation_, abi.encodeWithSignature("initialize(address,address,address,uint256,string,string,uint8)", - underlying_, - comptroller_, - interestRateModel_, - initialExchangeRateMantissa_, - name_, - symbol_, - decimals_)); + delegateTo( + implementation_, + abi.encodeWithSignature( + "initialize(address,address,address,uint256,string,string,uint8)", + underlying_, + comptroller_, + interestRateModel_, + initialExchangeRateMantissa_, + name_, + symbol_, + decimals_ + ) + ); // New implementations always get set via the settor (post-initialize) _setImplementation(implementation_, false, becomeImplementationData); @@ -57,7 +64,11 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ - function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public { + function _setImplementation( + address implementation_, + bool allowResign, + bytes memory becomeImplementationData + ) public { require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin"); if (allowResign) { @@ -78,7 +89,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function mint(uint mintAmount) external returns (uint) { + function mint(uint256 mintAmount) external returns (uint256) { mintAmount; // Shh delegateAndReturn(); } @@ -89,7 +100,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeem(uint redeemTokens) external returns (uint) { + function redeem(uint256 redeemTokens) external returns (uint256) { redeemTokens; // Shh delegateAndReturn(); } @@ -100,17 +111,17 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeemUnderlying(uint redeemAmount) external returns (uint) { + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { redeemAmount; // Shh delegateAndReturn(); } /** - * @notice Sender borrows assets from the protocol to their own address - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function borrow(uint borrowAmount) external returns (uint) { + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { borrowAmount; // Shh delegateAndReturn(); } @@ -120,7 +131,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function repayBorrow(uint repayAmount) external returns (uint) { + function repayBorrow(uint256 repayAmount) external returns (uint256) { repayAmount; // Shh delegateAndReturn(); } @@ -131,8 +142,9 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) { - borrower; repayAmount; // Shh + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256) { + borrower; + repayAmount; // Shh delegateAndReturn(); } @@ -144,16 +156,14 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param repayAmount The amount of the underlying borrowed asset to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) { - borrower; repayAmount; cTokenCollateral; // Shh - delegateAndReturn(); - } - - /** - * @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 { + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256) { + borrower; + repayAmount; + cTokenCollateral; // Shh delegateAndReturn(); } @@ -163,8 +173,9 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ - function transfer(address dst, uint amount) external returns (bool) { - dst; amount; // Shh + function transfer(address dst, uint256 amount) external returns (bool) { + dst; + amount; // Shh delegateAndReturn(); } @@ -175,8 +186,14 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ - function transferFrom(address src, address dst, uint256 amount) external returns (bool) { - src; dst; amount; // Shh + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool) { + src; + dst; + amount; // Shh delegateAndReturn(); } @@ -189,7 +206,8 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool) { - spender; amount; // Shh + spender; + amount; // Shh delegateAndReturn(); } @@ -199,8 +217,9 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ - function allowance(address owner, address spender) external view returns (uint) { - owner; spender; // Shh + function allowance(address owner, address spender) external view returns (uint256) { + owner; + spender; // Shh delegateToViewAndReturn(); } @@ -209,7 +228,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ - function balanceOf(address owner) external view returns (uint) { + function balanceOf(address owner) external view returns (uint256) { owner; // Shh delegateToViewAndReturn(); } @@ -220,7 +239,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ - function balanceOfUnderlying(address owner) external returns (uint) { + function balanceOfUnderlying(address owner) external returns (uint256) { owner; // Shh delegateAndReturn(); } @@ -231,7 +250,16 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ - function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) { + function getAccountSnapshot(address account) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ) + { account; // Shh delegateToViewAndReturn(); } @@ -240,7 +268,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @notice Returns the current per-block borrow interest rate for this cToken * @return The borrow interest rate per block, scaled by 1e18 */ - function borrowRatePerBlock() external view returns (uint) { + function borrowRatePerBlock() external view returns (uint256) { delegateToViewAndReturn(); } @@ -248,7 +276,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @notice Returns the current per-block supply interest rate for this cToken * @return The supply interest rate per block, scaled by 1e18 */ - function supplyRatePerBlock() external view returns (uint) { + function supplyRatePerBlock() external view returns (uint256) { delegateToViewAndReturn(); } @@ -256,7 +284,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ - function totalBorrowsCurrent() external returns (uint) { + function totalBorrowsCurrent() external returns (uint256) { delegateAndReturn(); } @@ -265,7 +293,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ - function borrowBalanceCurrent(address account) external returns (uint) { + function borrowBalanceCurrent(address account) external returns (uint256) { account; // Shh delegateAndReturn(); } @@ -275,16 +303,16 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param account The address whose balance should be calculated * @return The calculated balance */ - function borrowBalanceStored(address account) public view returns (uint) { + function borrowBalanceStored(address account) public view returns (uint256) { account; // Shh delegateToViewAndReturn(); } - /** + /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ - function exchangeRateCurrent() public returns (uint) { + function exchangeRateCurrent() public returns (uint256) { delegateAndReturn(); } @@ -293,7 +321,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ - function exchangeRateStored() public view returns (uint) { + function exchangeRateStored() public view returns (uint256) { delegateToViewAndReturn(); } @@ -301,16 +329,16 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @notice Get cash balance of this cToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ - function getCash() external view returns (uint) { + function getCash() external view returns (uint256) { delegateToViewAndReturn(); } /** - * @notice Applies accrued interest to total borrows and reserves. - * @dev This calculates interest accrued from the last checkpointed block - * up to the current block and writes new checkpoint to storage. - */ - function accrueInterest() public returns (uint) { + * @notice Applies accrued interest to total borrows and reserves. + * @dev This calculates interest accrued from the last checkpointed block + * up to the current block and writes new checkpoint to storage. + */ + function accrueInterest() public returns (uint256) { delegateAndReturn(); } @@ -323,50 +351,56 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint) { - liquidator; borrower; seizeTokens; // Shh + function seize( + address liquidator, + address borrower, + uint256 seizeTokens + ) external returns (uint256) { + liquidator; + borrower; + seizeTokens; // Shh delegateAndReturn(); } /*** Admin Functions ***/ /** - * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @param newPendingAdmin New pending admin. - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) { + * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @param newPendingAdmin New pending admin. + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256) { newPendingAdmin; // Shh delegateAndReturn(); } /** - * @notice Sets a new comptroller for the market - * @dev Admin function to set a new comptroller - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) { + * @notice Sets a new comptroller for the market + * @dev Admin function to set a new comptroller + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setComptroller(ComptrollerInterface newComptroller) public returns (uint256) { newComptroller; // Shh delegateAndReturn(); } /** - * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh - * @dev Admin function to accrue interest and set a new reserve factor - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint) { + * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh + * @dev Admin function to accrue interest and set a new reserve factor + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setReserveFactor(uint256 newReserveFactorMantissa) external returns (uint256) { newReserveFactorMantissa; // Shh delegateAndReturn(); } /** - * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin - * @dev Admin function for pending admin to accept role and update admin - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _acceptAdmin() external returns (uint) { + * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + * @dev Admin function for pending admin to accept role and update admin + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _acceptAdmin() external returns (uint256) { delegateAndReturn(); } @@ -375,7 +409,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param addAmount Amount of reserves to add * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _addReserves(uint addAmount) external returns (uint) { + function _addReserves(uint256 addAmount) external returns (uint256) { addAmount; // Shh delegateAndReturn(); } @@ -385,7 +419,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _reduceReserves(uint reduceAmount) external returns (uint) { + function _reduceReserves(uint256 reduceAmount) external returns (uint256) { reduceAmount; // Shh delegateAndReturn(); } @@ -396,7 +430,7 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) { + function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256) { newInterestRateModel; // Shh delegateAndReturn(); } @@ -436,7 +470,9 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @return The returned bytes from the delegatecall */ function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { - (bool success, bytes memory returnData) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", data)); + (bool success, bytes memory returnData) = address(this).staticcall( + abi.encodeWithSignature("delegateToImplementation(bytes)", data) + ); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize) @@ -446,15 +482,21 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac } function delegateToViewAndReturn() private view returns (bytes memory) { - (bool success, ) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", msg.data)); + (bool success, ) = address(this).staticcall( + abi.encodeWithSignature("delegateToImplementation(bytes)", msg.data) + ); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize) switch success - case 0 { revert(free_mem_ptr, returndatasize) } - default { return(add(free_mem_ptr, 0x40), returndatasize) } + case 0 { + revert(free_mem_ptr, returndatasize) + } + default { + return(add(free_mem_ptr, 0x40), returndatasize) + } } } @@ -466,8 +508,12 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac returndatacopy(free_mem_ptr, 0, returndatasize) switch success - case 0 { revert(free_mem_ptr, returndatasize) } - default { return(free_mem_ptr, returndatasize) } + case 0 { + revert(free_mem_ptr, returndatasize) + } + default { + return(free_mem_ptr, returndatasize) + } } } @@ -475,8 +521,8 @@ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterfac * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts */ - function () external payable { - require(msg.value == 0,"CErc20Delegator:fallback: cannot send value to fallback"); + function() external payable { + require(msg.value == 0, "CErc20Delegator:fallback: cannot send value to fallback"); // delegate all other functions to current implementation delegateAndReturn(); diff --git a/contracts/CErc20Immutable.sol b/contracts/CErc20Immutable.sol index 164f1bc..90eac6a 100644 --- a/contracts/CErc20Immutable.sol +++ b/contracts/CErc20Immutable.sol @@ -19,19 +19,29 @@ contract CErc20Immutable is CErc20 { * @param decimals_ ERC-20 decimal precision of this token * @param admin_ Address of the administrator of this token */ - constructor(address underlying_, - ComptrollerInterface comptroller_, - InterestRateModel interestRateModel_, - uint initialExchangeRateMantissa_, - string memory name_, - string memory symbol_, - uint8 decimals_, - address payable admin_) public { + constructor( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_, + address payable admin_ + ) public { // Creator of the contract is admin during initialization admin = msg.sender; // Initialize the market - initialize(underlying_, comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); + initialize( + underlying_, + comptroller_, + interestRateModel_, + initialExchangeRateMantissa_, + name_, + symbol_, + decimals_ + ); // Set the proper admin now that initialization is done admin = admin_; diff --git a/contracts/CEther.sol b/contracts/CEther.sol index e7bd14e..fe59096 100644 --- a/contracts/CEther.sol +++ b/contracts/CEther.sol @@ -1,13 +1,13 @@ pragma solidity ^0.5.16; -import "./CToken.sol"; +import "./CTokenDeprecated.sol"; /** * @title Compound's CEther Contract * @notice CToken which wraps Ether * @author Compound */ -contract CEther is CToken { +contract CEther is CTokenDeprecated { /** * @notice Construct a new CEther money market * @param comptroller_ The address of the Comptroller @@ -18,13 +18,15 @@ contract CEther is CToken { * @param decimals_ ERC-20 decimal precision of this token * @param admin_ Address of the administrator of this token */ - constructor(ComptrollerInterface comptroller_, - InterestRateModel interestRateModel_, - uint initialExchangeRateMantissa_, - string memory name_, - string memory symbol_, - uint8 decimals_, - address payable admin_) public { + constructor( + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_, + address payable admin_ + ) public { // Creator of the contract is admin during initialization admin = msg.sender; @@ -34,7 +36,6 @@ contract CEther is CToken { admin = admin_; } - /*** User Interface ***/ /** @@ -42,7 +43,7 @@ contract CEther is CToken { * @dev Reverts upon any failure */ function mint() external payable { - (uint err,) = mintInternal(msg.value); + (uint256 err, ) = mintInternal(msg.value); requireNoError(err, "mint failed"); } @@ -52,7 +53,7 @@ contract CEther is CToken { * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeem(uint redeemTokens) external returns (uint) { + function redeem(uint256 redeemTokens) external returns (uint256) { return redeemInternal(redeemTokens); } @@ -62,16 +63,16 @@ contract CEther is CToken { * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeemUnderlying(uint redeemAmount) external returns (uint) { + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { return redeemUnderlyingInternal(redeemAmount); } /** - * @notice Sender borrows assets from the protocol to their own address - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function borrow(uint borrowAmount) external returns (uint) { + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { return borrowInternal(borrowAmount); } @@ -80,7 +81,7 @@ contract CEther is CToken { * @dev Reverts upon any failure */ function repayBorrow() external payable { - (uint err,) = repayBorrowInternal(msg.value); + (uint256 err, ) = repayBorrowInternal(msg.value); requireNoError(err, "repayBorrow failed"); } @@ -90,7 +91,7 @@ contract CEther is CToken { * @param borrower the account with the debt being payed off */ function repayBorrowBehalf(address borrower) external payable { - (uint err,) = repayBorrowBehalfInternal(borrower, msg.value); + (uint256 err, ) = repayBorrowBehalfInternal(borrower, msg.value); requireNoError(err, "repayBorrowBehalf failed"); } @@ -101,16 +102,16 @@ contract CEther is CToken { * @param borrower The borrower of this cToken to be liquidated * @param cTokenCollateral The market in which to seize collateral from the borrower */ - function liquidateBorrow(address borrower, CToken cTokenCollateral) external payable { - (uint err,) = liquidateBorrowInternal(borrower, msg.value, cTokenCollateral); + function liquidateBorrow(address borrower, CTokenDeprecated cTokenCollateral) external payable { + (uint256 err, ) = liquidateBorrowInternal(borrower, msg.value, cTokenCollateral); requireNoError(err, "liquidateBorrow failed"); } /** * @notice Send Ether to CEther to mint */ - function () external payable { - (uint err,) = mintInternal(msg.value); + function() external payable { + (uint256 err, ) = mintInternal(msg.value); requireNoError(err, "mint failed"); } @@ -121,8 +122,8 @@ contract CEther is CToken { * @dev This excludes the value of the current message, if any * @return The quantity of Ether owned by this contract */ - function getCashPrior() internal view returns (uint) { - (MathError err, uint startingBalance) = subUInt(address(this).balance, msg.value); + function getCashPrior() internal view returns (uint256) { + (MathError err, uint256 startingBalance) = subUInt(address(this).balance, msg.value); require(err == MathError.NO_ERROR); return startingBalance; } @@ -133,36 +134,36 @@ contract CEther is CToken { * @param amount Amount of Ether being sent * @return The actual amount of Ether transferred */ - function doTransferIn(address from, uint amount) internal returns (uint) { + function doTransferIn(address from, uint256 amount) internal returns (uint256) { // Sanity checks require(msg.sender == from, "sender mismatch"); require(msg.value == amount, "value mismatch"); return amount; } - function doTransferOut(address payable to, uint amount) internal { + function doTransferOut(address payable to, uint256 amount) internal { /* Send the Ether, with minimal gas and revert on failure */ to.transfer(amount); } - function requireNoError(uint errCode, string memory message) internal pure { - if (errCode == uint(Error.NO_ERROR)) { + function requireNoError(uint256 errCode, string memory message) internal pure { + if (errCode == uint256(Error.NO_ERROR)) { return; } bytes memory fullMessage = new bytes(bytes(message).length + 5); - uint i; + uint256 i; for (i = 0; i < bytes(message).length; i++) { fullMessage[i] = bytes(message)[i]; } - fullMessage[i+0] = byte(uint8(32)); - fullMessage[i+1] = byte(uint8(40)); - fullMessage[i+2] = byte(uint8(48 + ( errCode / 10 ))); - fullMessage[i+3] = byte(uint8(48 + ( errCode % 10 ))); - fullMessage[i+4] = byte(uint8(41)); + fullMessage[i + 0] = bytes1(uint8(32)); + fullMessage[i + 1] = bytes1(uint8(40)); + fullMessage[i + 2] = bytes1(uint8(48 + (errCode / 10))); + fullMessage[i + 3] = bytes1(uint8(48 + (errCode % 10))); + fullMessage[i + 4] = bytes1(uint8(41)); - require(errCode == uint(Error.NO_ERROR), string(fullMessage)); + require(errCode == uint256(Error.NO_ERROR), string(fullMessage)); } } diff --git a/contracts/CSLPDelegate.sol b/contracts/CSLPDelegate.sol index 74b20a0..a6f4477 100644 --- a/contracts/CSLPDelegate.sol +++ b/contracts/CSLPDelegate.sol @@ -15,16 +15,22 @@ interface IMasterChef { } function deposit(uint256, uint256) external; + function withdraw(uint256, uint256) external; - function sushi() view external returns (address); - function poolInfo(uint256) view external returns (PoolInfo memory); - function userInfo(uint256, address) view external returns (UserInfo memory); + + function sushi() external view returns (address); + + function poolInfo(uint256) external view returns (PoolInfo memory); + + function userInfo(uint256, address) external view returns (UserInfo memory); + function pendingSushi(uint256, address) external view returns (uint256); } // Ref: https://etherscan.io/address/0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272#code interface ISushiBar { function enter(uint256 _amount) external; + function leave(uint256 _share) external; } @@ -52,7 +58,7 @@ contract CSLPDelegate is CCapableErc20Delegate { /** * @notice Pool ID of this LP in MasterChef */ - uint public pid; + uint256 public pid; /** * @notice Container for sushi rewards state @@ -60,8 +66,8 @@ contract CSLPDelegate is CCapableErc20Delegate { * @member index The last updated index */ struct SushiRewardState { - uint balance; - uint index; + uint256 balance; + uint256 index; } /** @@ -72,12 +78,12 @@ contract CSLPDelegate is CCapableErc20Delegate { /** * @notice The index of every SLP supplier */ - mapping(address => uint) public slpSupplierIndex; + mapping(address => uint256) public slpSupplierIndex; /** * @notice The xSushi amount of every user */ - mapping(address => uint) public xSushiUserAccrued; + mapping(address => uint256) public xSushiUserAccrued; /** * @notice Delegate interface to become the implementation @@ -86,7 +92,10 @@ contract CSLPDelegate is CCapableErc20Delegate { function _becomeImplementation(bytes memory data) public { super._becomeImplementation(data); - (address masterChefAddress_, address sushiBarAddress_, uint pid_) = abi.decode(data, (address, address, uint)); + (address masterChefAddress_, address sushiBarAddress_, uint256 pid_) = abi.decode( + data, + (address, address, uint256) + ); masterChef = masterChefAddress_; sushiBar = sushiBarAddress_; sushi = IMasterChef(masterChef).sushi(); @@ -96,30 +105,30 @@ contract CSLPDelegate is CCapableErc20Delegate { pid = pid_; // Approve moving our SLP into the master chef contract. - EIP20Interface(underlying).approve(masterChefAddress_, uint(-1)); + EIP20Interface(underlying).approve(masterChefAddress_, uint256(-1)); // Approve moving sushi rewards into the sushi bar contract. - EIP20Interface(sushi).approve(sushiBarAddress_, uint(-1)); + EIP20Interface(sushi).approve(sushiBarAddress_, uint256(-1)); } /** * @notice Manually claim sushi rewards by user * @return The amount of sushi rewards user claims */ - function claimSushi(address account) public returns (uint) { + function claimSushi(address account) public returns (uint256) { claimAndStakeSushi(); updateSLPSupplyIndex(); updateSupplierIndex(account); // Get user's xSushi accrued. - uint xSushiBalance = xSushiUserAccrued[account]; + uint256 xSushiBalance = xSushiUserAccrued[account]; if (xSushiBalance > 0) { // Withdraw user xSushi balance and subtract the amount in slpSupplyState ISushiBar(sushiBar).leave(xSushiBalance); slpSupplyState.balance = sub_(slpSupplyState.balance, xSushiBalance); - uint balance = sushiBalance(); + uint256 balance = sushiBalance(); EIP20Interface(sushi).transfer(account, balance); // Clear user's xSushi accrued. @@ -140,7 +149,12 @@ contract CSLPDelegate is CCapableErc20Delegate { * @param tokens The number of tokens to transfer * @return Whether or not the transfer succeeded */ - function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) { + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256) { claimAndStakeSushi(); updateSLPSupplyIndex(); @@ -156,7 +170,7 @@ contract CSLPDelegate is CCapableErc20Delegate { * @notice Gets balance of this contract in terms of the underlying * @return The quantity of underlying tokens owned by this contract */ - function getCashPrior() internal view returns (uint) { + function getCashPrior() internal view returns (uint256) { IMasterChef.UserInfo memory userInfo = IMasterChef(masterChef).userInfo(pid, address(this)); return userInfo.amount; } @@ -165,9 +179,16 @@ contract CSLPDelegate is CCapableErc20Delegate { * @notice Transfer the underlying to this contract and sweep into master chef * @param from Address to transfer funds from * @param amount Amount of underlying to transfer + * @param isNative The amount is in native or not * @return The actual amount that is transferred */ - function doTransferIn(address from, uint amount) internal returns (uint) { + function doTransferIn( + address from, + uint256 amount, + bool isNative + ) internal returns (uint256) { + isNative; // unused + // Perform the EIP-20 transfer in EIP20Interface token = EIP20Interface(underlying); require(token.transferFrom(from, address(this), amount), "unexpected EIP-20 transfer in return"); @@ -190,8 +211,15 @@ contract CSLPDelegate is CCapableErc20Delegate { * @notice Transfer the underlying from this contract, after sweeping out of master chef * @param to Address to transfer funds to * @param amount Amount of underlying to transfer + * @param isNative The amount is in native or not */ - function doTransferOut(address payable to, uint amount) internal { + function doTransferOut( + address payable to, + uint256 amount, + bool isNative + ) internal { + isNative; // unused + // Withdraw the underlying tokens from masterChef. IMasterChef(masterChef).withdraw(pid, amount); @@ -220,9 +248,9 @@ contract CSLPDelegate is CCapableErc20Delegate { } function updateSLPSupplyIndex() internal { - uint xSushiBalance = xSushiBalance(); - uint xSushiAccrued = sub_(xSushiBalance, slpSupplyState.balance); - uint supplyTokens = CToken(address(this)).totalSupply(); + uint256 xSushiBalance = xSushiBalance(); + uint256 xSushiAccrued = sub_(xSushiBalance, slpSupplyState.balance); + uint256 supplyTokens = CToken(address(this)).totalSupply(); Double memory ratio = supplyTokens > 0 ? fraction(xSushiAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: slpSupplyState.index}), ratio); @@ -236,18 +264,18 @@ contract CSLPDelegate is CCapableErc20Delegate { Double memory supplierIndex = Double({mantissa: slpSupplierIndex[supplier]}); Double memory deltaIndex = sub_(supplyIndex, supplierIndex); if (deltaIndex.mantissa > 0) { - uint supplierTokens = CToken(address(this)).balanceOf(supplier); - uint supplierDelta = mul_(supplierTokens, deltaIndex); + uint256 supplierTokens = CToken(address(this)).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); xSushiUserAccrued[supplier] = add_(xSushiUserAccrued[supplier], supplierDelta); slpSupplierIndex[supplier] = supplyIndex.mantissa; } } - function sushiBalance() internal view returns (uint) { + function sushiBalance() internal view returns (uint256) { return EIP20Interface(sushi).balanceOf(address(this)); } - function xSushiBalance() internal view returns (uint) { + function xSushiBalance() internal view returns (uint256) { return EIP20Interface(sushiBar).balanceOf(address(this)); } } diff --git a/contracts/CToken.sol b/contracts/CToken.sol index ee9a4f6..41152aa 100644 --- a/contracts/CToken.sol +++ b/contracts/CToken.sol @@ -23,12 +23,14 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param symbol_ EIP-20 symbol of this token * @param decimals_ EIP-20 decimal precision of this token */ - function initialize(ComptrollerInterface comptroller_, - InterestRateModel interestRateModel_, - uint initialExchangeRateMantissa_, - string memory name_, - string memory symbol_, - uint8 decimals_) public { + function initialize( + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_ + ) public { require(msg.sender == admin, "only admin may initialize the market"); require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once"); @@ -37,8 +39,8 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero."); // Set the comptroller - uint err = _setComptroller(comptroller_); - require(err == uint(Error.NO_ERROR), "setting comptroller failed"); + uint256 err = _setComptroller(comptroller_); + require(err == uint256(Error.NO_ERROR), "setting comptroller failed"); // Initialize block number and borrow index (block number mocks depend on comptroller being set) accrualBlockNumber = getBlockNumber(); @@ -46,7 +48,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { // Set the interest rate model (depends on block number / borrow index) err = _setInterestRateModelFresh(interestRateModel_); - require(err == uint(Error.NO_ERROR), "setting interest rate model failed"); + require(err == uint256(Error.NO_ERROR), "setting interest rate model failed"); name = name_; symbol = symbol_; @@ -56,60 +58,6 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { _notEntered = true; } - /** - * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` - * @dev Called by both `transfer` and `transferFrom` internally - * @param spender The address of the account performing the transfer - * @param src The address of the source account - * @param dst The address of the destination account - * @param tokens The number of tokens to transfer - * @return Whether or not the transfer succeeded - */ - function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) { - /* Fail if transfer not allowed */ - uint allowed = comptroller.transferAllowed(address(this), src, dst, tokens); - if (allowed != 0) { - return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed); - } - - /* Do not allow self-transfers */ - if (src == dst) { - return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED); - } - - /* Get the allowance, infinite for the account owner */ - uint startingAllowance = 0; - if (spender == src) { - startingAllowance = uint(-1); - } else { - startingAllowance = transferAllowances[src][spender]; - } - - /* Do the calculations, checking for {under,over}flow */ - uint allowanceNew = sub_(startingAllowance, tokens); - uint srcTokensNew = sub_(accountTokens[src], tokens); - uint dstTokensNew = add_(accountTokens[dst], tokens); - - ///////////////////////// - // EFFECTS & INTERACTIONS - // (No safe failures beyond this point) - - accountTokens[src] = srcTokensNew; - accountTokens[dst] = dstTokensNew; - - /* Eat some of the allowance (if necessary) */ - if (startingAllowance != uint(-1)) { - transferAllowances[src][spender] = allowanceNew; - } - - /* We emit a Transfer event */ - emit Transfer(src, dst, tokens); - - comptroller.transferVerify(address(this), src, dst, tokens); - - return uint(Error.NO_ERROR); - } - /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account @@ -117,7 +65,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @return Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) external nonReentrant returns (bool) { - return transferTokens(msg.sender, msg.sender, dst, amount) == uint(Error.NO_ERROR); + return transferTokens(msg.sender, msg.sender, dst, amount) == uint256(Error.NO_ERROR); } /** @@ -127,8 +75,12 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ - function transferFrom(address src, address dst, uint256 amount) external nonReentrant returns (bool) { - return transferTokens(msg.sender, src, dst, amount) == uint(Error.NO_ERROR); + function transferFrom( + address src, + address dst, + uint256 amount + ) external nonReentrant returns (bool) { + return transferTokens(msg.sender, src, dst, amount) == uint256(Error.NO_ERROR); } /** @@ -171,7 +123,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ - function balanceOfUnderlying(address owner) external returns (uint) { + function balanceOfUnderlying(address owner) external returns (uint256) { Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()}); return mul_ScalarTruncate(exchangeRate, accountTokens[owner]); } @@ -182,19 +134,28 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ - function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) { - uint cTokenBalance = accountTokens[account]; - uint borrowBalance = borrowBalanceStoredInternal(account); - uint exchangeRateMantissa = exchangeRateStoredInternal(); - - return (uint(Error.NO_ERROR), cTokenBalance, borrowBalance, exchangeRateMantissa); + function getAccountSnapshot(address account) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ) + { + uint256 cTokenBalance = getCTokenBalanceInternal(account); + uint256 borrowBalance = borrowBalanceStoredInternal(account); + uint256 exchangeRateMantissa = exchangeRateStoredInternal(); + + return (uint256(Error.NO_ERROR), cTokenBalance, borrowBalance, exchangeRateMantissa); } /** * @dev Function to simply retrieve block number * This exists mainly for inheriting test contracts to stub this result. */ - function getBlockNumber() internal view returns (uint) { + function getBlockNumber() internal view returns (uint256) { return block.number; } @@ -202,7 +163,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @notice Returns the current per-block borrow interest rate for this cToken * @return The borrow interest rate per block, scaled by 1e18 */ - function borrowRatePerBlock() external view returns (uint) { + function borrowRatePerBlock() external view returns (uint256) { return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves); } @@ -210,16 +171,53 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @notice Returns the current per-block supply interest rate for this cToken * @return The supply interest rate per block, scaled by 1e18 */ - function supplyRatePerBlock() external view returns (uint) { + function supplyRatePerBlock() external view returns (uint256) { return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa); } + /** + * @notice Returns the estimated per-block borrow interest rate for this cToken after some change + * @return The borrow interest rate per block, scaled by 1e18 + */ + function estimateBorrowRatePerBlockAfterChange(uint256 change, bool repay) external view returns (uint256) { + uint256 cashPriorNew; + uint256 totalBorrowsNew; + + if (repay) { + cashPriorNew = add_(getCashPrior(), change); + totalBorrowsNew = sub_(totalBorrows, change); + } else { + cashPriorNew = sub_(getCashPrior(), change); + totalBorrowsNew = add_(totalBorrows, change); + } + return interestRateModel.getBorrowRate(cashPriorNew, totalBorrowsNew, totalReserves); + } + + /** + * @notice Returns the estimated per-block supply interest rate for this cToken after some change + * @return The supply interest rate per block, scaled by 1e18 + */ + function estimateSupplyRatePerBlockAfterChange(uint256 change, bool repay) external view returns (uint256) { + uint256 cashPriorNew; + uint256 totalBorrowsNew; + + if (repay) { + cashPriorNew = add_(getCashPrior(), change); + totalBorrowsNew = sub_(totalBorrows, change); + } else { + cashPriorNew = sub_(getCashPrior(), change); + totalBorrowsNew = add_(totalBorrows, change); + } + + return interestRateModel.getSupplyRate(cashPriorNew, totalBorrowsNew, totalReserves, reserveFactorMantissa); + } + /** * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ - function totalBorrowsCurrent() external nonReentrant returns (uint) { - require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed"); + function totalBorrowsCurrent() external nonReentrant returns (uint256) { + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); return totalBorrows; } @@ -228,8 +226,8 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ - function borrowBalanceCurrent(address account) external nonReentrant returns (uint) { - require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed"); + function borrowBalanceCurrent(address account) external nonReentrant returns (uint256) { + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); return borrowBalanceStored(account); } @@ -238,7 +236,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param account The address whose balance should be calculated * @return The calculated balance */ - function borrowBalanceStored(address account) public view returns (uint) { + function borrowBalanceStored(address account) public view returns (uint256) { return borrowBalanceStoredInternal(account); } @@ -247,7 +245,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param account The address whose balance should be calculated * @return the calculated balance or 0 if error code is non-zero */ - function borrowBalanceStoredInternal(address account) internal view returns (uint) { + function borrowBalanceStoredInternal(address account) internal view returns (uint256) { /* Get borrowBalance and borrowIndex */ BorrowSnapshot storage borrowSnapshot = accountBorrows[account]; @@ -261,8 +259,8 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { /* Calculate new borrow balance using the interest index: * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex */ - uint principalTimesIndex = mul_(borrowSnapshot.principal, borrowIndex); - uint result = div_(principalTimesIndex, borrowSnapshot.interestIndex); + uint256 principalTimesIndex = mul_(borrowSnapshot.principal, borrowIndex); + uint256 result = div_(principalTimesIndex, borrowSnapshot.interestIndex); return result; } @@ -270,8 +268,8 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ - function exchangeRateCurrent() public nonReentrant returns (uint) { - require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed"); + function exchangeRateCurrent() public nonReentrant returns (uint256) { + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); return exchangeRateStored(); } @@ -280,7 +278,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ - function exchangeRateStored() public view returns (uint) { + function exchangeRateStored() public view returns (uint256) { return exchangeRateStoredInternal(); } @@ -289,8 +287,8 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @dev This function does not accrue interest before calculating the exchange rate * @return calculated exchange rate scaled by 1e18 */ - function exchangeRateStoredInternal() internal view returns (uint) { - uint _totalSupply = totalSupply; + function exchangeRateStoredInternal() internal view returns (uint256) { + uint256 _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: @@ -302,9 +300,9 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ - uint totalCash = getCashPrior(); - uint cashPlusBorrowsMinusReserves = sub_(add_(totalCash, totalBorrows), totalReserves); - uint exchangeRate = div_(cashPlusBorrowsMinusReserves, Exp({mantissa: _totalSupply})); + uint256 totalCash = getCashPrior(); + uint256 cashPlusBorrowsMinusReserves = sub_(add_(totalCash, totalBorrows), totalReserves); + uint256 exchangeRate = div_(cashPlusBorrowsMinusReserves, Exp({mantissa: _totalSupply})); return exchangeRate; } } @@ -313,7 +311,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @notice Get cash balance of this cToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ - function getCash() external view returns (uint) { + function getCash() external view returns (uint256) { return getCashPrior(); } @@ -322,28 +320,28 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @dev This calculates interest accrued from the last checkpointed block * up to the current block and writes new checkpoint to storage. */ - function accrueInterest() public returns (uint) { + function accrueInterest() public returns (uint256) { /* Remember the initial block number */ - uint currentBlockNumber = getBlockNumber(); - uint accrualBlockNumberPrior = accrualBlockNumber; + uint256 currentBlockNumber = getBlockNumber(); + uint256 accrualBlockNumberPrior = accrualBlockNumber; /* Short-circuit accumulating 0 interest */ if (accrualBlockNumberPrior == currentBlockNumber) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Read the previous values out of storage */ - uint cashPrior = getCashPrior(); - uint borrowsPrior = totalBorrows; - uint reservesPrior = totalReserves; - uint borrowIndexPrior = borrowIndex; + uint256 cashPrior = getCashPrior(); + uint256 borrowsPrior = totalBorrows; + uint256 reservesPrior = totalReserves; + uint256 borrowIndexPrior = borrowIndex; /* Calculate the current borrow interest rate */ - uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); + uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high"); /* Calculate the number of blocks elapsed since the last accrual */ - uint blockDelta = sub_(currentBlockNumber, accrualBlockNumberPrior); + uint256 blockDelta = sub_(currentBlockNumber, accrualBlockNumberPrior); /* * Calculate the interest accumulated into borrows and reserves and the new index: @@ -355,10 +353,14 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { */ Exp memory simpleInterestFactor = mul_(Exp({mantissa: borrowRateMantissa}), blockDelta); - uint interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior); - uint totalBorrowsNew = add_(interestAccumulated, borrowsPrior); - uint totalReservesNew = mul_ScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior); - uint borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); + uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior); + uint256 totalBorrowsNew = add_(interestAccumulated, borrowsPrior); + uint256 totalReservesNew = mul_ScalarTruncateAddUInt( + Exp({mantissa: reserveFactorMantissa}), + interestAccumulated, + reservesPrior + ); + uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); ///////////////////////// // EFFECTS & INTERACTIONS @@ -373,262 +375,109 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { /* We emit an AccrueInterest event */ emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply + * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. */ - function mintInternal(uint mintAmount) internal nonReentrant returns (uint, uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function mintInternal(uint256 mintAmount, bool isNative) internal nonReentrant returns (uint256, uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0); } // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to - return mintFresh(msg.sender, mintAmount); - } - - struct MintLocalVars { - Error err; - MathError mathErr; - uint exchangeRateMantissa; - uint mintTokens; - uint totalSupplyNew; - uint accountTokensNew; - uint actualMintAmount; - } - - /** - * @notice User supplies assets into the market and receives cTokens in exchange - * @dev Assumes interest has already been accrued up to the current block - * @param minter The address of the account which is supplying the assets - * @param mintAmount The amount of the underlying asset to supply - * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. - */ - function mintFresh(address minter, uint mintAmount) internal returns (uint, uint) { - /* Fail if mint not allowed */ - uint allowed = comptroller.mintAllowed(address(this), minter, mintAmount); - if (allowed != 0) { - return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0); - } - - /* Verify market's block number equals current block number */ - if (accrualBlockNumber != getBlockNumber()) { - return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0); - } - - MintLocalVars memory vars; - - vars.exchangeRateMantissa = exchangeRateStoredInternal(); - - ///////////////////////// - // EFFECTS & INTERACTIONS - // (No safe failures beyond this point) - - /* - * We call `doTransferIn` for the minter and the mintAmount. - * Note: The cToken must handle variations between ERC-20 and ETH underlying. - * `doTransferIn` reverts if anything goes wrong, since we can't be sure if - * side-effects occurred. The function returns the amount actually transferred, - * in case of a fee. On success, the cToken holds an additional `actualMintAmount` - * of cash. - */ - vars.actualMintAmount = doTransferIn(minter, mintAmount); - - /* - * We get the current exchange rate and calculate the number of cTokens to be minted: - * mintTokens = actualMintAmount / exchangeRate - */ - - vars.mintTokens = div_ScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa})); - - /* - * We calculate the new total supply of cTokens and minter token balance, checking for overflow: - * totalSupplyNew = totalSupply + mintTokens - * accountTokensNew = accountTokens[minter] + mintTokens - */ - vars.totalSupplyNew = add_(totalSupply, vars.mintTokens); - vars.accountTokensNew = add_(accountTokens[minter], vars.mintTokens); - - /* We write previously calculated values into storage */ - totalSupply = vars.totalSupplyNew; - accountTokens[minter] = vars.accountTokensNew; - - /* We emit a Mint event, and a Transfer event */ - emit Mint(minter, vars.actualMintAmount, vars.mintTokens); - emit Transfer(address(this), minter, vars.mintTokens); - - /* We call the defense hook */ - comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens); - - return (uint(Error.NO_ERROR), vars.actualMintAmount); + return mintFresh(msg.sender, mintAmount, isNative); } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying + * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeemInternal(uint redeemTokens) internal nonReentrant returns (uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function redeemInternal(uint256 redeemTokens, bool isNative) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); } // redeemFresh emits redeem-specific logs on errors, so we don't need to - return redeemFresh(msg.sender, redeemTokens, 0); + return redeemFresh(msg.sender, redeemTokens, 0, isNative); } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to receive from redeeming cTokens + * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant returns (uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function redeemUnderlyingInternal(uint256 redeemAmount, bool isNative) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); } // redeemFresh emits redeem-specific logs on errors, so we don't need to - return redeemFresh(msg.sender, 0, redeemAmount); - } - - struct RedeemLocalVars { - Error err; - MathError mathErr; - uint exchangeRateMantissa; - uint redeemTokens; - uint redeemAmount; - uint totalSupplyNew; - uint accountTokensNew; + return redeemFresh(msg.sender, 0, redeemAmount, isNative); } /** - * @notice User redeems cTokens in exchange for the underlying asset - * @dev Assumes interest has already been accrued up to the current block - * @param redeemer The address of the account which is redeeming the tokens - * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero) - * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal returns (uint) { - require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); - - RedeemLocalVars memory vars; - - /* exchangeRate = invoke Exchange Rate Stored() */ - vars.exchangeRateMantissa = exchangeRateStoredInternal(); - - /* If redeemTokensIn > 0: */ - if (redeemTokensIn > 0) { - /* - * We calculate the exchange rate and the amount of underlying to be redeemed: - * redeemTokens = redeemTokensIn - * redeemAmount = redeemTokensIn x exchangeRateCurrent - */ - vars.redeemTokens = redeemTokensIn; - vars.redeemAmount = mul_ScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn); - } else { - /* - * We get the current exchange rate and calculate the amount to be redeemed: - * redeemTokens = redeemAmountIn / exchangeRate - * redeemAmount = redeemAmountIn - */ - vars.redeemTokens = div_ScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa})); - vars.redeemAmount = redeemAmountIn; - } - - /* Fail if redeem not allowed */ - uint allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens); - if (allowed != 0) { - return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed); - } - - /* Verify market's block number equals current block number */ - if (accrualBlockNumber != getBlockNumber()) { - return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK); - } - - /* - * We calculate the new total supply and redeemer balance, checking for underflow: - * totalSupplyNew = totalSupply - redeemTokens - * accountTokensNew = accountTokens[redeemer] - redeemTokens - */ - vars.totalSupplyNew = sub_(totalSupply, vars.redeemTokens); - vars.accountTokensNew = sub_(accountTokens[redeemer], vars.redeemTokens); - - /* Fail gracefully if protocol has insufficient cash */ - if (getCashPrior() < vars.redeemAmount) { - return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE); - } - - ///////////////////////// - // EFFECTS & INTERACTIONS - // (No safe failures beyond this point) - - /* - * We invoke doTransferOut for the redeemer and the redeemAmount. - * Note: The cToken must handle variations between ERC-20 and ETH underlying. - * On success, the cToken has redeemAmount less of cash. - * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. - */ - doTransferOut(redeemer, vars.redeemAmount); - - /* We write previously calculated values into storage */ - totalSupply = vars.totalSupplyNew; - accountTokens[redeemer] = vars.accountTokensNew; - - /* We emit a Transfer event, and a Redeem event */ - emit Transfer(redeemer, address(this), vars.redeemTokens); - emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens); - - /* We call the defense hook */ - comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens); - - return uint(Error.NO_ERROR); - } - - /** - * @notice Sender borrows assets from the protocol to their own address - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function borrowInternal(uint borrowAmount) internal nonReentrant returns (uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function borrowInternal(uint256 borrowAmount, bool isNative) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED); } // borrowFresh emits borrow-specific logs on errors, so we don't need to - return borrowFresh(msg.sender, borrowAmount); + return borrowFresh(msg.sender, borrowAmount, isNative); } struct BorrowLocalVars { MathError mathErr; - uint accountBorrows; - uint accountBorrowsNew; - uint totalBorrowsNew; + uint256 accountBorrows; + uint256 accountBorrowsNew; + uint256 totalBorrowsNew; } /** - * @notice Users borrow assets from the protocol to their own address - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function borrowFresh(address payable borrower, uint borrowAmount) internal returns (uint) { + * @notice Users borrow assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @param isNative The amount is in native or not + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrowFresh( + address payable borrower, + uint256 borrowAmount, + bool isNative + ) internal returns (uint256) { /* Fail if borrow not allowed */ - uint allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount); + uint256 allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount); if (allowed != 0) { return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.BORROW_COMPTROLLER_REJECTION, allowed); } + /* + * Return if borrowAmount is zero. + * Put behind `borrowAllowed` for accuring potential COMP rewards. + */ + if (borrowAmount == 0) { + accountBorrows[borrower].interestIndex = borrowIndex; + return uint256(Error.NO_ERROR); + } + /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK); @@ -660,7 +509,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * On success, the cToken borrowAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ - doTransferOut(borrower, borrowAmount); + doTransferOut(borrower, borrowAmount, isNative); /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = vars.accountBorrowsNew; @@ -671,51 +520,58 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { emit Borrow(borrower, borrowAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); /* We call the defense hook */ - comptroller.borrowVerify(address(this), borrower, borrowAmount); + // unused function + // comptroller.borrowVerify(address(this), borrower, borrowAmount); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay + * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ - function repayBorrowInternal(uint repayAmount) internal nonReentrant returns (uint, uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function repayBorrowInternal(uint256 repayAmount, bool isNative) internal nonReentrant returns (uint256, uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0); } // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to - return repayBorrowFresh(msg.sender, msg.sender, repayAmount); + return repayBorrowFresh(msg.sender, msg.sender, repayAmount, isNative); } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay + * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ - function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant returns (uint, uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function repayBorrowBehalfInternal( + address borrower, + uint256 repayAmount, + bool isNative + ) internal nonReentrant returns (uint256, uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0); } // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to - return repayBorrowFresh(msg.sender, borrower, repayAmount); + return repayBorrowFresh(msg.sender, borrower, repayAmount, isNative); } struct RepayBorrowLocalVars { Error err; MathError mathErr; - uint repayAmount; - uint borrowerIndex; - uint accountBorrows; - uint accountBorrowsNew; - uint totalBorrowsNew; - uint actualRepayAmount; + uint256 repayAmount; + uint256 borrowerIndex; + uint256 accountBorrows; + uint256 accountBorrowsNew; + uint256 totalBorrowsNew; + uint256 actualRepayAmount; } /** @@ -723,13 +579,31 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param payer the account paying off the borrow * @param borrower the account with the debt being payed off * @param repayAmount the amount of undelrying tokens being returned + * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ - function repayBorrowFresh(address payer, address borrower, uint repayAmount) internal returns (uint, uint) { + function repayBorrowFresh( + address payer, + address borrower, + uint256 repayAmount, + bool isNative + ) internal returns (uint256, uint256) { /* Fail if repayBorrow not allowed */ - uint allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); + uint256 allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); if (allowed != 0) { - return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), 0); + return ( + failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), + 0 + ); + } + + /* + * Return if repayAmount is zero. + * Put behind `repayBorrowAllowed` for accuring potential COMP rewards. + */ + if (repayAmount == 0) { + accountBorrows[borrower].interestIndex = borrowIndex; + return (uint256(Error.NO_ERROR), 0); } /* Verify market's block number equals current block number */ @@ -746,7 +620,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { vars.accountBorrows = borrowBalanceStoredInternal(borrower); /* If repayAmount == -1, repayAmount = accountBorrows */ - if (repayAmount == uint(-1)) { + if (repayAmount == uint256(-1)) { vars.repayAmount = vars.accountBorrows; } else { vars.repayAmount = repayAmount; @@ -763,7 +637,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ - vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount); + vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount, isNative); /* * We calculate the new borrower and total borrow balances, failing on underflow: @@ -782,34 +656,41 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); /* We call the defense hook */ - comptroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex); + // unused function + // comptroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex); - return (uint(Error.NO_ERROR), vars.actualRepayAmount); + return (uint256(Error.NO_ERROR), vars.actualRepayAmount); } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated - * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ - function liquidateBorrowInternal(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal nonReentrant returns (uint, uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function liquidateBorrowInternal( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral, + bool isNative + ) internal nonReentrant returns (uint256, uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED), 0); } error = cTokenCollateral.accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0); } // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to - return liquidateBorrowFresh(msg.sender, borrower, repayAmount, cTokenCollateral); + return liquidateBorrowFresh(msg.sender, borrower, repayAmount, cTokenCollateral, isNative); } /** @@ -819,11 +700,24 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param liquidator The address repaying the borrow and seizing collateral * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay + * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ - function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal returns (uint, uint) { + function liquidateBorrowFresh( + address liquidator, + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral, + bool isNative + ) internal returns (uint256, uint256) { /* Fail if liquidate not allowed */ - uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(cTokenCollateral), liquidator, borrower, repayAmount); + uint256 allowed = comptroller.liquidateBorrowAllowed( + address(this), + address(cTokenCollateral), + liquidator, + borrower, + repayAmount + ); if (allowed != 0) { return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_COMPTROLLER_REJECTION, allowed), 0); } @@ -849,14 +743,18 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { } /* Fail if repayAmount = -1 */ - if (repayAmount == uint(-1)) { + if (repayAmount == uint256(-1)) { return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0); } - /* Fail if repayBorrow fails */ - (uint repayBorrowError, uint actualRepayAmount) = repayBorrowFresh(liquidator, borrower, repayAmount); - if (repayBorrowError != uint(Error.NO_ERROR)) { + (uint256 repayBorrowError, uint256 actualRepayAmount) = repayBorrowFresh( + liquidator, + borrower, + repayAmount, + isNative + ); + if (repayBorrowError != uint256(Error.NO_ERROR)) { return (fail(Error(repayBorrowError), FailureInfo.LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0); } @@ -865,14 +763,18 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { // (No safe failures beyond this point) /* We calculate the number of collateral tokens that will be seized */ - (uint amountSeizeError, uint seizeTokens) = comptroller.liquidateCalculateSeizeTokens(address(this), address(cTokenCollateral), actualRepayAmount); - require(amountSeizeError == uint(Error.NO_ERROR), "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"); + (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens( + address(this), + address(cTokenCollateral), + actualRepayAmount + ); + require(amountSeizeError == uint256(Error.NO_ERROR), "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"); /* Revert if borrower collateral token balance < seizeTokens */ require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH"); // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call - uint seizeError; + uint256 seizeError; if (address(cTokenCollateral) == address(this)) { seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens); } else { @@ -880,15 +782,16 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { } /* Revert if seize tokens fails (since we cannot be sure of side effects) */ - require(seizeError == uint(Error.NO_ERROR), "token seizure failed"); + require(seizeError == uint256(Error.NO_ERROR), "token seizure failed"); /* We emit a LiquidateBorrow event */ emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(cTokenCollateral), seizeTokens); /* We call the defense hook */ - comptroller.liquidateBorrowVerify(address(this), address(cTokenCollateral), liquidator, borrower, actualRepayAmount, seizeTokens); + // unused function + // comptroller.liquidateBorrowVerify(address(this), address(cTokenCollateral), liquidator, borrower, actualRepayAmount, seizeTokens); - return (uint(Error.NO_ERROR), actualRepayAmount); + return (uint256(Error.NO_ERROR), actualRepayAmount); } /** @@ -900,67 +803,23 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function seize(address liquidator, address borrower, uint seizeTokens) external nonReentrant returns (uint) { + function seize( + address liquidator, + address borrower, + uint256 seizeTokens + ) external nonReentrant returns (uint256) { return seizeInternal(msg.sender, liquidator, borrower, seizeTokens); } - /** - * @notice Transfers collateral tokens (this market) to the liquidator. - * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. - * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. - * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) - * @param liquidator The account receiving seized collateral - * @param borrower The account having collateral seized - * @param seizeTokens The number of cTokens to seize - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal returns (uint) { - /* Fail if seize not allowed */ - uint allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); - if (allowed != 0) { - return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed); - } - - /* Fail if borrower = liquidator */ - if (borrower == liquidator) { - return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER); - } - - /* - * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: - * borrowerTokensNew = accountTokens[borrower] - seizeTokens - * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens - */ - uint borrowerTokensNew = sub_(accountTokens[borrower], seizeTokens); - uint liquidatorTokensNew = add_(accountTokens[liquidator], seizeTokens); - - ///////////////////////// - // EFFECTS & INTERACTIONS - // (No safe failures beyond this point) - - /* We write the previously calculated values into storage */ - accountTokens[borrower] = borrowerTokensNew; - accountTokens[liquidator] = liquidatorTokensNew; - - /* Emit a Transfer event */ - emit Transfer(borrower, liquidator, seizeTokens); - - /* We call the defense hook */ - comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens); - - return uint(Error.NO_ERROR); - } - - /*** Admin Functions ***/ /** - * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @param newPendingAdmin New pending admin. - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) { + * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @param newPendingAdmin New pending admin. + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256) { // Check caller = admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); @@ -975,15 +834,15 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin - * @dev Admin function for pending admin to accept role and update admin - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _acceptAdmin() external returns (uint) { + * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + * @dev Admin function for pending admin to accept role and update admin + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _acceptAdmin() external returns (uint256) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin || msg.sender == address(0)) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); @@ -1002,15 +861,15 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets a new comptroller for the market - * @dev Admin function to set a new comptroller - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) { + * @notice Sets a new comptroller for the market + * @dev Admin function to set a new comptroller + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setComptroller(ComptrollerInterface newComptroller) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK); @@ -1026,17 +885,17 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { // Emit NewComptroller(oldComptroller, newComptroller) emit NewComptroller(oldComptroller, newComptroller); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh - * @dev Admin function to accrue interest and set a new reserve factor - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setReserveFactor(uint newReserveFactorMantissa) external nonReentrant returns (uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh + * @dev Admin function to accrue interest and set a new reserve factor + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setReserveFactor(uint256 newReserveFactorMantissa) external nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed. return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED); } @@ -1045,11 +904,11 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { } /** - * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) - * @dev Admin function to set a new reserve factor - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) { + * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) + * @dev Admin function to set a new reserve factor + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK); @@ -1065,28 +924,29 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK); } - uint oldReserveFactorMantissa = reserveFactorMantissa; + uint256 oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** * @notice Accrues interest and reduces reserves by transferring from msg.sender * @param addAmount Amount of addition to reserves + * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _addReservesInternal(uint addAmount) internal nonReentrant returns (uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function _addReservesInternal(uint256 addAmount, bool isNative) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED); } // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to. - (error, ) = _addReservesFresh(addAmount); + (error, ) = _addReservesFresh(addAmount, isNative); return error; } @@ -1094,12 +954,13 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @notice Add reserves by transferring from caller * @dev Requires fresh interest accrual * @param addAmount Amount of addition to reserves + * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees */ - function _addReservesFresh(uint addAmount) internal returns (uint, uint) { + function _addReservesFresh(uint256 addAmount, bool isNative) internal returns (uint256, uint256) { // totalReserves + actualAddAmount - uint totalReservesNew; - uint actualAddAmount; + uint256 totalReservesNew; + uint256 actualAddAmount; // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { @@ -1118,7 +979,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * it returns the amount actually transferred, in case of a fee. */ - actualAddAmount = doTransferIn(msg.sender, addAmount); + actualAddAmount = doTransferIn(msg.sender, addAmount, isNative); totalReservesNew = add_(totalReserves, actualAddAmount); @@ -1129,18 +990,17 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew); /* Return (NO_ERROR, actualAddAmount) */ - return (uint(Error.NO_ERROR), actualAddAmount); + return (uint256(Error.NO_ERROR), actualAddAmount); } - /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _reduceReserves(uint reduceAmount) external nonReentrant returns (uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function _reduceReserves(uint256 reduceAmount) external nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. return fail(Error(error), FailureInfo.REDUCE_RESERVES_ACCRUE_INTEREST_FAILED); } @@ -1154,9 +1014,9 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _reduceReservesFresh(uint reduceAmount) internal returns (uint) { + function _reduceReservesFresh(uint256 reduceAmount) internal returns (uint256) { // totalReserves - reduceAmount - uint totalReservesNew; + uint256 totalReservesNew; // Check caller is admin if (msg.sender != admin) { @@ -1188,11 +1048,12 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { totalReserves = totalReservesNew; // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. - doTransferOut(admin, reduceAmount); + // Restrict reducing reserves in native token. Implementations except `CWrappedNative` won't use parameter `isNative`. + doTransferOut(admin, reduceAmount, true); emit ReservesReduced(admin, reduceAmount, totalReservesNew); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -1201,9 +1062,9 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) { - uint error = accrueInterest(); - if (error != uint(Error.NO_ERROR)) { + function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted change of interest rate model failed return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED); } @@ -1217,8 +1078,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) { - + function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint256) { // Used to store old model for use in the event that is emitted on success InterestRateModel oldInterestRateModel; @@ -1244,7 +1104,7 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel) emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /*** Safe Token ***/ @@ -1254,21 +1114,77 @@ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { * @dev This excludes the value of the current message, if any * @return The quantity of underlying owned by this contract */ - function getCashPrior() internal view returns (uint); + function getCashPrior() internal view returns (uint256); /** * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee. * This may revert due to insufficient balance or insufficient allowance. */ - function doTransferIn(address from, uint amount) internal returns (uint); + function doTransferIn( + address from, + uint256 amount, + bool isNative + ) internal returns (uint256); /** * @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting. * If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract. * If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions. */ - function doTransferOut(address payable to, uint amount) internal; + function doTransferOut( + address payable to, + uint256 amount, + bool isNative + ) internal; + + /** + * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` + * @dev Called by both `transfer` and `transferFrom` internally + */ + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256); + + /** + * @notice Get the account's cToken balances + */ + function getCTokenBalanceInternal(address account) internal view returns (uint256); + + /** + * @notice User supplies assets into the market and receives cTokens in exchange + * @dev Assumes interest has already been accrued up to the current block + */ + function mintFresh( + address minter, + uint256 mintAmount, + bool isNative + ) internal returns (uint256, uint256); + /** + * @notice User redeems cTokens in exchange for the underlying asset + * @dev Assumes interest has already been accrued up to the current block + */ + function redeemFresh( + address payable redeemer, + uint256 redeemTokensIn, + uint256 redeemAmountIn, + bool isNative + ) internal returns (uint256); + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. + * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. + */ + function seizeInternal( + address seizerToken, + address liquidator, + address borrower, + uint256 seizeTokens + ) internal returns (uint256); /*** Reentrancy Guard ***/ diff --git a/contracts/CTokenAdmin.sol b/contracts/CTokenAdmin.sol new file mode 100644 index 0000000..785a971 --- /dev/null +++ b/contracts/CTokenAdmin.sol @@ -0,0 +1,233 @@ +pragma solidity ^0.5.16; + +import "./CErc20.sol"; +import "./CToken.sol"; +import "./EIP20NonStandardInterface.sol"; + +contract CTokenAdmin { + address public constant ethAddress = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + /// @notice Admin address + address payable public admin; + + /// @notice Reserve manager address + address payable public reserveManager; + + /// @notice Emits when a new admin is assigned + event SetAdmin(address indexed oldAdmin, address indexed newAdmin); + + /// @notice Emits when a new reserve manager is assigned + event SetReserveManager(address indexed oldReserveManager, address indexed newAdmin); + + /** + * @dev Throws if called by any account other than the admin. + */ + modifier onlyAdmin() { + require(msg.sender == admin, "only the admin may call this function"); + _; + } + + /** + * @dev Throws if called by any account other than the reserve manager. + */ + modifier onlyReserveManager() { + require(msg.sender == reserveManager, "only the reserve manager may call this function"); + _; + } + + constructor(address payable _admin) public { + _setAdmin(_admin); + } + + /** + * @notice Get cToken admin + * @param cToken The cToken address + */ + function getCTokenAdmin(address cToken) public view returns (address) { + return CToken(cToken).admin(); + } + + /** + * @notice Set cToken pending admin + * @param cToken The cToken address + * @param newPendingAdmin The new pending admin + */ + function _setPendingAdmin(address cToken, address payable newPendingAdmin) external onlyAdmin returns (uint256) { + return CTokenInterface(cToken)._setPendingAdmin(newPendingAdmin); + } + + /** + * @notice Accept cToken admin + * @param cToken The cToken address + */ + function _acceptAdmin(address cToken) external onlyAdmin returns (uint256) { + return CTokenInterface(cToken)._acceptAdmin(); + } + + /** + * @notice Set cToken comptroller + * @param cToken The cToken address + * @param newComptroller The new comptroller address + */ + function _setComptroller(address cToken, ComptrollerInterface newComptroller) external onlyAdmin returns (uint256) { + return CTokenInterface(cToken)._setComptroller(newComptroller); + } + + /** + * @notice Set cToken reserve factor + * @param cToken The cToken address + * @param newReserveFactorMantissa The new reserve factor + */ + function _setReserveFactor(address cToken, uint256 newReserveFactorMantissa) external onlyAdmin returns (uint256) { + return CTokenInterface(cToken)._setReserveFactor(newReserveFactorMantissa); + } + + /** + * @notice Reduce cToken reserve + * @param cToken The cToken address + * @param reduceAmount The amount of reduction + */ + function _reduceReserves(address cToken, uint256 reduceAmount) external onlyAdmin returns (uint256) { + return CTokenInterface(cToken)._reduceReserves(reduceAmount); + } + + /** + * @notice Set cToken IRM + * @param cToken The cToken address + * @param newInterestRateModel The new IRM address + */ + function _setInterestRateModel(address cToken, InterestRateModel newInterestRateModel) + external + onlyAdmin + returns (uint256) + { + return CTokenInterface(cToken)._setInterestRateModel(newInterestRateModel); + } + + /** + * @notice Set cToken collateral cap + * @dev It will revert if the cToken is not CCollateralCap. + * @param cToken The cToken address + * @param newCollateralCap The new collateral cap + */ + function _setCollateralCap(address cToken, uint256 newCollateralCap) external onlyAdmin { + CCollateralCapErc20Interface(cToken)._setCollateralCap(newCollateralCap); + } + + /** + * @notice Set cToken new implementation + * @param cToken The cToken address + * @param implementation The new implementation + * @param becomeImplementationData The payload data + */ + function _setImplementation( + address cToken, + address implementation, + bool allowResign, + bytes calldata becomeImplementationData + ) external onlyAdmin { + CDelegatorInterface(cToken)._setImplementation(implementation, allowResign, becomeImplementationData); + } + + /** + * @notice Extract reserves by the reserve manager + * @param cToken The cToken address + * @param reduceAmount The amount of reduction + */ + function extractReserves(address cToken, uint256 reduceAmount) external onlyReserveManager { + require(CTokenInterface(cToken)._reduceReserves(reduceAmount) == 0, "failed to reduce reserves"); + + address underlying; + if (compareStrings(CToken(cToken).symbol(), "crETH")) { + underlying = ethAddress; + } else { + underlying = CErc20(cToken).underlying(); + } + _transferToken(underlying, reserveManager, reduceAmount); + } + + /** + * @notice Seize the stock assets + * @param token The token address + */ + function seize(address token) external onlyAdmin { + uint256 amount; + if (token == ethAddress) { + amount = address(this).balance; + } else { + amount = EIP20NonStandardInterface(token).balanceOf(address(this)); + } + if (amount > 0) { + _transferToken(token, admin, amount); + } + } + + /** + * @notice Set the admin + * @param newAdmin The new admin + */ + function setAdmin(address payable newAdmin) external onlyAdmin { + _setAdmin(newAdmin); + } + + /** + * @notice Set the reserve manager + * @param newReserveManager The new reserve manager + */ + function setReserveManager(address payable newReserveManager) external onlyAdmin { + address oldReserveManager = reserveManager; + reserveManager = newReserveManager; + + emit SetReserveManager(oldReserveManager, newReserveManager); + } + + /* Internal functions */ + + function _setAdmin(address payable newAdmin) private { + require(newAdmin != address(0), "new admin cannot be zero address"); + address oldAdmin = admin; + admin = newAdmin; + emit SetAdmin(oldAdmin, newAdmin); + } + + function _transferToken( + address token, + address payable to, + uint256 amount + ) private { + require(to != address(0), "receiver cannot be zero address"); + if (token == ethAddress) { + to.transfer(amount); + } else { + EIP20NonStandardInterface(token).transfer(to, amount); + + bool success; + assembly { + switch returndatasize() + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a complaint ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + if lt(returndatasize(), 32) { + revert(0, 0) // This is a non-compliant ERC-20, revert. + } + returndatacopy(0, 0, 32) // Vyper compiler before 0.2.8 will not truncate RETURNDATASIZE. + success := mload(0) // See here: https://github.com/vyperlang/vyper/security/advisories/GHSA-375m-5fvv-xq23 + } + } + require(success, "TOKEN_TRANSFER_OUT_FAILED"); + } + } + + function compareStrings(string memory a, string memory b) private pure returns (bool) { + return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); + } + + function() external payable {} +} diff --git a/contracts/CTokenDeprecated.sol b/contracts/CTokenDeprecated.sol new file mode 100644 index 0000000..7ec221a --- /dev/null +++ b/contracts/CTokenDeprecated.sol @@ -0,0 +1,1353 @@ +pragma solidity ^0.5.16; + +import "./ComptrollerInterface.sol"; +import "./CTokenInterfaces.sol"; +import "./ErrorReporter.sol"; +import "./Exponential.sol"; +import "./EIP20Interface.sol"; +import "./EIP20NonStandardInterface.sol"; +import "./InterestRateModel.sol"; + +/** + * @title Deprecated CToken Contract only for CEther. + * @dev CEther will not be used anymore and existing CEther can't be upgraded. + * @author Cream + */ +contract CTokenDeprecated is CTokenInterface, Exponential, TokenErrorReporter { + /** + * @notice Initialize the money market + * @param comptroller_ The address of the Comptroller + * @param interestRateModel_ The address of the interest rate model + * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 + * @param name_ EIP-20 name of this token + * @param symbol_ EIP-20 symbol of this token + * @param decimals_ EIP-20 decimal precision of this token + */ + function initialize( + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_ + ) public { + require(msg.sender == admin, "only admin may initialize the market"); + require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once"); + + // Set initial exchange rate + initialExchangeRateMantissa = initialExchangeRateMantissa_; + require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero."); + + // Set the comptroller + uint256 err = _setComptroller(comptroller_); + require(err == uint256(Error.NO_ERROR), "setting comptroller failed"); + + // Initialize block number and borrow index (block number mocks depend on comptroller being set) + accrualBlockNumber = getBlockNumber(); + borrowIndex = mantissaOne; + + // Set the interest rate model (depends on block number / borrow index) + err = _setInterestRateModelFresh(interestRateModel_); + require(err == uint256(Error.NO_ERROR), "setting interest rate model failed"); + + name = name_; + symbol = symbol_; + decimals = decimals_; + + // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund) + _notEntered = true; + } + + /** + * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` + * @dev Called by both `transfer` and `transferFrom` internally + * @param spender The address of the account performing the transfer + * @param src The address of the source account + * @param dst The address of the destination account + * @param tokens The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256) { + /* Fail if transfer not allowed */ + uint256 allowed = comptroller.transferAllowed(address(this), src, dst, tokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed); + } + + /* Do not allow self-transfers */ + if (src == dst) { + return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED); + } + + /* Get the allowance, infinite for the account owner */ + uint256 startingAllowance = 0; + if (spender == src) { + startingAllowance = uint256(-1); + } else { + startingAllowance = transferAllowances[src][spender]; + } + + /* Do the calculations, checking for {under,over}flow */ + uint256 allowanceNew = sub_(startingAllowance, tokens); + uint256 srcTokensNew = sub_(accountTokens[src], tokens); + uint256 dstTokensNew = add_(accountTokens[dst], tokens); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + accountTokens[src] = srcTokensNew; + accountTokens[dst] = dstTokensNew; + + /* Eat some of the allowance (if necessary) */ + if (startingAllowance != uint256(-1)) { + transferAllowances[src][spender] = allowanceNew; + } + + /* We emit a Transfer event */ + emit Transfer(src, dst, tokens); + + comptroller.transferVerify(address(this), src, dst, tokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Transfer `amount` tokens from `msg.sender` to `dst` + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transfer(address dst, uint256 amount) external nonReentrant returns (bool) { + return transferTokens(msg.sender, msg.sender, dst, amount) == uint256(Error.NO_ERROR); + } + + /** + * @notice Transfer `amount` tokens from `src` to `dst` + * @param src The address of the source account + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferFrom( + address src, + address dst, + uint256 amount + ) external nonReentrant returns (bool) { + return transferTokens(msg.sender, src, dst, amount) == uint256(Error.NO_ERROR); + } + + /** + * @notice Approve `spender` to transfer up to `amount` from `src` + * @dev This will overwrite the approval amount for `spender` + * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) + * @param spender The address of the account which may transfer tokens + * @param amount The number of tokens that are approved (-1 means infinite) + * @return Whether or not the approval succeeded + */ + function approve(address spender, uint256 amount) external returns (bool) { + address src = msg.sender; + transferAllowances[src][spender] = amount; + emit Approval(src, spender, amount); + return true; + } + + /** + * @notice Get the current allowance from `owner` for `spender` + * @param owner The address of the account which owns the tokens to be spent + * @param spender The address of the account which may transfer tokens + * @return The number of tokens allowed to be spent (-1 means infinite) + */ + function allowance(address owner, address spender) external view returns (uint256) { + return transferAllowances[owner][spender]; + } + + /** + * @notice Get the token balance of the `owner` + * @param owner The address of the account to query + * @return The number of tokens owned by `owner` + */ + function balanceOf(address owner) external view returns (uint256) { + return accountTokens[owner]; + } + + /** + * @notice Get the underlying balance of the `owner` + * @dev This also accrues interest in a transaction + * @param owner The address of the account to query + * @return The amount of underlying owned by `owner` + */ + function balanceOfUnderlying(address owner) external returns (uint256) { + Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()}); + return mul_ScalarTruncate(exchangeRate, accountTokens[owner]); + } + + /** + * @notice Get a snapshot of the account's balances, and the cached exchange rate + * @dev This is used by comptroller to more efficiently perform liquidity checks. + * @param account Address of the account to snapshot + * @return (possible error, token balance, borrow balance, exchange rate mantissa) + */ + function getAccountSnapshot(address account) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ) + { + uint256 cTokenBalance = accountTokens[account]; + uint256 borrowBalance = borrowBalanceStoredInternal(account); + uint256 exchangeRateMantissa = exchangeRateStoredInternal(); + + return (uint256(Error.NO_ERROR), cTokenBalance, borrowBalance, exchangeRateMantissa); + } + + /** + * @dev Function to simply retrieve block number + * This exists mainly for inheriting test contracts to stub this result. + */ + function getBlockNumber() internal view returns (uint256) { + return block.number; + } + + /** + * @notice Returns the current per-block borrow interest rate for this cToken + * @return The borrow interest rate per block, scaled by 1e18 + */ + function borrowRatePerBlock() external view returns (uint256) { + return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves); + } + + /** + * @notice Returns the current per-block supply interest rate for this cToken + * @return The supply interest rate per block, scaled by 1e18 + */ + function supplyRatePerBlock() external view returns (uint256) { + return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa); + } + + /** + * @notice Returns the current total borrows plus accrued interest + * @return The total borrows with interest + */ + function totalBorrowsCurrent() external nonReentrant returns (uint256) { + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); + return totalBorrows; + } + + /** + * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex + * @param account The address whose balance should be calculated after updating borrowIndex + * @return The calculated balance + */ + function borrowBalanceCurrent(address account) external nonReentrant returns (uint256) { + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); + return borrowBalanceStored(account); + } + + /** + * @notice Return the borrow balance of account based on stored data + * @param account The address whose balance should be calculated + * @return The calculated balance + */ + function borrowBalanceStored(address account) public view returns (uint256) { + return borrowBalanceStoredInternal(account); + } + + /** + * @notice Return the borrow balance of account based on stored data + * @param account The address whose balance should be calculated + * @return the calculated balance or 0 if error code is non-zero + */ + function borrowBalanceStoredInternal(address account) internal view returns (uint256) { + /* Get borrowBalance and borrowIndex */ + BorrowSnapshot storage borrowSnapshot = accountBorrows[account]; + + /* If borrowBalance = 0 then borrowIndex is likely also 0. + * Rather than failing the calculation with a division by 0, we immediately return 0 in this case. + */ + if (borrowSnapshot.principal == 0) { + return 0; + } + + /* Calculate new borrow balance using the interest index: + * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex + */ + uint256 principalTimesIndex = mul_(borrowSnapshot.principal, borrowIndex); + uint256 result = div_(principalTimesIndex, borrowSnapshot.interestIndex); + return result; + } + + /** + * @notice Accrue interest then return the up-to-date exchange rate + * @return Calculated exchange rate scaled by 1e18 + */ + function exchangeRateCurrent() public nonReentrant returns (uint256) { + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); + return exchangeRateStored(); + } + + /** + * @notice Calculates the exchange rate from the underlying to the CToken + * @dev This function does not accrue interest before calculating the exchange rate + * @return Calculated exchange rate scaled by 1e18 + */ + function exchangeRateStored() public view returns (uint256) { + return exchangeRateStoredInternal(); + } + + /** + * @notice Calculates the exchange rate from the underlying to the CToken + * @dev This function does not accrue interest before calculating the exchange rate + * @return calculated exchange rate scaled by 1e18 + */ + function exchangeRateStoredInternal() internal view returns (uint256) { + uint256 _totalSupply = totalSupply; + if (_totalSupply == 0) { + /* + * If there are no tokens minted: + * exchangeRate = initialExchangeRate + */ + return initialExchangeRateMantissa; + } else { + /* + * Otherwise: + * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply + */ + uint256 totalCash = getCashPrior(); + uint256 cashPlusBorrowsMinusReserves = sub_(add_(totalCash, totalBorrows), totalReserves); + uint256 exchangeRate = div_(cashPlusBorrowsMinusReserves, Exp({mantissa: _totalSupply})); + return exchangeRate; + } + } + + /** + * @notice Get cash balance of this cToken in the underlying asset + * @return The quantity of underlying asset owned by this contract + */ + function getCash() external view returns (uint256) { + return getCashPrior(); + } + + /** + * @notice Applies accrued interest to total borrows and reserves + * @dev This calculates interest accrued from the last checkpointed block + * up to the current block and writes new checkpoint to storage. + */ + function accrueInterest() public returns (uint256) { + /* Remember the initial block number */ + uint256 currentBlockNumber = getBlockNumber(); + uint256 accrualBlockNumberPrior = accrualBlockNumber; + + /* Short-circuit accumulating 0 interest */ + if (accrualBlockNumberPrior == currentBlockNumber) { + return uint256(Error.NO_ERROR); + } + + /* Read the previous values out of storage */ + uint256 cashPrior = getCashPrior(); + uint256 borrowsPrior = totalBorrows; + uint256 reservesPrior = totalReserves; + uint256 borrowIndexPrior = borrowIndex; + + /* Calculate the current borrow interest rate */ + uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); + require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high"); + + /* Calculate the number of blocks elapsed since the last accrual */ + uint256 blockDelta = sub_(currentBlockNumber, accrualBlockNumberPrior); + + /* + * Calculate the interest accumulated into borrows and reserves and the new index: + * simpleInterestFactor = borrowRate * blockDelta + * interestAccumulated = simpleInterestFactor * totalBorrows + * totalBorrowsNew = interestAccumulated + totalBorrows + * totalReservesNew = interestAccumulated * reserveFactor + totalReserves + * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex + */ + + Exp memory simpleInterestFactor = mul_(Exp({mantissa: borrowRateMantissa}), blockDelta); + uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior); + uint256 totalBorrowsNew = add_(interestAccumulated, borrowsPrior); + uint256 totalReservesNew = mul_ScalarTruncateAddUInt( + Exp({mantissa: reserveFactorMantissa}), + interestAccumulated, + reservesPrior + ); + uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* We write the previously calculated values into storage */ + accrualBlockNumber = currentBlockNumber; + borrowIndex = borrowIndexNew; + totalBorrows = totalBorrowsNew; + totalReserves = totalReservesNew; + + /* We emit an AccrueInterest event */ + emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Sender supplies assets into the market and receives cTokens in exchange + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param mintAmount The amount of the underlying asset to supply + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. + */ + function mintInternal(uint256 mintAmount) internal nonReentrant returns (uint256, uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed + return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0); + } + // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to + return mintFresh(msg.sender, mintAmount); + } + + struct MintLocalVars { + Error err; + MathError mathErr; + uint256 exchangeRateMantissa; + uint256 mintTokens; + uint256 totalSupplyNew; + uint256 accountTokensNew; + uint256 actualMintAmount; + } + + /** + * @notice User supplies assets into the market and receives cTokens in exchange + * @dev Assumes interest has already been accrued up to the current block + * @param minter The address of the account which is supplying the assets + * @param mintAmount The amount of the underlying asset to supply + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. + */ + function mintFresh(address minter, uint256 mintAmount) internal returns (uint256, uint256) { + /* Fail if mint not allowed */ + uint256 allowed = comptroller.mintAllowed(address(this), minter, mintAmount); + if (allowed != 0) { + return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0); + } + + MintLocalVars memory vars; + + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We call `doTransferIn` for the minter and the mintAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * `doTransferIn` reverts if anything goes wrong, since we can't be sure if + * side-effects occurred. The function returns the amount actually transferred, + * in case of a fee. On success, the cToken holds an additional `actualMintAmount` + * of cash. + */ + vars.actualMintAmount = doTransferIn(minter, mintAmount); + + /* + * We get the current exchange rate and calculate the number of cTokens to be minted: + * mintTokens = actualMintAmount / exchangeRate + */ + + vars.mintTokens = div_ScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa})); + + /* + * We calculate the new total supply of cTokens and minter token balance, checking for overflow: + * totalSupplyNew = totalSupply + mintTokens + * accountTokensNew = accountTokens[minter] + mintTokens + */ + vars.totalSupplyNew = add_(totalSupply, vars.mintTokens); + vars.accountTokensNew = add_(accountTokens[minter], vars.mintTokens); + + /* We write previously calculated values into storage */ + totalSupply = vars.totalSupplyNew; + accountTokens[minter] = vars.accountTokensNew; + + /* We emit a Mint event, and a Transfer event */ + emit Mint(minter, vars.actualMintAmount, vars.mintTokens); + emit Transfer(address(this), minter, vars.mintTokens); + + /* We call the defense hook */ + comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens); + + return (uint256(Error.NO_ERROR), vars.actualMintAmount); + } + + /** + * @notice Sender redeems cTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemTokens The number of cTokens to redeem into underlying + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemInternal(uint256 redeemTokens) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed + return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); + } + // redeemFresh emits redeem-specific logs on errors, so we don't need to + return redeemFresh(msg.sender, redeemTokens, 0); + } + + /** + * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemAmount The amount of underlying to receive from redeeming cTokens + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemUnderlyingInternal(uint256 redeemAmount) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed + return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); + } + // redeemFresh emits redeem-specific logs on errors, so we don't need to + return redeemFresh(msg.sender, 0, redeemAmount); + } + + struct RedeemLocalVars { + Error err; + MathError mathErr; + uint256 exchangeRateMantissa; + uint256 redeemTokens; + uint256 redeemAmount; + uint256 totalSupplyNew; + uint256 accountTokensNew; + } + + /** + * @notice User redeems cTokens in exchange for the underlying asset + * @dev Assumes interest has already been accrued up to the current block + * @param redeemer The address of the account which is redeeming the tokens + * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero) + * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemFresh( + address payable redeemer, + uint256 redeemTokensIn, + uint256 redeemAmountIn + ) internal returns (uint256) { + require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); + + RedeemLocalVars memory vars; + + /* exchangeRate = invoke Exchange Rate Stored() */ + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + /* If redeemTokensIn > 0: */ + if (redeemTokensIn > 0) { + /* + * We calculate the exchange rate and the amount of underlying to be redeemed: + * redeemTokens = redeemTokensIn + * redeemAmount = redeemTokensIn x exchangeRateCurrent + */ + vars.redeemTokens = redeemTokensIn; + vars.redeemAmount = mul_ScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn); + } else { + /* + * We get the current exchange rate and calculate the amount to be redeemed: + * redeemTokens = redeemAmountIn / exchangeRate + * redeemAmount = redeemAmountIn + */ + vars.redeemTokens = div_ScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa})); + vars.redeemAmount = redeemAmountIn; + } + + /* Fail if redeem not allowed */ + uint256 allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK); + } + + /* + * We calculate the new total supply and redeemer balance, checking for underflow: + * totalSupplyNew = totalSupply - redeemTokens + * accountTokensNew = accountTokens[redeemer] - redeemTokens + */ + vars.totalSupplyNew = sub_(totalSupply, vars.redeemTokens); + vars.accountTokensNew = sub_(accountTokens[redeemer], vars.redeemTokens); + + /* Fail gracefully if protocol has insufficient cash */ + if (getCashPrior() < vars.redeemAmount) { + return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We invoke doTransferOut for the redeemer and the redeemAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken has redeemAmount less of cash. + * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. + */ + doTransferOut(redeemer, vars.redeemAmount); + + /* We write previously calculated values into storage */ + totalSupply = vars.totalSupplyNew; + accountTokens[redeemer] = vars.accountTokensNew; + + /* We emit a Transfer event, and a Redeem event */ + emit Transfer(redeemer, address(this), vars.redeemTokens); + emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens); + + /* We call the defense hook */ + comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrowInternal(uint256 borrowAmount) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed + return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED); + } + // borrowFresh emits borrow-specific logs on errors, so we don't need to + return borrowFresh(msg.sender, borrowAmount); + } + + struct BorrowLocalVars { + MathError mathErr; + uint256 accountBorrows; + uint256 accountBorrowsNew; + uint256 totalBorrowsNew; + } + + /** + * @notice Users borrow assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrowFresh(address payable borrower, uint256 borrowAmount) internal returns (uint256) { + /* Fail if borrow not allowed */ + uint256 allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.BORROW_COMPTROLLER_REJECTION, allowed); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK); + } + + /* Fail gracefully if protocol has insufficient underlying cash */ + if (getCashPrior() < borrowAmount) { + return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.BORROW_CASH_NOT_AVAILABLE); + } + + BorrowLocalVars memory vars; + + /* + * We calculate the new borrower and total borrow balances, failing on overflow: + * accountBorrowsNew = accountBorrows + borrowAmount + * totalBorrowsNew = totalBorrows + borrowAmount + */ + vars.accountBorrows = borrowBalanceStoredInternal(borrower); + vars.accountBorrowsNew = add_(vars.accountBorrows, borrowAmount); + vars.totalBorrowsNew = add_(totalBorrows, borrowAmount); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We invoke doTransferOut for the borrower and the borrowAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken borrowAmount less of cash. + * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. + */ + doTransferOut(borrower, borrowAmount); + + /* We write the previously calculated values into storage */ + accountBorrows[borrower].principal = vars.accountBorrowsNew; + accountBorrows[borrower].interestIndex = borrowIndex; + totalBorrows = vars.totalBorrowsNew; + + /* We emit a Borrow event */ + emit Borrow(borrower, borrowAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); + + /* We call the defense hook */ + comptroller.borrowVerify(address(this), borrower, borrowAmount); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Sender repays their own borrow + * @param repayAmount The amount to repay + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. + */ + function repayBorrowInternal(uint256 repayAmount) internal nonReentrant returns (uint256, uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed + return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0); + } + // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to + return repayBorrowFresh(msg.sender, msg.sender, repayAmount); + } + + /** + * @notice Sender repays a borrow belonging to borrower + * @param borrower the account with the debt being payed off + * @param repayAmount The amount to repay + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. + */ + function repayBorrowBehalfInternal(address borrower, uint256 repayAmount) + internal + nonReentrant + returns (uint256, uint256) + { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed + return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0); + } + // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to + return repayBorrowFresh(msg.sender, borrower, repayAmount); + } + + struct RepayBorrowLocalVars { + Error err; + MathError mathErr; + uint256 repayAmount; + uint256 borrowerIndex; + uint256 accountBorrows; + uint256 accountBorrowsNew; + uint256 totalBorrowsNew; + uint256 actualRepayAmount; + } + + /** + * @notice Borrows are repaid by another user (possibly the borrower). + * @param payer the account paying off the borrow + * @param borrower the account with the debt being payed off + * @param repayAmount the amount of undelrying tokens being returned + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. + */ + function repayBorrowFresh( + address payer, + address borrower, + uint256 repayAmount + ) internal returns (uint256, uint256) { + /* Fail if repayBorrow not allowed */ + uint256 allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); + if (allowed != 0) { + return ( + failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), + 0 + ); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.REPAY_BORROW_FRESHNESS_CHECK), 0); + } + + RepayBorrowLocalVars memory vars; + + /* We remember the original borrowerIndex for verification purposes */ + vars.borrowerIndex = accountBorrows[borrower].interestIndex; + + /* We fetch the amount the borrower owes, with accumulated interest */ + vars.accountBorrows = borrowBalanceStoredInternal(borrower); + + /* If repayAmount == -1, repayAmount = accountBorrows */ + if (repayAmount == uint256(-1)) { + vars.repayAmount = vars.accountBorrows; + } else { + vars.repayAmount = repayAmount; + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We call doTransferIn for the payer and the repayAmount + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken holds an additional repayAmount of cash. + * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. + * it returns the amount actually transferred, in case of a fee. + */ + vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount); + + /* + * We calculate the new borrower and total borrow balances, failing on underflow: + * accountBorrowsNew = accountBorrows - actualRepayAmount + * totalBorrowsNew = totalBorrows - actualRepayAmount + */ + vars.accountBorrowsNew = sub_(vars.accountBorrows, vars.actualRepayAmount); + vars.totalBorrowsNew = sub_(totalBorrows, vars.actualRepayAmount); + + /* We write the previously calculated values into storage */ + accountBorrows[borrower].principal = vars.accountBorrowsNew; + accountBorrows[borrower].interestIndex = borrowIndex; + totalBorrows = vars.totalBorrowsNew; + + /* We emit a RepayBorrow event */ + emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); + + /* We call the defense hook */ + comptroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex); + + return (uint256(Error.NO_ERROR), vars.actualRepayAmount); + } + + /** + * @notice The sender liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @param borrower The borrower of this cToken to be liquidated + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @param repayAmount The amount of the underlying borrowed asset to repay + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. + */ + function liquidateBorrowInternal( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) internal nonReentrant returns (uint256, uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed + return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED), 0); + } + + error = cTokenCollateral.accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed + return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0); + } + + // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to + return liquidateBorrowFresh(msg.sender, borrower, repayAmount, cTokenCollateral); + } + + /** + * @notice The liquidator liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @param borrower The borrower of this cToken to be liquidated + * @param liquidator The address repaying the borrow and seizing collateral + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @param repayAmount The amount of the underlying borrowed asset to repay + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. + */ + function liquidateBorrowFresh( + address liquidator, + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) internal returns (uint256, uint256) { + /* Fail if liquidate not allowed */ + uint256 allowed = comptroller.liquidateBorrowAllowed( + address(this), + address(cTokenCollateral), + liquidator, + borrower, + repayAmount + ); + if (allowed != 0) { + return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_COMPTROLLER_REJECTION, allowed), 0); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_FRESHNESS_CHECK), 0); + } + + /* Verify cTokenCollateral market's block number equals current block number */ + if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_COLLATERAL_FRESHNESS_CHECK), 0); + } + + /* Fail if borrower = liquidator */ + if (borrower == liquidator) { + return (fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_LIQUIDATOR_IS_BORROWER), 0); + } + + /* Fail if repayAmount = 0 */ + if (repayAmount == 0) { + return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_ZERO), 0); + } + + /* Fail if repayAmount = -1 */ + if (repayAmount == uint256(-1)) { + return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0); + } + + /* Fail if repayBorrow fails */ + (uint256 repayBorrowError, uint256 actualRepayAmount) = repayBorrowFresh(liquidator, borrower, repayAmount); + if (repayBorrowError != uint256(Error.NO_ERROR)) { + return (fail(Error(repayBorrowError), FailureInfo.LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* We calculate the number of collateral tokens that will be seized */ + (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens( + address(this), + address(cTokenCollateral), + actualRepayAmount + ); + require(amountSeizeError == uint256(Error.NO_ERROR), "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"); + + /* Revert if borrower collateral token balance < seizeTokens */ + require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH"); + + // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call + uint256 seizeError; + if (address(cTokenCollateral) == address(this)) { + seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens); + } else { + seizeError = cTokenCollateral.seize(liquidator, borrower, seizeTokens); + } + + /* Revert if seize tokens fails (since we cannot be sure of side effects) */ + require(seizeError == uint256(Error.NO_ERROR), "token seizure failed"); + + /* We emit a LiquidateBorrow event */ + emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(cTokenCollateral), seizeTokens); + + /* We call the defense hook */ + comptroller.liquidateBorrowVerify( + address(this), + address(cTokenCollateral), + liquidator, + borrower, + actualRepayAmount, + seizeTokens + ); + + return (uint256(Error.NO_ERROR), actualRepayAmount); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Will fail unless called by another cToken during the process of liquidation. + * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seize( + address liquidator, + address borrower, + uint256 seizeTokens + ) external nonReentrant returns (uint256) { + return seizeInternal(msg.sender, liquidator, borrower, seizeTokens); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. + * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. + * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seizeInternal( + address seizerToken, + address liquidator, + address borrower, + uint256 seizeTokens + ) internal returns (uint256) { + /* Fail if seize not allowed */ + uint256 allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed); + } + + /* Fail if borrower = liquidator */ + if (borrower == liquidator) { + return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER); + } + + /* + * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: + * borrowerTokensNew = accountTokens[borrower] - seizeTokens + * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens + */ + uint256 borrowerTokensNew = sub_(accountTokens[borrower], seizeTokens); + uint256 liquidatorTokensNew = add_(accountTokens[liquidator], seizeTokens); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* We write the previously calculated values into storage */ + accountTokens[borrower] = borrowerTokensNew; + accountTokens[liquidator] = liquidatorTokensNew; + + /* Emit a Transfer event */ + emit Transfer(borrower, liquidator, seizeTokens); + + /* We call the defense hook */ + comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens); + + return uint256(Error.NO_ERROR); + } + + /*** Admin Functions ***/ + + /** + * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @param newPendingAdmin New pending admin. + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256) { + // Check caller = admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); + } + + // Save current value, if any, for inclusion in log + address oldPendingAdmin = pendingAdmin; + + // Store pendingAdmin with value newPendingAdmin + pendingAdmin = newPendingAdmin; + + // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) + emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + * @dev Admin function for pending admin to accept role and update admin + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _acceptAdmin() external returns (uint256) { + // Check caller is pendingAdmin and pendingAdmin ≠ address(0) + if (msg.sender != pendingAdmin || msg.sender == address(0)) { + return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); + } + + // Save current values for inclusion in log + address oldAdmin = admin; + address oldPendingAdmin = pendingAdmin; + + // Store admin with value pendingAdmin + admin = pendingAdmin; + + // Clear the pending value + pendingAdmin = address(0); + + emit NewAdmin(oldAdmin, admin); + emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Sets a new comptroller for the market + * @dev Admin function to set a new comptroller + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setComptroller(ComptrollerInterface newComptroller) public returns (uint256) { + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK); + } + + ComptrollerInterface oldComptroller = comptroller; + // Ensure invoke comptroller.isComptroller() returns true + require(newComptroller.isComptroller(), "marker method returned false"); + + // Set market's comptroller to newComptroller + comptroller = newComptroller; + + // Emit NewComptroller(oldComptroller, newComptroller) + emit NewComptroller(oldComptroller, newComptroller); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh + * @dev Admin function to accrue interest and set a new reserve factor + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setReserveFactor(uint256 newReserveFactorMantissa) external nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed. + return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED); + } + // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to. + return _setReserveFactorFresh(newReserveFactorMantissa); + } + + /** + * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) + * @dev Admin function to set a new reserve factor + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal returns (uint256) { + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK); + } + + // Verify market's block number equals current block number + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK); + } + + // Check newReserveFactor ≤ maxReserveFactor + if (newReserveFactorMantissa > reserveFactorMaxMantissa) { + return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK); + } + + uint256 oldReserveFactorMantissa = reserveFactorMantissa; + reserveFactorMantissa = newReserveFactorMantissa; + + emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Accrues interest and reduces reserves by transferring from msg.sender + * @param addAmount Amount of addition to reserves + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _addReservesInternal(uint256 addAmount) internal nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. + return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED); + } + + // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to. + (error, ) = _addReservesFresh(addAmount); + return error; + } + + /** + * @notice Add reserves by transferring from caller + * @dev Requires fresh interest accrual + * @param addAmount Amount of addition to reserves + * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees + */ + function _addReservesFresh(uint256 addAmount) internal returns (uint256, uint256) { + // totalReserves + actualAddAmount + uint256 totalReservesNew; + uint256 actualAddAmount; + + // We fail gracefully unless market's block number equals current block number + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.ADD_RESERVES_FRESH_CHECK), actualAddAmount); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We call doTransferIn for the caller and the addAmount + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken holds an additional addAmount of cash. + * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. + * it returns the amount actually transferred, in case of a fee. + */ + + actualAddAmount = doTransferIn(msg.sender, addAmount); + + totalReservesNew = add_(totalReserves, actualAddAmount); + + // Store reserves[n+1] = reserves[n] + actualAddAmount + totalReserves = totalReservesNew; + + /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */ + emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew); + + /* Return (NO_ERROR, actualAddAmount) */ + return (uint256(Error.NO_ERROR), actualAddAmount); + } + + /** + * @notice Accrues interest and reduces reserves by transferring to admin + * @param reduceAmount Amount of reduction to reserves + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _reduceReserves(uint256 reduceAmount) external nonReentrant returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. + return fail(Error(error), FailureInfo.REDUCE_RESERVES_ACCRUE_INTEREST_FAILED); + } + // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to. + return _reduceReservesFresh(reduceAmount); + } + + /** + * @notice Reduces reserves by transferring to admin + * @dev Requires fresh interest accrual + * @param reduceAmount Amount of reduction to reserves + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _reduceReservesFresh(uint256 reduceAmount) internal returns (uint256) { + // totalReserves - reduceAmount + uint256 totalReservesNew; + + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.REDUCE_RESERVES_ADMIN_CHECK); + } + + // We fail gracefully unless market's block number equals current block number + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK); + } + + // Fail gracefully if protocol has insufficient underlying cash + if (getCashPrior() < reduceAmount) { + return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_AVAILABLE); + } + + // Check reduceAmount ≤ reserves[n] (totalReserves) + if (reduceAmount > totalReserves) { + return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + totalReservesNew = sub_(totalReserves, reduceAmount); + + // Store reserves[n+1] = reserves[n] - reduceAmount + totalReserves = totalReservesNew; + + // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. + doTransferOut(admin, reduceAmount); + + emit ReservesReduced(admin, reduceAmount, totalReservesNew); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh + * @dev Admin function to accrue interest and update the interest rate model + * @param newInterestRateModel the new interest rate model to use + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256) { + uint256 error = accrueInterest(); + if (error != uint256(Error.NO_ERROR)) { + // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted change of interest rate model failed + return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED); + } + // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to. + return _setInterestRateModelFresh(newInterestRateModel); + } + + /** + * @notice updates the interest rate model (*requires fresh interest accrual) + * @dev Admin function to update the interest rate model + * @param newInterestRateModel the new interest rate model to use + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint256) { + // Used to store old model for use in the event that is emitted on success + InterestRateModel oldInterestRateModel; + + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK); + } + + // We fail gracefully unless market's block number equals current block number + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK); + } + + // Track the market's current interest rate model + oldInterestRateModel = interestRateModel; + + // Ensure invoke newInterestRateModel.isInterestRateModel() returns true + require(newInterestRateModel.isInterestRateModel(), "marker method returned false"); + + // Set the interest rate model to newInterestRateModel + interestRateModel = newInterestRateModel; + + // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel) + emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel); + + return uint256(Error.NO_ERROR); + } + + /*** Safe Token ***/ + + /** + * @notice Gets 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 owned by this contract + */ + function getCashPrior() internal view returns (uint256); + + /** + * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee. + * This may revert due to insufficient balance or insufficient allowance. + */ + function doTransferIn(address from, uint256 amount) internal returns (uint256); + + /** + * @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting. + * If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract. + * If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions. + */ + function doTransferOut(address payable to, uint256 amount) internal; + + /*** Reentrancy Guard ***/ + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + */ + modifier nonReentrant() { + require(_notEntered, "re-entered"); + _notEntered = false; + _; + _notEntered = true; // get a gas-refund post-Istanbul + } +} diff --git a/contracts/CTokenInterfaces.sol b/contracts/CTokenInterfaces.sol index b737892..98c2099 100644 --- a/contracts/CTokenInterfaces.sol +++ b/contracts/CTokenInterfaces.sol @@ -2,6 +2,7 @@ pragma solidity ^0.5.16; import "./ComptrollerInterface.sol"; import "./InterestRateModel.sol"; +import "./ERC3156FlashBorrowerInterface.sol"; contract CTokenStorage { /** @@ -28,12 +29,12 @@ contract CTokenStorage { * @notice Maximum borrow rate that can ever be applied (.0005% / block) */ - uint internal constant borrowRateMaxMantissa = 0.0005e16; + uint256 internal constant borrowRateMaxMantissa = 0.0005e16; /** * @notice Maximum fraction of interest that can be set aside for reserves */ - uint internal constant reserveFactorMaxMantissa = 1e18; + uint256 internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract @@ -58,47 +59,47 @@ contract CTokenStorage { /** * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) */ - uint internal initialExchangeRateMantissa; + uint256 internal initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves */ - uint public reserveFactorMantissa; + uint256 public reserveFactorMantissa; /** * @notice Block number that interest was last accrued at */ - uint public accrualBlockNumber; + uint256 public accrualBlockNumber; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ - uint public borrowIndex; + uint256 public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ - uint public totalBorrows; + uint256 public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ - uint public totalReserves; + uint256 public totalReserves; /** * @notice Total number of tokens in circulation */ - uint public totalSupply; + uint256 public totalSupply; /** * @notice Official record of token balances for each account */ - mapping (address => uint) internal accountTokens; + mapping(address => uint256) internal accountTokens; /** * @notice Approved token transfer amounts on behalf of others */ - mapping (address => mapping (address => uint)) internal transferAllowances; + mapping(address => mapping(address => uint256)) internal transferAllowances; /** * @notice Container for borrow balance information @@ -106,8 +107,8 @@ contract CTokenStorage { * @member interestIndex Global borrowIndex as of the most recent balance-changing action */ struct BorrowSnapshot { - uint principal; - uint interestIndex; + uint256 principal; + uint256 interestIndex; } /** @@ -116,45 +117,99 @@ contract CTokenStorage { mapping(address => BorrowSnapshot) internal accountBorrows; } +contract CErc20Storage { + /** + * @notice Underlying asset for this CToken + */ + address public underlying; + + /** + * @notice Implementation address for this contract + */ + address public implementation; +} + +contract CSupplyCapStorage { + /** + * @notice Internal cash counter for this CToken. Should equal underlying.balanceOf(address(this)) for CERC20. + */ + uint256 public internalCash; +} + +contract CCollateralCapStorage { + /** + * @notice Total number of tokens used as collateral in circulation. + */ + uint256 public totalCollateralTokens; + + /** + * @notice Record of token balances which could be treated as collateral for each account. + * If collateral cap is not set, the value should be equal to accountTokens. + */ + mapping(address => uint256) public accountCollateralTokens; + + /** + * @notice Check if accountCollateralTokens have been initialized. + */ + mapping(address => bool) public isCollateralTokenInit; + + /** + * @notice Collateral cap for this CToken, zero for no cap. + */ + uint256 public collateralCap; +} + +/*** Interface ***/ + contract CTokenInterface is CTokenStorage { /** * @notice Indicator that this is a CToken contract (for inspection) */ bool public constant isCToken = true; - /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ - event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); + event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows); /** * @notice Event emitted when tokens are minted */ - event Mint(address minter, uint mintAmount, uint mintTokens); + event Mint(address minter, uint256 mintAmount, uint256 mintTokens); /** * @notice Event emitted when tokens are redeemed */ - event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); + event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens); /** * @notice Event emitted when underlying is borrowed */ - event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); + event Borrow(address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows); /** * @notice Event emitted when a borrow is repaid */ - event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); + event RepayBorrow( + address payer, + address borrower, + uint256 repayAmount, + uint256 accountBorrows, + uint256 totalBorrows + ); /** * @notice Event emitted when a borrow is liquidated */ - event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); - + event LiquidateBorrow( + address liquidator, + address borrower, + uint256 repayAmount, + address cTokenCollateral, + uint256 seizeTokens + ); /*** Admin Events ***/ @@ -181,122 +236,216 @@ contract CTokenInterface is CTokenStorage { /** * @notice Event emitted when the reserve factor is changed */ - event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); + event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa); /** * @notice Event emitted when the reserves are added */ - event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); + event ReservesAdded(address benefactor, uint256 addAmount, uint256 newTotalReserves); /** * @notice Event emitted when the reserves are reduced */ - event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); + event ReservesReduced(address admin, uint256 reduceAmount, uint256 newTotalReserves); /** * @notice EIP20 Transfer event */ - event Transfer(address indexed from, address indexed to, uint amount); + event Transfer(address indexed from, address indexed to, uint256 amount); /** * @notice EIP20 Approval event */ - event Approval(address indexed owner, address indexed spender, uint amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); /** * @notice Failure event */ - event Failure(uint error, uint info, uint detail); - + event Failure(uint256 error, uint256 info, uint256 detail); /*** User Interface ***/ - function transfer(address dst, uint amount) external returns (bool); - function transferFrom(address src, address dst, uint amount) external returns (bool); - function approve(address spender, uint amount) external returns (bool); - function allowance(address owner, address spender) external view returns (uint); - function balanceOf(address owner) external view returns (uint); - function balanceOfUnderlying(address owner) external returns (uint); - function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint); - function borrowRatePerBlock() external view returns (uint); - function supplyRatePerBlock() external view returns (uint); - function totalBorrowsCurrent() external returns (uint); - function borrowBalanceCurrent(address account) external returns (uint); - function borrowBalanceStored(address account) public view returns (uint); - function exchangeRateCurrent() public returns (uint); - function exchangeRateStored() public view returns (uint); - function getCash() external view returns (uint); - function accrueInterest() public returns (uint); - function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint); + function transfer(address dst, uint256 amount) external returns (bool); + + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool); + + function approve(address spender, uint256 amount) external returns (bool); + + function allowance(address owner, address spender) external view returns (uint256); + + function balanceOf(address owner) external view returns (uint256); + + function balanceOfUnderlying(address owner) external returns (uint256); + + function getAccountSnapshot(address account) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ); + + function borrowRatePerBlock() external view returns (uint256); + + function supplyRatePerBlock() external view returns (uint256); + + function totalBorrowsCurrent() external returns (uint256); + + function borrowBalanceCurrent(address account) external returns (uint256); + function borrowBalanceStored(address account) public view returns (uint256); + + function exchangeRateCurrent() public returns (uint256); + + function exchangeRateStored() public view returns (uint256); + + function getCash() external view returns (uint256); + + function accrueInterest() public returns (uint256); + + function seize( + address liquidator, + address borrower, + uint256 seizeTokens + ) external returns (uint256); /*** Admin Functions ***/ - function _setPendingAdmin(address payable newPendingAdmin) external returns (uint); - function _acceptAdmin() external returns (uint); - function _setComptroller(ComptrollerInterface newComptroller) public returns (uint); - function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint); - function _reduceReserves(uint reduceAmount) external returns (uint); - function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint); -} + function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256); -contract CErc20Storage { - /** - * @notice Underlying asset for this CToken - */ - address public underlying; + function _acceptAdmin() external returns (uint256); + + function _setComptroller(ComptrollerInterface newComptroller) public returns (uint256); + + function _setReserveFactor(uint256 newReserveFactorMantissa) external returns (uint256); + + function _reduceReserves(uint256 reduceAmount) external returns (uint256); + + function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256); } contract CErc20Interface is CErc20Storage { - /*** User Interface ***/ - function mint(uint mintAmount) external returns (uint); - function redeem(uint redeemTokens) external returns (uint); - function redeemUnderlying(uint redeemAmount) external returns (uint); - function borrow(uint borrowAmount) external returns (uint); - 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 mint(uint256 mintAmount) external returns (uint256); - /*** Admin Functions ***/ + function redeem(uint256 redeemTokens) external returns (uint256); + + function redeemUnderlying(uint256 redeemAmount) external returns (uint256); + + function borrow(uint256 borrowAmount) external returns (uint256); - function _addReserves(uint addAmount) external returns (uint); + function repayBorrow(uint256 repayAmount) external returns (uint256); + + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256); + + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256); + + function _addReserves(uint256 addAmount) external returns (uint256); } -contract CCapableErc20Interface is CErc20Storage { +contract CWrappedNativeInterface is CErc20Interface { + /** + * @notice Flash loan fee ratio + */ + uint256 public constant flashFeeBips = 3; + + /*** Market Events ***/ + + /** + * @notice Event emitted when a flashloan occured + */ + event Flashloan(address indexed receiver, uint256 amount, uint256 totalFee, uint256 reservesFee); /*** User Interface ***/ - function mint(uint mintAmount) external returns (uint); - function redeem(uint redeemTokens) external returns (uint); - function redeemUnderlying(uint redeemAmount) external returns (uint); - function borrow(uint borrowAmount) external returns (uint); - 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; + function mintNative() external payable returns (uint256); - /*** Admin Functions ***/ + function redeemNative(uint256 redeemTokens) external returns (uint256); + + function redeemUnderlyingNative(uint256 redeemAmount) external returns (uint256); + + function borrowNative(uint256 borrowAmount) external returns (uint256); + + function repayBorrowNative() external payable returns (uint256); - function _addReserves(uint addAmount) external returns (uint); + function repayBorrowBehalfNative(address borrower) external payable returns (uint256); + + function liquidateBorrowNative(address borrower, CTokenInterface cTokenCollateral) + external + payable + returns (uint256); + + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address initiator, + uint256 amount, + bytes calldata data + ) external returns (bool); + + function _addReservesNative() external payable returns (uint256); } -contract CDelegationStorage { +contract CCapableErc20Interface is CErc20Interface, CSupplyCapStorage { /** - * @notice Implementation address for this contract + * @notice Flash loan fee ratio */ - address public implementation; + uint256 public constant flashFeeBips = 3; + + /*** Market Events ***/ + + /** + * @notice Event emitted when a flashloan occured + */ + event Flashloan(address indexed receiver, uint256 amount, uint256 totalFee, uint256 reservesFee); + + /*** User Interface ***/ + + function gulp() external; } -contract CDelegationStorageExtension is CDelegationStorage { +contract CCollateralCapErc20Interface is CCapableErc20Interface, CCollateralCapStorage { + /*** Admin Events ***/ + /** - * @notice Internal cash counter for this CToken. Should equal underlying.balanceOf(address(this)) for CERC20. + * @notice Event emitted when collateral cap is set */ - uint256 public internalCash; + event NewCollateralCap(address token, uint256 newCap); + + /** + * @notice Event emitted when user collateral is changed + */ + event UserCollateralChanged(address account, uint256 newCollateralTokens); + + /*** User Interface ***/ + + function registerCollateral(address account) external returns (uint256); + + function unregisterCollateral(address account) external; + + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address initiator, + uint256 amount, + bytes calldata data + ) external returns (bool); + + /*** Admin Functions ***/ + + function _setCollateralCap(uint256 newCollateralCap) external; } -contract CDelegatorInterface is CDelegationStorage { +contract CDelegatorInterface { /** * @notice Emitted when implementation is changed */ @@ -308,10 +457,14 @@ contract CDelegatorInterface is CDelegationStorage { * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ - function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public; + function _setImplementation( + address implementation_, + bool allowResign, + bytes memory becomeImplementationData + ) public; } -contract CDelegateInterface is CDelegationStorage { +contract CDelegateInterface { /** * @notice Called by the delegator on a delegate to initialize it for duty * @dev Should revert if any issues arise which make it unfit for delegation @@ -325,16 +478,17 @@ contract CDelegateInterface is CDelegationStorage { function _resignImplementation() public; } -contract CCapableDelegateInterface is CDelegationStorageExtension { - /** - * @notice Called by the delegator on a delegate to initialize it for duty - * @dev Should revert if any issues arise which make it unfit for delegation - * @param data The encoded bytes data for any initialization - */ - function _becomeImplementation(bytes memory data) public; - - /** - * @notice Called by the delegator on a delegate to forfeit its responsibility - */ - function _resignImplementation() public; +/*** External interface ***/ + +/** + * @title Flash loan receiver interface + */ +interface IFlashloanReceiver { + function executeOperation( + address sender, + address underlying, + uint256 amount, + uint256 fee, + bytes calldata params + ) external; } diff --git a/contracts/CWrappedNative.sol b/contracts/CWrappedNative.sol new file mode 100644 index 0000000..cdf7e5d --- /dev/null +++ b/contracts/CWrappedNative.sol @@ -0,0 +1,734 @@ +pragma solidity ^0.5.16; + +import "./CToken.sol"; +import "./ERC3156FlashBorrowerInterface.sol"; +import "./ERC3156FlashLenderInterface.sol"; + +/** + * @title Wrapped native token interface + */ +interface WrappedNativeInterface { + function deposit() external payable; + + function withdraw(uint256 wad) external; +} + +/** + * @title Cream's CWrappedNative Contract + * @notice CTokens which wrap the native token + * @author Cream + */ +contract CWrappedNative is CToken, CWrappedNativeInterface { + /** + * @notice Initialize the new money market + * @param underlying_ The address of the underlying asset + * @param comptroller_ The address of the Comptroller + * @param interestRateModel_ The address of the interest rate model + * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 + * @param name_ ERC-20 name of this token + * @param symbol_ ERC-20 symbol of this token + * @param decimals_ ERC-20 decimal precision of this token + */ + function initialize( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_ + ) public { + // CToken initialize does the bulk of the work + super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); + + // Set underlying and sanity check it + underlying = underlying_; + EIP20Interface(underlying).totalSupply(); + WrappedNativeInterface(underlying); + } + + /*** User Interface ***/ + + /** + * @notice Sender supplies assets into the market and receives cTokens in exchange + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param mintAmount The amount of the underlying asset to supply + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function mint(uint256 mintAmount) external returns (uint256) { + (uint256 err, ) = mintInternal(mintAmount, false); + require(err == 0, "mint failed"); + } + + /** + * @notice Sender supplies assets into the market and receives cTokens in exchange + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function mintNative() external payable returns (uint256) { + (uint256 err, ) = mintInternal(msg.value, true); + require(err == 0, "mint native failed"); + } + + /** + * @notice Sender redeems cTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param redeemTokens The number of cTokens to redeem into underlying + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeem(uint256 redeemTokens) external returns (uint256) { + require(redeemInternal(redeemTokens, false) == 0, "redeem failed"); + } + + /** + * @notice Sender redeems cTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @param redeemTokens The number of cTokens to redeem into underlying + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemNative(uint256 redeemTokens) external returns (uint256) { + require(redeemInternal(redeemTokens, true) == 0, "redeem native failed"); + } + + /** + * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param redeemAmount The amount of underlying to redeem + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { + require(redeemUnderlyingInternal(redeemAmount, false) == 0, "redeem underlying failed"); + } + + /** + * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @param redeemAmount The amount of underlying to redeem + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemUnderlyingNative(uint256 redeemAmount) external returns (uint256) { + require(redeemUnderlyingInternal(redeemAmount, true) == 0, "redeem underlying native failed"); + } + + /** + * @notice Sender borrows assets from the protocol to their own address + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { + require(borrowInternal(borrowAmount, false) == 0, "borrow failed"); + } + + /** + * @notice Sender borrows assets from the protocol to their own address + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrowNative(uint256 borrowAmount) external returns (uint256) { + require(borrowInternal(borrowAmount, true) == 0, "borrow native failed"); + } + + /** + * @notice Sender repays their own borrow + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrow(uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowInternal(repayAmount, false); + require(err == 0, "repay failed"); + } + + /** + * @notice Sender repays their own borrow + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowNative() external payable returns (uint256) { + (uint256 err, ) = repayBorrowInternal(msg.value, true); + require(err == 0, "repay native failed"); + } + + /** + * @notice Sender repays a borrow belonging to borrower + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param borrower the account with the debt being payed off + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256) { + (uint256 err, ) = repayBorrowBehalfInternal(borrower, repayAmount, false); + require(err == 0, "repay behalf failed"); + } + + /** + * @notice Sender repays a borrow belonging to borrower + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @param borrower the account with the debt being payed off + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowBehalfNative(address borrower) external payable returns (uint256) { + (uint256 err, ) = repayBorrowBehalfInternal(borrower, msg.value, true); + require(err == 0, "repay behalf native failed"); + } + + /** + * @notice The sender liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param borrower The borrower of this cToken to be liquidated + * @param repayAmount The amount of the underlying borrowed asset to repay + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256) { + (uint256 err, ) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral, false); + require(err == 0, "liquidate borrow failed"); + } + + /** + * @notice The sender liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @param borrower The borrower of this cToken to be liquidated + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function liquidateBorrowNative(address borrower, CTokenInterface cTokenCollateral) + external + payable + returns (uint256) + { + (uint256 err, ) = liquidateBorrowInternal(borrower, msg.value, cTokenCollateral, true); + require(err == 0, "liquidate borrow native failed"); + } + + /** + * @notice Get the max flash loan amount + */ + function maxFlashLoan() external view returns (uint256) { + uint256 amount = 0; + if ( + ComptrollerInterfaceExtension(address(comptroller)).flashloanAllowed(address(this), address(0), amount, "") + ) { + amount = getCashPrior(); + } + return amount; + } + + /** + * @notice Get the flash loan fees + * @param amount amount of token to borrow + */ + function flashFee(uint256 amount) external view returns (uint256) { + require( + ComptrollerInterfaceExtension(address(comptroller)).flashloanAllowed(address(this), address(0), amount, ""), + "flashloan is paused" + ); + return div_(mul_(amount, flashFeeBips), 10000); + } + + /** + * @notice Flash loan funds to a given account. + * @param receiver The receiver address for the funds + * @param initiator flash loan initiator + * @param amount The amount of the funds to be loaned + * @param data The other data + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address initiator, + uint256 amount, + bytes calldata data + ) external nonReentrant returns (bool) { + require(amount > 0, "flashLoan amount should be greater than zero"); + require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); + require( + ComptrollerInterfaceExtension(address(comptroller)).flashloanAllowed( + address(this), + address(receiver), + amount, + data + ), + "flashloan is paused" + ); + uint256 cashBefore = getCashPrior(); + require(cashBefore >= amount, "INSUFFICIENT_LIQUIDITY"); + + // 1. calculate fee, 1 bips = 1/10000 + uint256 totalFee = this.flashFee(amount); + + // 2. transfer fund to receiver + doTransferOut(address(uint160(address(receiver))), amount, false); + + // 3. update totalBorrows + totalBorrows = add_(totalBorrows, amount); + + // 4. execute receiver's callback function + require( + receiver.onFlashLoan(initiator, underlying, amount, totalFee, data) == + keccak256("ERC3156FlashBorrowerInterface.onFlashLoan"), + "IERC3156: Callback failed" + ); + + // 5. take amount + fee from receiver, then check balance + uint256 repaymentAmount = add_(amount, totalFee); + + doTransferIn(address(receiver), repaymentAmount, false); + + uint256 cashAfter = getCashPrior(); + require(cashAfter == add_(cashBefore, totalFee), "BALANCE_INCONSISTENT"); + + // 6. update totalReserves and totalBorrows + uint256 reservesFee = mul_ScalarTruncate(Exp({mantissa: reserveFactorMantissa}), totalFee); + totalReserves = add_(totalReserves, reservesFee); + totalBorrows = sub_(totalBorrows, amount); + + emit Flashloan(address(receiver), amount, totalFee, reservesFee); + return true; + } + + function() external payable { + require(msg.sender == underlying, "only wrapped native contract could send native token"); + } + + /** + * @notice The sender adds to reserves. + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for backward compatibility + * @param addAmount The amount fo underlying token to add as reserves + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _addReserves(uint256 addAmount) external returns (uint256) { + require(_addReservesInternal(addAmount, false) == 0, "add reserves failed"); + } + + /** + * @notice The sender adds to reserves. + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * Keep return in the function signature for consistency + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _addReservesNative() external payable returns (uint256) { + require(_addReservesInternal(msg.value, true) == 0, "add reserves failed"); + } + + /*** Safe Token ***/ + + /** + * @notice Gets 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 getCashPrior() internal view returns (uint256) { + EIP20Interface token = EIP20Interface(underlying); + return token.balanceOf(address(this)); + } + + /** + * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case. + * This will revert due to insufficient balance or insufficient allowance. + * This function returns the actual amount received, + * which may be less than `amount` if there is a fee attached to the transfer. + * + * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. + * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca + */ + function doTransferIn( + address from, + uint256 amount, + bool isNative + ) internal returns (uint256) { + if (isNative) { + // Sanity checks + require(msg.sender == from, "sender mismatch"); + require(msg.value == amount, "value mismatch"); + + // Convert received native token to wrapped token + WrappedNativeInterface(underlying).deposit.value(amount)(); + return amount; + } else { + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); + uint256 balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); + token.transferFrom(from, address(this), amount); + + bool success; + assembly { + switch returndatasize() + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a compliant ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } + } + require(success, "TOKEN_TRANSFER_IN_FAILED"); + + // Calculate the amount that was *actually* transferred + uint256 balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); + return sub_(balanceAfter, balanceBefore); + } + } + + /** + * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory + * error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to + * insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified + * it is >= amount, this should not revert in normal conditions. + * + * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. + * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca + */ + function doTransferOut( + address payable to, + uint256 amount, + bool isNative + ) internal { + if (isNative) { + // Convert wrapped token to native token + WrappedNativeInterface(underlying).withdraw(amount); + /* Send the Ether, with minimal gas and revert on failure */ + to.transfer(amount); + } else { + EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); + token.transfer(to, amount); + + bool success; + assembly { + switch returndatasize() + case 0 { + // This is a non-standard ERC-20 + success := not(0) // set success to true + } + case 32 { + // This is a complaint ERC-20 + returndatacopy(0, 0, 32) + success := mload(0) // Set `success = returndata` of external call + } + default { + // This is an excessively non-compliant ERC-20, revert. + revert(0, 0) + } + } + require(success, "TOKEN_TRANSFER_OUT_FAILED"); + } + } + + /** + * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` + * @dev Called by both `transfer` and `transferFrom` internally + * @param spender The address of the account performing the transfer + * @param src The address of the source account + * @param dst The address of the destination account + * @param tokens The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferTokens( + address spender, + address src, + address dst, + uint256 tokens + ) internal returns (uint256) { + /* Fail if transfer not allowed */ + uint256 allowed = comptroller.transferAllowed(address(this), src, dst, tokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed); + } + + /* Do not allow self-transfers */ + if (src == dst) { + return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED); + } + + /* Get the allowance, infinite for the account owner */ + uint256 startingAllowance = 0; + if (spender == src) { + startingAllowance = uint256(-1); + } else { + startingAllowance = transferAllowances[src][spender]; + } + + /* Do the calculations, checking for {under,over}flow */ + accountTokens[src] = sub_(accountTokens[src], tokens); + accountTokens[dst] = add_(accountTokens[dst], tokens); + + /* Eat some of the allowance (if necessary) */ + if (startingAllowance != uint256(-1)) { + transferAllowances[src][spender] = sub_(startingAllowance, tokens); + } + + /* We emit a Transfer event */ + emit Transfer(src, dst, tokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Get the account's cToken balances + * @param account The address of the account + */ + function getCTokenBalanceInternal(address account) internal view returns (uint256) { + return accountTokens[account]; + } + + struct MintLocalVars { + uint256 exchangeRateMantissa; + uint256 mintTokens; + uint256 actualMintAmount; + } + + /** + * @notice User supplies assets into the market and receives cTokens in exchange + * @dev Assumes interest has already been accrued up to the current block + * @param minter The address of the account which is supplying the assets + * @param mintAmount The amount of the underlying asset to supply + * @param isNative The amount is in native or not + * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. + */ + function mintFresh( + address minter, + uint256 mintAmount, + bool isNative + ) internal returns (uint256, uint256) { + /* Fail if mint not allowed */ + uint256 allowed = comptroller.mintAllowed(address(this), minter, mintAmount); + if (allowed != 0) { + return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0); + } + + /* + * Return if mintAmount is zero. + * Put behind `mintAllowed` for accuring potential COMP rewards. + */ + if (mintAmount == 0) { + return (uint256(Error.NO_ERROR), 0); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0); + } + + MintLocalVars memory vars; + + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We call `doTransferIn` for the minter and the mintAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * `doTransferIn` reverts if anything goes wrong, since we can't be sure if + * side-effects occurred. The function returns the amount actually transferred, + * in case of a fee. On success, the cToken holds an additional `actualMintAmount` + * of cash. + */ + vars.actualMintAmount = doTransferIn(minter, mintAmount, isNative); + + /* + * We get the current exchange rate and calculate the number of cTokens to be minted: + * mintTokens = actualMintAmount / exchangeRate + */ + vars.mintTokens = div_ScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa})); + + /* + * We calculate the new total supply of cTokens and minter token balance, checking for overflow: + * totalSupply = totalSupply + mintTokens + * accountTokens[minter] = accountTokens[minter] + mintTokens + */ + totalSupply = add_(totalSupply, vars.mintTokens); + accountTokens[minter] = add_(accountTokens[minter], vars.mintTokens); + + /* We emit a Mint event, and a Transfer event */ + emit Mint(minter, vars.actualMintAmount, vars.mintTokens); + emit Transfer(address(this), minter, vars.mintTokens); + + return (uint256(Error.NO_ERROR), vars.actualMintAmount); + } + + struct RedeemLocalVars { + uint256 exchangeRateMantissa; + uint256 redeemTokens; + uint256 redeemAmount; + uint256 totalSupplyNew; + uint256 accountTokensNew; + } + + /** + * @notice User redeems cTokens in exchange for the underlying asset + * @dev Assumes interest has already been accrued up to the current block. Only one of redeemTokensIn or redeemAmountIn may be non-zero and it would do nothing if both are zero. + * @param redeemer The address of the account which is redeeming the tokens + * @param redeemTokensIn The number of cTokens to redeem into underlying + * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens + * @param isNative The amount is in native or not + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemFresh( + address payable redeemer, + uint256 redeemTokensIn, + uint256 redeemAmountIn, + bool isNative + ) internal returns (uint256) { + require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); + + RedeemLocalVars memory vars; + + /* exchangeRate = invoke Exchange Rate Stored() */ + vars.exchangeRateMantissa = exchangeRateStoredInternal(); + + /* If redeemTokensIn > 0: */ + if (redeemTokensIn > 0) { + /* + * We calculate the exchange rate and the amount of underlying to be redeemed: + * redeemTokens = redeemTokensIn + * redeemAmount = redeemTokensIn x exchangeRateCurrent + */ + vars.redeemTokens = redeemTokensIn; + vars.redeemAmount = mul_ScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn); + } else { + /* + * We get the current exchange rate and calculate the amount to be redeemed: + * redeemTokens = redeemAmountIn / exchangeRate + * redeemAmount = redeemAmountIn + */ + vars.redeemTokens = div_ScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa})); + vars.redeemAmount = redeemAmountIn; + } + + /* Fail if redeem not allowed */ + uint256 allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed); + } + + /* + * Return if redeemTokensIn and redeemAmountIn are zero. + * Put behind `redeemAllowed` for accuring potential COMP rewards. + */ + if (redeemTokensIn == 0 && redeemAmountIn == 0) { + return uint256(Error.NO_ERROR); + } + + /* Verify market's block number equals current block number */ + if (accrualBlockNumber != getBlockNumber()) { + return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK); + } + + /* + * We calculate the new total supply and redeemer balance, checking for underflow: + * totalSupplyNew = totalSupply - redeemTokens + * accountTokensNew = accountTokens[redeemer] - redeemTokens + */ + vars.totalSupplyNew = sub_(totalSupply, vars.redeemTokens); + vars.accountTokensNew = sub_(accountTokens[redeemer], vars.redeemTokens); + + /* Fail gracefully if protocol has insufficient cash */ + if (getCashPrior() < vars.redeemAmount) { + return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE); + } + + ///////////////////////// + // EFFECTS & INTERACTIONS + // (No safe failures beyond this point) + + /* + * We invoke doTransferOut for the redeemer and the redeemAmount. + * Note: The cToken must handle variations between ERC-20 and ETH underlying. + * On success, the cToken has redeemAmount less of cash. + * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. + */ + doTransferOut(redeemer, vars.redeemAmount, isNative); + + /* We write previously calculated values into storage */ + totalSupply = vars.totalSupplyNew; + accountTokens[redeemer] = vars.accountTokensNew; + + /* We emit a Transfer event, and a Redeem event */ + emit Transfer(redeemer, address(this), vars.redeemTokens); + emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens); + + /* We call the defense hook */ + comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. + * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. + * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seizeInternal( + address seizerToken, + address liquidator, + address borrower, + uint256 seizeTokens + ) internal returns (uint256) { + /* Fail if seize not allowed */ + uint256 allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); + if (allowed != 0) { + return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed); + } + + /* + * Return if seizeTokens is zero. + * Put behind `seizeAllowed` for accuring potential COMP rewards. + */ + if (seizeTokens == 0) { + return uint256(Error.NO_ERROR); + } + + /* Fail if borrower = liquidator */ + if (borrower == liquidator) { + return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER); + } + + /* + * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: + * borrowerTokensNew = accountTokens[borrower] - seizeTokens + * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens + */ + accountTokens[borrower] = sub_(accountTokens[borrower], seizeTokens); + accountTokens[liquidator] = add_(accountTokens[liquidator], seizeTokens); + + /* Emit a Transfer event */ + emit Transfer(borrower, liquidator, seizeTokens); + + return uint256(Error.NO_ERROR); + } +} diff --git a/contracts/CWrappedNativeDelegate.sol b/contracts/CWrappedNativeDelegate.sol new file mode 100644 index 0000000..49d6061 --- /dev/null +++ b/contracts/CWrappedNativeDelegate.sol @@ -0,0 +1,53 @@ +pragma solidity ^0.5.16; + +import "./CWrappedNative.sol"; + +/** + * @title Cream's CWrappedNativeDelegate Contract + * @notice CTokens which wrap an EIP-20 underlying and are delegated to + * @author Cream + */ +contract CWrappedNativeDelegate is CWrappedNative { + /** + * @notice Construct an empty delegate + */ + constructor() public {} + + /** + * @notice Called by the delegator on a delegate to initialize it for duty + * @param data The encoded bytes data for any initialization + */ + function _becomeImplementation(bytes memory data) public { + // Shh -- currently unused + data; + + // Shh -- we don't ever want this hook to be marked pure + if (false) { + implementation = address(0); + } + + require(msg.sender == admin, "only the admin may call _becomeImplementation"); + + // Set CToken version in comptroller and convert native token to wrapped token. + ComptrollerInterfaceExtension(address(comptroller)).updateCTokenVersion( + address(this), + ComptrollerV2Storage.Version.WRAPPEDNATIVE + ); + uint256 balance = address(this).balance; + if (balance > 0) { + WrappedNativeInterface(underlying).deposit.value(balance)(); + } + } + + /** + * @notice Called by the delegator on a delegate to forfeit its responsibility + */ + function _resignImplementation() public { + // Shh -- we don't ever want this hook to be marked pure + if (false) { + implementation = address(0); + } + + require(msg.sender == admin, "only the admin may call _resignImplementation"); + } +} diff --git a/contracts/CWrappedNativeDelegator.sol b/contracts/CWrappedNativeDelegator.sol new file mode 100644 index 0000000..89d3031 --- /dev/null +++ b/contracts/CWrappedNativeDelegator.sol @@ -0,0 +1,632 @@ +pragma solidity ^0.5.16; + +import "./CTokenInterfaces.sol"; + +/** + * @title Compound's CWrappedNativeDelegator Contract + * @notice CTokens which wrap an EIP-20 underlying and delegate to an implementation + * @author Compound + */ +contract CWrappedNativeDelegator is CTokenInterface, CWrappedNativeInterface, CDelegatorInterface { + /** + * @notice Construct a new money market + * @param underlying_ The address of the underlying asset + * @param comptroller_ The address of the Comptroller + * @param interestRateModel_ The address of the interest rate model + * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 + * @param name_ ERC-20 name of this token + * @param symbol_ ERC-20 symbol of this token + * @param decimals_ ERC-20 decimal precision of this token + * @param admin_ Address of the administrator of this token + * @param implementation_ The address of the implementation the contract delegates to + * @param becomeImplementationData The encoded args for becomeImplementation + */ + constructor( + address underlying_, + ComptrollerInterface comptroller_, + InterestRateModel interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_, + address payable admin_, + address implementation_, + bytes memory becomeImplementationData + ) public { + // Creator of the contract is admin during initialization + admin = msg.sender; + + // First delegate gets to initialize the delegator (i.e. storage contract) + delegateTo( + implementation_, + abi.encodeWithSignature( + "initialize(address,address,address,uint256,string,string,uint8)", + underlying_, + comptroller_, + interestRateModel_, + initialExchangeRateMantissa_, + name_, + symbol_, + decimals_ + ) + ); + + // New implementations always get set via the settor (post-initialize) + _setImplementation(implementation_, false, becomeImplementationData); + + // Set the proper admin now that initialization is done + admin = admin_; + } + + /** + * @notice Called by the admin to update the implementation of the delegator + * @param implementation_ The address of the new implementation for delegation + * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation + * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation + */ + function _setImplementation( + address implementation_, + bool allowResign, + bytes memory becomeImplementationData + ) public { + require(msg.sender == admin, "CWrappedNativeDelegator::_setImplementation: Caller must be admin"); + + if (allowResign) { + delegateToImplementation(abi.encodeWithSignature("_resignImplementation()")); + } + + address oldImplementation = implementation; + implementation = implementation_; + + delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData)); + + emit NewImplementation(oldImplementation, implementation); + } + + /** + * @notice Sender supplies assets into the market and receives cTokens in exchange + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param mintAmount The amount of the underlying asset to supply + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function mint(uint256 mintAmount) external returns (uint256) { + mintAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender supplies assets into the market and receives cTokens in exchange + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function mintNative() external payable returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Sender redeems cTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemTokens The number of cTokens to redeem into underlying + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeem(uint256 redeemTokens) external returns (uint256) { + redeemTokens; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender redeems cTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemTokens The number of cTokens to redeem into underlying + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemNative(uint256 redeemTokens) external returns (uint256) { + redeemTokens; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemAmount The amount of underlying to redeem + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { + redeemAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemAmount The amount of underlying to redeem + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function redeemUnderlyingNative(uint256 redeemAmount) external returns (uint256) { + redeemAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrow(uint256 borrowAmount) external returns (uint256) { + borrowAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrowNative(uint256 borrowAmount) external returns (uint256) { + borrowAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender repays their own borrow + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrow(uint256 repayAmount) external returns (uint256) { + repayAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender repays their own borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowNative() external payable returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Sender repays a borrow belonging to borrower + * @param borrower the account with the debt being payed off + * @param repayAmount The amount to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256) { + borrower; + repayAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Sender repays a borrow belonging to borrower + * @param borrower the account with the debt being payed off + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function repayBorrowBehalfNative(address borrower) external payable returns (uint256) { + borrower; // Shh + delegateAndReturn(); + } + + /** + * @notice The sender liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @param borrower The borrower of this cToken to be liquidated + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @param repayAmount The amount of the underlying borrowed asset to repay + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function liquidateBorrow( + address borrower, + uint256 repayAmount, + CTokenInterface cTokenCollateral + ) external returns (uint256) { + borrower; + repayAmount; + cTokenCollateral; // Shh + delegateAndReturn(); + } + + /** + * @notice The sender liquidates the borrowers collateral. + * The collateral seized is transferred to the liquidator. + * @param borrower The borrower of this cToken to be liquidated + * @param cTokenCollateral The market in which to seize collateral from the borrower + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function liquidateBorrowNative(address borrower, CTokenInterface cTokenCollateral) + external + payable + returns (uint256) + { + borrower; + cTokenCollateral; // Shh + delegateAndReturn(); + } + + /** + * @notice Flash loan funds to a given account. + * @param receiver The receiver address for the funds + * @param amount The amount of the funds to be loaned + * @param data The other data + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address initiator, + uint256 amount, + bytes calldata data + ) external returns (bool) { + receiver; + amount; + data; // Shh + delegateAndReturn(); + } + + /** + * @notice Transfer `amount` tokens from `msg.sender` to `dst` + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transfer(address dst, uint256 amount) external returns (bool) { + dst; + amount; // Shh + delegateAndReturn(); + } + + /** + * @notice Transfer `amount` tokens from `src` to `dst` + * @param src The address of the source account + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool) { + src; + dst; + amount; // Shh + delegateAndReturn(); + } + + /** + * @notice Approve `spender` to transfer up to `amount` from `src` + * @dev This will overwrite the approval amount for `spender` + * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) + * @param spender The address of the account which may transfer tokens + * @param amount The number of tokens that are approved (-1 means infinite) + * @return Whether or not the approval succeeded + */ + function approve(address spender, uint256 amount) external returns (bool) { + spender; + amount; // Shh + delegateAndReturn(); + } + + /** + * @notice Get the current allowance from `owner` for `spender` + * @param owner The address of the account which owns the tokens to be spent + * @param spender The address of the account which may transfer tokens + * @return The number of tokens allowed to be spent (-1 means infinite) + */ + function allowance(address owner, address spender) external view returns (uint256) { + owner; + spender; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Get the token balance of the `owner` + * @param owner The address of the account to query + * @return The number of tokens owned by `owner` + */ + function balanceOf(address owner) external view returns (uint256) { + owner; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Get the underlying balance of the `owner` + * @dev This also accrues interest in a transaction + * @param owner The address of the account to query + * @return The amount of underlying owned by `owner` + */ + function balanceOfUnderlying(address owner) external returns (uint256) { + owner; // Shh + delegateAndReturn(); + } + + /** + * @notice Get a snapshot of the account's balances, and the cached exchange rate + * @dev This is used by comptroller to more efficiently perform liquidity checks. + * @param account Address of the account to snapshot + * @return (possible error, token balance, borrow balance, exchange rate mantissa) + */ + function getAccountSnapshot(address account) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ) + { + account; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Returns the current per-block borrow interest rate for this cToken + * @return The borrow interest rate per block, scaled by 1e18 + */ + function borrowRatePerBlock() external view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Returns the current per-block supply interest rate for this cToken + * @return The supply interest rate per block, scaled by 1e18 + */ + function supplyRatePerBlock() external view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Returns the current total borrows plus accrued interest + * @return The total borrows with interest + */ + function totalBorrowsCurrent() external returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex + * @param account The address whose balance should be calculated after updating borrowIndex + * @return The calculated balance + */ + function borrowBalanceCurrent(address account) external returns (uint256) { + account; // Shh + delegateAndReturn(); + } + + /** + * @notice Return the borrow balance of account based on stored data + * @param account The address whose balance should be calculated + * @return The calculated balance + */ + function borrowBalanceStored(address account) public view returns (uint256) { + account; // Shh + delegateToViewAndReturn(); + } + + /** + * @notice Accrue interest then return the up-to-date exchange rate + * @return Calculated exchange rate scaled by 1e18 + */ + function exchangeRateCurrent() public returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Calculates the exchange rate from the underlying to the CToken + * @dev This function does not accrue interest before calculating the exchange rate + * @return Calculated exchange rate scaled by 1e18 + */ + function exchangeRateStored() public view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Get cash balance of this cToken in the underlying asset + * @return The quantity of underlying asset owned by this contract + */ + function getCash() external view returns (uint256) { + delegateToViewAndReturn(); + } + + /** + * @notice Applies accrued interest to total borrows and reserves. + * @dev This calculates interest accrued from the last checkpointed block + * up to the current block and writes new checkpoint to storage. + */ + function accrueInterest() public returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Transfers collateral tokens (this market) to the liquidator. + * @dev Will fail unless called by another cToken during the process of liquidation. + * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. + * @param liquidator The account receiving seized collateral + * @param borrower The account having collateral seized + * @param seizeTokens The number of cTokens to seize + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function seize( + address liquidator, + address borrower, + uint256 seizeTokens + ) external returns (uint256) { + liquidator; + borrower; + seizeTokens; // Shh + delegateAndReturn(); + } + + /*** Admin Functions ***/ + + /** + * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @param newPendingAdmin New pending admin. + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256) { + newPendingAdmin; // Shh + delegateAndReturn(); + } + + /** + * @notice Sets a new comptroller for the market + * @dev Admin function to set a new comptroller + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setComptroller(ComptrollerInterface newComptroller) public returns (uint256) { + newComptroller; // Shh + delegateAndReturn(); + } + + /** + * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh + * @dev Admin function to accrue interest and set a new reserve factor + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setReserveFactor(uint256 newReserveFactorMantissa) external returns (uint256) { + newReserveFactorMantissa; // Shh + delegateAndReturn(); + } + + /** + * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + * @dev Admin function for pending admin to accept role and update admin + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _acceptAdmin() external returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Accrues interest and adds reserves by transferring from admin + * @param addAmount Amount of reserves to add + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _addReserves(uint256 addAmount) external returns (uint256) { + addAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Accrues interest and adds reserves by transferring from admin + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _addReservesNative() external payable returns (uint256) { + delegateAndReturn(); + } + + /** + * @notice Accrues interest and reduces reserves by transferring to admin + * @param reduceAmount Amount of reduction to reserves + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _reduceReserves(uint256 reduceAmount) external returns (uint256) { + reduceAmount; // Shh + delegateAndReturn(); + } + + /** + * @notice Accrues interest and updates the interest rate model using _setInterestRateModelFresh + * @dev Admin function to accrue interest and update the interest rate model + * @param newInterestRateModel the new interest rate model to use + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256) { + newInterestRateModel; // Shh + delegateAndReturn(); + } + + /** + * @notice Internal method to delegate execution to another contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + * @param callee The contract to delegatecall + * @param data The raw data to delegatecall + * @return The returned bytes from the delegatecall + */ + function delegateTo(address callee, bytes memory data) internal returns (bytes memory) { + (bool success, bytes memory returnData) = callee.delegatecall(data); + assembly { + if eq(success, 0) { + revert(add(returnData, 0x20), returndatasize) + } + } + return returnData; + } + + /** + * @notice Delegates execution to the implementation contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + * @param data The raw data to delegatecall + * @return The returned bytes from the delegatecall + */ + function delegateToImplementation(bytes memory data) public returns (bytes memory) { + return delegateTo(implementation, data); + } + + /** + * @notice Delegates execution to an implementation contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop. + * @param data The raw data to delegatecall + * @return The returned bytes from the delegatecall + */ + function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { + (bool success, bytes memory returnData) = address(this).staticcall( + abi.encodeWithSignature("delegateToImplementation(bytes)", data) + ); + assembly { + if eq(success, 0) { + revert(add(returnData, 0x20), returndatasize) + } + } + return abi.decode(returnData, (bytes)); + } + + function delegateToViewAndReturn() private view returns (bytes memory) { + (bool success, ) = address(this).staticcall( + abi.encodeWithSignature("delegateToImplementation(bytes)", msg.data) + ); + + assembly { + let free_mem_ptr := mload(0x40) + returndatacopy(free_mem_ptr, 0, returndatasize) + + switch success + case 0 { + revert(free_mem_ptr, returndatasize) + } + default { + return(add(free_mem_ptr, 0x40), returndatasize) + } + } + } + + function delegateAndReturn() private returns (bytes memory) { + (bool success, ) = implementation.delegatecall(msg.data); + + assembly { + let free_mem_ptr := mload(0x40) + returndatacopy(free_mem_ptr, 0, returndatasize) + + switch success + case 0 { + revert(free_mem_ptr, returndatasize) + } + default { + return(free_mem_ptr, returndatasize) + } + } + } + + /** + * @notice Delegates execution to an implementation contract + * @dev It returns to the external caller whatever the implementation returns or forwards reverts + */ + function() external payable { + // delegate all other functions to current implementation + delegateAndReturn(); + } +} diff --git a/contracts/CarefulMath.sol b/contracts/CarefulMath.sol index 4f5be2e..6c1359f 100644 --- a/contracts/CarefulMath.sol +++ b/contracts/CarefulMath.sol @@ -1,13 +1,12 @@ pragma solidity ^0.5.16; /** - * @title Careful Math - * @author Compound - * @notice Derived from OpenZeppelin's SafeMath library - * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol - */ + * @title Careful Math + * @author Compound + * @notice Derived from OpenZeppelin's SafeMath library + * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol + */ contract CarefulMath { - /** * @dev Possible error codes that we can return */ @@ -19,14 +18,14 @@ contract CarefulMath { } /** - * @dev Multiplies two numbers, returns an error on overflow. - */ - function mulUInt(uint a, uint b) internal pure returns (MathError, uint) { + * @dev Multiplies two numbers, returns an error on overflow. + */ + function mulUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { if (a == 0) { return (MathError.NO_ERROR, 0); } - uint c = a * b; + uint256 c = a * b; if (c / a != b) { return (MathError.INTEGER_OVERFLOW, 0); @@ -36,9 +35,9 @@ contract CarefulMath { } /** - * @dev Integer division of two numbers, truncating the quotient. - */ - function divUInt(uint a, uint b) internal pure returns (MathError, uint) { + * @dev Integer division of two numbers, truncating the quotient. + */ + function divUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { if (b == 0) { return (MathError.DIVISION_BY_ZERO, 0); } @@ -47,9 +46,9 @@ contract CarefulMath { } /** - * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). - */ - function subUInt(uint a, uint b) internal pure returns (MathError, uint) { + * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). + */ + function subUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { if (b <= a) { return (MathError.NO_ERROR, a - b); } else { @@ -58,10 +57,10 @@ contract CarefulMath { } /** - * @dev Adds two numbers, returns an error on overflow. - */ - function addUInt(uint a, uint b) internal pure returns (MathError, uint) { - uint c = a + b; + * @dev Adds two numbers, returns an error on overflow. + */ + function addUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { + uint256 c = a + b; if (c >= a) { return (MathError.NO_ERROR, c); @@ -71,10 +70,14 @@ contract CarefulMath { } /** - * @dev add a and b and then subtract c - */ - function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) { - (MathError err0, uint sum) = addUInt(a, b); + * @dev add a and b and then subtract c + */ + function addThenSubUInt( + uint256 a, + uint256 b, + uint256 c + ) internal pure returns (MathError, uint256) { + (MathError err0, uint256 sum) = addUInt(a, b); if (err0 != MathError.NO_ERROR) { return (err0, 0); @@ -82,4 +85,4 @@ contract CarefulMath { return subUInt(sum, c); } -} \ No newline at end of file +} diff --git a/contracts/Comptroller.sol b/contracts/Comptroller.sol index 4a3be8a..6b3ed17 100644 --- a/contracts/Comptroller.sol +++ b/contracts/Comptroller.sol @@ -6,17 +6,21 @@ import "./Exponential.sol"; import "./PriceOracle.sol"; import "./ComptrollerInterface.sol"; import "./ComptrollerStorage.sol"; +import "./LiquidityMiningInterface.sol"; import "./Unitroller.sol"; import "./Governance/Comp.sol"; /** * @title Compound's Comptroller Contract - * @author Compound (modified by Arr00) + * @author Compound (modified by Cream) */ -contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { +contract Comptroller is ComptrollerV7Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { /// @notice Emitted when an admin supports a market event MarketListed(CToken cToken); + /// @notice Emitted when an admin delists a market + event MarketDelisted(CToken cToken); + /// @notice Emitted when an account enters a market event MarketEntered(CToken cToken, address account); @@ -24,13 +28,13 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE event MarketExited(CToken cToken, address account); /// @notice Emitted when close factor is changed by admin - event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); /// @notice Emitted when a collateral factor is changed by admin - event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); + event NewCollateralFactor(CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); /// @notice Emitted when liquidation incentive is changed by admin - event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); @@ -38,41 +42,51 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE /// @notice Emitted when pause guardian is changed event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); + /// @notice Emitted when liquidity mining module is changed + event NewLiquidityMining(address oldLiquidityMining, address newLiquidityMining); + /// @notice Emitted when an action is paused globally event ActionPaused(string action, bool pauseState); /// @notice Emitted when an action is paused on a market event ActionPaused(CToken cToken, string action, bool pauseState); - /// @notice Emitted when a new COMP speed is calculated for a market - event CompSpeedUpdated(CToken indexed cToken, uint newSpeed); - /// @notice Emitted when COMP is distributed to a supplier - event DistributedSupplierComp(CToken indexed cToken, address indexed supplier, uint compDelta, uint compSupplyIndex); + event DistributedSupplierComp( + CToken indexed cToken, + address indexed supplier, + uint256 compDelta, + uint256 compSupplyIndex + ); /// @notice Emitted when COMP is distributed to a borrower - event DistributedBorrowerComp(CToken indexed cToken, address indexed borrower, uint compDelta, uint compBorrowIndex); + event DistributedBorrowerComp( + CToken indexed cToken, + address indexed borrower, + uint256 compDelta, + uint256 compBorrowIndex + ); /// @notice Emitted when borrow cap for a cToken is changed - event NewBorrowCap(CToken indexed cToken, uint newBorrowCap); + event NewBorrowCap(CToken indexed cToken, uint256 newBorrowCap); /// @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); + event NewSupplyCap(CToken indexed cToken, uint256 newSupplyCap); /// @notice Emitted when supply cap guardian is changed event NewSupplyCapGuardian(address oldSupplyCapGuardian, address newSupplyCapGuardian); - /// @notice The threshold above which the flywheel transfers COMP, in wei - uint public constant compClaimThreshold = 0.001e18; + /// @notice Emitted when cToken version is changed + event NewCTokenVersion(CToken cToken, Version oldVersion, Version newVersion); /// @notice The initial COMP index for a market uint224 public constant compInitialIndex = 1e36; // No collateralFactorMantissa may exceed this value - uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 constructor() public { admin = msg.sender; @@ -106,14 +120,14 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ - function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { - uint len = cTokens.length; + function enterMarkets(address[] memory cTokens) public returns (uint256[] memory) { + uint256 len = cTokens.length; - uint[] memory results = new uint[](len); - for (uint i = 0; i < len; i++) { + uint256[] memory results = new uint256[](len); + for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); - results[i] = uint(addToMarketInternal(cToken, msg.sender)); + results[i] = uint256(addToMarketInternal(cToken, msg.sender)); } return results; @@ -133,6 +147,11 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE return Error.MARKET_NOT_LISTED; } + if (marketToJoin.version == Version.COLLATERALCAP) { + // register collateral for the borrower if the token is CollateralCap version. + CCollateralCapErc20Interface(address(cToken)).registerCollateral(borrower); + } + if (marketToJoin.accountMembership[borrower] == true) { // already joined return Error.NO_ERROR; @@ -158,10 +177,10 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ - function exitMarket(address cTokenAddress) external returns (uint) { + function exitMarket(address cTokenAddress) external returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ - (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ @@ -170,16 +189,20 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE } /* Fail if the sender is not permitted to redeem all of their tokens */ - uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } - Market storage marketToExit = markets[address(cToken)]; + Market storage marketToExit = markets[cTokenAddress]; + + if (marketToExit.version == Version.COLLATERALCAP) { + CCollateralCapErc20Interface(cTokenAddress).unregisterCollateral(msg.sender); + } /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ @@ -188,9 +211,9 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; - uint len = userAssetList.length; - uint assetIndex = len; - for (uint i = 0; i < len; i++) { + uint256 len = userAssetList.length; + uint256 assetIndex = len; + for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; @@ -202,12 +225,23 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE // copy last item in list to location of item to be removed, reduce length by 1 CToken[] storage storedList = accountAssets[msg.sender]; - storedList[assetIndex] = storedList[storedList.length - 1]; + if (assetIndex != storedList.length - 1) { + storedList[assetIndex] = storedList[storedList.length - 1]; + } storedList.length--; emit MarketExited(cToken, msg.sender); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); + } + + /** + * @notice Return a specific market is listed or not + * @param cTokenAddress The address of the asset to be checked + * @return Whether or not the market is listed + */ + function isMarketListed(address cTokenAddress) public view returns (bool) { + return markets[cTokenAddress].isListed; } /*** Policy Hooks ***/ @@ -219,36 +253,36 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken], "mint is paused"); // Shh - currently unused minter; - if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + if (!isMarketListed(cToken)) { + return uint256(Error.MARKET_NOT_LISTED); } - uint supplyCap = supplyCaps[cToken]; + uint256 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(); + uint256 totalCash = CToken(cToken).getCash(); + uint256 totalBorrows = CToken(cToken).totalBorrows(); + uint256 totalReserves = CToken(cToken).totalReserves(); // totalSupplies = totalCash + totalBorrows - totalReserves - (MathError mathErr, uint totalSupplies) = addThenSubUInt(totalCash, totalBorrows, totalReserves); + (MathError mathErr, uint256 totalSupplies) = addThenSubUInt(totalCash, totalBorrows, totalReserves); require(mathErr == MathError.NO_ERROR, "totalSupplies failed"); - uint nextTotalSupplies = add_(totalSupplies, mintAmount); + uint256 nextTotalSupplies = add_(totalSupplies, mintAmount); require(nextTotalSupplies < supplyCap, "market supply cap reached"); } - // Keep the flywheel moving - updateCompSupplyIndex(cToken); - distributeSupplierComp(cToken, minter, false); - - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -258,7 +292,12 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ - function mintVerify(address cToken, address minter, uint actualMintAmount, uint mintTokens) external { + function mintVerify( + address cToken, + address minter, + uint256 actualMintAmount, + uint256 mintTokens + ) external { // Shh - currently unused cToken; minter; @@ -278,39 +317,43 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { - uint allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); - if (allowed != uint(Error.NO_ERROR)) { - return allowed; - } - - // Keep the flywheel moving - updateCompSupplyIndex(cToken); - distributeSupplierComp(cToken, redeemer, false); - - return uint(Error.NO_ERROR); + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256) { + return redeemAllowedInternal(cToken, redeemer, redeemTokens); } - function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { - if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + function redeemAllowedInternal( + address cToken, + address redeemer, + uint256 redeemTokens + ) internal view returns (uint256) { + if (!isMarketListed(cToken)) { + return uint256(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + redeemer, + CToken(cToken), + redeemTokens, + 0 + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -320,7 +363,12 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ - function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external { // Shh - currently unused cToken; redeemer; @@ -338,12 +386,16 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "borrow is paused"); - if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + if (!isMarketListed(cToken)) { + return uint256(Error.MARKET_NOT_LISTED); } if (!markets[cToken].accountMembership[borrower]) { @@ -353,7 +405,7 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } // it should be impossible to break the important invariant @@ -361,32 +413,31 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { - return uint(Error.PRICE_ERROR); + return uint256(Error.PRICE_ERROR); } - - uint borrowCap = borrowCaps[cToken]; + uint256 borrowCap = borrowCaps[cToken]; // Borrow cap of 0 corresponds to unlimited borrowing if (borrowCap != 0) { - uint totalBorrows = CToken(cToken).totalBorrows(); - uint nextTotalBorrows = add_(totalBorrows, borrowAmount); + uint256 totalBorrows = CToken(cToken).totalBorrows(); + uint256 nextTotalBorrows = add_(totalBorrows, borrowAmount); require(nextTotalBorrows < borrowCap, "market borrow cap reached"); } - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + borrower, + CToken(cToken), + 0, + borrowAmount + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - // Keep the flywheel moving - Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); - updateCompBorrowIndex(cToken, borrowIndex); - distributeBorrowerComp(cToken, borrower, borrowIndex, false); - - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -395,7 +446,11 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ - function borrowVerify(address cToken, address borrower, uint borrowAmount) external { + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external { // Shh - currently unused cToken; borrower; @@ -419,22 +474,18 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE address cToken, address payer, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; - if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + if (!isMarketListed(cToken)) { + return uint256(Error.MARKET_NOT_LISTED); } - // Keep the flywheel moving - Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); - updateCompBorrowIndex(cToken, borrowIndex); - distributeBorrowerComp(cToken, borrower, borrowIndex, false); - - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -448,8 +499,9 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE address cToken, address payer, address borrower, - uint actualRepayAmount, - uint borrowerIndex) external { + uint256 actualRepayAmount, + uint256 borrowerIndex + ) external { // Shh - currently unused cToken; payer; @@ -476,31 +528,32 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE address cTokenCollateral, address liquidator, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused liquidator; - if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { - return uint(Error.MARKET_NOT_LISTED); + if (!isMarketListed(cTokenBorrowed) || !isMarketListed(cTokenCollateral)) { + return uint256(Error.MARKET_NOT_LISTED); } /* The borrower must have shortfall in order to be liquidatable */ - (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); + (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall == 0) { - return uint(Error.INSUFFICIENT_SHORTFALL); + return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ - uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); - uint maxClose = mul_ScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + uint256 maxClose = mul_ScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (repayAmount > maxClose) { - return uint(Error.TOO_MUCH_REPAY); + return uint256(Error.TOO_MUCH_REPAY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -516,8 +569,9 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE address cTokenCollateral, address liquidator, address borrower, - uint actualRepayAmount, - uint seizeTokens) external { + uint256 actualRepayAmount, + uint256 seizeTokens + ) external { // Shh - currently unused cTokenBorrowed; cTokenCollateral; @@ -545,27 +599,25 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external returns (uint) { + uint256 seizeTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); // Shh - currently unused + liquidator; + borrower; seizeTokens; - if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { - return uint(Error.MARKET_NOT_LISTED); + if (!isMarketListed(cTokenCollateral) || !isMarketListed(cTokenBorrowed)) { + return uint256(Error.MARKET_NOT_LISTED); } if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { - return uint(Error.COMPTROLLER_MISMATCH); + return uint256(Error.COMPTROLLER_MISMATCH); } - // Keep the flywheel moving - updateCompSupplyIndex(cTokenCollateral); - distributeSupplierComp(cTokenCollateral, borrower, false); - distributeSupplierComp(cTokenCollateral, liquidator, false); - - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -581,7 +633,8 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external { + uint256 seizeTokens + ) external { // Shh - currently unused cTokenCollateral; cTokenBorrowed; @@ -603,23 +656,21 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); + // Shh - currently unused + dst; + // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens - uint allowed = redeemAllowedInternal(cToken, src, transferTokens); - if (allowed != uint(Error.NO_ERROR)) { - return allowed; - } - - // Keep the flywheel moving - updateCompSupplyIndex(cToken); - distributeSupplierComp(cToken, src, false); - distributeSupplierComp(cToken, dst, false); - - return uint(Error.NO_ERROR); + return redeemAllowedInternal(cToken, src, transferTokens); } /** @@ -629,7 +680,12 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ - function transferVerify(address cToken, address src, address dst, uint transferTokens) external { + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external { // Shh - currently unused cToken; src; @@ -642,6 +698,42 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE } } + /** + * @notice Checks if the account should be allowed to transfer tokens in the given market + * @param cToken The market to verify the transfer against + * @param receiver The account which receives the tokens + * @param amount The amount of the tokens + * @param params The other parameters + */ + + function flashloanAllowed( + address cToken, + address receiver, + uint256 amount, + bytes calldata params + ) external view returns (bool) { + return !flashloanGuardianPaused[cToken]; + } + + /** + * @notice Update CToken's version. + * @param cToken Version of the asset being updated + * @param newVersion The new version + */ + function updateCTokenVersion(address cToken, Version newVersion) external { + require(msg.sender == cToken, "only cToken could update its version"); + + // This function will be called when a new CToken implementation becomes active. + // If a new CToken is newly created, this market is not listed yet. The version of + // this market will be taken care of when calling `_supportMarket`. + if (isMarketListed(cToken)) { + Version oldVersion = markets[cToken].version; + markets[cToken].version = newVersion; + + emit NewCTokenVersion(CToken(cToken), oldVersion, newVersion); + } + } + /*** Liquidity/Liquidation Calculations ***/ /** @@ -650,12 +742,12 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { - uint sumCollateral; - uint sumBorrowPlusEffects; - uint cTokenBalance; - uint borrowBalance; - uint exchangeRateMantissa; - uint oraclePriceMantissa; + uint256 sumCollateral; + uint256 sumBorrowPlusEffects; + uint256 cTokenBalance; + uint256 borrowBalance; + uint256 exchangeRateMantissa; + uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; @@ -668,10 +760,23 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidity(address account) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); - - return (uint(err), liquidity, shortfall); + function getAccountLiquidity(address account) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(0), + 0, + 0 + ); + + return (uint256(err), liquidity, shortfall); } /** @@ -680,7 +785,15 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { + function getAccountLiquidityInternal(address account) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); } @@ -697,10 +810,24 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE function getHypotheticalAccountLiquidity( address account, address cTokenModify, - uint redeemTokens, - uint borrowAmount) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); - return (uint(err), liquidity, shortfall); + uint256 redeemTokens, + uint256 borrowAmount + ) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(cTokenModify), + redeemTokens, + borrowAmount + ); + return (uint256(err), liquidity, shortfall); } /** @@ -718,22 +845,40 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, - uint redeemTokens, - uint borrowAmount) internal view returns (Error, uint, uint) { - + uint256 redeemTokens, + uint256 borrowAmount + ) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { AccountLiquidityLocalVars memory vars; // Holds all our calculation results - uint oErr; + uint256 oErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; - for (uint i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken - (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); - if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( + account + ); + if (oErr != 0) { + // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } + + // Unlike compound protocol, getUnderlyingPrice is relatively expensive because we use ChainLink as our primary price feed. + // If user has no supply / borrow balance on this asset, and user is not redeeming / borrowing this asset, skip it. + if (vars.cTokenBalance == 0 && vars.borrowBalance == 0 && asset != cTokenModify) { + continue; + } + vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); @@ -751,17 +896,29 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE vars.sumCollateral = mul_ScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral); // sumBorrowPlusEffects += oraclePrice * borrowBalance - vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); + vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( + vars.oraclePrice, + vars.borrowBalance, + vars.sumBorrowPlusEffects + ); // Calculate effects of interacting with cTokenModify if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens - vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects); + vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( + vars.tokensToDenom, + redeemTokens, + vars.sumBorrowPlusEffects + ); // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount - vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); + vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( + vars.oraclePrice, + borrowAmount, + vars.sumBorrowPlusEffects + ); } } @@ -781,12 +938,16 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ - function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 actualRepayAmount + ) external view returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ - uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); - uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { - return (uint(Error.PRICE_ERROR), 0); + return (uint256(Error.PRICE_ERROR), 0); } /* @@ -795,23 +956,26 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ - uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error - Exp memory numerator = mul_(Exp({mantissa: liquidationIncentiveMantissa}), Exp({mantissa: priceBorrowedMantissa})); + uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + Exp memory numerator = mul_( + Exp({mantissa: liquidationIncentiveMantissa}), + Exp({mantissa: priceBorrowedMantissa}) + ); Exp memory denominator = mul_(Exp({mantissa: priceCollateralMantissa}), Exp({mantissa: exchangeRateMantissa})); Exp memory ratio = div_(numerator, denominator); - uint seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); + uint256 seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); - return (uint(Error.NO_ERROR), seizeTokens); + return (uint256(Error.NO_ERROR), seizeTokens); } /*** Admin Functions ***/ /** - * @notice Sets a new price oracle for the comptroller - * @dev Admin function to set a new price oracle - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPriceOracle(PriceOracle newOracle) public returns (uint) { + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); @@ -826,36 +990,36 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the closeFactor used when liquidating borrows - * @dev Admin function to set closeFactor - * @param newCloseFactorMantissa New close factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) { + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); } - uint oldCloseFactorMantissa = closeFactorMantissa; + uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the collateralFactor for a market - * @dev Admin function to set per-market collateralFactor - * @param cToken The market to set the factor on - * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint) { + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint256 newCollateralFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); @@ -881,29 +1045,29 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE } // Set market's collateral factor to new collateral factor, remember old value - uint oldCollateralFactorMantissa = market.collateralFactorMantissa; + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets liquidationIncentive - * @dev Admin function to set liquidationIncentive - * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); } // Save current value for use in log - uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; @@ -911,54 +1075,66 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Add the market to the markets mapping and set it as listed - * @dev Admin function to set isListed and add support for the market - * @param cToken The address of the market (token) to list - * @return uint 0=success, otherwise a failure. (See enum Error for details) - */ - function _supportMarket(CToken cToken) external returns (uint) { - if (msg.sender != admin) { - return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); - } - - if (markets[address(cToken)].isListed) { - return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); - } + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @param version The version of the market (token) + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken, Version version) external returns (uint256) { + require(msg.sender == admin, "only admin may support market"); + require(!isMarketListed(address(cToken)), "market already listed"); cToken.isCToken(); // Sanity check to make sure its really a CToken - // TODO: isComped is unused. Remove it in v2. - markets[address(cToken)] = Market({isListed: true, isComped: true, collateralFactorMantissa: 0}); + markets[address(cToken)] = Market({ + isListed: true, + isComped: true, + collateralFactorMantissa: 0, + version: version + }); _addMarketInternal(address(cToken)); emit MarketListed(cToken); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } - function _addMarketInternal(address cToken) internal { - for (uint i = 0; i < allMarkets.length; i ++) { - require(allMarkets[i] != CToken(cToken), "market already added"); - } - allMarkets.push(CToken(cToken)); - } + /** + * @notice Remove the market from the markets mapping + * @param cToken The address of the market (token) to delist + */ + function _delistMarket(CToken cToken) external { + require(msg.sender == admin, "only admin may delist market"); + require(isMarketListed(address(cToken)), "market not listed"); + require(cToken.totalSupply() == 0, "market not empty"); + + cToken.isCToken(); // Sanity check to make sure its really a CToken - function _dropInvalidMarket() external { - address invalidMarket = 0xBdf447B39D152d6A234B4c02772B8ab5D1783F72; + delete markets[address(cToken)]; - for (uint i = 0; i < allMarkets.length; i++) { - if (address(allMarkets[i]) == invalidMarket) { + for (uint256 i = 0; i < allMarkets.length; i++) { + if (allMarkets[i] == cToken) { allMarkets[i] = allMarkets[allMarkets.length - 1]; delete allMarkets[allMarkets.length - 1]; allMarkets.length--; break; } } + + emit MarketDelisted(cToken); + } + + function _addMarketInternal(address cToken) internal { + for (uint256 i = 0; i < allMarkets.length; i++) { + require(allMarkets[i] != CToken(cToken), "market already added"); + } + allMarkets.push(CToken(cToken)); } /** @@ -979,40 +1155,48 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE } /** - * @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"); + * @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. If the total borrows + * already exceeded the cap, it will prevent anyone to borrow. + * @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, uint256[] 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; + uint256 numMarkets = cTokens.length; + uint256 numSupplyCaps = newSupplyCaps.length; require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input"); - for (uint i = 0; i < numMarkets; i++) { + for (uint256 i = 0; i < numMarkets; i++) { supplyCaps[address(cTokens[i])] = newSupplyCaps[i]; emit NewSupplyCap(cTokens[i], newSupplyCaps[i]); } } /** - * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. - * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. - * @param cTokens The addresses of the markets (tokens) to change the borrow caps for - * @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"); + * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. + * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. If the total supplies + * already exceeded the cap, it will prevent anyone to mint. + * @param cTokens The addresses of the markets (tokens) to change the borrow caps for + * @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, uint256[] calldata newBorrowCaps) external { + 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; + uint256 numMarkets = cTokens.length; + uint256 numBorrowCaps = newBorrowCaps.length; require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); - for (uint i = 0; i < numMarkets; i++) { + for (uint256 i = 0; i < numMarkets; i++) { borrowCaps[address(cTokens[i])] = newBorrowCaps[i]; emit NewBorrowCap(cTokens[i], newBorrowCaps[i]); } @@ -1040,7 +1224,7 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ - function _setPauseGuardian(address newPauseGuardian) public returns (uint) { + function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); } @@ -1054,11 +1238,30 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); + } + + /** + * @notice Admin function to set the liquidity mining module address + * @dev Removing the liquidity mining module address could cause the inconsistency in the LM module. + * @param newLiquidityMining The address of the new liquidity mining module + */ + function _setLiquidityMining(address newLiquidityMining) external { + require(msg.sender == admin, "only admin can set liquidity mining module"); + require(LiquidityMiningInterface(newLiquidityMining).comptroller() == address(this), "mismatch comptroller"); + + // Save current value for inclusion in log + address oldLiquidityMining = liquidityMining; + + // Store pauseGuardian with value newLiquidityMining + liquidityMining = newLiquidityMining; + + // Emit NewLiquidityMining(OldLiquidityMining, NewLiquidityMining) + emit NewLiquidityMining(oldLiquidityMining, liquidityMining); } function _setMintPaused(CToken cToken, bool state) public returns (bool) { - require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); + require(isMarketListed(address(cToken)), "cannot pause a market that is not listed"); require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); @@ -1068,7 +1271,7 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE } function _setBorrowPaused(CToken cToken, bool state) public returns (bool) { - require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); + require(isMarketListed(address(cToken)), "cannot pause a market that is not listed"); require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); @@ -1077,6 +1280,16 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE return state; } + function _setFlashloanPaused(CToken cToken, bool state) public returns (bool) { + require(isMarketListed(address(cToken)), "cannot pause a market that is not listed"); + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + flashloanGuardianPaused[address(cToken)] = state; + emit ActionPaused(cToken, "Flashloan", state); + return state; + } + function _setTransferPaused(bool state) public returns (bool) { require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); @@ -1100,67 +1313,19 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE require(unitroller._acceptImplementation() == 0, "change not authorized"); } - /** - * @notice Checks caller is admin, or this contract is becoming the new implementation - */ - function adminOrInitializing() internal view returns (bool) { - return msg.sender == admin || msg.sender == comptrollerImplementation; - } - /*** Comp Distribution ***/ - /** - * @notice Accrue COMP to the market by updating the supply index - * @param cToken The market whose supply index to update - */ - function updateCompSupplyIndex(address cToken) internal { - CompMarketState storage supplyState = compSupplyState[cToken]; - uint supplySpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(supplyState.block)); - if (deltaBlocks > 0 && supplySpeed > 0) { - uint supplyTokens = CToken(cToken).totalSupply(); - uint compAccrued = mul_(deltaBlocks, supplySpeed); - Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); - Double memory index = add_(Double({mantissa: supplyState.index}), ratio); - compSupplyState[cToken] = CompMarketState({ - index: safe224(index.mantissa, "new index exceeds 224 bits"), - block: safe32(blockNumber, "block number exceeds 32 bits") - }); - } else if (deltaBlocks > 0) { - supplyState.block = safe32(blockNumber, "block number exceeds 32 bits"); - } - } - - /** - * @notice Accrue COMP to the market by updating the borrow index - * @param cToken The market whose borrow index to update - */ - function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { - CompMarketState storage borrowState = compBorrowState[cToken]; - uint borrowSpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(borrowState.block)); - if (deltaBlocks > 0 && borrowSpeed > 0) { - uint borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); - uint compAccrued = mul_(deltaBlocks, borrowSpeed); - Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); - Double memory index = add_(Double({mantissa: borrowState.index}), ratio); - compBorrowState[cToken] = CompMarketState({ - index: safe224(index.mantissa, "new index exceeds 224 bits"), - block: safe32(blockNumber, "block number exceeds 32 bits") - }); - } else if (deltaBlocks > 0) { - borrowState.block = safe32(blockNumber, "block number exceeds 32 bits"); - } - } - /** * @notice Calculate COMP accrued by a supplier and possibly transfer it to them * @param cToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute COMP to */ - function distributeSupplierComp(address cToken, address supplier, bool distributeAll) internal { + function distributeSupplierComp(address cToken, address supplier) internal { + // We won't relaunch LM program on comptroller again. Do nothing if the user's supplierIndex is 0. + if (compSupplierIndex[cToken][supplier] == 0) { + return; + } + CompMarketState storage supplyState = compSupplyState[cToken]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({mantissa: compSupplierIndex[cToken][supplier]}); @@ -1171,10 +1336,10 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); - uint supplierTokens = CToken(cToken).balanceOf(supplier); - uint supplierDelta = mul_(supplierTokens, deltaIndex); - uint supplierAccrued = add_(compAccrued[supplier], supplierDelta); - compAccrued[supplier] = transferComp(supplier, supplierAccrued, distributeAll ? 0 : compClaimThreshold); + uint256 supplierTokens = CToken(cToken).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); + uint256 supplierAccrued = add_(compAccrued[supplier], supplierDelta); + compAccrued[supplier] = transferComp(supplier, supplierAccrued); emit DistributedSupplierComp(CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa); } @@ -1184,7 +1349,16 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param cToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute COMP to */ - function distributeBorrowerComp(address cToken, address borrower, Exp memory marketBorrowIndex, bool distributeAll) internal { + function distributeBorrowerComp( + address cToken, + address borrower, + Exp memory marketBorrowIndex + ) internal { + // We won't relaunch LM program on comptroller again. Do nothing if the user's borrowerIndex is 0. + if (compBorrowerIndex[cToken][borrower] == 0) { + return; + } + CompMarketState storage borrowState = compBorrowState[cToken]; Double memory borrowIndex = Double({mantissa: borrowState.index}); Double memory borrowerIndex = Double({mantissa: compBorrowerIndex[cToken][borrower]}); @@ -1192,10 +1366,10 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE if (borrowerIndex.mantissa > 0) { Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); - uint borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); - uint borrowerDelta = mul_(borrowerAmount, deltaIndex); - uint borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); - compAccrued[borrower] = transferComp(borrower, borrowerAccrued, distributeAll ? 0 : compClaimThreshold); + uint256 borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); + uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); + uint256 borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); + compAccrued[borrower] = transferComp(borrower, borrowerAccrued); emit DistributedBorrowerComp(CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa); } } @@ -1207,10 +1381,10 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param userAccrued The amount of COMP to (possibly) transfer * @return The amount of COMP which was NOT transferred to the user */ - function transferComp(address user, uint userAccrued, uint threshold) internal returns (uint) { - if (userAccrued >= threshold && userAccrued > 0) { + function transferComp(address user, uint256 userAccrued) internal returns (uint256) { + if (userAccrued > 0) { Comp comp = Comp(getCompAddress()); - uint compRemaining = comp.balanceOf(address(this)); + uint256 compRemaining = comp.balanceOf(address(this)); if (userAccrued <= compRemaining) { comp.transfer(user, userAccrued); return 0; @@ -1224,18 +1398,9 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param holder The address to claim COMP for */ function claimComp(address holder) public { - return claimComp(holder, allMarkets); - } - - /** - * @notice Claim all the comp accrued by holder in the specified markets - * @param holder The address to claim COMP for - * @param cTokens The list of markets to claim COMP in - */ - function claimComp(address holder, CToken[] memory cTokens) public { address[] memory holders = new address[](1); holders[0] = holder; - claimComp(holders, cTokens, true, true); + return claimComp(holders, allMarkets, true, true); } /** @@ -1245,73 +1410,29 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE * @param borrowers Whether or not to claim COMP earned by borrowing * @param suppliers Whether or not to claim COMP earned by supplying */ - function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public { - for (uint i = 0; i < cTokens.length; i++) { + function claimComp( + address[] memory holders, + CToken[] memory cTokens, + bool borrowers, + bool suppliers + ) public { + for (uint256 i = 0; i < cTokens.length; i++) { CToken cToken = cTokens[i]; - require(markets[address(cToken)].isListed, "market must be listed"); + require(isMarketListed(address(cToken)), "market must be listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); - updateCompBorrowIndex(address(cToken), borrowIndex); - for (uint j = 0; j < holders.length; j++) { - distributeBorrowerComp(address(cToken), holders[j], borrowIndex, true); + for (uint256 j = 0; j < holders.length; j++) { + distributeBorrowerComp(address(cToken), holders[j], borrowIndex); } } if (suppliers == true) { - updateCompSupplyIndex(address(cToken)); - for (uint j = 0; j < holders.length; j++) { - distributeSupplierComp(address(cToken), holders[j], true); + for (uint256 j = 0; j < holders.length; j++) { + distributeSupplierComp(address(cToken), holders[j]); } } } } - /*** Comp Distribution Admin ***/ - - /** - * @notice Set cTokens compSpeed - * @param cTokens The addresses of cTokens - * @param speeds The list of COMP speeds - */ - function _setCompSpeeds(address[] memory cTokens, uint[] memory speeds) public { - require(msg.sender == admin, "only admin can set comp speeds"); - - uint numMarkets = cTokens.length; - uint numSpeeds = speeds.length; - - require(numMarkets != 0 && numMarkets == numSpeeds, "invalid input"); - - for (uint i = 0; i < numMarkets; i++) { - if (speeds[i] > 0) { - _initCompState(cTokens[i]); - } - - // Update supply and borrow index. - CToken cToken = CToken(cTokens[i]); - Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); - updateCompSupplyIndex(address(cToken)); - updateCompBorrowIndex(address(cToken), borrowIndex); - - compSpeeds[address(cToken)] = speeds[i]; - emit CompSpeedUpdated(cToken, speeds[i]); - } - } - - function _initCompState(address cToken) internal { - if (compSupplyState[cToken].index == 0 && compSupplyState[cToken].block == 0) { - compSupplyState[cToken] = CompMarketState({ - index: compInitialIndex, - block: safe32(getBlockNumber(), "block number exceeds 32 bits") - }); - } - - if (compBorrowState[cToken].index == 0 && compBorrowState[cToken].block == 0) { - compBorrowState[cToken] = CompMarketState({ - index: compInitialIndex, - block: safe32(getBlockNumber(), "block number exceeds 32 bits") - }); - } - } - /** * @notice Return all of the markets * @dev The automatic getter may be used to access an individual market. @@ -1321,7 +1442,7 @@ contract Comptroller is ComptrollerV5Storage, ComptrollerInterface, ComptrollerE return allMarkets; } - function getBlockNumber() public view returns (uint) { + function getBlockNumber() public view returns (uint256) { return block.number; } diff --git a/contracts/ComptrollerG1.sol b/contracts/ComptrollerG1.sol index 9e243f6..a210cdb 100644 --- a/contracts/ComptrollerG1.sol +++ b/contracts/ComptrollerG1.sol @@ -20,14 +20,12 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @notice Whether or not this market is listed */ bool isListed; - /** * @notice Multiplier representing the most one can borrow against their collateral in this market. * For instance, 0.9 to allow borrowing 90% of collateral value. * Must be between 0 and 1, and stored as a mantissa. */ - uint collateralFactorMantissa; - + uint256 collateralFactorMantissa; /** * @notice Per-market mapping of "accounts in this asset" */ @@ -58,22 +56,22 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle /** * @notice Emitted when close factor is changed by admin */ - event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); /** * @notice Emitted when a collateral factor is changed by admin */ - event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); + event NewCollateralFactor(CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); /** * @notice Emitted when liquidation incentive is changed by admin */ - event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); /** * @notice Emitted when maxAssets is changed by admin */ - event NewMaxAssets(uint oldMaxAssets, uint newMaxAssets); + event NewMaxAssets(uint256 oldMaxAssets, uint256 newMaxAssets); /** * @notice Emitted when price oracle is changed @@ -81,19 +79,19 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); // closeFactorMantissa must be strictly greater than this value - uint constant closeFactorMinMantissa = 5e16; // 0.05 + uint256 constant closeFactorMinMantissa = 5e16; // 0.05 // closeFactorMantissa must not exceed this value - uint constant closeFactorMaxMantissa = 9e17; // 0.9 + uint256 constant closeFactorMaxMantissa = 9e17; // 0.9 // No collateralFactorMantissa may exceed this value - uint constant collateralFactorMaxMantissa = 9e17; // 0.9 + uint256 constant collateralFactorMaxMantissa = 9e17; // 0.9 // liquidationIncentiveMantissa must be no less than this value - uint constant liquidationIncentiveMinMantissa = mantissaOne; + uint256 constant liquidationIncentiveMinMantissa = mantissaOne; // liquidationIncentiveMantissa must be no greater than this value - uint constant liquidationIncentiveMaxMantissa = 15e17; // 1.5 + uint256 constant liquidationIncentiveMaxMantissa = 15e17; // 1.5 constructor() public { admin = msg.sender; @@ -127,29 +125,29 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ - function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { - uint len = cTokens.length; + function enterMarkets(address[] memory cTokens) public returns (uint256[] memory) { + uint256 len = cTokens.length; - uint[] memory results = new uint[](len); - for (uint i = 0; i < len; i++) { + uint256[] memory results = new uint256[](len); + for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); Market storage marketToJoin = markets[address(cToken)]; if (!marketToJoin.isListed) { // if market is not listed, cannot join move along - results[i] = uint(Error.MARKET_NOT_LISTED); + results[i] = uint256(Error.MARKET_NOT_LISTED); continue; } if (marketToJoin.accountMembership[msg.sender] == true) { // if already joined, move along - results[i] = uint(Error.NO_ERROR); + results[i] = uint256(Error.NO_ERROR); continue; } - if (accountAssets[msg.sender].length >= maxAssets) { + if (accountAssets[msg.sender].length >= maxAssets) { // if no space, cannot join, move along - results[i] = uint(Error.TOO_MANY_ASSETS); + results[i] = uint256(Error.TOO_MANY_ASSETS); continue; } @@ -163,7 +161,7 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle emit MarketEntered(cToken, msg.sender); - results[i] = uint(Error.NO_ERROR); + results[i] = uint256(Error.NO_ERROR); } return results; @@ -176,10 +174,10 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ - function exitMarket(address cTokenAddress) external returns (uint) { + function exitMarket(address cTokenAddress) external returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ - (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ @@ -188,7 +186,7 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle } /* Fail if the sender is not permitted to redeem all of their tokens */ - uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } @@ -197,7 +195,7 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ @@ -206,9 +204,9 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; - uint len = userAssetList.length; - uint assetIndex = len; - for (uint i = 0; i < len; i++) { + uint256 len = userAssetList.length; + uint256 assetIndex = len; + for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; @@ -225,7 +223,7 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle emit MarketExited(cToken, msg.sender); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /*** Policy Hooks ***/ @@ -237,17 +235,21 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { - minter; // currently unused - mintAmount; // currently unused + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256) { + minter; // currently unused + mintAmount; // currently unused if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -257,11 +259,16 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param mintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ - function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external { - cToken; // currently unused - minter; // currently unused - mintAmount; // currently unused - mintTokens; // currently unused + function mintVerify( + address cToken, + address minter, + uint256 mintAmount, + uint256 mintTokens + ) external { + cToken; // currently unused + minter; // currently unused + mintAmount; // currently unused + mintTokens; // currently unused if (false) { maxAssets = maxAssets; // not pure @@ -275,32 +282,45 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256) { return redeemAllowedInternal(cToken, redeemer, redeemTokens); } - function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { + function redeemAllowedInternal( + address cToken, + address redeemer, + uint256 redeemTokens + ) internal view returns (uint256) { if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + redeemer, + CToken(cToken), + redeemTokens, + 0 + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -310,11 +330,16 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ - function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { - cToken; // currently unused - redeemer; // currently unused - redeemAmount; // currently unused - redeemTokens; // currently unused + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external { + cToken; // currently unused + redeemer; // currently unused + redeemAmount; // currently unused + redeemTokens; // currently unused // Require tokens is zero or amount is also zero if (redeemTokens == 0 && redeemAmount > 0) { @@ -329,30 +354,39 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256) { if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks if (!markets[cToken].accountMembership[borrower]) { - return uint(Error.MARKET_NOT_ENTERED); + return uint256(Error.MARKET_NOT_ENTERED); } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { - return uint(Error.PRICE_ERROR); + return uint256(Error.PRICE_ERROR); } - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + borrower, + CToken(cToken), + 0, + borrowAmount + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -361,10 +395,14 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ - function borrowVerify(address cToken, address borrower, uint borrowAmount) external { - cToken; // currently unused - borrower; // currently unused - borrowAmount; // currently unused + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external { + cToken; // currently unused + borrower; // currently unused + borrowAmount; // currently unused if (false) { maxAssets = maxAssets; // not pure @@ -383,18 +421,19 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint repayAmount) external returns (uint) { - payer; // currently unused - borrower; // currently unused - repayAmount; // currently unused + uint256 repayAmount + ) external returns (uint256) { + payer; // currently unused + borrower; // currently unused + repayAmount; // currently unused if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -408,12 +447,13 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint repayAmount, - uint borrowerIndex) external { - cToken; // currently unused - payer; // currently unused - borrower; // currently unused - repayAmount; // currently unused + uint256 repayAmount, + uint256 borrowerIndex + ) external { + cToken; // currently unused + payer; // currently unused + borrower; // currently unused + repayAmount; // currently unused borrowerIndex; // currently unused if (false) { @@ -434,37 +474,38 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint repayAmount) external returns (uint) { - liquidator; // currently unused - borrower; // currently unused - repayAmount; // currently unused + uint256 repayAmount + ) external returns (uint256) { + liquidator; // currently unused + borrower; // currently unused + repayAmount; // currently unused if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks /* The borrower must have shortfall in order to be liquidatable */ - (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); + (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall == 0) { - return uint(Error.INSUFFICIENT_SHORTFALL); + return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ - uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); - (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + (MathError mathErr, uint256 maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (mathErr != MathError.NO_ERROR) { - return uint(Error.MATH_ERROR); + return uint256(Error.MATH_ERROR); } if (repayAmount > maxClose) { - return uint(Error.TOO_MUCH_REPAY); + return uint256(Error.TOO_MUCH_REPAY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -480,14 +521,15 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint repayAmount, - uint seizeTokens) external { - cTokenBorrowed; // currently unused + uint256 repayAmount, + uint256 seizeTokens + ) external { + cTokenBorrowed; // currently unused cTokenCollateral; // currently unused - liquidator; // currently unused - borrower; // currently unused - repayAmount; // currently unused - seizeTokens; // currently unused + liquidator; // currently unused + borrower; // currently unused + repayAmount; // currently unused + seizeTokens; // currently unused if (false) { maxAssets = maxAssets; // not pure @@ -507,22 +549,23 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external returns (uint) { - liquidator; // currently unused - borrower; // currently unused - seizeTokens; // currently unused + uint256 seizeTokens + ) external returns (uint256) { + liquidator; // currently unused + borrower; // currently unused + seizeTokens; // currently unused if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { - return uint(Error.COMPTROLLER_MISMATCH); + return uint256(Error.COMPTROLLER_MISMATCH); } // *may include Policy Hook-type checks - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -538,12 +581,13 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external { + uint256 seizeTokens + ) external { cTokenCollateral; // currently unused - cTokenBorrowed; // currently unused - liquidator; // currently unused - borrower; // currently unused - seizeTokens; // currently unused + cTokenBorrowed; // currently unused + liquidator; // currently unused + borrower; // currently unused + seizeTokens; // currently unused if (false) { maxAssets = maxAssets; // not pure @@ -558,10 +602,15 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { - cToken; // currently unused - src; // currently unused - dst; // currently unused + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256) { + cToken; // currently unused + src; // currently unused + dst; // currently unused transferTokens; // currently unused // *may include Policy Hook-type checks @@ -578,10 +627,15 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ - function transferVerify(address cToken, address src, address dst, uint transferTokens) external { - cToken; // currently unused - src; // currently unused - dst; // currently unused + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external { + cToken; // currently unused + src; // currently unused + dst; // currently unused transferTokens; // currently unused if (false) { @@ -597,12 +651,12 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { - uint sumCollateral; - uint sumBorrowPlusEffects; - uint cTokenBalance; - uint borrowBalance; - uint exchangeRateMantissa; - uint oraclePriceMantissa; + uint256 sumCollateral; + uint256 sumBorrowPlusEffects; + uint256 cTokenBalance; + uint256 borrowBalance; + uint256 exchangeRateMantissa; + uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; @@ -615,10 +669,23 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidity(address account) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); + function getAccountLiquidity(address account) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(0), + 0, + 0 + ); - return (uint(err), liquidity, shortfall); + return (uint256(err), liquidity, shortfall); } /** @@ -627,7 +694,15 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { + function getAccountLiquidityInternal(address account) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); } @@ -646,21 +721,32 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, - uint redeemTokens, - uint borrowAmount) internal view returns (Error, uint, uint) { - + uint256 redeemTokens, + uint256 borrowAmount + ) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { AccountLiquidityLocalVars memory vars; // Holds all our calculation results - uint oErr; + uint256 oErr; MathError mErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; - for (uint i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken - (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); - if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( + account + ); + if (oErr != 0) { + // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); @@ -680,13 +766,21 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle } // sumCollateral += tokensToEther * cTokenBalance - (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToEther, vars.cTokenBalance, vars.sumCollateral); + (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt( + vars.tokensToEther, + vars.cTokenBalance, + vars.sumCollateral + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // sumBorrowPlusEffects += oraclePrice * borrowBalance - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + vars.borrowBalance, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -695,14 +789,22 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToEther * redeemTokens - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToEther, redeemTokens, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.tokensToEther, + redeemTokens, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + borrowAmount, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -725,12 +827,16 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * @param repayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ - function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint repayAmount) external view returns (uint, uint) { + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 repayAmount + ) external view returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ - uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); - uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { - return (uint(Error.PRICE_ERROR), 0); + return (uint256(Error.PRICE_ERROR), 0); } /* @@ -739,8 +845,8 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * seizeTokens = seizeAmount / exchangeRate * = repayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ - uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error - uint seizeTokens; + uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + uint256 seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; @@ -748,35 +854,35 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle (mathErr, numerator) = mulExp(liquidationIncentiveMantissa, priceBorrowedMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, ratio) = divExp(numerator, denominator); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, seizeTokens) = mulScalarTruncate(ratio, repayAmount); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } - return (uint(Error.NO_ERROR), seizeTokens); + return (uint256(Error.NO_ERROR), seizeTokens); } /*** Admin Functions ***/ /** - * @notice Sets a new price oracle for the comptroller - * @dev Admin function to set a new price oracle - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPriceOracle(PriceOracle newOracle) public returns (uint) { + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin OR currently initialzing as new unitroller implementation if (!adminOrInitializing()) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); @@ -794,16 +900,16 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the closeFactor used when liquidating borrows - * @dev Admin function to set closeFactor - * @param newCloseFactorMantissa New close factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint256) { + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin OR currently initialzing as new unitroller implementation if (!adminOrInitializing()) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); @@ -820,21 +926,21 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); } - uint oldCloseFactorMantissa = closeFactorMantissa; + uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the collateralFactor for a market - * @dev Admin function to set per-market collateralFactor - * @param cToken The market to set the factor on - * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint256) { + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint256 newCollateralFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); @@ -860,41 +966,41 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle } // Set market's collateral factor to new collateral factor, remember old value - uint oldCollateralFactorMantissa = market.collateralFactorMantissa; + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets maxAssets which controls how many markets can be entered - * @dev Admin function to set maxAssets - * @param newMaxAssets New max assets - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setMaxAssets(uint newMaxAssets) external returns (uint) { + * @notice Sets maxAssets which controls how many markets can be entered + * @dev Admin function to set maxAssets + * @param newMaxAssets New max assets + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setMaxAssets(uint256 newMaxAssets) external returns (uint256) { // Check caller is admin OR currently initialzing as new unitroller implementation if (!adminOrInitializing()) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); } - uint oldMaxAssets = maxAssets; + uint256 oldMaxAssets = maxAssets; maxAssets = newMaxAssets; emit NewMaxAssets(oldMaxAssets, newMaxAssets); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets liquidationIncentive - * @dev Admin function to set liquidationIncentive - * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin OR currently initialzing as new unitroller implementation if (!adminOrInitializing()) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); @@ -913,7 +1019,7 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle } // Save current value for use in log - uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; @@ -921,16 +1027,16 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Add the market to the markets mapping and set it as listed - * @dev Admin function to set isListed and add support for the market - * @param cToken The address of the market (token) to list - * @return uint 0=success, otherwise a failure. (See enum Error for details) - */ - function _supportMarket(CToken cToken) external returns (uint) { + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken) external returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } @@ -944,12 +1050,18 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle markets[address(cToken)] = Market({isListed: true, collateralFactorMantissa: 0}); emit MarketListed(cToken); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } - function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool reinitializing) public { + function _become( + Unitroller unitroller, + PriceOracle _oracle, + uint256 _closeFactorMantissa, + uint256 _maxAssets, + bool reinitializing + ) public { require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); - uint changeStatus = unitroller._acceptImplementation(); + uint256 changeStatus = unitroller._acceptImplementation(); require(changeStatus == 0, "change not authorized"); @@ -957,20 +1069,20 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle ComptrollerG1 freshBrainedComptroller = ComptrollerG1(address(unitroller)); // Ensure invoke _setPriceOracle() = 0 - uint err = freshBrainedComptroller._setPriceOracle(_oracle); - require (err == uint(Error.NO_ERROR), "set price oracle error"); + uint256 err = freshBrainedComptroller._setPriceOracle(_oracle); + require(err == uint256(Error.NO_ERROR), "set price oracle error"); // Ensure invoke _setCloseFactor() = 0 err = freshBrainedComptroller._setCloseFactor(_closeFactorMantissa); - require (err == uint(Error.NO_ERROR), "set close factor error"); + require(err == uint256(Error.NO_ERROR), "set close factor error"); // Ensure invoke _setMaxAssets() = 0 err = freshBrainedComptroller._setMaxAssets(_maxAssets); - require (err == uint(Error.NO_ERROR), "set max asssets error"); + require(err == uint256(Error.NO_ERROR), "set max asssets error"); // Ensure invoke _setLiquidationIncentive(liquidationIncentiveMinMantissa) = 0 err = freshBrainedComptroller._setLiquidationIncentive(liquidationIncentiveMinMantissa); - require (err == uint(Error.NO_ERROR), "set liquidation incentive error"); + require(err == uint256(Error.NO_ERROR), "set liquidation incentive error"); } } @@ -981,13 +1093,10 @@ contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, Comptrolle * without tx.origin also being admin, but both are included for extra safety */ function adminOrInitializing() internal view returns (bool) { - bool initializing = ( - msg.sender == comptrollerImplementation - && - //solium-disable-next-line security/no-tx-origin - tx.origin == admin - ); + bool initializing = (msg.sender == comptrollerImplementation && + //solium-disable-next-line security/no-tx-origin + tx.origin == admin); bool isAdmin = msg.sender == admin; return isAdmin || initializing; } -} \ No newline at end of file +} diff --git a/contracts/ComptrollerG2.sol b/contracts/ComptrollerG2.sol index d7aa0cb..88ae8b0 100644 --- a/contracts/ComptrollerG2.sol +++ b/contracts/ComptrollerG2.sol @@ -31,22 +31,22 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle /** * @notice Emitted when close factor is changed by admin */ - event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); /** * @notice Emitted when a collateral factor is changed by admin */ - event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); + event NewCollateralFactor(CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); /** * @notice Emitted when liquidation incentive is changed by admin */ - event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); /** * @notice Emitted when maxAssets is changed by admin */ - event NewMaxAssets(uint oldMaxAssets, uint newMaxAssets); + event NewMaxAssets(uint256 oldMaxAssets, uint256 newMaxAssets); /** * @notice Emitted when price oracle is changed @@ -69,19 +69,19 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle event ActionPaused(CToken cToken, string action, bool pauseState); // closeFactorMantissa must be strictly greater than this value - uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 + uint256 internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value - uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value - uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 // liquidationIncentiveMantissa must be no less than this value - uint internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 + uint256 internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 // liquidationIncentiveMantissa must be no greater than this value - uint internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 + uint256 internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 constructor() public { admin = msg.sender; @@ -115,14 +115,14 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ - function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { - uint len = cTokens.length; + function enterMarkets(address[] memory cTokens) public returns (uint256[] memory) { + uint256 len = cTokens.length; - uint[] memory results = new uint[](len); - for (uint i = 0; i < len; i++) { + uint256[] memory results = new uint256[](len); + for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); - results[i] = uint(addToMarketInternal(cToken, msg.sender)); + results[i] = uint256(addToMarketInternal(cToken, msg.sender)); } return results; @@ -147,7 +147,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle return Error.NO_ERROR; } - if (accountAssets[borrower].length >= maxAssets) { + if (accountAssets[borrower].length >= maxAssets) { // no space, cannot join return Error.TOO_MANY_ASSETS; } @@ -172,10 +172,10 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ - function exitMarket(address cTokenAddress) external returns (uint) { + function exitMarket(address cTokenAddress) external returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ - (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ @@ -184,7 +184,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle } /* Fail if the sender is not permitted to redeem all of their tokens */ - uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } @@ -193,7 +193,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ @@ -202,9 +202,9 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; - uint len = userAssetList.length; - uint assetIndex = len; - for (uint i = 0; i < len; i++) { + uint256 len = userAssetList.length; + uint256 assetIndex = len; + for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; @@ -221,7 +221,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle emit MarketExited(cToken, msg.sender); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /*** Policy Hooks ***/ @@ -233,7 +233,11 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken], "mint is paused"); @@ -242,12 +246,12 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle mintAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -257,7 +261,12 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ - function mintVerify(address cToken, address minter, uint actualMintAmount, uint mintTokens) external { + function mintVerify( + address cToken, + address minter, + uint256 actualMintAmount, + uint256 mintTokens + ) external { // Shh - currently unused cToken; minter; @@ -277,32 +286,45 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256) { return redeemAllowedInternal(cToken, redeemer, redeemTokens); } - function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { + function redeemAllowedInternal( + address cToken, + address redeemer, + uint256 redeemTokens + ) internal view returns (uint256) { if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + redeemer, + CToken(cToken), + redeemTokens, + 0 + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -312,7 +334,12 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ - function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external { // Shh - currently unused cToken; redeemer; @@ -330,12 +357,16 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "borrow is paused"); if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks @@ -347,7 +378,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } // it should be impossible to break the important invariant @@ -355,18 +386,23 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { - return uint(Error.PRICE_ERROR); + return uint256(Error.PRICE_ERROR); } - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + borrower, + CToken(cToken), + 0, + borrowAmount + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -375,7 +411,11 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ - function borrowVerify(address cToken, address borrower, uint borrowAmount) external { + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external { // Shh - currently unused cToken; borrower; @@ -399,19 +439,20 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -425,8 +466,9 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint actualRepayAmount, - uint borrowerIndex) external { + uint256 actualRepayAmount, + uint256 borrowerIndex + ) external { // Shh - currently unused cToken; payer; @@ -453,36 +495,37 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused liquidator; if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // *may include Policy Hook-type checks /* The borrower must have shortfall in order to be liquidatable */ - (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); + (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall == 0) { - return uint(Error.INSUFFICIENT_SHORTFALL); + return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ - uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); - (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + (MathError mathErr, uint256 maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (mathErr != MathError.NO_ERROR) { - return uint(Error.MATH_ERROR); + return uint256(Error.MATH_ERROR); } if (repayAmount > maxClose) { - return uint(Error.TOO_MUCH_REPAY); + return uint256(Error.TOO_MUCH_REPAY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -498,8 +541,9 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint actualRepayAmount, - uint seizeTokens) external { + uint256 actualRepayAmount, + uint256 seizeTokens + ) external { // Shh - currently unused cTokenBorrowed; cTokenCollateral; @@ -527,7 +571,8 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external returns (uint) { + uint256 seizeTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); @@ -537,16 +582,16 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle seizeTokens; if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { - return uint(Error.COMPTROLLER_MISMATCH); + return uint256(Error.COMPTROLLER_MISMATCH); } // *may include Policy Hook-type checks - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -562,7 +607,8 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external { + uint256 seizeTokens + ) external { // Shh - currently unused cTokenCollateral; cTokenBorrowed; @@ -584,7 +630,12 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); @@ -605,7 +656,12 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ - function transferVerify(address cToken, address src, address dst, uint transferTokens) external { + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external { // Shh - currently unused cToken; src; @@ -626,12 +682,12 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { - uint sumCollateral; - uint sumBorrowPlusEffects; - uint cTokenBalance; - uint borrowBalance; - uint exchangeRateMantissa; - uint oraclePriceMantissa; + uint256 sumCollateral; + uint256 sumBorrowPlusEffects; + uint256 cTokenBalance; + uint256 borrowBalance; + uint256 exchangeRateMantissa; + uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; @@ -644,10 +700,23 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidity(address account) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); - - return (uint(err), liquidity, shortfall); + function getAccountLiquidity(address account) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(0), + 0, + 0 + ); + + return (uint256(err), liquidity, shortfall); } /** @@ -656,7 +725,15 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { + function getAccountLiquidityInternal(address account) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); } @@ -673,10 +750,24 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidity( address account, address cTokenModify, - uint redeemTokens, - uint borrowAmount) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); - return (uint(err), liquidity, shortfall); + uint256 redeemTokens, + uint256 borrowAmount + ) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(cTokenModify), + redeemTokens, + borrowAmount + ); + return (uint256(err), liquidity, shortfall); } /** @@ -694,21 +785,32 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, - uint redeemTokens, - uint borrowAmount) internal view returns (Error, uint, uint) { - + uint256 redeemTokens, + uint256 borrowAmount + ) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { AccountLiquidityLocalVars memory vars; // Holds all our calculation results - uint oErr; + uint256 oErr; MathError mErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; - for (uint i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken - (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); - if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( + account + ); + if (oErr != 0) { + // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); @@ -728,13 +830,21 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle } // sumCollateral += tokensToEther * cTokenBalance - (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToEther, vars.cTokenBalance, vars.sumCollateral); + (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt( + vars.tokensToEther, + vars.cTokenBalance, + vars.sumCollateral + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // sumBorrowPlusEffects += oraclePrice * borrowBalance - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + vars.borrowBalance, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -743,14 +853,22 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToEther * redeemTokens - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToEther, redeemTokens, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.tokensToEther, + redeemTokens, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + borrowAmount, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -773,12 +891,16 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ - function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 actualRepayAmount + ) external view returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ - uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); - uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { - return (uint(Error.PRICE_ERROR), 0); + return (uint256(Error.PRICE_ERROR), 0); } /* @@ -787,8 +909,8 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ - uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error - uint seizeTokens; + uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + uint256 seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; @@ -796,35 +918,35 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle (mathErr, numerator) = mulExp(liquidationIncentiveMantissa, priceBorrowedMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, ratio) = divExp(numerator, denominator); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, seizeTokens) = mulScalarTruncate(ratio, actualRepayAmount); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } - return (uint(Error.NO_ERROR), seizeTokens); + return (uint256(Error.NO_ERROR), seizeTokens); } /*** Admin Functions ***/ /** - * @notice Sets a new price oracle for the comptroller - * @dev Admin function to set a new price oracle - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPriceOracle(PriceOracle newOracle) public returns (uint) { + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); @@ -839,16 +961,16 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the closeFactor used when liquidating borrows - * @dev Admin function to set closeFactor - * @param newCloseFactorMantissa New close factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint256) { + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); @@ -865,21 +987,21 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); } - uint oldCloseFactorMantissa = closeFactorMantissa; + uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the collateralFactor for a market - * @dev Admin function to set per-market collateralFactor - * @param cToken The market to set the factor on - * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint256) { + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint256 newCollateralFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); @@ -905,41 +1027,41 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle } // Set market's collateral factor to new collateral factor, remember old value - uint oldCollateralFactorMantissa = market.collateralFactorMantissa; + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets maxAssets which controls how many markets can be entered - * @dev Admin function to set maxAssets - * @param newMaxAssets New max assets - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setMaxAssets(uint newMaxAssets) external returns (uint) { + * @notice Sets maxAssets which controls how many markets can be entered + * @dev Admin function to set maxAssets + * @param newMaxAssets New max assets + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setMaxAssets(uint256 newMaxAssets) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); } - uint oldMaxAssets = maxAssets; + uint256 oldMaxAssets = maxAssets; maxAssets = newMaxAssets; emit NewMaxAssets(oldMaxAssets, newMaxAssets); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets liquidationIncentive - * @dev Admin function to set liquidationIncentive - * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); @@ -958,7 +1080,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle } // Save current value for use in log - uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; @@ -966,16 +1088,16 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Add the market to the markets mapping and set it as listed - * @dev Admin function to set isListed and add support for the market - * @param cToken The address of the market (token) to list - * @return uint 0=success, otherwise a failure. (See enum Error for details) - */ - function _supportMarket(CToken cToken) external returns (uint) { + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken) external returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } @@ -986,10 +1108,15 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle cToken.isCToken(); // Sanity check to make sure its really a CToken - markets[address(cToken)] = Market({isListed: true, isComped: false, collateralFactorMantissa: 0}); + markets[address(cToken)] = Market({ + isListed: true, + isComped: false, + collateralFactorMantissa: 0, + version: Version.VANILLA + }); emit MarketListed(cToken); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -997,7 +1124,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ - function _setPauseGuardian(address newPauseGuardian) public returns (uint) { + function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); } @@ -1011,7 +1138,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } function _setMintPaused(CToken cToken, bool state) public returns (bool) { @@ -1055,7 +1182,7 @@ contract ComptrollerG2 is ComptrollerV2Storage, ComptrollerInterface, Comptrolle function _become(Unitroller unitroller) public { require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); - uint changeStatus = unitroller._acceptImplementation(); + uint256 changeStatus = unitroller._acceptImplementation(); require(changeStatus == 0, "change not authorized"); } } diff --git a/contracts/ComptrollerG3.sol b/contracts/ComptrollerG3.sol index 2553ddc..a477589 100644 --- a/contracts/ComptrollerG3.sol +++ b/contracts/ComptrollerG3.sol @@ -24,16 +24,16 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle event MarketExited(CToken cToken, address account); /// @notice Emitted when close factor is changed by admin - event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); /// @notice Emitted when a collateral factor is changed by admin - event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); + event NewCollateralFactor(CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); /// @notice Emitted when liquidation incentive is changed by admin - event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); /// @notice Emitted when maxAssets is changed by admin - event NewMaxAssets(uint oldMaxAssets, uint newMaxAssets); + event NewMaxAssets(uint256 oldMaxAssets, uint256 newMaxAssets); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); @@ -51,37 +51,47 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle event MarketComped(CToken cToken, bool isComped); /// @notice Emitted when COMP rate is changed - event NewCompRate(uint oldCompRate, uint newCompRate); + event NewCompRate(uint256 oldCompRate, uint256 newCompRate); /// @notice Emitted when a new COMP speed is calculated for a market - event CompSpeedUpdated(CToken indexed cToken, uint newSpeed); + event CompSpeedUpdated(CToken indexed cToken, uint256 newSpeed); /// @notice Emitted when COMP is distributed to a supplier - event DistributedSupplierComp(CToken indexed cToken, address indexed supplier, uint compDelta, uint compSupplyIndex); + event DistributedSupplierComp( + CToken indexed cToken, + address indexed supplier, + uint256 compDelta, + uint256 compSupplyIndex + ); /// @notice Emitted when COMP is distributed to a borrower - event DistributedBorrowerComp(CToken indexed cToken, address indexed borrower, uint compDelta, uint compBorrowIndex); + event DistributedBorrowerComp( + CToken indexed cToken, + address indexed borrower, + uint256 compDelta, + uint256 compBorrowIndex + ); /// @notice The threshold above which the flywheel transfers COMP, in wei - uint public constant compClaimThreshold = 0.001e18; + uint256 public constant compClaimThreshold = 0.001e18; /// @notice The initial COMP index for a market uint224 public constant compInitialIndex = 1e36; // closeFactorMantissa must be strictly greater than this value - uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 + uint256 internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value - uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value - uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 // liquidationIncentiveMantissa must be no less than this value - uint internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 + uint256 internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 // liquidationIncentiveMantissa must be no greater than this value - uint internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 + uint256 internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 constructor() public { admin = msg.sender; @@ -115,14 +125,14 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ - function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { - uint len = cTokens.length; + function enterMarkets(address[] memory cTokens) public returns (uint256[] memory) { + uint256 len = cTokens.length; - uint[] memory results = new uint[](len); - for (uint i = 0; i < len; i++) { + uint256[] memory results = new uint256[](len); + for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); - results[i] = uint(addToMarketInternal(cToken, msg.sender)); + results[i] = uint256(addToMarketInternal(cToken, msg.sender)); } return results; @@ -147,7 +157,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle return Error.NO_ERROR; } - if (accountAssets[borrower].length >= maxAssets) { + if (accountAssets[borrower].length >= maxAssets) { // no space, cannot join return Error.TOO_MANY_ASSETS; } @@ -172,10 +182,10 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ - function exitMarket(address cTokenAddress) external returns (uint) { + function exitMarket(address cTokenAddress) external returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ - (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ @@ -184,7 +194,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } /* Fail if the sender is not permitted to redeem all of their tokens */ - uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } @@ -193,7 +203,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ @@ -202,9 +212,9 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; - uint len = userAssetList.length; - uint assetIndex = len; - for (uint i = 0; i < len; i++) { + uint256 len = userAssetList.length; + uint256 assetIndex = len; + for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; @@ -221,7 +231,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle emit MarketExited(cToken, msg.sender); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /*** Policy Hooks ***/ @@ -233,7 +243,11 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken], "mint is paused"); @@ -242,14 +256,14 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle mintAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, minter, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -259,7 +273,12 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ - function mintVerify(address cToken, address minter, uint actualMintAmount, uint mintTokens) external { + function mintVerify( + address cToken, + address minter, + uint256 actualMintAmount, + uint256 mintTokens + ) external { // Shh - currently unused cToken; minter; @@ -279,9 +298,13 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { - uint allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); - if (allowed != uint(Error.NO_ERROR)) { + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256) { + uint256 allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); + if (allowed != uint256(Error.NO_ERROR)) { return allowed; } @@ -289,29 +312,38 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, redeemer, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } - function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { + function redeemAllowedInternal( + address cToken, + address redeemer, + uint256 redeemTokens + ) internal view returns (uint256) { if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + redeemer, + CToken(cToken), + redeemTokens, + 0 + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -321,7 +353,12 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ - function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external { // Shh - currently unused cToken; redeemer; @@ -339,12 +376,16 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "borrow is paused"); if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (!markets[cToken].accountMembership[borrower]) { @@ -354,7 +395,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } // it should be impossible to break the important invariant @@ -362,15 +403,20 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { - return uint(Error.PRICE_ERROR); + return uint256(Error.PRICE_ERROR); } - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + borrower, + CToken(cToken), + 0, + borrowAmount + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } // Keep the flywheel moving @@ -378,7 +424,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -387,7 +433,11 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ - function borrowVerify(address cToken, address borrower, uint borrowAmount) external { + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external { // Shh - currently unused cToken; borrower; @@ -411,14 +461,15 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving @@ -426,7 +477,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -440,8 +491,9 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint actualRepayAmount, - uint borrowerIndex) external { + uint256 actualRepayAmount, + uint256 borrowerIndex + ) external { // Shh - currently unused cToken; payer; @@ -468,34 +520,35 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused liquidator; if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } /* The borrower must have shortfall in order to be liquidatable */ - (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); + (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall == 0) { - return uint(Error.INSUFFICIENT_SHORTFALL); + return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ - uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); - (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + (MathError mathErr, uint256 maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (mathErr != MathError.NO_ERROR) { - return uint(Error.MATH_ERROR); + return uint256(Error.MATH_ERROR); } if (repayAmount > maxClose) { - return uint(Error.TOO_MUCH_REPAY); + return uint256(Error.TOO_MUCH_REPAY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -511,8 +564,9 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint actualRepayAmount, - uint seizeTokens) external { + uint256 actualRepayAmount, + uint256 seizeTokens + ) external { // Shh - currently unused cTokenBorrowed; cTokenCollateral; @@ -540,7 +594,8 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external returns (uint) { + uint256 seizeTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); @@ -548,11 +603,11 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle seizeTokens; if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { - return uint(Error.COMPTROLLER_MISMATCH); + return uint256(Error.COMPTROLLER_MISMATCH); } // Keep the flywheel moving @@ -560,7 +615,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle distributeSupplierComp(cTokenCollateral, borrower, false); distributeSupplierComp(cTokenCollateral, liquidator, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -576,7 +631,8 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external { + uint256 seizeTokens + ) external { // Shh - currently unused cTokenCollateral; cTokenBorrowed; @@ -598,14 +654,19 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens - uint allowed = redeemAllowedInternal(cToken, src, transferTokens); - if (allowed != uint(Error.NO_ERROR)) { + uint256 allowed = redeemAllowedInternal(cToken, src, transferTokens); + if (allowed != uint256(Error.NO_ERROR)) { return allowed; } @@ -614,7 +675,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle distributeSupplierComp(cToken, src, false); distributeSupplierComp(cToken, dst, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -624,7 +685,12 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ - function transferVerify(address cToken, address src, address dst, uint transferTokens) external { + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external { // Shh - currently unused cToken; src; @@ -645,12 +711,12 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { - uint sumCollateral; - uint sumBorrowPlusEffects; - uint cTokenBalance; - uint borrowBalance; - uint exchangeRateMantissa; - uint oraclePriceMantissa; + uint256 sumCollateral; + uint256 sumBorrowPlusEffects; + uint256 cTokenBalance; + uint256 borrowBalance; + uint256 exchangeRateMantissa; + uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; @@ -663,10 +729,23 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidity(address account) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); - - return (uint(err), liquidity, shortfall); + function getAccountLiquidity(address account) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(0), + 0, + 0 + ); + + return (uint256(err), liquidity, shortfall); } /** @@ -675,7 +754,15 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { + function getAccountLiquidityInternal(address account) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); } @@ -692,10 +779,24 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidity( address account, address cTokenModify, - uint redeemTokens, - uint borrowAmount) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); - return (uint(err), liquidity, shortfall); + uint256 redeemTokens, + uint256 borrowAmount + ) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(cTokenModify), + redeemTokens, + borrowAmount + ); + return (uint256(err), liquidity, shortfall); } /** @@ -713,21 +814,32 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, - uint redeemTokens, - uint borrowAmount) internal view returns (Error, uint, uint) { - + uint256 redeemTokens, + uint256 borrowAmount + ) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { AccountLiquidityLocalVars memory vars; // Holds all our calculation results - uint oErr; + uint256 oErr; MathError mErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; - for (uint i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken - (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); - if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( + account + ); + if (oErr != 0) { + // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); @@ -747,13 +859,21 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } // sumCollateral += tokensToDenom * cTokenBalance - (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral); + (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt( + vars.tokensToDenom, + vars.cTokenBalance, + vars.sumCollateral + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // sumBorrowPlusEffects += oraclePrice * borrowBalance - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + vars.borrowBalance, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -762,14 +882,22 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.tokensToDenom, + redeemTokens, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + borrowAmount, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -792,12 +920,16 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ - function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 actualRepayAmount + ) external view returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ - uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); - uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { - return (uint(Error.PRICE_ERROR), 0); + return (uint256(Error.PRICE_ERROR), 0); } /* @@ -806,8 +938,8 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ - uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error - uint seizeTokens; + uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + uint256 seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; @@ -815,35 +947,35 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle (mathErr, numerator) = mulExp(liquidationIncentiveMantissa, priceBorrowedMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, ratio) = divExp(numerator, denominator); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, seizeTokens) = mulScalarTruncate(ratio, actualRepayAmount); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } - return (uint(Error.NO_ERROR), seizeTokens); + return (uint256(Error.NO_ERROR), seizeTokens); } /*** Admin Functions ***/ /** - * @notice Sets a new price oracle for the comptroller - * @dev Admin function to set a new price oracle - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPriceOracle(PriceOracle newOracle) public returns (uint) { + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); @@ -858,16 +990,16 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the closeFactor used when liquidating borrows - * @dev Admin function to set closeFactor - * @param newCloseFactorMantissa New close factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) { + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); @@ -884,21 +1016,21 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); } - uint oldCloseFactorMantissa = closeFactorMantissa; + uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the collateralFactor for a market - * @dev Admin function to set per-market collateralFactor - * @param cToken The market to set the factor on - * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint) { + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint256 newCollateralFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); @@ -924,41 +1056,41 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } // Set market's collateral factor to new collateral factor, remember old value - uint oldCollateralFactorMantissa = market.collateralFactorMantissa; + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets maxAssets which controls how many markets can be entered - * @dev Admin function to set maxAssets - * @param newMaxAssets New max assets - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setMaxAssets(uint newMaxAssets) external returns (uint) { + * @notice Sets maxAssets which controls how many markets can be entered + * @dev Admin function to set maxAssets + * @param newMaxAssets New max assets + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setMaxAssets(uint256 newMaxAssets) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); } - uint oldMaxAssets = maxAssets; + uint256 oldMaxAssets = maxAssets; maxAssets = newMaxAssets; emit NewMaxAssets(oldMaxAssets, newMaxAssets); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets liquidationIncentive - * @dev Admin function to set liquidationIncentive - * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); @@ -977,7 +1109,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } // Save current value for use in log - uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; @@ -985,16 +1117,16 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Add the market to the markets mapping and set it as listed - * @dev Admin function to set isListed and add support for the market - * @param cToken The address of the market (token) to list - * @return uint 0=success, otherwise a failure. (See enum Error for details) - */ - function _supportMarket(CToken cToken) external returns (uint) { + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken) external returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } @@ -1005,17 +1137,22 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle cToken.isCToken(); // Sanity check to make sure its really a CToken - markets[address(cToken)] = Market({isListed: true, isComped: false, collateralFactorMantissa: 0}); + markets[address(cToken)] = Market({ + isListed: true, + isComped: false, + collateralFactorMantissa: 0, + version: Version.VANILLA + }); _addMarketInternal(address(cToken)); emit MarketListed(cToken); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } function _addMarketInternal(address cToken) internal { - for (uint i = 0; i < allMarkets.length; i ++) { + for (uint256 i = 0; i < allMarkets.length; i++) { require(allMarkets[i] != CToken(cToken), "market already added"); } allMarkets.push(CToken(cToken)); @@ -1026,7 +1163,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ - function _setPauseGuardian(address newPauseGuardian) public returns (uint) { + function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); } @@ -1040,7 +1177,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } function _setMintPaused(CToken cToken, bool state) public returns (bool) { @@ -1081,21 +1218,30 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle return state; } - function _become(Unitroller unitroller, uint compRate_, address[] memory compMarketsToAdd, address[] memory otherMarketsToAdd) public { + function _become( + Unitroller unitroller, + uint256 compRate_, + address[] memory compMarketsToAdd, + address[] memory otherMarketsToAdd + ) public { require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); require(unitroller._acceptImplementation() == 0, "change not authorized"); ComptrollerG3(address(unitroller))._becomeG3(compRate_, compMarketsToAdd, otherMarketsToAdd); } - function _becomeG3(uint compRate_, address[] memory compMarketsToAdd, address[] memory otherMarketsToAdd) public { + function _becomeG3( + uint256 compRate_, + address[] memory compMarketsToAdd, + address[] memory otherMarketsToAdd + ) public { require(msg.sender == comptrollerImplementation, "only brains can become itself"); - for (uint i = 0; i < compMarketsToAdd.length; i++) { + for (uint256 i = 0; i < compMarketsToAdd.length; i++) { _addMarketInternal(address(compMarketsToAdd[i])); } - for (uint i = 0; i < otherMarketsToAdd.length; i++) { + for (uint256 i = 0; i < otherMarketsToAdd.length; i++) { _addMarketInternal(address(otherMarketsToAdd[i])); } @@ -1118,7 +1264,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function refreshCompSpeeds() public { CToken[] memory allMarkets_ = allMarkets; - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets_[i]; Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompSupplyIndex(address(cToken)); @@ -1127,7 +1273,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle Exp memory totalUtility = Exp({mantissa: 0}); Exp[] memory utilities = new Exp[](allMarkets_.length); - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets_[i]; if (markets[address(cToken)].isComped) { Exp memory assetPrice = Exp({mantissa: oracle.getUnderlyingPrice(cToken)}); @@ -1138,9 +1284,9 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } } - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets[i]; - uint newSpeed = totalUtility.mantissa > 0 ? mul_(compRate, div_(utilities[i], totalUtility)) : 0; + uint256 newSpeed = totalUtility.mantissa > 0 ? mul_(compRate, div_(utilities[i], totalUtility)) : 0; compSpeeds[address(cToken)] = newSpeed; emit CompSpeedUpdated(cToken, newSpeed); } @@ -1152,12 +1298,12 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle */ function updateCompSupplyIndex(address cToken) internal { CompMarketState storage supplyState = compSupplyState[cToken]; - uint supplySpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(supplyState.block)); + uint256 supplySpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block)); if (deltaBlocks > 0 && supplySpeed > 0) { - uint supplyTokens = CToken(cToken).totalSupply(); - uint compAccrued = mul_(deltaBlocks, supplySpeed); + uint256 supplyTokens = CToken(cToken).totalSupply(); + uint256 compAccrued = mul_(deltaBlocks, supplySpeed); Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: supplyState.index}), ratio); compSupplyState[cToken] = CompMarketState({ @@ -1175,12 +1321,12 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle */ function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { CompMarketState storage borrowState = compBorrowState[cToken]; - uint borrowSpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(borrowState.block)); + uint256 borrowSpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block)); if (deltaBlocks > 0 && borrowSpeed > 0) { - uint borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); - uint compAccrued = mul_(deltaBlocks, borrowSpeed); + uint256 borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); + uint256 compAccrued = mul_(deltaBlocks, borrowSpeed); Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: borrowState.index}), ratio); compBorrowState[cToken] = CompMarketState({ @@ -1197,7 +1343,11 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute COMP to */ - function distributeSupplierComp(address cToken, address supplier, bool distributeAll) internal { + function distributeSupplierComp( + address cToken, + address supplier, + bool distributeAll + ) internal { CompMarketState storage supplyState = compSupplyState[cToken]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({mantissa: compSupplierIndex[cToken][supplier]}); @@ -1208,9 +1358,9 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); - uint supplierTokens = CToken(cToken).balanceOf(supplier); - uint supplierDelta = mul_(supplierTokens, deltaIndex); - uint supplierAccrued = add_(compAccrued[supplier], supplierDelta); + uint256 supplierTokens = CToken(cToken).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); + uint256 supplierAccrued = add_(compAccrued[supplier], supplierDelta); compAccrued[supplier] = transferComp(supplier, supplierAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedSupplierComp(CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa); } @@ -1221,7 +1371,12 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute COMP to */ - function distributeBorrowerComp(address cToken, address borrower, Exp memory marketBorrowIndex, bool distributeAll) internal { + function distributeBorrowerComp( + address cToken, + address borrower, + Exp memory marketBorrowIndex, + bool distributeAll + ) internal { CompMarketState storage borrowState = compBorrowState[cToken]; Double memory borrowIndex = Double({mantissa: borrowState.index}); Double memory borrowerIndex = Double({mantissa: compBorrowerIndex[cToken][borrower]}); @@ -1229,9 +1384,9 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle if (borrowerIndex.mantissa > 0) { Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); - uint borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); - uint borrowerDelta = mul_(borrowerAmount, deltaIndex); - uint borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); + uint256 borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); + uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); + uint256 borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); compAccrued[borrower] = transferComp(borrower, borrowerAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedBorrowerComp(CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa); } @@ -1244,10 +1399,14 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param userAccrued The amount of COMP to (possibly) transfer * @return The amount of COMP which was NOT transferred to the user */ - function transferComp(address user, uint userAccrued, uint threshold) internal returns (uint) { + function transferComp( + address user, + uint256 userAccrued, + uint256 threshold + ) internal returns (uint256) { if (userAccrued >= threshold && userAccrued > 0) { Comp comp = Comp(getCompAddress()); - uint compRemaining = comp.balanceOf(address(this)); + uint256 compRemaining = comp.balanceOf(address(this)); if (userAccrued <= compRemaining) { comp.transfer(user, userAccrued); return 0; @@ -1282,20 +1441,25 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param borrowers Whether or not to claim COMP earned by borrowing * @param suppliers Whether or not to claim COMP earned by supplying */ - function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public { - for (uint i = 0; i < cTokens.length; i++) { + function claimComp( + address[] memory holders, + CToken[] memory cTokens, + bool borrowers, + bool suppliers + ) public { + for (uint256 i = 0; i < cTokens.length; i++) { CToken cToken = cTokens[i]; require(markets[address(cToken)].isListed, "market must be listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompBorrowIndex(address(cToken), borrowIndex); - for (uint j = 0; j < holders.length; j++) { + for (uint256 j = 0; j < holders.length; j++) { distributeBorrowerComp(address(cToken), holders[j], borrowIndex, true); } } if (suppliers == true) { updateCompSupplyIndex(address(cToken)); - for (uint j = 0; j < holders.length; j++) { + for (uint256 j = 0; j < holders.length; j++) { distributeSupplierComp(address(cToken), holders[j], true); } } @@ -1308,10 +1472,10 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @notice Set the amount of COMP distributed per block * @param compRate_ The amount of COMP wei per block to distribute */ - function _setCompRate(uint compRate_) public { + function _setCompRate(uint256 compRate_) public { require(adminOrInitializing(), "only admin can change comp rate"); - uint oldRate = compRate; + uint256 oldRate = compRate; compRate = compRate_; emit NewCompRate(oldRate, compRate_); @@ -1325,7 +1489,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function _addCompMarkets(address[] memory cTokens) public { require(adminOrInitializing(), "only admin can add comp market"); - for (uint i = 0; i < cTokens.length; i++) { + for (uint256 i = 0; i < cTokens.length; i++) { _addCompMarketInternal(cTokens[i]); } @@ -1380,7 +1544,7 @@ contract ComptrollerG3 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle return allMarkets; } - function getBlockNumber() public view returns (uint) { + function getBlockNumber() public view returns (uint256) { return block.number; } diff --git a/contracts/ComptrollerG4.sol b/contracts/ComptrollerG4.sol index 8563ef4..72304ec 100644 --- a/contracts/ComptrollerG4.sol +++ b/contracts/ComptrollerG4.sol @@ -24,16 +24,16 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle event MarketExited(CToken cToken, address account); /// @notice Emitted when close factor is changed by admin - event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); /// @notice Emitted when a collateral factor is changed by admin - event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); + event NewCollateralFactor(CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); /// @notice Emitted when liquidation incentive is changed by admin - event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); /// @notice Emitted when maxAssets is changed by admin - event NewMaxAssets(uint oldMaxAssets, uint newMaxAssets); + event NewMaxAssets(uint256 oldMaxAssets, uint256 newMaxAssets); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); @@ -51,37 +51,47 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle event MarketComped(CToken cToken, bool isComped); /// @notice Emitted when COMP rate is changed - event NewCompRate(uint oldCompRate, uint newCompRate); + event NewCompRate(uint256 oldCompRate, uint256 newCompRate); /// @notice Emitted when a new COMP speed is calculated for a market - event CompSpeedUpdated(CToken indexed cToken, uint newSpeed); + event CompSpeedUpdated(CToken indexed cToken, uint256 newSpeed); /// @notice Emitted when COMP is distributed to a supplier - event DistributedSupplierComp(CToken indexed cToken, address indexed supplier, uint compDelta, uint compSupplyIndex); + event DistributedSupplierComp( + CToken indexed cToken, + address indexed supplier, + uint256 compDelta, + uint256 compSupplyIndex + ); /// @notice Emitted when COMP is distributed to a borrower - event DistributedBorrowerComp(CToken indexed cToken, address indexed borrower, uint compDelta, uint compBorrowIndex); + event DistributedBorrowerComp( + CToken indexed cToken, + address indexed borrower, + uint256 compDelta, + uint256 compBorrowIndex + ); /// @notice The threshold above which the flywheel transfers COMP, in wei - uint public constant compClaimThreshold = 0.001e18; + uint256 public constant compClaimThreshold = 0.001e18; /// @notice The initial COMP index for a market uint224 public constant compInitialIndex = 1e36; // closeFactorMantissa must be strictly greater than this value - uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 + uint256 internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value - uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value - uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 // liquidationIncentiveMantissa must be no less than this value - uint internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 + uint256 internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 // liquidationIncentiveMantissa must be no greater than this value - uint internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 + uint256 internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 constructor() public { admin = msg.sender; @@ -115,14 +125,14 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ - function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { - uint len = cTokens.length; + function enterMarkets(address[] memory cTokens) public returns (uint256[] memory) { + uint256 len = cTokens.length; - uint[] memory results = new uint[](len); - for (uint i = 0; i < len; i++) { + uint256[] memory results = new uint256[](len); + for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); - results[i] = uint(addToMarketInternal(cToken, msg.sender)); + results[i] = uint256(addToMarketInternal(cToken, msg.sender)); } return results; @@ -147,7 +157,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle return Error.NO_ERROR; } - if (accountAssets[borrower].length >= maxAssets) { + if (accountAssets[borrower].length >= maxAssets) { // no space, cannot join return Error.TOO_MANY_ASSETS; } @@ -172,10 +182,10 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ - function exitMarket(address cTokenAddress) external returns (uint) { + function exitMarket(address cTokenAddress) external returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ - (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ @@ -184,7 +194,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } /* Fail if the sender is not permitted to redeem all of their tokens */ - uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } @@ -193,7 +203,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ @@ -202,9 +212,9 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; - uint len = userAssetList.length; - uint assetIndex = len; - for (uint i = 0; i < len; i++) { + uint256 len = userAssetList.length; + uint256 assetIndex = len; + for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; @@ -221,7 +231,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle emit MarketExited(cToken, msg.sender); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /*** Policy Hooks ***/ @@ -233,7 +243,11 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken], "mint is paused"); @@ -242,14 +256,14 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle mintAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, minter, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -259,7 +273,12 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ - function mintVerify(address cToken, address minter, uint actualMintAmount, uint mintTokens) external { + function mintVerify( + address cToken, + address minter, + uint256 actualMintAmount, + uint256 mintTokens + ) external { // Shh - currently unused cToken; minter; @@ -279,9 +298,13 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { - uint allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); - if (allowed != uint(Error.NO_ERROR)) { + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256) { + uint256 allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); + if (allowed != uint256(Error.NO_ERROR)) { return allowed; } @@ -289,29 +312,38 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, redeemer, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } - function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { + function redeemAllowedInternal( + address cToken, + address redeemer, + uint256 redeemTokens + ) internal view returns (uint256) { if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + redeemer, + CToken(cToken), + redeemTokens, + 0 + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -321,7 +353,12 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ - function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external { // Shh - currently unused cToken; redeemer; @@ -339,12 +376,16 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "borrow is paused"); if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (!markets[cToken].accountMembership[borrower]) { @@ -354,7 +395,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } // it should be impossible to break the important invariant @@ -362,15 +403,20 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { - return uint(Error.PRICE_ERROR); + return uint256(Error.PRICE_ERROR); } - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + borrower, + CToken(cToken), + 0, + borrowAmount + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } // Keep the flywheel moving @@ -378,7 +424,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -387,7 +433,11 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ - function borrowVerify(address cToken, address borrower, uint borrowAmount) external { + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external { // Shh - currently unused cToken; borrower; @@ -411,14 +461,15 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving @@ -426,7 +477,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -440,8 +491,9 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint actualRepayAmount, - uint borrowerIndex) external { + uint256 actualRepayAmount, + uint256 borrowerIndex + ) external { // Shh - currently unused cToken; payer; @@ -468,34 +520,35 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused liquidator; if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } /* The borrower must have shortfall in order to be liquidatable */ - (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); + (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall == 0) { - return uint(Error.INSUFFICIENT_SHORTFALL); + return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ - uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); - (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + (MathError mathErr, uint256 maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (mathErr != MathError.NO_ERROR) { - return uint(Error.MATH_ERROR); + return uint256(Error.MATH_ERROR); } if (repayAmount > maxClose) { - return uint(Error.TOO_MUCH_REPAY); + return uint256(Error.TOO_MUCH_REPAY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -511,8 +564,9 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint actualRepayAmount, - uint seizeTokens) external { + uint256 actualRepayAmount, + uint256 seizeTokens + ) external { // Shh - currently unused cTokenBorrowed; cTokenCollateral; @@ -540,7 +594,8 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external returns (uint) { + uint256 seizeTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); @@ -548,11 +603,11 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle seizeTokens; if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { - return uint(Error.COMPTROLLER_MISMATCH); + return uint256(Error.COMPTROLLER_MISMATCH); } // Keep the flywheel moving @@ -560,7 +615,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle distributeSupplierComp(cTokenCollateral, borrower, false); distributeSupplierComp(cTokenCollateral, liquidator, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -576,7 +631,8 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external { + uint256 seizeTokens + ) external { // Shh - currently unused cTokenCollateral; cTokenBorrowed; @@ -598,14 +654,19 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens - uint allowed = redeemAllowedInternal(cToken, src, transferTokens); - if (allowed != uint(Error.NO_ERROR)) { + uint256 allowed = redeemAllowedInternal(cToken, src, transferTokens); + if (allowed != uint256(Error.NO_ERROR)) { return allowed; } @@ -614,7 +675,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle distributeSupplierComp(cToken, src, false); distributeSupplierComp(cToken, dst, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -624,7 +685,12 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ - function transferVerify(address cToken, address src, address dst, uint transferTokens) external { + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external { // Shh - currently unused cToken; src; @@ -645,12 +711,12 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { - uint sumCollateral; - uint sumBorrowPlusEffects; - uint cTokenBalance; - uint borrowBalance; - uint exchangeRateMantissa; - uint oraclePriceMantissa; + uint256 sumCollateral; + uint256 sumBorrowPlusEffects; + uint256 cTokenBalance; + uint256 borrowBalance; + uint256 exchangeRateMantissa; + uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; @@ -663,10 +729,23 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidity(address account) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); - - return (uint(err), liquidity, shortfall); + function getAccountLiquidity(address account) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(0), + 0, + 0 + ); + + return (uint256(err), liquidity, shortfall); } /** @@ -675,7 +754,15 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { + function getAccountLiquidityInternal(address account) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); } @@ -692,10 +779,24 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidity( address account, address cTokenModify, - uint redeemTokens, - uint borrowAmount) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); - return (uint(err), liquidity, shortfall); + uint256 redeemTokens, + uint256 borrowAmount + ) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(cTokenModify), + redeemTokens, + borrowAmount + ); + return (uint256(err), liquidity, shortfall); } /** @@ -713,21 +814,32 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, - uint redeemTokens, - uint borrowAmount) internal view returns (Error, uint, uint) { - + uint256 redeemTokens, + uint256 borrowAmount + ) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { AccountLiquidityLocalVars memory vars; // Holds all our calculation results - uint oErr; + uint256 oErr; MathError mErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; - for (uint i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken - (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); - if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( + account + ); + if (oErr != 0) { + // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); @@ -747,13 +859,21 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } // sumCollateral += tokensToDenom * cTokenBalance - (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral); + (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt( + vars.tokensToDenom, + vars.cTokenBalance, + vars.sumCollateral + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // sumBorrowPlusEffects += oraclePrice * borrowBalance - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + vars.borrowBalance, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -762,14 +882,22 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.tokensToDenom, + redeemTokens, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + borrowAmount, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -792,12 +920,16 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ - function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 actualRepayAmount + ) external view returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ - uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); - uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { - return (uint(Error.PRICE_ERROR), 0); + return (uint256(Error.PRICE_ERROR), 0); } /* @@ -806,8 +938,8 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ - uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error - uint seizeTokens; + uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + uint256 seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; @@ -815,35 +947,35 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle (mathErr, numerator) = mulExp(liquidationIncentiveMantissa, priceBorrowedMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, ratio) = divExp(numerator, denominator); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, seizeTokens) = mulScalarTruncate(ratio, actualRepayAmount); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } - return (uint(Error.NO_ERROR), seizeTokens); + return (uint256(Error.NO_ERROR), seizeTokens); } /*** Admin Functions ***/ /** - * @notice Sets a new price oracle for the comptroller - * @dev Admin function to set a new price oracle - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPriceOracle(PriceOracle newOracle) public returns (uint) { + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); @@ -858,16 +990,16 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the closeFactor used when liquidating borrows - * @dev Admin function to set closeFactor - * @param newCloseFactorMantissa New close factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) { + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); @@ -884,21 +1016,21 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); } - uint oldCloseFactorMantissa = closeFactorMantissa; + uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the collateralFactor for a market - * @dev Admin function to set per-market collateralFactor - * @param cToken The market to set the factor on - * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint) { + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint256 newCollateralFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); @@ -924,41 +1056,41 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } // Set market's collateral factor to new collateral factor, remember old value - uint oldCollateralFactorMantissa = market.collateralFactorMantissa; + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets maxAssets which controls how many markets can be entered - * @dev Admin function to set maxAssets - * @param newMaxAssets New max assets - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setMaxAssets(uint newMaxAssets) external returns (uint) { + * @notice Sets maxAssets which controls how many markets can be entered + * @dev Admin function to set maxAssets + * @param newMaxAssets New max assets + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setMaxAssets(uint256 newMaxAssets) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); } - uint oldMaxAssets = maxAssets; + uint256 oldMaxAssets = maxAssets; maxAssets = newMaxAssets; emit NewMaxAssets(oldMaxAssets, newMaxAssets); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets liquidationIncentive - * @dev Admin function to set liquidationIncentive - * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); @@ -977,7 +1109,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } // Save current value for use in log - uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; @@ -985,16 +1117,16 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Add the market to the markets mapping and set it as listed - * @dev Admin function to set isListed and add support for the market - * @param cToken The address of the market (token) to list - * @return uint 0=success, otherwise a failure. (See enum Error for details) - */ - function _supportMarket(CToken cToken) external returns (uint) { + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken) external returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } @@ -1005,17 +1137,22 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle cToken.isCToken(); // Sanity check to make sure its really a CToken - markets[address(cToken)] = Market({isListed: true, isComped: false, collateralFactorMantissa: 0}); + markets[address(cToken)] = Market({ + isListed: true, + isComped: false, + collateralFactorMantissa: 0, + version: Version.VANILLA + }); _addMarketInternal(address(cToken)); emit MarketListed(cToken); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } function _addMarketInternal(address cToken) internal { - for (uint i = 0; i < allMarkets.length; i ++) { + for (uint256 i = 0; i < allMarkets.length; i++) { require(allMarkets[i] != CToken(cToken), "market already added"); } allMarkets.push(CToken(cToken)); @@ -1026,7 +1163,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ - function _setPauseGuardian(address newPauseGuardian) public returns (uint) { + function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); } @@ -1040,7 +1177,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } function _setMintPaused(CToken cToken, bool state) public returns (bool) { @@ -1106,7 +1243,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function refreshCompSpeedsInternal() internal { CToken[] memory allMarkets_ = allMarkets; - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets_[i]; Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompSupplyIndex(address(cToken)); @@ -1115,7 +1252,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle Exp memory totalUtility = Exp({mantissa: 0}); Exp[] memory utilities = new Exp[](allMarkets_.length); - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets_[i]; if (markets[address(cToken)].isComped) { Exp memory assetPrice = Exp({mantissa: oracle.getUnderlyingPrice(cToken)}); @@ -1125,9 +1262,9 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } } - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets[i]; - uint newSpeed = totalUtility.mantissa > 0 ? mul_(compRate, div_(utilities[i], totalUtility)) : 0; + uint256 newSpeed = totalUtility.mantissa > 0 ? mul_(compRate, div_(utilities[i], totalUtility)) : 0; compSpeeds[address(cToken)] = newSpeed; emit CompSpeedUpdated(cToken, newSpeed); } @@ -1139,12 +1276,12 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle */ function updateCompSupplyIndex(address cToken) internal { CompMarketState storage supplyState = compSupplyState[cToken]; - uint supplySpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(supplyState.block)); + uint256 supplySpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block)); if (deltaBlocks > 0 && supplySpeed > 0) { - uint supplyTokens = CToken(cToken).totalSupply(); - uint compAccrued = mul_(deltaBlocks, supplySpeed); + uint256 supplyTokens = CToken(cToken).totalSupply(); + uint256 compAccrued = mul_(deltaBlocks, supplySpeed); Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: supplyState.index}), ratio); compSupplyState[cToken] = CompMarketState({ @@ -1162,12 +1299,12 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle */ function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { CompMarketState storage borrowState = compBorrowState[cToken]; - uint borrowSpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(borrowState.block)); + uint256 borrowSpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block)); if (deltaBlocks > 0 && borrowSpeed > 0) { - uint borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); - uint compAccrued = mul_(deltaBlocks, borrowSpeed); + uint256 borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); + uint256 compAccrued = mul_(deltaBlocks, borrowSpeed); Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: borrowState.index}), ratio); compBorrowState[cToken] = CompMarketState({ @@ -1184,7 +1321,11 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute COMP to */ - function distributeSupplierComp(address cToken, address supplier, bool distributeAll) internal { + function distributeSupplierComp( + address cToken, + address supplier, + bool distributeAll + ) internal { CompMarketState storage supplyState = compSupplyState[cToken]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({mantissa: compSupplierIndex[cToken][supplier]}); @@ -1195,9 +1336,9 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); - uint supplierTokens = CToken(cToken).balanceOf(supplier); - uint supplierDelta = mul_(supplierTokens, deltaIndex); - uint supplierAccrued = add_(compAccrued[supplier], supplierDelta); + uint256 supplierTokens = CToken(cToken).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); + uint256 supplierAccrued = add_(compAccrued[supplier], supplierDelta); compAccrued[supplier] = transferComp(supplier, supplierAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedSupplierComp(CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa); } @@ -1208,7 +1349,12 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param cToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute COMP to */ - function distributeBorrowerComp(address cToken, address borrower, Exp memory marketBorrowIndex, bool distributeAll) internal { + function distributeBorrowerComp( + address cToken, + address borrower, + Exp memory marketBorrowIndex, + bool distributeAll + ) internal { CompMarketState storage borrowState = compBorrowState[cToken]; Double memory borrowIndex = Double({mantissa: borrowState.index}); Double memory borrowerIndex = Double({mantissa: compBorrowerIndex[cToken][borrower]}); @@ -1216,9 +1362,9 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle if (borrowerIndex.mantissa > 0) { Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); - uint borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); - uint borrowerDelta = mul_(borrowerAmount, deltaIndex); - uint borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); + uint256 borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); + uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); + uint256 borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); compAccrued[borrower] = transferComp(borrower, borrowerAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedBorrowerComp(CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa); } @@ -1231,10 +1377,14 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param userAccrued The amount of COMP to (possibly) transfer * @return The amount of COMP which was NOT transferred to the user */ - function transferComp(address user, uint userAccrued, uint threshold) internal returns (uint) { + function transferComp( + address user, + uint256 userAccrued, + uint256 threshold + ) internal returns (uint256) { if (userAccrued >= threshold && userAccrued > 0) { Comp comp = Comp(getCompAddress()); - uint compRemaining = comp.balanceOf(address(this)); + uint256 compRemaining = comp.balanceOf(address(this)); if (userAccrued <= compRemaining) { comp.transfer(user, userAccrued); return 0; @@ -1269,20 +1419,25 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @param borrowers Whether or not to claim COMP earned by borrowing * @param suppliers Whether or not to claim COMP earned by supplying */ - function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public { - for (uint i = 0; i < cTokens.length; i++) { + function claimComp( + address[] memory holders, + CToken[] memory cTokens, + bool borrowers, + bool suppliers + ) public { + for (uint256 i = 0; i < cTokens.length; i++) { CToken cToken = cTokens[i]; require(markets[address(cToken)].isListed, "market must be listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompBorrowIndex(address(cToken), borrowIndex); - for (uint j = 0; j < holders.length; j++) { + for (uint256 j = 0; j < holders.length; j++) { distributeBorrowerComp(address(cToken), holders[j], borrowIndex, true); } } if (suppliers == true) { updateCompSupplyIndex(address(cToken)); - for (uint j = 0; j < holders.length; j++) { + for (uint256 j = 0; j < holders.length; j++) { distributeSupplierComp(address(cToken), holders[j], true); } } @@ -1295,10 +1450,10 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle * @notice Set the amount of COMP distributed per block * @param compRate_ The amount of COMP wei per block to distribute */ - function _setCompRate(uint compRate_) public { + function _setCompRate(uint256 compRate_) public { require(adminOrInitializing(), "only admin can change comp rate"); - uint oldRate = compRate; + uint256 oldRate = compRate; compRate = compRate_; emit NewCompRate(oldRate, compRate_); @@ -1312,7 +1467,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle function _addCompMarkets(address[] memory cTokens) public { require(adminOrInitializing(), "only admin can add comp market"); - for (uint i = 0; i < cTokens.length; i++) { + for (uint256 i = 0; i < cTokens.length; i++) { _addCompMarketInternal(cTokens[i]); } @@ -1367,7 +1522,7 @@ contract ComptrollerG4 is ComptrollerV3Storage, ComptrollerInterface, Comptrolle return allMarkets; } - function getBlockNumber() public view returns (uint) { + function getBlockNumber() public view returns (uint256) { return block.number; } diff --git a/contracts/ComptrollerG5.sol b/contracts/ComptrollerG5.sol index 1cdbbd7..bf2752f 100644 --- a/contracts/ComptrollerG5.sol +++ b/contracts/ComptrollerG5.sol @@ -24,16 +24,16 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle event MarketExited(CToken cToken, address account); /// @notice Emitted when close factor is changed by admin - event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); /// @notice Emitted when a collateral factor is changed by admin - event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); + event NewCollateralFactor(CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); /// @notice Emitted when liquidation incentive is changed by admin - event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); /// @notice Emitted when maxAssets is changed by admin - event NewMaxAssets(uint oldMaxAssets, uint newMaxAssets); + event NewMaxAssets(uint256 oldMaxAssets, uint256 newMaxAssets); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); @@ -51,43 +51,53 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle event MarketComped(CToken cToken, bool isComped); /// @notice Emitted when COMP rate is changed - event NewCompRate(uint oldCompRate, uint newCompRate); + event NewCompRate(uint256 oldCompRate, uint256 newCompRate); /// @notice Emitted when a new COMP speed is calculated for a market - event CompSpeedUpdated(CToken indexed cToken, uint newSpeed); + event CompSpeedUpdated(CToken indexed cToken, uint256 newSpeed); /// @notice Emitted when COMP is distributed to a supplier - event DistributedSupplierComp(CToken indexed cToken, address indexed supplier, uint compDelta, uint compSupplyIndex); + event DistributedSupplierComp( + CToken indexed cToken, + address indexed supplier, + uint256 compDelta, + uint256 compSupplyIndex + ); /// @notice Emitted when COMP is distributed to a borrower - event DistributedBorrowerComp(CToken indexed cToken, address indexed borrower, uint compDelta, uint compBorrowIndex); + event DistributedBorrowerComp( + CToken indexed cToken, + address indexed borrower, + uint256 compDelta, + uint256 compBorrowIndex + ); /// @notice Emitted when borrow cap for a cToken is changed - event NewBorrowCap(CToken indexed cToken, uint newBorrowCap); + event NewBorrowCap(CToken indexed cToken, uint256 newBorrowCap); /// @notice Emitted when borrow cap guardian is changed event NewBorrowCapGuardian(address oldBorrowCapGuardian, address newBorrowCapGuardian); /// @notice The threshold above which the flywheel transfers COMP, in wei - uint public constant compClaimThreshold = 0.001e18; + uint256 public constant compClaimThreshold = 0.001e18; /// @notice The initial COMP index for a market uint224 public constant compInitialIndex = 1e36; // closeFactorMantissa must be strictly greater than this value - uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 + uint256 internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value - uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value - uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 + uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 // liquidationIncentiveMantissa must be no less than this value - uint internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 + uint256 internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 // liquidationIncentiveMantissa must be no greater than this value - uint internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 + uint256 internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 constructor() public { admin = msg.sender; @@ -121,14 +131,14 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ - function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { - uint len = cTokens.length; + function enterMarkets(address[] memory cTokens) public returns (uint256[] memory) { + uint256 len = cTokens.length; - uint[] memory results = new uint[](len); - for (uint i = 0; i < len; i++) { + uint256[] memory results = new uint256[](len); + for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); - results[i] = uint(addToMarketInternal(cToken, msg.sender)); + results[i] = uint256(addToMarketInternal(cToken, msg.sender)); } return results; @@ -153,7 +163,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle return Error.NO_ERROR; } - if (accountAssets[borrower].length >= maxAssets) { + if (accountAssets[borrower].length >= maxAssets) { // no space, cannot join return Error.TOO_MANY_ASSETS; } @@ -178,10 +188,10 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ - function exitMarket(address cTokenAddress) external returns (uint) { + function exitMarket(address cTokenAddress) external returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ - (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ @@ -190,7 +200,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle } /* Fail if the sender is not permitted to redeem all of their tokens */ - uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } @@ -199,7 +209,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ @@ -208,9 +218,9 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; - uint len = userAssetList.length; - uint assetIndex = len; - for (uint i = 0; i < len; i++) { + uint256 len = userAssetList.length; + uint256 assetIndex = len; + for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; @@ -227,7 +237,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle emit MarketExited(cToken, msg.sender); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /*** Policy Hooks ***/ @@ -239,7 +249,11 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken], "mint is paused"); @@ -248,14 +262,14 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle mintAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, minter, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -265,7 +279,12 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ - function mintVerify(address cToken, address minter, uint actualMintAmount, uint mintTokens) external { + function mintVerify( + address cToken, + address minter, + uint256 actualMintAmount, + uint256 mintTokens + ) external { // Shh - currently unused cToken; minter; @@ -285,9 +304,13 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { - uint allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); - if (allowed != uint(Error.NO_ERROR)) { + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256) { + uint256 allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); + if (allowed != uint256(Error.NO_ERROR)) { return allowed; } @@ -295,29 +318,38 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, redeemer, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } - function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { + function redeemAllowedInternal( + address cToken, + address redeemer, + uint256 redeemTokens + ) internal view returns (uint256) { if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + redeemer, + CToken(cToken), + redeemTokens, + 0 + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -327,7 +359,12 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ - function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external { // Shh - currently unused cToken; redeemer; @@ -345,12 +382,16 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "borrow is paused"); if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (!markets[cToken].accountMembership[borrower]) { @@ -360,7 +401,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } // it should be impossible to break the important invariant @@ -368,25 +409,29 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { - return uint(Error.PRICE_ERROR); + return uint256(Error.PRICE_ERROR); } - - uint borrowCap = borrowCaps[cToken]; + uint256 borrowCap = borrowCaps[cToken]; // Borrow cap of 0 corresponds to unlimited borrowing if (borrowCap != 0) { - uint totalBorrows = CToken(cToken).totalBorrows(); - (MathError mathErr, uint nextTotalBorrows) = addUInt(totalBorrows, borrowAmount); + uint256 totalBorrows = CToken(cToken).totalBorrows(); + (MathError mathErr, uint256 nextTotalBorrows) = addUInt(totalBorrows, borrowAmount); require(mathErr == MathError.NO_ERROR, "total borrows overflow"); require(nextTotalBorrows < borrowCap, "market borrow cap reached"); } - (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + borrower, + CToken(cToken), + 0, + borrowAmount + ); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall > 0) { - return uint(Error.INSUFFICIENT_LIQUIDITY); + return uint256(Error.INSUFFICIENT_LIQUIDITY); } // Keep the flywheel moving @@ -394,7 +439,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -403,7 +448,11 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ - function borrowVerify(address cToken, address borrower, uint borrowAmount) external { + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external { // Shh - currently unused cToken; borrower; @@ -427,14 +476,15 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; if (!markets[cToken].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving @@ -442,7 +492,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -456,8 +506,9 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle address cToken, address payer, address borrower, - uint actualRepayAmount, - uint borrowerIndex) external { + uint256 actualRepayAmount, + uint256 borrowerIndex + ) external { // Shh - currently unused cToken; payer; @@ -484,34 +535,35 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint repayAmount) external returns (uint) { + uint256 repayAmount + ) external returns (uint256) { // Shh - currently unused liquidator; if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } /* The borrower must have shortfall in order to be liquidatable */ - (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); + (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { - return uint(err); + return uint256(err); } if (shortfall == 0) { - return uint(Error.INSUFFICIENT_SHORTFALL); + return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ - uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); - (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + (MathError mathErr, uint256 maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (mathErr != MathError.NO_ERROR) { - return uint(Error.MATH_ERROR); + return uint256(Error.MATH_ERROR); } if (repayAmount > maxClose) { - return uint(Error.TOO_MUCH_REPAY); + return uint256(Error.TOO_MUCH_REPAY); } - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -527,8 +579,9 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle address cTokenCollateral, address liquidator, address borrower, - uint actualRepayAmount, - uint seizeTokens) external { + uint256 actualRepayAmount, + uint256 seizeTokens + ) external { // Shh - currently unused cTokenBorrowed; cTokenCollateral; @@ -556,7 +609,8 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external returns (uint) { + uint256 seizeTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); @@ -564,11 +618,11 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle seizeTokens; if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { - return uint(Error.MARKET_NOT_LISTED); + return uint256(Error.MARKET_NOT_LISTED); } if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { - return uint(Error.COMPTROLLER_MISMATCH); + return uint256(Error.COMPTROLLER_MISMATCH); } // Keep the flywheel moving @@ -576,7 +630,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle distributeSupplierComp(cTokenCollateral, borrower, false); distributeSupplierComp(cTokenCollateral, liquidator, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -592,7 +646,8 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external { + uint256 seizeTokens + ) external { // Shh - currently unused cTokenCollateral; cTokenBorrowed; @@ -614,14 +669,19 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ - function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens - uint allowed = redeemAllowedInternal(cToken, src, transferTokens); - if (allowed != uint(Error.NO_ERROR)) { + uint256 allowed = redeemAllowedInternal(cToken, src, transferTokens); + if (allowed != uint256(Error.NO_ERROR)) { return allowed; } @@ -630,7 +690,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle distributeSupplierComp(cToken, src, false); distributeSupplierComp(cToken, dst, false); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -640,7 +700,12 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ - function transferVerify(address cToken, address src, address dst, uint transferTokens) external { + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external { // Shh - currently unused cToken; src; @@ -661,12 +726,12 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { - uint sumCollateral; - uint sumBorrowPlusEffects; - uint cTokenBalance; - uint borrowBalance; - uint exchangeRateMantissa; - uint oraclePriceMantissa; + uint256 sumCollateral; + uint256 sumBorrowPlusEffects; + uint256 cTokenBalance; + uint256 borrowBalance; + uint256 exchangeRateMantissa; + uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; @@ -679,10 +744,23 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidity(address account) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); - - return (uint(err), liquidity, shortfall); + function getAccountLiquidity(address account) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(0), + 0, + 0 + ); + + return (uint256(err), liquidity, shortfall); } /** @@ -691,7 +769,15 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ - function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { + function getAccountLiquidityInternal(address account) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); } @@ -708,10 +794,24 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidity( address account, address cTokenModify, - uint redeemTokens, - uint borrowAmount) public view returns (uint, uint, uint) { - (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); - return (uint(err), liquidity, shortfall); + uint256 redeemTokens, + uint256 borrowAmount + ) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(cTokenModify), + redeemTokens, + borrowAmount + ); + return (uint256(err), liquidity, shortfall); } /** @@ -729,21 +829,32 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, - uint redeemTokens, - uint borrowAmount) internal view returns (Error, uint, uint) { - + uint256 redeemTokens, + uint256 borrowAmount + ) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { AccountLiquidityLocalVars memory vars; // Holds all our calculation results - uint oErr; + uint256 oErr; MathError mErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; - for (uint i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken - (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); - if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( + account + ); + if (oErr != 0) { + // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); @@ -763,13 +874,21 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle } // sumCollateral += tokensToDenom * cTokenBalance - (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral); + (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt( + vars.tokensToDenom, + vars.cTokenBalance, + vars.sumCollateral + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // sumBorrowPlusEffects += oraclePrice * borrowBalance - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + vars.borrowBalance, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -778,14 +897,22 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.tokensToDenom, + redeemTokens, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount - (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt( + vars.oraclePrice, + borrowAmount, + vars.sumBorrowPlusEffects + ); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } @@ -808,12 +935,16 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ - function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 actualRepayAmount + ) external view returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ - uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); - uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { - return (uint(Error.PRICE_ERROR), 0); + return (uint256(Error.PRICE_ERROR), 0); } /* @@ -822,8 +953,8 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ - uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error - uint seizeTokens; + uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + uint256 seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; @@ -831,35 +962,35 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle (mathErr, numerator) = mulExp(liquidationIncentiveMantissa, priceBorrowedMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, ratio) = divExp(numerator, denominator); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } (mathErr, seizeTokens) = mulScalarTruncate(ratio, actualRepayAmount); if (mathErr != MathError.NO_ERROR) { - return (uint(Error.MATH_ERROR), 0); + return (uint256(Error.MATH_ERROR), 0); } - return (uint(Error.NO_ERROR), seizeTokens); + return (uint256(Error.NO_ERROR), seizeTokens); } /*** Admin Functions ***/ /** - * @notice Sets a new price oracle for the comptroller - * @dev Admin function to set a new price oracle - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPriceOracle(PriceOracle newOracle) public returns (uint) { + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); @@ -874,16 +1005,16 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the closeFactor used when liquidating borrows - * @dev Admin function to set closeFactor - * @param newCloseFactorMantissa New close factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) { + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); @@ -900,21 +1031,21 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); } - uint oldCloseFactorMantissa = closeFactorMantissa; + uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets the collateralFactor for a market - * @dev Admin function to set per-market collateralFactor - * @param cToken The market to set the factor on - * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint) { + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint256 newCollateralFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); @@ -940,41 +1071,41 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle } // Set market's collateral factor to new collateral factor, remember old value - uint oldCollateralFactorMantissa = market.collateralFactorMantissa; + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets maxAssets which controls how many markets can be entered - * @dev Admin function to set maxAssets - * @param newMaxAssets New max assets - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setMaxAssets(uint newMaxAssets) external returns (uint) { + * @notice Sets maxAssets which controls how many markets can be entered + * @dev Admin function to set maxAssets + * @param newMaxAssets New max assets + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setMaxAssets(uint256 newMaxAssets) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); } - uint oldMaxAssets = maxAssets; + uint256 oldMaxAssets = maxAssets; maxAssets = newMaxAssets; emit NewMaxAssets(oldMaxAssets, newMaxAssets); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Sets liquidationIncentive - * @dev Admin function to set liquidationIncentive - * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 - * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) - */ - function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); @@ -993,7 +1124,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle } // Save current value for use in log - uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; @@ -1001,16 +1132,16 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Add the market to the markets mapping and set it as listed - * @dev Admin function to set isListed and add support for the market - * @param cToken The address of the market (token) to list - * @return uint 0=success, otherwise a failure. (See enum Error for details) - */ - function _supportMarket(CToken cToken) external returns (uint) { + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken) external returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } @@ -1021,38 +1152,45 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle cToken.isCToken(); // Sanity check to make sure its really a CToken - markets[address(cToken)] = Market({isListed: true, isComped: false, collateralFactorMantissa: 0}); + markets[address(cToken)] = Market({ + isListed: true, + isComped: false, + collateralFactorMantissa: 0, + version: Version.VANILLA + }); _addMarketInternal(address(cToken)); emit MarketListed(cToken); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } function _addMarketInternal(address cToken) internal { - for (uint i = 0; i < allMarkets.length; i ++) { + for (uint256 i = 0; i < allMarkets.length; i++) { require(allMarkets[i] != CToken(cToken), "market already added"); } allMarkets.push(CToken(cToken)); } - /** - * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. - * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. - * @param cTokens The addresses of the markets (tokens) to change the borrow caps for - * @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"); + * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. + * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. + * @param cTokens The addresses of the markets (tokens) to change the borrow caps for + * @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, uint256[] calldata newBorrowCaps) external { + 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; + uint256 numMarkets = cTokens.length; + uint256 numBorrowCaps = newBorrowCaps.length; require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); - for(uint i = 0; i < numMarkets; i++) { + for (uint256 i = 0; i < numMarkets; i++) { borrowCaps[address(cTokens[i])] = newBorrowCaps[i]; emit NewBorrowCap(cTokens[i], newBorrowCaps[i]); } @@ -1080,7 +1218,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ - function _setPauseGuardian(address newPauseGuardian) public returns (uint) { + function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); } @@ -1094,7 +1232,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } function _setMintPaused(CToken cToken, bool state) public returns (bool) { @@ -1160,7 +1298,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle function refreshCompSpeedsInternal() internal { CToken[] memory allMarkets_ = allMarkets; - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets_[i]; Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompSupplyIndex(address(cToken)); @@ -1169,7 +1307,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle Exp memory totalUtility = Exp({mantissa: 0}); Exp[] memory utilities = new Exp[](allMarkets_.length); - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets_[i]; if (markets[address(cToken)].isComped) { Exp memory assetPrice = Exp({mantissa: oracle.getUnderlyingPrice(cToken)}); @@ -1179,9 +1317,9 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle } } - for (uint i = 0; i < allMarkets_.length; i++) { + for (uint256 i = 0; i < allMarkets_.length; i++) { CToken cToken = allMarkets[i]; - uint newSpeed = totalUtility.mantissa > 0 ? mul_(compRate, div_(utilities[i], totalUtility)) : 0; + uint256 newSpeed = totalUtility.mantissa > 0 ? mul_(compRate, div_(utilities[i], totalUtility)) : 0; compSpeeds[address(cToken)] = newSpeed; emit CompSpeedUpdated(cToken, newSpeed); } @@ -1193,12 +1331,12 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle */ function updateCompSupplyIndex(address cToken) internal { CompMarketState storage supplyState = compSupplyState[cToken]; - uint supplySpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(supplyState.block)); + uint256 supplySpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block)); if (deltaBlocks > 0 && supplySpeed > 0) { - uint supplyTokens = CToken(cToken).totalSupply(); - uint compAccrued = mul_(deltaBlocks, supplySpeed); + uint256 supplyTokens = CToken(cToken).totalSupply(); + uint256 compAccrued = mul_(deltaBlocks, supplySpeed); Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: supplyState.index}), ratio); compSupplyState[cToken] = CompMarketState({ @@ -1216,12 +1354,12 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle */ function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { CompMarketState storage borrowState = compBorrowState[cToken]; - uint borrowSpeed = compSpeeds[cToken]; - uint blockNumber = getBlockNumber(); - uint deltaBlocks = sub_(blockNumber, uint(borrowState.block)); + uint256 borrowSpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block)); if (deltaBlocks > 0 && borrowSpeed > 0) { - uint borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); - uint compAccrued = mul_(deltaBlocks, borrowSpeed); + uint256 borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); + uint256 compAccrued = mul_(deltaBlocks, borrowSpeed); Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: borrowState.index}), ratio); compBorrowState[cToken] = CompMarketState({ @@ -1238,7 +1376,11 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param cToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute COMP to */ - function distributeSupplierComp(address cToken, address supplier, bool distributeAll) internal { + function distributeSupplierComp( + address cToken, + address supplier, + bool distributeAll + ) internal { CompMarketState storage supplyState = compSupplyState[cToken]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({mantissa: compSupplierIndex[cToken][supplier]}); @@ -1249,9 +1391,9 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); - uint supplierTokens = CToken(cToken).balanceOf(supplier); - uint supplierDelta = mul_(supplierTokens, deltaIndex); - uint supplierAccrued = add_(compAccrued[supplier], supplierDelta); + uint256 supplierTokens = CToken(cToken).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); + uint256 supplierAccrued = add_(compAccrued[supplier], supplierDelta); compAccrued[supplier] = transferComp(supplier, supplierAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedSupplierComp(CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa); } @@ -1262,7 +1404,12 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param cToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute COMP to */ - function distributeBorrowerComp(address cToken, address borrower, Exp memory marketBorrowIndex, bool distributeAll) internal { + function distributeBorrowerComp( + address cToken, + address borrower, + Exp memory marketBorrowIndex, + bool distributeAll + ) internal { CompMarketState storage borrowState = compBorrowState[cToken]; Double memory borrowIndex = Double({mantissa: borrowState.index}); Double memory borrowerIndex = Double({mantissa: compBorrowerIndex[cToken][borrower]}); @@ -1270,9 +1417,9 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle if (borrowerIndex.mantissa > 0) { Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); - uint borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); - uint borrowerDelta = mul_(borrowerAmount, deltaIndex); - uint borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); + uint256 borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); + uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); + uint256 borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); compAccrued[borrower] = transferComp(borrower, borrowerAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedBorrowerComp(CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa); } @@ -1285,10 +1432,14 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param userAccrued The amount of COMP to (possibly) transfer * @return The amount of COMP which was NOT transferred to the user */ - function transferComp(address user, uint userAccrued, uint threshold) internal returns (uint) { + function transferComp( + address user, + uint256 userAccrued, + uint256 threshold + ) internal returns (uint256) { if (userAccrued >= threshold && userAccrued > 0) { Comp comp = Comp(getCompAddress()); - uint compRemaining = comp.balanceOf(address(this)); + uint256 compRemaining = comp.balanceOf(address(this)); if (userAccrued <= compRemaining) { comp.transfer(user, userAccrued); return 0; @@ -1323,20 +1474,25 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @param borrowers Whether or not to claim COMP earned by borrowing * @param suppliers Whether or not to claim COMP earned by supplying */ - function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public { - for (uint i = 0; i < cTokens.length; i++) { + function claimComp( + address[] memory holders, + CToken[] memory cTokens, + bool borrowers, + bool suppliers + ) public { + for (uint256 i = 0; i < cTokens.length; i++) { CToken cToken = cTokens[i]; require(markets[address(cToken)].isListed, "market must be listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompBorrowIndex(address(cToken), borrowIndex); - for (uint j = 0; j < holders.length; j++) { + for (uint256 j = 0; j < holders.length; j++) { distributeBorrowerComp(address(cToken), holders[j], borrowIndex, true); } } if (suppliers == true) { updateCompSupplyIndex(address(cToken)); - for (uint j = 0; j < holders.length; j++) { + for (uint256 j = 0; j < holders.length; j++) { distributeSupplierComp(address(cToken), holders[j], true); } } @@ -1349,10 +1505,10 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle * @notice Set the amount of COMP distributed per block * @param compRate_ The amount of COMP wei per block to distribute */ - function _setCompRate(uint compRate_) public { + function _setCompRate(uint256 compRate_) public { require(adminOrInitializing(), "only admin can change comp rate"); - uint oldRate = compRate; + uint256 oldRate = compRate; compRate = compRate_; emit NewCompRate(oldRate, compRate_); @@ -1366,7 +1522,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle function _addCompMarkets(address[] memory cTokens) public { require(adminOrInitializing(), "only admin can add comp market"); - for (uint i = 0; i < cTokens.length; i++) { + for (uint256 i = 0; i < cTokens.length; i++) { _addCompMarketInternal(cTokens[i]); } @@ -1421,7 +1577,7 @@ contract ComptrollerG5 is ComptrollerV4Storage, ComptrollerInterface, Comptrolle return allMarkets; } - function getBlockNumber() public view returns (uint) { + function getBlockNumber() public view returns (uint256) { return block.number; } diff --git a/contracts/ComptrollerG6.sol b/contracts/ComptrollerG6.sol new file mode 100644 index 0000000..95605ad --- /dev/null +++ b/contracts/ComptrollerG6.sol @@ -0,0 +1,1481 @@ +pragma solidity ^0.5.16; + +import "./CToken.sol"; +import "./ErrorReporter.sol"; +import "./Exponential.sol"; +import "./PriceOracle.sol"; +import "./ComptrollerInterface.sol"; +import "./ComptrollerStorage.sol"; +import "./Unitroller.sol"; +import "./Governance/Comp.sol"; + +/** + * @title Compound's Comptroller Contract + * @author Compound (modified by Arr00) + */ +contract ComptrollerG6 is ComptrollerV5Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { + /// @notice Emitted when an admin supports a market + event MarketListed(CToken cToken); + + /// @notice Emitted when an account enters a market + event MarketEntered(CToken cToken, address account); + + /// @notice Emitted when an account exits a market + event MarketExited(CToken cToken, address account); + + /// @notice Emitted when close factor is changed by admin + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); + + /// @notice Emitted when a collateral factor is changed by admin + event NewCollateralFactor(CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); + + /// @notice Emitted when liquidation incentive is changed by admin + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); + + /// @notice Emitted when price oracle is changed + event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); + + /// @notice Emitted when pause guardian is changed + event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); + + /// @notice Emitted when an action is paused globally + event ActionPaused(string action, bool pauseState); + + /// @notice Emitted when an action is paused on a market + event ActionPaused(CToken cToken, string action, bool pauseState); + + /// @notice Emitted when a new COMP speed is calculated for a market + event CompSpeedUpdated(CToken indexed cToken, uint256 newSpeed); + + /// @notice Emitted when COMP is distributed to a supplier + event DistributedSupplierComp( + CToken indexed cToken, + address indexed supplier, + uint256 compDelta, + uint256 compSupplyIndex + ); + + /// @notice Emitted when COMP is distributed to a borrower + event DistributedBorrowerComp( + CToken indexed cToken, + address indexed borrower, + uint256 compDelta, + uint256 compBorrowIndex + ); + + /// @notice Emitted when borrow cap for a cToken is changed + event NewBorrowCap(CToken indexed cToken, uint256 newBorrowCap); + + /// @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, uint256 newSupplyCap); + + /// @notice Emitted when supply cap guardian is changed + event NewSupplyCapGuardian(address oldSupplyCapGuardian, address newSupplyCapGuardian); + + /// @notice The threshold above which the flywheel transfers COMP, in wei + uint256 public constant compClaimThreshold = 0.001e18; + + /// @notice The initial COMP index for a market + uint224 public constant compInitialIndex = 1e36; + + // No collateralFactorMantissa may exceed this value + uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 + + constructor() public { + admin = msg.sender; + } + + /*** Assets You Are In ***/ + + /** + * @notice Returns the assets an account has entered + * @param account The address of the account to pull assets for + * @return A dynamic list with the assets the account has entered + */ + function getAssetsIn(address account) external view returns (CToken[] memory) { + CToken[] memory assetsIn = accountAssets[account]; + + return assetsIn; + } + + /** + * @notice Returns whether the given account is entered in the given asset + * @param account The address of the account to check + * @param cToken The cToken to check + * @return True if the account is in the asset, otherwise false. + */ + function checkMembership(address account, CToken cToken) external view returns (bool) { + return markets[address(cToken)].accountMembership[account]; + } + + /** + * @notice Add assets to be included in account liquidity calculation + * @param cTokens The list of addresses of the cToken markets to be enabled + * @return Success indicator for whether each corresponding market was entered + */ + function enterMarkets(address[] memory cTokens) public returns (uint256[] memory) { + uint256 len = cTokens.length; + + uint256[] memory results = new uint256[](len); + for (uint256 i = 0; i < len; i++) { + CToken cToken = CToken(cTokens[i]); + + results[i] = uint256(addToMarketInternal(cToken, msg.sender)); + } + + return results; + } + + /** + * @notice Add the market to the borrower's "assets in" for liquidity calculations + * @param cToken The market to enter + * @param borrower The address of the account to modify + * @return Success indicator for whether the market was entered + */ + function addToMarketInternal(CToken cToken, address borrower) internal returns (Error) { + Market storage marketToJoin = markets[address(cToken)]; + + if (!marketToJoin.isListed) { + // market is not listed, cannot join + return Error.MARKET_NOT_LISTED; + } + + if (marketToJoin.accountMembership[borrower] == true) { + // already joined + return Error.NO_ERROR; + } + + // survived the gauntlet, add to list + // NOTE: we store these somewhat redundantly as a significant optimization + // this avoids having to iterate through the list for the most common use cases + // that is, only when we need to perform liquidity checks + // and not whenever we want to check if an account is in a particular market + marketToJoin.accountMembership[borrower] = true; + accountAssets[borrower].push(cToken); + + emit MarketEntered(cToken, borrower); + + return Error.NO_ERROR; + } + + /** + * @notice Removes asset from sender's account liquidity calculation + * @dev Sender must not have an outstanding borrow balance in the asset, + * or be providing necessary collateral for an outstanding borrow. + * @param cTokenAddress The address of the asset to be removed + * @return Whether or not the account successfully exited the market + */ + function exitMarket(address cTokenAddress) external returns (uint256) { + CToken cToken = CToken(cTokenAddress); + /* Get sender tokensHeld and amountOwed underlying from the cToken */ + (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code + + /* Fail if the sender has a borrow balance */ + if (amountOwed != 0) { + return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED); + } + + /* Fail if the sender is not permitted to redeem all of their tokens */ + uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + if (allowed != 0) { + return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); + } + + Market storage marketToExit = markets[address(cToken)]; + + /* Return true if the sender is not already ‘in’ the market */ + if (!marketToExit.accountMembership[msg.sender]) { + return uint256(Error.NO_ERROR); + } + + /* Set cToken account membership to false */ + delete marketToExit.accountMembership[msg.sender]; + + /* Delete cToken from the account’s list of assets */ + // load into memory for faster iteration + CToken[] memory userAssetList = accountAssets[msg.sender]; + uint256 len = userAssetList.length; + uint256 assetIndex = len; + for (uint256 i = 0; i < len; i++) { + if (userAssetList[i] == cToken) { + assetIndex = i; + break; + } + } + + // We *must* have found the asset in the list or our redundant data structure is broken + assert(assetIndex < len); + + // copy last item in list to location of item to be removed, reduce length by 1 + CToken[] storage storedList = accountAssets[msg.sender]; + storedList[assetIndex] = storedList[storedList.length - 1]; + storedList.length--; + + emit MarketExited(cToken, msg.sender); + + return uint256(Error.NO_ERROR); + } + + /*** Policy Hooks ***/ + + /** + * @notice Checks if the account should be allowed to mint tokens in the given market + * @param cToken The market to verify the mint against + * @param minter The account which would get the minted tokens + * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens + * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256) { + // Pausing is a very serious situation - we revert to sound the alarms + require(!mintGuardianPaused[cToken], "mint is paused"); + + // Shh - currently unused + minter; + + if (!markets[cToken].isListed) { + return uint256(Error.MARKET_NOT_LISTED); + } + + uint256 supplyCap = supplyCaps[cToken]; + // Supply cap of 0 corresponds to unlimited supplying + if (supplyCap != 0) { + uint256 totalCash = CToken(cToken).getCash(); + uint256 totalBorrows = CToken(cToken).totalBorrows(); + uint256 totalReserves = CToken(cToken).totalReserves(); + // totalSupplies = totalCash + totalBorrows - totalReserves + (MathError mathErr, uint256 totalSupplies) = addThenSubUInt(totalCash, totalBorrows, totalReserves); + require(mathErr == MathError.NO_ERROR, "totalSupplies failed"); + + uint256 nextTotalSupplies = add_(totalSupplies, mintAmount); + require(nextTotalSupplies < supplyCap, "market supply cap reached"); + } + + // Keep the flywheel moving + updateCompSupplyIndex(cToken); + distributeSupplierComp(cToken, minter, false); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Validates mint and reverts on rejection. May emit logs. + * @param cToken Asset being minted + * @param minter The address minting the tokens + * @param actualMintAmount The amount of the underlying asset being minted + * @param mintTokens The number of tokens being minted + */ + function mintVerify( + address cToken, + address minter, + uint256 actualMintAmount, + uint256 mintTokens + ) external { + // Shh - currently unused + cToken; + minter; + actualMintAmount; + mintTokens; + + // Shh - we don't ever want this hook to be marked pure + if (false) { + maxAssets = maxAssets; + } + } + + /** + * @notice Checks if the account should be allowed to redeem tokens in the given market + * @param cToken The market to verify the redeem against + * @param redeemer The account which would redeem the tokens + * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market + * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256) { + uint256 allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); + if (allowed != uint256(Error.NO_ERROR)) { + return allowed; + } + + // Keep the flywheel moving + updateCompSupplyIndex(cToken); + distributeSupplierComp(cToken, redeemer, false); + + return uint256(Error.NO_ERROR); + } + + function redeemAllowedInternal( + address cToken, + address redeemer, + uint256 redeemTokens + ) internal view returns (uint256) { + if (!markets[cToken].isListed) { + return uint256(Error.MARKET_NOT_LISTED); + } + + /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ + if (!markets[cToken].accountMembership[redeemer]) { + return uint256(Error.NO_ERROR); + } + + /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + redeemer, + CToken(cToken), + redeemTokens, + 0 + ); + if (err != Error.NO_ERROR) { + return uint256(err); + } + if (shortfall > 0) { + return uint256(Error.INSUFFICIENT_LIQUIDITY); + } + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Validates redeem and reverts on rejection. May emit logs. + * @param cToken Asset being redeemed + * @param redeemer The address redeeming the tokens + * @param redeemAmount The amount of the underlying asset being redeemed + * @param redeemTokens The number of tokens being redeemed + */ + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external { + // Shh - currently unused + cToken; + redeemer; + + // Require tokens is zero or amount is also zero + if (redeemTokens == 0 && redeemAmount > 0) { + revert("redeemTokens zero"); + } + } + + /** + * @notice Checks if the account should be allowed to borrow the underlying asset of the given market + * @param cToken The market to verify the borrow against + * @param borrower The account which would borrow the asset + * @param borrowAmount The amount of underlying the account would borrow + * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256) { + // Pausing is a very serious situation - we revert to sound the alarms + require(!borrowGuardianPaused[cToken], "borrow is paused"); + + if (!markets[cToken].isListed) { + return uint256(Error.MARKET_NOT_LISTED); + } + + if (!markets[cToken].accountMembership[borrower]) { + // only cTokens may call borrowAllowed if borrower not in market + require(msg.sender == cToken, "sender must be cToken"); + + // attempt to add borrower to the market + Error err = addToMarketInternal(CToken(msg.sender), borrower); + if (err != Error.NO_ERROR) { + return uint256(err); + } + + // it should be impossible to break the important invariant + assert(markets[cToken].accountMembership[borrower]); + } + + if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { + return uint256(Error.PRICE_ERROR); + } + + uint256 borrowCap = borrowCaps[cToken]; + // Borrow cap of 0 corresponds to unlimited borrowing + if (borrowCap != 0) { + uint256 totalBorrows = CToken(cToken).totalBorrows(); + uint256 nextTotalBorrows = add_(totalBorrows, borrowAmount); + require(nextTotalBorrows < borrowCap, "market borrow cap reached"); + } + + (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + borrower, + CToken(cToken), + 0, + borrowAmount + ); + if (err != Error.NO_ERROR) { + return uint256(err); + } + if (shortfall > 0) { + return uint256(Error.INSUFFICIENT_LIQUIDITY); + } + + // Keep the flywheel moving + Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); + updateCompBorrowIndex(cToken, borrowIndex); + distributeBorrowerComp(cToken, borrower, borrowIndex, false); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Validates borrow and reverts on rejection. May emit logs. + * @param cToken Asset whose underlying is being borrowed + * @param borrower The address borrowing the underlying + * @param borrowAmount The amount of the underlying asset requested to borrow + */ + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external { + // Shh - currently unused + cToken; + borrower; + borrowAmount; + + // Shh - we don't ever want this hook to be marked pure + if (false) { + maxAssets = maxAssets; + } + } + + /** + * @notice Checks if the account should be allowed to repay a borrow in the given market + * @param cToken The market to verify the repay against + * @param payer The account which would repay the asset + * @param borrower The account which would borrowed the asset + * @param repayAmount The amount of the underlying asset the account would repay + * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function repayBorrowAllowed( + address cToken, + address payer, + address borrower, + uint256 repayAmount + ) external returns (uint256) { + // Shh - currently unused + payer; + borrower; + repayAmount; + + if (!markets[cToken].isListed) { + return uint256(Error.MARKET_NOT_LISTED); + } + + // Keep the flywheel moving + Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); + updateCompBorrowIndex(cToken, borrowIndex); + distributeBorrowerComp(cToken, borrower, borrowIndex, false); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Validates repayBorrow and reverts on rejection. May emit logs. + * @param cToken Asset being repaid + * @param payer The address repaying the borrow + * @param borrower The address of the borrower + * @param actualRepayAmount The amount of underlying being repaid + */ + function repayBorrowVerify( + address cToken, + address payer, + address borrower, + uint256 actualRepayAmount, + uint256 borrowerIndex + ) external { + // Shh - currently unused + cToken; + payer; + borrower; + actualRepayAmount; + borrowerIndex; + + // Shh - we don't ever want this hook to be marked pure + if (false) { + maxAssets = maxAssets; + } + } + + /** + * @notice Checks if the liquidation should be allowed to occur + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param repayAmount The amount of underlying being repaid + */ + function liquidateBorrowAllowed( + address cTokenBorrowed, + address cTokenCollateral, + address liquidator, + address borrower, + uint256 repayAmount + ) external returns (uint256) { + // Shh - currently unused + liquidator; + + if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { + return uint256(Error.MARKET_NOT_LISTED); + } + + /* The borrower must have shortfall in order to be liquidatable */ + (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); + if (err != Error.NO_ERROR) { + return uint256(err); + } + if (shortfall == 0) { + return uint256(Error.INSUFFICIENT_SHORTFALL); + } + + /* The liquidator may not repay more than what is allowed by the closeFactor */ + uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + uint256 maxClose = mul_ScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + if (repayAmount > maxClose) { + return uint256(Error.TOO_MUCH_REPAY); + } + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Validates liquidateBorrow and reverts on rejection. May emit logs. + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param actualRepayAmount The amount of underlying being repaid + */ + function liquidateBorrowVerify( + address cTokenBorrowed, + address cTokenCollateral, + address liquidator, + address borrower, + uint256 actualRepayAmount, + uint256 seizeTokens + ) external { + // Shh - currently unused + cTokenBorrowed; + cTokenCollateral; + liquidator; + borrower; + actualRepayAmount; + seizeTokens; + + // Shh - we don't ever want this hook to be marked pure + if (false) { + maxAssets = maxAssets; + } + } + + /** + * @notice Checks if the seizing of assets should be allowed to occur + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param seizeTokens The number of collateral tokens to seize + */ + function seizeAllowed( + address cTokenCollateral, + address cTokenBorrowed, + address liquidator, + address borrower, + uint256 seizeTokens + ) external returns (uint256) { + // Pausing is a very serious situation - we revert to sound the alarms + require(!seizeGuardianPaused, "seize is paused"); + + // Shh - currently unused + seizeTokens; + + if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { + return uint256(Error.MARKET_NOT_LISTED); + } + + if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { + return uint256(Error.COMPTROLLER_MISMATCH); + } + + // Keep the flywheel moving + updateCompSupplyIndex(cTokenCollateral); + distributeSupplierComp(cTokenCollateral, borrower, false); + distributeSupplierComp(cTokenCollateral, liquidator, false); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Validates seize and reverts on rejection. May emit logs. + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param seizeTokens The number of collateral tokens to seize + */ + function seizeVerify( + address cTokenCollateral, + address cTokenBorrowed, + address liquidator, + address borrower, + uint256 seizeTokens + ) external { + // Shh - currently unused + cTokenCollateral; + cTokenBorrowed; + liquidator; + borrower; + seizeTokens; + + // Shh - we don't ever want this hook to be marked pure + if (false) { + maxAssets = maxAssets; + } + } + + /** + * @notice Checks if the account should be allowed to transfer tokens in the given market + * @param cToken The market to verify the transfer against + * @param src The account which sources the tokens + * @param dst The account which receives the tokens + * @param transferTokens The number of cTokens to transfer + * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256) { + // Pausing is a very serious situation - we revert to sound the alarms + require(!transferGuardianPaused, "transfer is paused"); + + // Currently the only consideration is whether or not + // the src is allowed to redeem this many tokens + uint256 allowed = redeemAllowedInternal(cToken, src, transferTokens); + if (allowed != uint256(Error.NO_ERROR)) { + return allowed; + } + + // Keep the flywheel moving + updateCompSupplyIndex(cToken); + distributeSupplierComp(cToken, src, false); + distributeSupplierComp(cToken, dst, false); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Validates transfer and reverts on rejection. May emit logs. + * @param cToken Asset being transferred + * @param src The account which sources the tokens + * @param dst The account which receives the tokens + * @param transferTokens The number of cTokens to transfer + */ + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external { + // Shh - currently unused + cToken; + src; + dst; + transferTokens; + + // Shh - we don't ever want this hook to be marked pure + if (false) { + maxAssets = maxAssets; + } + } + + /*** Liquidity/Liquidation Calculations ***/ + + /** + * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. + * Note that `cTokenBalance` is the number of cTokens the account owns in the market, + * whereas `borrowBalance` is the amount of underlying that the account has borrowed. + */ + struct AccountLiquidityLocalVars { + uint256 sumCollateral; + uint256 sumBorrowPlusEffects; + uint256 cTokenBalance; + uint256 borrowBalance; + uint256 exchangeRateMantissa; + uint256 oraclePriceMantissa; + Exp collateralFactor; + Exp exchangeRate; + Exp oraclePrice; + Exp tokensToDenom; + } + + /** + * @notice Determine the current account liquidity wrt collateral requirements + * @return (possible error code (semi-opaque), + account liquidity in excess of collateral requirements, + * account shortfall below collateral requirements) + */ + function getAccountLiquidity(address account) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(0), + 0, + 0 + ); + + return (uint256(err), liquidity, shortfall); + } + + /** + * @notice Determine the current account liquidity wrt collateral requirements + * @return (possible error code, + account liquidity in excess of collateral requirements, + * account shortfall below collateral requirements) + */ + function getAccountLiquidityInternal(address account) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { + return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); + } + + /** + * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed + * @param cTokenModify The market to hypothetically redeem/borrow in + * @param account The account to determine liquidity for + * @param redeemTokens The number of tokens to hypothetically redeem + * @param borrowAmount The amount of underlying to hypothetically borrow + * @return (possible error code (semi-opaque), + hypothetical account liquidity in excess of collateral requirements, + * hypothetical account shortfall below collateral requirements) + */ + function getHypotheticalAccountLiquidity( + address account, + address cTokenModify, + uint256 redeemTokens, + uint256 borrowAmount + ) + public + view + returns ( + uint256, + uint256, + uint256 + ) + { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + CToken(cTokenModify), + redeemTokens, + borrowAmount + ); + return (uint256(err), liquidity, shortfall); + } + + /** + * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed + * @param cTokenModify The market to hypothetically redeem/borrow in + * @param account The account to determine liquidity for + * @param redeemTokens The number of tokens to hypothetically redeem + * @param borrowAmount The amount of underlying to hypothetically borrow + * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data, + * without calculating accumulated interest. + * @return (possible error code, + hypothetical account liquidity in excess of collateral requirements, + * hypothetical account shortfall below collateral requirements) + */ + function getHypotheticalAccountLiquidityInternal( + address account, + CToken cTokenModify, + uint256 redeemTokens, + uint256 borrowAmount + ) + internal + view + returns ( + Error, + uint256, + uint256 + ) + { + AccountLiquidityLocalVars memory vars; // Holds all our calculation results + uint256 oErr; + + // For each asset the account is in + CToken[] memory assets = accountAssets[account]; + for (uint256 i = 0; i < assets.length; i++) { + CToken asset = assets[i]; + + // Read the balances and exchange rate from the cToken + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( + account + ); + if (oErr != 0) { + // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + return (Error.SNAPSHOT_ERROR, 0, 0); + } + vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); + vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); + + // Get the normalized price of the asset + vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); + if (vars.oraclePriceMantissa == 0) { + return (Error.PRICE_ERROR, 0, 0); + } + vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); + + // Pre-compute a conversion factor from tokens -> ether (normalized price value) + vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice); + + // sumCollateral += tokensToDenom * cTokenBalance + vars.sumCollateral = mul_ScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral); + + // sumBorrowPlusEffects += oraclePrice * borrowBalance + vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( + vars.oraclePrice, + vars.borrowBalance, + vars.sumBorrowPlusEffects + ); + + // Calculate effects of interacting with cTokenModify + if (asset == cTokenModify) { + // redeem effect + // sumBorrowPlusEffects += tokensToDenom * redeemTokens + vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( + vars.tokensToDenom, + redeemTokens, + vars.sumBorrowPlusEffects + ); + + // borrow effect + // sumBorrowPlusEffects += oraclePrice * borrowAmount + vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( + vars.oraclePrice, + borrowAmount, + vars.sumBorrowPlusEffects + ); + } + } + + // These are safe, as the underflow condition is checked first + if (vars.sumCollateral > vars.sumBorrowPlusEffects) { + return (Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0); + } else { + return (Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral); + } + } + + /** + * @notice Calculate number of tokens of collateral asset to seize given an underlying amount + * @dev Used in liquidation (called in cToken.liquidateBorrowFresh) + * @param cTokenBorrowed The address of the borrowed cToken + * @param cTokenCollateral The address of the collateral cToken + * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens + * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) + */ + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 actualRepayAmount + ) external view returns (uint256, uint256) { + /* Read oracle prices for borrowed and collateral markets */ + uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { + return (uint256(Error.PRICE_ERROR), 0); + } + + /* + * Get the exchange rate and calculate the number of collateral tokens to seize: + * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral + * seizeTokens = seizeAmount / exchangeRate + * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) + */ + uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + Exp memory numerator = mul_( + Exp({mantissa: liquidationIncentiveMantissa}), + Exp({mantissa: priceBorrowedMantissa}) + ); + Exp memory denominator = mul_(Exp({mantissa: priceCollateralMantissa}), Exp({mantissa: exchangeRateMantissa})); + Exp memory ratio = div_(numerator, denominator); + uint256 seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); + + return (uint256(Error.NO_ERROR), seizeTokens); + } + + /*** Admin Functions ***/ + + /** + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); + } + + // Track the old oracle for the comptroller + PriceOracle oldOracle = oracle; + + // Set comptroller's oracle to newOracle + oracle = newOracle; + + // Emit NewPriceOracle(oldOracle, newOracle) + emit NewPriceOracle(oldOracle, newOracle); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); + } + + uint256 oldCloseFactorMantissa = closeFactorMantissa; + closeFactorMantissa = newCloseFactorMantissa; + emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint256 newCollateralFactorMantissa) external returns (uint256) { + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); + } + + // Verify market is listed + Market storage market = markets[address(cToken)]; + if (!market.isListed) { + return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS); + } + + Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa}); + + // Check collateral factor <= 0.9 + Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa}); + if (lessThanExp(highLimit, newCollateralFactorExp)) { + return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION); + } + + // If collateral factor != 0, fail if price == 0 + if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(cToken) == 0) { + return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); + } + + // Set market's collateral factor to new collateral factor, remember old value + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; + market.collateralFactorMantissa = newCollateralFactorMantissa; + + // Emit event with asset, old collateral factor, and new collateral factor + emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); + } + + // Save current value for use in log + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + + // Set liquidation incentive to new incentive + liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; + + // Emit event with old incentive, new incentive + emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); + + return uint256(Error.NO_ERROR); + } + + /** + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken) external returns (uint256) { + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); + } + + if (markets[address(cToken)].isListed) { + return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); + } + + cToken.isCToken(); // Sanity check to make sure its really a CToken + + // TODO: isComped is unused. Remove it in v2. + markets[address(cToken)] = Market({ + isListed: true, + isComped: true, + collateralFactorMantissa: 0, + version: Version.VANILLA + }); + + _addMarketInternal(address(cToken)); + + emit MarketListed(cToken); + + return uint256(Error.NO_ERROR); + } + + function _addMarketInternal(address cToken) internal { + for (uint256 i = 0; i < allMarkets.length; i++) { + require(allMarkets[i] != CToken(cToken), "market already added"); + } + allMarkets.push(CToken(cToken)); + } + + /** + * @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 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, uint256[] calldata newSupplyCaps) external { + require( + msg.sender == admin || msg.sender == supplyCapGuardian, + "only admin or supply cap guardian can set supply caps" + ); + + uint256 numMarkets = cTokens.length; + uint256 numSupplyCaps = newSupplyCaps.length; + + require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input"); + + for (uint256 i = 0; i < numMarkets; i++) { + supplyCaps[address(cTokens[i])] = newSupplyCaps[i]; + emit NewSupplyCap(cTokens[i], newSupplyCaps[i]); + } + } + + /** + * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. + * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. + * @param cTokens The addresses of the markets (tokens) to change the borrow caps for + * @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, uint256[] calldata newBorrowCaps) external { + require( + msg.sender == admin || msg.sender == borrowCapGuardian, + "only admin or borrow cap guardian can set borrow caps" + ); + + uint256 numMarkets = cTokens.length; + uint256 numBorrowCaps = newBorrowCaps.length; + + require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); + + for (uint256 i = 0; i < numMarkets; i++) { + borrowCaps[address(cTokens[i])] = newBorrowCaps[i]; + emit NewBorrowCap(cTokens[i], newBorrowCaps[i]); + } + } + + /** + * @notice Admin function to change the Borrow Cap Guardian + * @param newBorrowCapGuardian The address of the new Borrow Cap Guardian + */ + function _setBorrowCapGuardian(address newBorrowCapGuardian) external { + require(msg.sender == admin, "only admin can set borrow cap guardian"); + + // Save current value for inclusion in log + address oldBorrowCapGuardian = borrowCapGuardian; + + // Store borrowCapGuardian with value newBorrowCapGuardian + borrowCapGuardian = newBorrowCapGuardian; + + // Emit NewBorrowCapGuardian(OldBorrowCapGuardian, NewBorrowCapGuardian) + emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian); + } + + /** + * @notice Admin function to change the Pause Guardian + * @param newPauseGuardian The address of the new Pause Guardian + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); + } + + // Save current value for inclusion in log + address oldPauseGuardian = pauseGuardian; + + // Store pauseGuardian with value newPauseGuardian + pauseGuardian = newPauseGuardian; + + // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) + emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); + + return uint256(Error.NO_ERROR); + } + + function _setMintPaused(CToken cToken, bool state) public returns (bool) { + require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + mintGuardianPaused[address(cToken)] = state; + emit ActionPaused(cToken, "Mint", state); + return state; + } + + function _setBorrowPaused(CToken cToken, bool state) public returns (bool) { + require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + borrowGuardianPaused[address(cToken)] = state; + emit ActionPaused(cToken, "Borrow", state); + return state; + } + + function _setTransferPaused(bool state) public returns (bool) { + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + transferGuardianPaused = state; + emit ActionPaused("Transfer", state); + return state; + } + + function _setSeizePaused(bool state) public returns (bool) { + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + seizeGuardianPaused = state; + emit ActionPaused("Seize", state); + return state; + } + + function _become(Unitroller unitroller) public { + require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); + require(unitroller._acceptImplementation() == 0, "change not authorized"); + } + + /** + * @notice Checks caller is admin, or this contract is becoming the new implementation + */ + function adminOrInitializing() internal view returns (bool) { + return msg.sender == admin || msg.sender == comptrollerImplementation; + } + + /*** Comp Distribution ***/ + + /** + * @notice Accrue COMP to the market by updating the supply index + * @param cToken The market whose supply index to update + */ + function updateCompSupplyIndex(address cToken) internal { + CompMarketState storage supplyState = compSupplyState[cToken]; + uint256 supplySpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block)); + if (deltaBlocks > 0 && supplySpeed > 0) { + uint256 supplyTokens = CToken(cToken).totalSupply(); + uint256 compAccrued = mul_(deltaBlocks, supplySpeed); + Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); + Double memory index = add_(Double({mantissa: supplyState.index}), ratio); + compSupplyState[cToken] = CompMarketState({ + index: safe224(index.mantissa, "new index exceeds 224 bits"), + block: safe32(blockNumber, "block number exceeds 32 bits") + }); + } else if (deltaBlocks > 0) { + supplyState.block = safe32(blockNumber, "block number exceeds 32 bits"); + } + } + + /** + * @notice Accrue COMP to the market by updating the borrow index + * @param cToken The market whose borrow index to update + */ + function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { + CompMarketState storage borrowState = compBorrowState[cToken]; + uint256 borrowSpeed = compSpeeds[cToken]; + uint256 blockNumber = getBlockNumber(); + uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block)); + if (deltaBlocks > 0 && borrowSpeed > 0) { + uint256 borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); + uint256 compAccrued = mul_(deltaBlocks, borrowSpeed); + Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); + Double memory index = add_(Double({mantissa: borrowState.index}), ratio); + compBorrowState[cToken] = CompMarketState({ + index: safe224(index.mantissa, "new index exceeds 224 bits"), + block: safe32(blockNumber, "block number exceeds 32 bits") + }); + } else if (deltaBlocks > 0) { + borrowState.block = safe32(blockNumber, "block number exceeds 32 bits"); + } + } + + /** + * @notice Calculate COMP accrued by a supplier and possibly transfer it to them + * @param cToken The market in which the supplier is interacting + * @param supplier The address of the supplier to distribute COMP to + */ + function distributeSupplierComp( + address cToken, + address supplier, + bool distributeAll + ) internal { + CompMarketState storage supplyState = compSupplyState[cToken]; + Double memory supplyIndex = Double({mantissa: supplyState.index}); + Double memory supplierIndex = Double({mantissa: compSupplierIndex[cToken][supplier]}); + compSupplierIndex[cToken][supplier] = supplyIndex.mantissa; + + if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { + supplierIndex.mantissa = compInitialIndex; + } + + Double memory deltaIndex = sub_(supplyIndex, supplierIndex); + uint256 supplierTokens = CToken(cToken).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); + uint256 supplierAccrued = add_(compAccrued[supplier], supplierDelta); + compAccrued[supplier] = transferComp(supplier, supplierAccrued, distributeAll ? 0 : compClaimThreshold); + emit DistributedSupplierComp(CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa); + } + + /** + * @notice Calculate COMP accrued by a borrower and possibly transfer it to them + * @dev Borrowers will not begin to accrue until after the first interaction with the protocol. + * @param cToken The market in which the borrower is interacting + * @param borrower The address of the borrower to distribute COMP to + */ + function distributeBorrowerComp( + address cToken, + address borrower, + Exp memory marketBorrowIndex, + bool distributeAll + ) internal { + CompMarketState storage borrowState = compBorrowState[cToken]; + Double memory borrowIndex = Double({mantissa: borrowState.index}); + Double memory borrowerIndex = Double({mantissa: compBorrowerIndex[cToken][borrower]}); + compBorrowerIndex[cToken][borrower] = borrowIndex.mantissa; + + if (borrowerIndex.mantissa > 0) { + Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); + uint256 borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); + uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); + uint256 borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); + compAccrued[borrower] = transferComp(borrower, borrowerAccrued, distributeAll ? 0 : compClaimThreshold); + emit DistributedBorrowerComp(CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa); + } + } + + /** + * @notice Transfer COMP to the user, if they are above the threshold + * @dev Note: If there is not enough COMP, we do not perform the transfer all. + * @param user The address of the user to transfer COMP to + * @param userAccrued The amount of COMP to (possibly) transfer + * @return The amount of COMP which was NOT transferred to the user + */ + function transferComp( + address user, + uint256 userAccrued, + uint256 threshold + ) internal returns (uint256) { + if (userAccrued >= threshold && userAccrued > 0) { + Comp comp = Comp(getCompAddress()); + uint256 compRemaining = comp.balanceOf(address(this)); + if (userAccrued <= compRemaining) { + comp.transfer(user, userAccrued); + return 0; + } + } + return userAccrued; + } + + /** + * @notice Claim all the comp accrued by holder in all markets + * @param holder The address to claim COMP for + */ + function claimComp(address holder) public { + return claimComp(holder, allMarkets); + } + + /** + * @notice Claim all the comp accrued by holder in the specified markets + * @param holder The address to claim COMP for + * @param cTokens The list of markets to claim COMP in + */ + function claimComp(address holder, CToken[] memory cTokens) public { + address[] memory holders = new address[](1); + holders[0] = holder; + claimComp(holders, cTokens, true, true); + } + + /** + * @notice Claim all comp accrued by the holders + * @param holders The addresses to claim COMP for + * @param cTokens The list of markets to claim COMP in + * @param borrowers Whether or not to claim COMP earned by borrowing + * @param suppliers Whether or not to claim COMP earned by supplying + */ + function claimComp( + address[] memory holders, + CToken[] memory cTokens, + bool borrowers, + bool suppliers + ) public { + for (uint256 i = 0; i < cTokens.length; i++) { + CToken cToken = cTokens[i]; + require(markets[address(cToken)].isListed, "market must be listed"); + if (borrowers == true) { + Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); + updateCompBorrowIndex(address(cToken), borrowIndex); + for (uint256 j = 0; j < holders.length; j++) { + distributeBorrowerComp(address(cToken), holders[j], borrowIndex, true); + } + } + if (suppliers == true) { + updateCompSupplyIndex(address(cToken)); + for (uint256 j = 0; j < holders.length; j++) { + distributeSupplierComp(address(cToken), holders[j], true); + } + } + } + } + + /*** Comp Distribution Admin ***/ + + /** + * @notice Set cTokens compSpeed + * @param cTokens The addresses of cTokens + * @param speeds The list of COMP speeds + */ + function _setCompSpeeds(address[] memory cTokens, uint256[] memory speeds) public { + require(msg.sender == admin, "only admin can set comp speeds"); + + uint256 numMarkets = cTokens.length; + uint256 numSpeeds = speeds.length; + + require(numMarkets != 0 && numMarkets == numSpeeds, "invalid input"); + + for (uint256 i = 0; i < numMarkets; i++) { + if (speeds[i] > 0) { + _initCompState(cTokens[i]); + } + + // Update supply and borrow index. + CToken cToken = CToken(cTokens[i]); + Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); + updateCompSupplyIndex(address(cToken)); + updateCompBorrowIndex(address(cToken), borrowIndex); + + compSpeeds[address(cToken)] = speeds[i]; + emit CompSpeedUpdated(cToken, speeds[i]); + } + } + + function _initCompState(address cToken) internal { + if (compSupplyState[cToken].index == 0 && compSupplyState[cToken].block == 0) { + compSupplyState[cToken] = CompMarketState({ + index: compInitialIndex, + block: safe32(getBlockNumber(), "block number exceeds 32 bits") + }); + } + + if (compBorrowState[cToken].index == 0 && compBorrowState[cToken].block == 0) { + compBorrowState[cToken] = CompMarketState({ + index: compInitialIndex, + block: safe32(getBlockNumber(), "block number exceeds 32 bits") + }); + } + } + + /** + * @notice Return all of the markets + * @dev The automatic getter may be used to access an individual market. + * @return The list of market addresses + */ + function getAllMarkets() public view returns (CToken[] memory) { + return allMarkets; + } + + function getBlockNumber() public view returns (uint256) { + return block.number; + } + + /** + * @notice Return the address of the COMP token + * @return The address of COMP + */ + function getCompAddress() public view returns (address) { + return 0x2ba592F78dB6436527729929AAf6c908497cB200; + } +} diff --git a/contracts/ComptrollerInterface.sol b/contracts/ComptrollerInterface.sol index ce63c78..f53c9ef 100644 --- a/contracts/ComptrollerInterface.sol +++ b/contracts/ComptrollerInterface.sol @@ -1,71 +1,138 @@ pragma solidity ^0.5.16; +import "./CToken.sol"; +import "./ComptrollerStorage.sol"; + contract ComptrollerInterface { /// @notice Indicator that this is a Comptroller contract (for inspection) bool public constant isComptroller = true; /*** Assets You Are In ***/ - function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); - function exitMarket(address cToken) external returns (uint); + function enterMarkets(address[] calldata cTokens) external returns (uint256[] memory); + + function exitMarket(address cToken) external returns (uint256); /*** Policy Hooks ***/ - function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint); - function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external; + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256); + + function mintVerify( + address cToken, + address minter, + uint256 mintAmount, + uint256 mintTokens + ) external; + + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256); + + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external; - function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint); - function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external; + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256); - function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint); - function borrowVerify(address cToken, address borrower, uint borrowAmount) external; + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external; function repayBorrowAllowed( address cToken, address payer, address borrower, - uint repayAmount) external returns (uint); + uint256 repayAmount + ) external returns (uint256); + function repayBorrowVerify( address cToken, address payer, address borrower, - uint repayAmount, - uint borrowerIndex) external; + uint256 repayAmount, + uint256 borrowerIndex + ) external; function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, - uint repayAmount) external returns (uint); + uint256 repayAmount + ) external returns (uint256); + function liquidateBorrowVerify( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, - uint repayAmount, - uint seizeTokens) external; + uint256 repayAmount, + uint256 seizeTokens + ) external; function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external returns (uint); + uint256 seizeTokens + ) external returns (uint256); + function seizeVerify( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, - uint seizeTokens) external; + uint256 seizeTokens + ) external; + + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256); - function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint); - function transferVerify(address cToken, address src, address dst, uint transferTokens) external; + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external; /*** Liquidity/Liquidation Calculations ***/ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, - uint repayAmount) external view returns (uint, uint); + uint256 repayAmount + ) external view returns (uint256, uint256); +} + +interface ComptrollerInterfaceExtension { + function checkMembership(address account, CToken cToken) external view returns (bool); + + function updateCTokenVersion(address cToken, ComptrollerV2Storage.Version version) external; + + function flashloanAllowed( + address cToken, + address receiver, + uint256 amount, + bytes calldata params + ) external view returns (bool); } diff --git a/contracts/ComptrollerStorage.sol b/contracts/ComptrollerStorage.sol index 963ee0d..2d6fded 100644 --- a/contracts/ComptrollerStorage.sol +++ b/contracts/ComptrollerStorage.sol @@ -5,28 +5,27 @@ import "./PriceOracle.sol"; contract UnitrollerAdminStorage { /** - * @notice Administrator for this contract - */ + * @notice Administrator for this contract + */ address public admin; /** - * @notice Pending administrator for this contract - */ + * @notice Pending administrator for this contract + */ address public pendingAdmin; /** - * @notice Active brains of Unitroller - */ + * @notice Active brains of Unitroller + */ address public comptrollerImplementation; /** - * @notice Pending brains of Unitroller - */ + * @notice Pending brains of Unitroller + */ address public pendingComptrollerImplementation; } contract ComptrollerV1Storage is UnitrollerAdminStorage { - /** * @notice Oracle which gives the price of any given asset */ @@ -35,42 +34,46 @@ contract ComptrollerV1Storage is UnitrollerAdminStorage { /** * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow */ - uint public closeFactorMantissa; + uint256 public closeFactorMantissa; /** * @notice Multiplier representing the discount on collateral that a liquidator receives */ - uint public liquidationIncentiveMantissa; + uint256 public liquidationIncentiveMantissa; /** * @notice Max number of assets a single account can participate in (borrow or use as collateral) */ - uint public maxAssets; + uint256 public maxAssets; /** * @notice Per-account mapping of "assets you are in", capped by maxAssets */ mapping(address => CToken[]) public accountAssets; - } contract ComptrollerV2Storage is ComptrollerV1Storage { + enum Version { + VANILLA, + COLLATERALCAP, + WRAPPEDNATIVE + } + struct Market { /// @notice Whether or not this market is listed bool isListed; - /** * @notice Multiplier representing the most one can borrow against their collateral in this market. * For instance, 0.9 to allow borrowing 90% of collateral value. * Must be between 0 and 1, and stored as a mantissa. */ - uint collateralFactorMantissa; - + uint256 collateralFactorMantissa; /// @notice Per-market mapping of "accounts in this asset" mapping(address => bool) accountMembership; - /// @notice Whether or not this market receives COMP bool isComped; + /// @notice CToken version + Version version; } /** @@ -79,7 +82,6 @@ contract ComptrollerV2Storage is ComptrollerV1Storage { */ mapping(address => Market) public markets; - /** * @notice The Pause Guardian can pause certain actions as a safety mechanism. * Actions which allow users to remove their own assets cannot be paused. @@ -98,7 +100,6 @@ contract ComptrollerV3Storage is ComptrollerV2Storage { struct CompMarketState { /// @notice The market's last updated compBorrowIndex or compSupplyIndex uint224 index; - /// @notice The block number the index was last updated at uint32 block; } @@ -107,10 +108,10 @@ contract ComptrollerV3Storage is ComptrollerV2Storage { CToken[] public allMarkets; /// @notice The rate at which the flywheel distributes COMP, per block - uint public compRate; + uint256 public compRate; /// @notice The portion of compRate that each market currently receives - mapping(address => uint) public compSpeeds; + mapping(address => uint256) public compSpeeds; /// @notice The COMP market supply state for each market mapping(address => CompMarketState) public compSupplyState; @@ -119,13 +120,13 @@ contract ComptrollerV3Storage is ComptrollerV2Storage { mapping(address => CompMarketState) public compBorrowState; /// @notice The COMP borrow index for each market for each supplier as of the last time they accrued COMP - mapping(address => mapping(address => uint)) public compSupplierIndex; + mapping(address => mapping(address => uint256)) public compSupplierIndex; /// @notice The COMP borrow index for each market for each borrower as of the last time they accrued COMP - mapping(address => mapping(address => uint)) public compBorrowerIndex; + mapping(address => mapping(address => uint256)) public compBorrowerIndex; /// @notice The COMP accrued but not yet transferred to each user - mapping(address => uint) public compAccrued; + mapping(address => uint256) public compAccrued; } contract ComptrollerV4Storage is ComptrollerV3Storage { @@ -133,7 +134,7 @@ contract ComptrollerV4Storage is ComptrollerV3Storage { address public borrowCapGuardian; // @notice Borrow caps enforced by borrowAllowed for each cToken address. Defaults to zero which corresponds to unlimited borrowing. - mapping(address => uint) public borrowCaps; + mapping(address => uint256) public borrowCaps; } contract ComptrollerV5Storage is ComptrollerV4Storage { @@ -141,5 +142,15 @@ contract ComptrollerV5Storage is ComptrollerV4Storage { address public supplyCapGuardian; // @notice Supply caps enforced by mintAllowed for each cToken address. Defaults to zero which corresponds to unlimited supplying. - mapping(address => uint) public supplyCaps; + mapping(address => uint256) public supplyCaps; +} + +contract ComptrollerV6Storage is ComptrollerV5Storage { + // @notice flashloanGuardianPaused can pause flash loan as a safety mechanism. + mapping(address => bool) public flashloanGuardianPaused; +} + +contract ComptrollerV7Storage is ComptrollerV6Storage { + /// @notice liquidityMining the liquidity mining module that handles the LM rewards distribution. + address public liquidityMining; } diff --git a/contracts/EIP20Interface.sol b/contracts/EIP20Interface.sol index f791e56..b0a6501 100644 --- a/contracts/EIP20Interface.sol +++ b/contracts/EIP20Interface.sol @@ -6,13 +6,15 @@ pragma solidity ^0.5.16; */ interface EIP20Interface { function name() external view returns (string memory); + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); /** - * @notice Get the total number of tokens in circulation - * @return The supply of tokens - */ + * @notice Get the total number of tokens in circulation + * @return The supply of tokens + */ function totalSupply() external view returns (uint256); /** @@ -23,38 +25,42 @@ interface EIP20Interface { function balanceOf(address owner) external view returns (uint256 balance); /** - * @notice Transfer `amount` tokens from `msg.sender` to `dst` - * @param dst The address of the destination account - * @param amount The number of tokens to transfer - * @return Whether or not the transfer succeeded - */ + * @notice Transfer `amount` tokens from `msg.sender` to `dst` + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ function transfer(address dst, uint256 amount) external returns (bool success); /** - * @notice Transfer `amount` tokens from `src` to `dst` - * @param src The address of the source account - * @param dst The address of the destination account - * @param amount The number of tokens to transfer - * @return Whether or not the transfer succeeded - */ - function transferFrom(address src, address dst, uint256 amount) external returns (bool success); + * @notice Transfer `amount` tokens from `src` to `dst` + * @param src The address of the source account + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool success); /** - * @notice Approve `spender` to transfer up to `amount` from `src` - * @dev This will overwrite the approval amount for `spender` - * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) - * @param spender The address of the account which may transfer tokens - * @param amount The number of tokens that are approved (-1 means infinite) - * @return Whether or not the approval succeeded - */ + * @notice Approve `spender` to transfer up to `amount` from `src` + * @dev This will overwrite the approval amount for `spender` + * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) + * @param spender The address of the account which may transfer tokens + * @param amount The number of tokens that are approved (-1 means infinite) + * @return Whether or not the approval succeeded + */ function approve(address spender, uint256 amount) external returns (bool success); /** - * @notice Get the current allowance from `owner` for `spender` - * @param owner The address of the account which owns the tokens to be spent - * @param spender The address of the account which may transfer tokens - * @return The number of tokens allowed to be spent (-1 means infinite) - */ + * @notice Get the current allowance from `owner` for `spender` + * @param owner The address of the account which owns the tokens to be spent + * @param spender The address of the account which may transfer tokens + * @return The number of tokens allowed to be spent (-1 means infinite) + */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); diff --git a/contracts/EIP20NonStandardInterface.sol b/contracts/EIP20NonStandardInterface.sol index 0f69570..e5e2177 100644 --- a/contracts/EIP20NonStandardInterface.sol +++ b/contracts/EIP20NonStandardInterface.sol @@ -6,7 +6,6 @@ pragma solidity ^0.5.16; * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ interface EIP20NonStandardInterface { - /** * @notice Get the total number of tokens in circulation * @return The supply of tokens @@ -27,10 +26,10 @@ interface EIP20NonStandardInterface { /// /** - * @notice Transfer `amount` tokens from `msg.sender` to `dst` - * @param dst The address of the destination account - * @param amount The number of tokens to transfer - */ + * @notice Transfer `amount` tokens from `msg.sender` to `dst` + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + */ function transfer(address dst, uint256 amount) external; /// @@ -40,29 +39,33 @@ interface EIP20NonStandardInterface { /// /** - * @notice Transfer `amount` tokens from `src` to `dst` - * @param src The address of the source account - * @param dst The address of the destination account - * @param amount The number of tokens to transfer - */ - function transferFrom(address src, address dst, uint256 amount) external; + * @notice Transfer `amount` tokens from `src` to `dst` + * @param src The address of the source account + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + */ + function transferFrom( + address src, + address dst, + uint256 amount + ) external; /** - * @notice Approve `spender` to transfer up to `amount` from `src` - * @dev This will overwrite the approval amount for `spender` - * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) - * @param spender The address of the account which may transfer tokens - * @param amount The number of tokens that are approved - * @return Whether or not the approval succeeded - */ + * @notice Approve `spender` to transfer up to `amount` from `src` + * @dev This will overwrite the approval amount for `spender` + * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) + * @param spender The address of the account which may transfer tokens + * @param amount The number of tokens that are approved + * @return Whether or not the approval succeeded + */ function approve(address spender, uint256 amount) external returns (bool success); /** - * @notice Get the current allowance from `owner` for `spender` - * @param owner The address of the account which owns the tokens to be spent - * @param spender The address of the account which may transfer tokens - * @return The number of tokens allowed to be spent - */ + * @notice Get the current allowance from `owner` for `spender` + * @param owner The address of the account which owns the tokens to be spent + * @param spender The address of the account which may transfer tokens + * @return The number of tokens allowed to be spent + */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); diff --git a/contracts/ERC20.sol b/contracts/ERC20.sol new file mode 100644 index 0000000..0dc28b3 --- /dev/null +++ b/contracts/ERC20.sol @@ -0,0 +1,308 @@ +pragma solidity ^0.5.16; + +import "./SafeMath.sol"; + +interface ERC20Base { + event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); + + function totalSupply() external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + function approve(address spender, uint256 value) external returns (bool); + + function balanceOf(address who) external view returns (uint256); +} + +contract ERC20 is ERC20Base { + function transfer(address to, uint256 value) external returns (bool); + + function transferFrom( + address from, + address to, + uint256 value + ) external returns (bool); +} + +contract ERC20NS is ERC20Base { + function transfer(address to, uint256 value) external; + + function transferFrom( + address from, + address to, + uint256 value + ) external; +} + +/** + * @title Standard ERC20 token + * @dev Implementation of the basic standard token. + * See https://github.com/ethereum/EIPs/issues/20 + */ +contract StandardToken is ERC20 { + using SafeMath for uint256; + + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + mapping(address => mapping(address => uint256)) public allowance; + mapping(address => uint256) public balanceOf; + + constructor( + uint256 _initialAmount, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol + ) public { + totalSupply = _initialAmount; + balanceOf[msg.sender] = _initialAmount; + name = _tokenName; + symbol = _tokenSymbol; + decimals = _decimalUnits; + } + + function transfer(address dst, uint256 amount) external returns (bool) { + balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount, "Insufficient balance"); + balanceOf[dst] = balanceOf[dst].add(amount, "Balance overflow"); + emit Transfer(msg.sender, dst, amount); + return true; + } + + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool) { + allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount, "Insufficient allowance"); + balanceOf[src] = balanceOf[src].sub(amount, "Insufficient balance"); + balanceOf[dst] = balanceOf[dst].add(amount, "Balance overflow"); + emit Transfer(src, dst, amount); + return true; + } + + function approve(address _spender, uint256 amount) external returns (bool) { + allowance[msg.sender][_spender] = amount; + emit Approval(msg.sender, _spender, amount); + return true; + } +} + +/** + * @title Non-Standard ERC20 token + * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` + * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca + */ +contract NonStandardToken is ERC20NS { + using SafeMath for uint256; + + string public name; + uint8 public decimals; + string public symbol; + uint256 public totalSupply; + mapping(address => mapping(address => uint256)) public allowance; + mapping(address => uint256) public balanceOf; + + constructor( + uint256 _initialAmount, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol + ) public { + totalSupply = _initialAmount; + balanceOf[msg.sender] = _initialAmount; + name = _tokenName; + symbol = _tokenSymbol; + decimals = _decimalUnits; + } + + function transfer(address dst, uint256 amount) external { + balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount, "Insufficient balance"); + balanceOf[dst] = balanceOf[dst].add(amount, "Balance overflow"); + emit Transfer(msg.sender, dst, amount); + } + + function transferFrom( + address src, + address dst, + uint256 amount + ) external { + allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount, "Insufficient allowance"); + balanceOf[src] = balanceOf[src].sub(amount, "Insufficient balance"); + balanceOf[dst] = balanceOf[dst].add(amount, "Balance overflow"); + emit Transfer(src, dst, amount); + } + + function approve(address _spender, uint256 amount) external returns (bool) { + allowance[msg.sender][_spender] = amount; + emit Approval(msg.sender, _spender, amount); + return true; + } +} + +contract ERC20Harness is StandardToken { + // To support testing, we can specify addresses for which transferFrom should fail and return false + mapping(address => bool) public failTransferFromAddresses; + + // To support testing, we allow the contract to always fail `transfer`. + mapping(address => bool) public failTransferToAddresses; + + constructor( + uint256 _initialAmount, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol + ) public StandardToken(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol) {} + + function harnessSetFailTransferFromAddress(address src, bool _fail) public { + failTransferFromAddresses[src] = _fail; + } + + function harnessSetFailTransferToAddress(address dst, bool _fail) public { + failTransferToAddresses[dst] = _fail; + } + + function harnessSetBalance(address _account, uint256 _amount) public { + balanceOf[_account] = _amount; + } + + function transfer(address dst, uint256 amount) external returns (bool success) { + // Added for testing purposes + if (failTransferToAddresses[dst]) { + return false; + } + balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount, "Insufficient balance"); + balanceOf[dst] = balanceOf[dst].add(amount, "Balance overflow"); + emit Transfer(msg.sender, dst, amount); + return true; + } + + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool success) { + // Added for testing purposes + if (failTransferFromAddresses[src]) { + return false; + } + allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount, "Insufficient allowance"); + balanceOf[src] = balanceOf[src].sub(amount, "Insufficient balance"); + balanceOf[dst] = balanceOf[dst].add(amount, "Balance overflow"); + emit Transfer(src, dst, amount); + return true; + } +} + +contract CTokenHarness is ERC20Harness { + bool public constant isCToken = true; + + address public comptroller; + + constructor( + uint256 _initialAmount, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol, + address _comptroller + ) public ERC20Harness(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol) { + comptroller = _comptroller; + } +} + +interface CurveTokenV3Interface { + function minter() external view returns (address); +} + +contract CurveTokenHarness is ERC20Harness, CurveTokenV3Interface { + address public minter; + + constructor( + uint256 _initialAmount, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol, + address _minter + ) public ERC20Harness(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol) { + minter = _minter; + } +} + +interface CurveSwapInterface { + function get_virtual_price() external view returns (uint256); +} + +contract CurveSwapHarness is CurveSwapInterface { + uint256 private virtualPrice; + + constructor(uint256 _virtualPrice) public { + virtualPrice = _virtualPrice; + } + + function get_virtual_price() external view returns (uint256) { + return virtualPrice; + } +} + +interface YVaultV1Interface { + function token() external view returns (address); + + function getPricePerFullShare() external view returns (uint256); +} + +contract YVaultV1TokenHarness is ERC20Harness, YVaultV1Interface { + address private underlying; + uint256 private price; + + constructor( + uint256 _initialAmount, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol, + address _token, + uint256 _price + ) public ERC20Harness(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol) { + underlying = _token; + price = _price; + } + + function token() external view returns (address) { + return underlying; + } + + function getPricePerFullShare() external view returns (uint256) { + return price; + } +} + +interface YVaultV2Interface { + function token() external view returns (address); + + function pricePerShare() external view returns (uint256); +} + +contract YVaultV2TokenHarness is ERC20Harness, YVaultV2Interface { + address private underlying; + uint256 private price; + + constructor( + uint256 _initialAmount, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol, + address _token, + uint256 _price + ) public ERC20Harness(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol) { + underlying = _token; + price = _price; + } + + function token() external view returns (address) { + return underlying; + } + + function pricePerShare() external view returns (uint256) { + return price; + } +} diff --git a/contracts/ERC3156FlashBorrowerInterface.sol b/contracts/ERC3156FlashBorrowerInterface.sol new file mode 100644 index 0000000..af30365 --- /dev/null +++ b/contracts/ERC3156FlashBorrowerInterface.sol @@ -0,0 +1,20 @@ +pragma solidity ^0.5.16; + +interface ERC3156FlashBorrowerInterface { + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param fee The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" + */ + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32); +} diff --git a/contracts/ERC3156FlashLenderInterface.sol b/contracts/ERC3156FlashLenderInterface.sol new file mode 100644 index 0000000..0c160c4 --- /dev/null +++ b/contracts/ERC3156FlashLenderInterface.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.5.16; +import "./ERC3156FlashBorrowerInterface.sol"; + +interface ERC3156FlashLenderInterface { + /** + * @dev The amount of currency available to be lent. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan(address token) external view returns (uint256); + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee(address token, uint256 amount) external view returns (uint256); + + /** + * @dev Initiate a flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address token, + uint256 amount, + bytes calldata data + ) external returns (bool); +} diff --git a/contracts/ErrorReporter.sol b/contracts/ErrorReporter.sol index b477df0..6b5d705 100644 --- a/contracts/ErrorReporter.sol +++ b/contracts/ErrorReporter.sol @@ -46,27 +46,31 @@ contract ComptrollerErrorReporter { } /** - * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary - * contract-specific code that enables us to report opaque error codes from upgradeable contracts. - **/ - event Failure(uint error, uint info, uint detail); + * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary + * contract-specific code that enables us to report opaque error codes from upgradeable contracts. + **/ + event Failure(uint256 error, uint256 info, uint256 detail); /** - * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator - */ - function fail(Error err, FailureInfo info) internal returns (uint) { - emit Failure(uint(err), uint(info), 0); + * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator + */ + function fail(Error err, FailureInfo info) internal returns (uint256) { + emit Failure(uint256(err), uint256(info), 0); - return uint(err); + return uint256(err); } /** - * @dev use this when reporting an opaque error from an upgradeable collaborator contract - */ - function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { - emit Failure(uint(err), uint(info), opaqueError); + * @dev use this when reporting an opaque error from an upgradeable collaborator contract + */ + function failOpaque( + Error err, + FailureInfo info, + uint256 opaqueError + ) internal returns (uint256) { + emit Failure(uint256(err), uint256(info), opaqueError); - return uint(err); + return uint256(err); } } @@ -158,26 +162,30 @@ contract TokenErrorReporter { } /** - * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary - * contract-specific code that enables us to report opaque error codes from upgradeable contracts. - **/ - event Failure(uint error, uint info, uint detail); + * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary + * contract-specific code that enables us to report opaque error codes from upgradeable contracts. + **/ + event Failure(uint256 error, uint256 info, uint256 detail); /** - * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator - */ - function fail(Error err, FailureInfo info) internal returns (uint) { - emit Failure(uint(err), uint(info), 0); + * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator + */ + function fail(Error err, FailureInfo info) internal returns (uint256) { + emit Failure(uint256(err), uint256(info), 0); - return uint(err); + return uint256(err); } /** - * @dev use this when reporting an opaque error from an upgradeable collaborator contract - */ - function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { - emit Failure(uint(err), uint(info), opaqueError); + * @dev use this when reporting an opaque error from an upgradeable collaborator contract + */ + function failOpaque( + Error err, + FailureInfo info, + uint256 opaqueError + ) internal returns (uint256) { + emit Failure(uint256(err), uint256(info), opaqueError); - return uint(err); + return uint256(err); } } diff --git a/contracts/Exponential.sol b/contracts/Exponential.sol index 58ecc4d..f9dbbec 100644 --- a/contracts/Exponential.sol +++ b/contracts/Exponential.sol @@ -10,17 +10,17 @@ import "./CarefulMath.sol"; * `Exp({mantissa: 5100000000000000000})`. */ contract Exponential is CarefulMath { - uint constant expScale = 1e18; - uint constant doubleScale = 1e36; - uint constant halfExpScale = expScale/2; - uint constant mantissaOne = expScale; + uint256 constant expScale = 1e18; + uint256 constant doubleScale = 1e36; + uint256 constant halfExpScale = expScale / 2; + uint256 constant mantissaOne = expScale; struct Exp { - uint mantissa; + uint256 mantissa; } struct Double { - uint mantissa; + uint256 mantissa; } /** @@ -28,13 +28,13 @@ contract Exponential is CarefulMath { * Note: Returns an error if (`num` * 10e18) > MAX_INT, * or if `denom` is zero. */ - function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) { - (MathError err0, uint scaledNumerator) = mulUInt(num, expScale); + function getExp(uint256 num, uint256 denom) internal pure returns (MathError, Exp memory) { + (MathError err0, uint256 scaledNumerator) = mulUInt(num, expScale); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } - (MathError err1, uint rational) = divUInt(scaledNumerator, denom); + (MathError err1, uint256 rational) = divUInt(scaledNumerator, denom); if (err1 != MathError.NO_ERROR) { return (err1, Exp({mantissa: 0})); } @@ -45,8 +45,8 @@ contract Exponential is CarefulMath { /** * @dev Adds two exponentials, returning a new exponential. */ - function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { - (MathError error, uint result) = addUInt(a.mantissa, b.mantissa); + function addExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { + (MathError error, uint256 result) = addUInt(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } @@ -54,8 +54,8 @@ contract Exponential is CarefulMath { /** * @dev Subtracts two exponentials, returning a new exponential. */ - function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { - (MathError error, uint result) = subUInt(a.mantissa, b.mantissa); + function subExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { + (MathError error, uint256 result) = subUInt(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } @@ -63,8 +63,8 @@ contract Exponential is CarefulMath { /** * @dev Multiply an Exp by a scalar, returning a new Exp. */ - function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) { - (MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar); + function mulScalar(Exp memory a, uint256 scalar) internal pure returns (MathError, Exp memory) { + (MathError err0, uint256 scaledMantissa) = mulUInt(a.mantissa, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -75,7 +75,7 @@ contract Exponential is CarefulMath { /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ - function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) { + function mulScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (MathError, uint256) { (MathError err, Exp memory product) = mulScalar(a, scalar); if (err != MathError.NO_ERROR) { return (err, 0); @@ -87,7 +87,11 @@ contract Exponential is CarefulMath { /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ - function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) { + function mulScalarTruncateAddUInt( + Exp memory a, + uint256 scalar, + uint256 addend + ) internal pure returns (MathError, uint256) { (MathError err, Exp memory product) = mulScalar(a, scalar); if (err != MathError.NO_ERROR) { return (err, 0); @@ -99,7 +103,7 @@ contract Exponential is CarefulMath { /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ - function mul_ScalarTruncate(Exp memory a, uint scalar) pure internal returns (uint) { + function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) { Exp memory product = mul_(a, scalar); return truncate(product); } @@ -107,7 +111,11 @@ contract Exponential is CarefulMath { /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ - function mul_ScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (uint) { + function mul_ScalarTruncateAddUInt( + Exp memory a, + uint256 scalar, + uint256 addend + ) internal pure returns (uint256) { Exp memory product = mul_(a, scalar); return add_(truncate(product), addend); } @@ -115,8 +123,8 @@ contract Exponential is CarefulMath { /** * @dev Divide an Exp by a scalar, returning a new Exp. */ - function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) { - (MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar); + function divScalar(Exp memory a, uint256 scalar) internal pure returns (MathError, Exp memory) { + (MathError err0, uint256 descaledMantissa) = divUInt(a.mantissa, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -127,7 +135,7 @@ contract Exponential is CarefulMath { /** * @dev Divide a scalar by an Exp, returning a new Exp. */ - function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) { + function divScalarByExp(uint256 scalar, Exp memory divisor) internal pure returns (MathError, Exp memory) { /* We are doing this as: getExp(mulUInt(expScale, scalar), divisor.mantissa) @@ -137,7 +145,7 @@ contract Exponential is CarefulMath { Scalar = s; `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` */ - (MathError err0, uint numerator) = mulUInt(expScale, scalar); + (MathError err0, uint256 numerator) = mulUInt(expScale, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -147,7 +155,7 @@ contract Exponential is CarefulMath { /** * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. */ - function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) { + function divScalarByExpTruncate(uint256 scalar, Exp memory divisor) internal pure returns (MathError, uint256) { (MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor); if (err != MathError.NO_ERROR) { return (err, 0); @@ -159,7 +167,7 @@ contract Exponential is CarefulMath { /** * @dev Divide a scalar by an Exp, returning a new Exp. */ - function div_ScalarByExp(uint scalar, Exp memory divisor) pure internal returns (Exp memory) { + function div_ScalarByExp(uint256 scalar, Exp memory divisor) internal pure returns (Exp memory) { /* We are doing this as: getExp(mulUInt(expScale, scalar), divisor.mantissa) @@ -169,14 +177,14 @@ contract Exponential is CarefulMath { Scalar = s; `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` */ - uint numerator = mul_(expScale, scalar); + uint256 numerator = mul_(expScale, scalar); return Exp({mantissa: div_(numerator, divisor)}); } /** * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. */ - function div_ScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (uint) { + function div_ScalarByExpTruncate(uint256 scalar, Exp memory divisor) internal pure returns (uint256) { Exp memory fraction = div_ScalarByExp(scalar, divisor); return truncate(fraction); } @@ -184,9 +192,8 @@ contract Exponential is CarefulMath { /** * @dev Multiplies two exponentials, returning a new exponential. */ - function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { - - (MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa); + function mulExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { + (MathError err0, uint256 doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -194,12 +201,12 @@ contract Exponential is CarefulMath { // We add half the scale before dividing so that we get rounding instead of truncation. // See "Listing 6" and text above it at https://accu.org/index.php/journals/1717 // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18. - (MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct); + (MathError err1, uint256 doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct); if (err1 != MathError.NO_ERROR) { return (err1, Exp({mantissa: 0})); } - (MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale); + (MathError err2, uint256 product) = divUInt(doubleScaledProductWithHalfScale, expScale); // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero. assert(err2 == MathError.NO_ERROR); @@ -209,14 +216,18 @@ contract Exponential is CarefulMath { /** * @dev Multiplies two exponentials given their mantissas, returning a new exponential. */ - function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) { + function mulExp(uint256 a, uint256 b) internal pure returns (MathError, Exp memory) { return mulExp(Exp({mantissa: a}), Exp({mantissa: b})); } /** * @dev Multiplies three exponentials, returning a new exponential. */ - function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) { + function mulExp3( + Exp memory a, + Exp memory b, + Exp memory c + ) internal pure returns (MathError, Exp memory) { (MathError err, Exp memory ab) = mulExp(a, b); if (err != MathError.NO_ERROR) { return (err, ab); @@ -229,7 +240,7 @@ contract Exponential is CarefulMath { * (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b, * which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa) */ - function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { + function divExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { return getExp(a.mantissa, b.mantissa); } @@ -237,7 +248,7 @@ contract Exponential is CarefulMath { * @dev Truncates the given exp to a whole number value. * For example, truncate(Exp{mantissa: 15 * expScale}) = 15 */ - function truncate(Exp memory exp) pure internal returns (uint) { + function truncate(Exp memory exp) internal pure returns (uint256) { // Note: We are not using careful math here as we're performing a division that cannot fail return exp.mantissa / expScale; } @@ -245,149 +256,165 @@ contract Exponential is CarefulMath { /** * @dev Checks if first Exp is less than second Exp. */ - function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { + function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa < right.mantissa; } /** * @dev Checks if left Exp <= right Exp. */ - function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) { + function lessThanOrEqualExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa <= right.mantissa; } /** * @dev returns true if Exp is exactly zero */ - function isZeroExp(Exp memory value) pure internal returns (bool) { + function isZeroExp(Exp memory value) internal pure returns (bool) { return value.mantissa == 0; } - function safe224(uint n, string memory errorMessage) pure internal returns (uint224) { + function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) { require(n < 2**224, errorMessage); return uint224(n); } - function safe32(uint n, string memory errorMessage) pure internal returns (uint32) { + function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } - function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { + function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: add_(a.mantissa, b.mantissa)}); } - function add_(Double memory a, Double memory b) pure internal returns (Double memory) { + function add_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: add_(a.mantissa, b.mantissa)}); } - function add_(uint a, uint b) pure internal returns (uint) { + function add_(uint256 a, uint256 b) internal pure returns (uint256) { return add_(a, b, "addition overflow"); } - function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { - uint c = a + b; + function add_( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + uint256 c = a + b; require(c >= a, errorMessage); return c; } - function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { + function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: sub_(a.mantissa, b.mantissa)}); } - function sub_(Double memory a, Double memory b) pure internal returns (Double memory) { + function sub_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: sub_(a.mantissa, b.mantissa)}); } - function sub_(uint a, uint b) pure internal returns (uint) { + function sub_(uint256 a, uint256 b) internal pure returns (uint256) { return sub_(a, b, "subtraction underflow"); } - function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { + function sub_( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } - function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { + function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale}); } - function mul_(Exp memory a, uint b) pure internal returns (Exp memory) { + function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b)}); } - function mul_(uint a, Exp memory b) pure internal returns (uint) { + function mul_(uint256 a, Exp memory b) internal pure returns (uint256) { return mul_(a, b.mantissa) / expScale; } - function mul_(Double memory a, Double memory b) pure internal returns (Double memory) { + function mul_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale}); } - function mul_(Double memory a, uint b) pure internal returns (Double memory) { + function mul_(Double memory a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b)}); } - function mul_(uint a, Double memory b) pure internal returns (uint) { + function mul_(uint256 a, Double memory b) internal pure returns (uint256) { return mul_(a, b.mantissa) / doubleScale; } - function mul_(uint a, uint b) pure internal returns (uint) { + function mul_(uint256 a, uint256 b) internal pure returns (uint256) { return mul_(a, b, "multiplication overflow"); } - function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { + function mul_( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { if (a == 0 || b == 0) { return 0; } - uint c = a * b; + uint256 c = a * b; require(c / a == b, errorMessage); return c; } - function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { + function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)}); } - function div_(Exp memory a, uint b) pure internal returns (Exp memory) { + function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) { return Exp({mantissa: div_(a.mantissa, b)}); } - function div_(uint a, Exp memory b) pure internal returns (uint) { + function div_(uint256 a, Exp memory b) internal pure returns (uint256) { return div_(mul_(a, expScale), b.mantissa); } - function div_(Double memory a, Double memory b) pure internal returns (Double memory) { + function div_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)}); } - function div_(Double memory a, uint b) pure internal returns (Double memory) { + function div_(Double memory a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: div_(a.mantissa, b)}); } - function div_(uint a, Double memory b) pure internal returns (uint) { + function div_(uint256 a, Double memory b) internal pure returns (uint256) { return div_(mul_(a, doubleScale), b.mantissa); } - function div_(uint a, uint b) pure internal returns (uint) { + function div_(uint256 a, uint256 b) internal pure returns (uint256) { return div_(a, b, "divide by zero"); } - function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { + function div_( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } - function fraction(uint a, uint b) pure internal returns (Double memory) { + function fraction(uint256 a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: div_(mul_(a, doubleScale), b)}); } // implementation from https://github.com/Uniswap/uniswap-lib/commit/99f3f28770640ba1bb1ff460ac7c5292fb8291a0 // original implementation: https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687 - function sqrt(uint x) pure internal returns (uint) { + function sqrt(uint256 x) internal pure returns (uint256) { if (x == 0) return 0; - uint xx = x; - uint r = 1; + uint256 xx = x; + uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; @@ -424,7 +451,7 @@ contract Exponential is CarefulMath { r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough - uint r1 = x / r; + uint256 r1 = x / r; return (r < r1 ? r : r1); } } diff --git a/contracts/FlashloanLender.sol b/contracts/FlashloanLender.sol new file mode 100644 index 0000000..7af8fcc --- /dev/null +++ b/contracts/FlashloanLender.sol @@ -0,0 +1,101 @@ +pragma solidity ^0.5.16; +import "./CCollateralCapErc20.sol"; +import "./CErc20.sol"; +import "./Comptroller.sol"; + +interface CERC20Interface { + function underlying() external view returns (address); +} + +contract FlashloanLender is ERC3156FlashLenderInterface { + /** + * @notice underlying token to cToken mapping + */ + mapping(address => address) public underlyingToCToken; + + /** + * @notice C.R.E.A.M. comptroller address + */ + address public comptroller; + + address public owner; + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == owner, "not owner"); + _; + } + + constructor(address _comptroller, address _owner) public { + comptroller = _comptroller; + owner = _owner; + initialiseUnderlyingMapping(); + } + + function maxFlashLoan(address token) external view returns (uint256) { + address cToken = underlyingToCToken[token]; + uint256 amount = 0; + if (cToken != address(0)) { + amount = CCollateralCapErc20(cToken).maxFlashLoan(); + } + return amount; + } + + function flashFee(address token, uint256 amount) external view returns (uint256) { + address cToken = underlyingToCToken[token]; + require(cToken != address(0), "cannot find cToken of this underlying in the mapping"); + return CCollateralCapErc20(cToken).flashFee(amount); + } + + function flashLoan( + ERC3156FlashBorrowerInterface receiver, + address token, + uint256 amount, + bytes calldata data + ) external returns (bool) { + address cToken = underlyingToCToken[token]; + require(cToken != address(0), "cannot find cToken of this underlying in the mapping"); + return CCollateralCapErc20(cToken).flashLoan(receiver, msg.sender, amount, data); + } + + function updateUnderlyingMapping(CToken[] calldata cTokens) external onlyOwner returns (bool) { + uint256 cTokenLength = cTokens.length; + for (uint256 i = 0; i < cTokenLength; i++) { + CToken cToken = cTokens[i]; + address underlying = CErc20(address(cToken)).underlying(); + underlyingToCToken[underlying] = address(cToken); + } + return true; + } + + function removeUnderlyingMapping(CToken[] calldata cTokens) external onlyOwner returns (bool) { + uint256 cTokenLength = cTokens.length; + for (uint256 i = 0; i < cTokenLength; i++) { + CToken cToken = cTokens[i]; + address underlying = CErc20(address(cToken)).underlying(); + underlyingToCToken[underlying] = address(0); + } + return true; + } + + /*** Internal Functions ***/ + + function compareStrings(string memory a, string memory b) private pure returns (bool) { + return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); + } + + function initialiseUnderlyingMapping() internal { + CToken[] memory cTokens = Comptroller(comptroller).getAllMarkets(); + uint256 cTokenLength = cTokens.length; + for (uint256 i = 0; i < cTokenLength; i++) { + CToken cToken = cTokens[i]; + if (compareStrings(cToken.symbol(), "crETH")) { + continue; + } + address underlying = CErc20(address(cToken)).underlying(); + underlyingToCToken[underlying] = address(cToken); + } + } +} diff --git a/contracts/FlashloanReceiver.sol b/contracts/FlashloanReceiver.sol new file mode 100644 index 0000000..5667e29 --- /dev/null +++ b/contracts/FlashloanReceiver.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.5.16; + +import "./ERC20.sol"; +import "./CCollateralCapErc20.sol"; +import "./ERC3156FlashLenderInterface.sol"; +import "./CWrappedNative.sol"; +import "./SafeMath.sol"; +// FlashloanReceiver is a simple flashloan receiver implementation for testing +contract FlashloanReceiver is ERC3156FlashBorrowerInterface { + using SafeMath for uint256; + + + address borrowToken; + + function doFlashloan( + address flashloanLender, + address cToken, + uint256 borrowAmount, + uint256 repayAmount + ) external { + borrowToken = CCollateralCapErc20(cToken).underlying(); + uint256 balanceBefore = ERC20(borrowToken).balanceOf(address(this)); + bytes memory data = abi.encode(cToken, borrowAmount, repayAmount); + ERC3156FlashLenderInterface(flashloanLender).flashLoan(this, borrowToken, borrowAmount, data); + } + + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32) { + require(initiator == address(this), "FlashBorrower: Untrusted loan initiator"); + ERC20(borrowToken).approve(msg.sender, amount.add(fee)); + (address cToken, uint256 borrowAmount, uint256 repayAmount) = abi.decode(data, (address, uint256, uint256)); + require(amount == borrowAmount, "Params not match"); + return keccak256("ERC3156FlashBorrowerInterface.onFlashLoan"); + } +} + diff --git a/contracts/Governance/Comp.sol b/contracts/Governance/Comp.sol index 523fcb4..c342867 100644 --- a/contracts/Governance/Comp.sol +++ b/contracts/Governance/Comp.sol @@ -12,16 +12,16 @@ contract Comp { uint8 public constant decimals = 18; /// @notice Total number of tokens in circulation - uint public constant totalSupply = 9000000e18; // 9 million Comp + uint256 public constant totalSupply = 9000000e18; // 9 million Comp /// @notice Allowance amounts on behalf of others - mapping (address => mapping (address => uint96)) internal allowances; + mapping(address => mapping(address => uint96)) internal allowances; /// @notice Official record of token balances for each account - mapping (address => uint96) internal balances; + mapping(address => uint96) internal balances; /// @notice A record of each accounts delegate - mapping (address => address) public delegates; + mapping(address => address) public delegates; /// @notice A checkpoint for marking number of votes from a given block struct Checkpoint { @@ -30,25 +30,27 @@ contract Comp { } /// @notice A record of votes checkpoints for each account, by index - mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; + mapping(address => mapping(uint32 => Checkpoint)) public checkpoints; /// @notice The number of checkpoints for each account - mapping (address => uint32) public numCheckpoints; + mapping(address => uint32) public numCheckpoints; /// @notice The EIP-712 typehash for the contract's domain - bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); + bytes32 public constant DOMAIN_TYPEHASH = + keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the delegation struct used by the contract - bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + bytes32 public constant DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); /// @notice A record of states for signing / validating signatures - mapping (address => uint) public nonces; + mapping(address => uint256) public nonces; /// @notice An event thats emitted when an account changes its delegate event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); /// @notice An event thats emitted when a delegate account's vote balance changes - event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); + event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); /// @notice The standard EIP-20 transfer event event Transfer(address indexed from, address indexed to, uint256 amount); @@ -71,7 +73,7 @@ contract Comp { * @param spender The address of the account spending the funds * @return The number of tokens approved */ - function allowance(address account, address spender) external view returns (uint) { + function allowance(address account, address spender) external view returns (uint256) { return allowances[account][spender]; } @@ -83,9 +85,9 @@ contract Comp { * @param rawAmount The number of tokens that are approved (2^256-1 means infinite) * @return Whether or not the approval succeeded */ - function approve(address spender, uint rawAmount) external returns (bool) { + function approve(address spender, uint256 rawAmount) external returns (bool) { uint96 amount; - if (rawAmount == uint(-1)) { + if (rawAmount == uint256(-1)) { amount = uint96(-1); } else { amount = safe96(rawAmount, "Comp::approve: amount exceeds 96 bits"); @@ -102,7 +104,7 @@ contract Comp { * @param account The address of the account to get the balance of * @return The number of tokens held */ - function balanceOf(address account) external view returns (uint) { + function balanceOf(address account) external view returns (uint256) { return balances[account]; } @@ -112,7 +114,7 @@ contract Comp { * @param rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ - function transfer(address dst, uint rawAmount) external returns (bool) { + function transfer(address dst, uint256 rawAmount) external returns (bool) { uint96 amount = safe96(rawAmount, "Comp::transfer: amount exceeds 96 bits"); _transferTokens(msg.sender, dst, amount); return true; @@ -125,13 +127,21 @@ contract Comp { * @param rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ - function transferFrom(address src, address dst, uint rawAmount) external returns (bool) { + function transferFrom( + address src, + address dst, + uint256 rawAmount + ) external returns (bool) { address spender = msg.sender; uint96 spenderAllowance = allowances[src][spender]; uint96 amount = safe96(rawAmount, "Comp::approve: amount exceeds 96 bits"); if (spender != src && spenderAllowance != uint96(-1)) { - uint96 newAllowance = sub96(spenderAllowance, amount, "Comp::transferFrom: transfer amount exceeds spender allowance"); + uint96 newAllowance = sub96( + spenderAllowance, + amount, + "Comp::transferFrom: transfer amount exceeds spender allowance" + ); allowances[src][spender] = newAllowance; emit Approval(src, spender, newAllowance); @@ -158,8 +168,17 @@ contract Comp { * @param r Half of the ECDSA signature pair * @param s Half of the ECDSA signature pair */ - function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public { - bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public { + bytes32 domainSeparator = keccak256( + abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)) + ); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); @@ -186,7 +205,7 @@ contract Comp { * @param blockNumber The block number to get the vote balance at * @return The number of votes the account had as of the given block */ - function getPriorVotes(address account, uint blockNumber) public view returns (uint96) { + function getPriorVotes(address account, uint256 blockNumber) public view returns (uint96) { require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined"); uint32 nCheckpoints = numCheckpoints[account]; @@ -230,7 +249,11 @@ contract Comp { _moveDelegates(currentDelegate, delegatee, delegatorBalance); } - function _transferTokens(address src, address dst, uint96 amount) internal { + function _transferTokens( + address src, + address dst, + uint96 amount + ) internal { require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address"); require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address"); @@ -241,7 +264,11 @@ contract Comp { _moveDelegates(delegates[src], delegates[dst], amount); } - function _moveDelegates(address srcRep, address dstRep, uint96 amount) internal { + function _moveDelegates( + address srcRep, + address dstRep, + uint96 amount + ) internal { if (srcRep != dstRep && amount > 0) { if (srcRep != address(0)) { uint32 srcRepNum = numCheckpoints[srcRep]; @@ -259,43 +286,58 @@ contract Comp { } } - function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal { - uint32 blockNumber = safe32(block.number, "Comp::_writeCheckpoint: block number exceeds 32 bits"); + function _writeCheckpoint( + address delegatee, + uint32 nCheckpoints, + uint96 oldVotes, + uint96 newVotes + ) internal { + uint32 blockNumber = safe32(block.number, "Comp::_writeCheckpoint: block number exceeds 32 bits"); - if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { - checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; - } else { - checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); - numCheckpoints[delegatee] = nCheckpoints + 1; - } + if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { + checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; + } else { + checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); + numCheckpoints[delegatee] = nCheckpoints + 1; + } - emit DelegateVotesChanged(delegatee, oldVotes, newVotes); + emit DelegateVotesChanged(delegatee, oldVotes, newVotes); } - function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { + function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } - function safe96(uint n, string memory errorMessage) internal pure returns (uint96) { + function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96) { require(n < 2**96, errorMessage); return uint96(n); } - function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { + function add96( + uint96 a, + uint96 b, + string memory errorMessage + ) internal pure returns (uint96) { uint96 c = a + b; require(c >= a, errorMessage); return c; } - function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { + function sub96( + uint96 a, + uint96 b, + string memory errorMessage + ) internal pure returns (uint96) { require(b <= a, errorMessage); return a - b; } - function getChainId() internal pure returns (uint) { + function getChainId() internal pure returns (uint256) { uint256 chainId; - assembly { chainId := chainid() } + assembly { + chainId := chainid() + } return chainId; } } diff --git a/contracts/InterestRateModel.sol b/contracts/InterestRateModel.sol index b1842cb..1aa5d8a 100644 --- a/contracts/InterestRateModel.sol +++ b/contracts/InterestRateModel.sol @@ -1,30 +1,38 @@ pragma solidity ^0.5.16; /** - * @title Compound's InterestRateModel Interface - * @author Compound - */ + * @title Compound's InterestRateModel Interface + * @author Compound + */ contract InterestRateModel { /// @notice Indicator that this is an InterestRateModel contract (for inspection) bool public constant isInterestRateModel = true; /** - * @notice Calculates the current borrow interest rate per block - * @param cash The total amount of cash the market has - * @param borrows The total amount of borrows the market has outstanding - * @param reserves The total amnount of reserves the market has - * @return The borrow rate per block (as a percentage, and scaled by 1e18) - */ - function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint); + * @notice Calculates the current borrow interest rate per block + * @param cash The total amount of cash the market has + * @param borrows The total amount of borrows the market has outstanding + * @param reserves The total amnount of reserves the market has + * @return The borrow rate per block (as a percentage, and scaled by 1e18) + */ + function getBorrowRate( + uint256 cash, + uint256 borrows, + uint256 reserves + ) external view returns (uint256); /** - * @notice Calculates the current supply interest rate per block - * @param cash The total amount of cash the market has - * @param borrows The total amount of borrows the market has outstanding - * @param reserves The total amnount of reserves the market has - * @param reserveFactorMantissa The current reserve factor the market has - * @return The supply rate per block (as a percentage, and scaled by 1e18) - */ - function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint); - + * @notice Calculates the current supply interest rate per block + * @param cash The total amount of cash the market has + * @param borrows The total amount of borrows the market has outstanding + * @param reserves The total amnount of reserves the market has + * @param reserveFactorMantissa The current reserve factor the market has + * @return The supply rate per block (as a percentage, and scaled by 1e18) + */ + function getSupplyRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 reserveFactorMantissa + ) external view returns (uint256); } diff --git a/contracts/JumpRateModel.sol b/contracts/JumpRateModel.sol deleted file mode 100644 index 7548aef..0000000 --- a/contracts/JumpRateModel.sol +++ /dev/null @@ -1,105 +0,0 @@ -pragma solidity ^0.5.16; - -import "./InterestRateModel.sol"; -import "./SafeMath.sol"; - -/** - * @title Compound's JumpRateModel Contract - * @author Compound - */ -contract JumpRateModel is InterestRateModel { - using SafeMath for uint; - - event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint jumpMultiplierPerBlock, uint kink); - - /** - * @notice The approximate number of blocks per year that is assumed by the interest rate model - */ - uint public constant blocksPerYear = 2102400; - - /** - * @notice The multiplier of utilization rate that gives the slope of the interest rate - */ - uint public multiplierPerBlock; - - /** - * @notice The base interest rate which is the y-intercept when utilization rate is 0 - */ - uint public baseRatePerBlock; - - /** - * @notice The multiplierPerBlock after hitting a specified utilization point - */ - uint public jumpMultiplierPerBlock; - - /** - * @notice The utilization point at which the jump multiplier is applied - */ - uint public kink; - - /** - * @notice Construct an interest rate model - * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18) - * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) - * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point - * @param kink_ The utilization point at which the jump multiplier is applied - */ - constructor(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) public { - baseRatePerBlock = baseRatePerYear.div(blocksPerYear); - multiplierPerBlock = multiplierPerYear.div(blocksPerYear); - jumpMultiplierPerBlock = jumpMultiplierPerYear.div(blocksPerYear); - kink = kink_; - - emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); - } - - /** - * @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)` - * @param cash The amount of cash in the market - * @param borrows The amount of borrows in the market - * @param reserves The amount of reserves in the market (currently unused) - * @return The utilization rate as a mantissa between [0, 1e18] - */ - function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) { - // Utilization rate is 0 when there are no borrows - if (borrows == 0) { - return 0; - } - - return borrows.mul(1e18).div(cash.add(borrows).sub(reserves)); - } - - /** - * @notice Calculates the current borrow rate per block, with the error code expected by the market - * @param cash The amount of cash in the market - * @param borrows The amount of borrows in the market - * @param reserves The amount of reserves in the market - * @return The borrow rate percentage per block as a mantissa (scaled by 1e18) - */ - function getBorrowRate(uint cash, uint borrows, uint reserves) public view returns (uint) { - uint util = utilizationRate(cash, borrows, reserves); - - if (util <= kink) { - return util.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); - } else { - uint normalRate = kink.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); - uint excessUtil = util.sub(kink); - return excessUtil.mul(jumpMultiplierPerBlock).div(1e18).add(normalRate); - } - } - - /** - * @notice Calculates the current supply rate per block - * @param cash The amount of cash in the market - * @param borrows The amount of borrows in the market - * @param reserves The amount of reserves in the market - * @param reserveFactorMantissa The current reserve factor for the market - * @return The supply rate percentage per block as a mantissa (scaled by 1e18) - */ - function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) public view returns (uint) { - uint oneMinusReserveFactor = uint(1e18).sub(reserveFactorMantissa); - uint borrowRate = getBorrowRate(cash, borrows, reserves); - uint rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18); - return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18); - } -} diff --git a/contracts/JumpRateModelV2.sol b/contracts/JumpRateModelV2.sol index 739d2cd..55335c5 100644 --- a/contracts/JumpRateModelV2.sol +++ b/contracts/JumpRateModelV2.sol @@ -4,14 +4,20 @@ import "./InterestRateModel.sol"; import "./SafeMath.sol"; /** - * @title Compound's JumpRateModel Contract V2 - * @author Compound (modified by Dharma Labs) - * @notice Version 2 modifies Version 1 by enabling updateable parameters. - */ + * @title Compound's JumpRateModel Contract V2 + * @author Compound (modified by Dharma Labs) + * @notice Version 2 modifies Version 1 by enabling updateable parameters. + */ contract JumpRateModelV2 is InterestRateModel { - using SafeMath for uint; + using SafeMath for uint256; - event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint jumpMultiplierPerBlock, uint kink); + event NewInterestParams( + uint256 baseRatePerBlock, + uint256 multiplierPerBlock, + uint256 jumpMultiplierPerBlock, + uint256 kink, + uint256 roof + ); /** * @notice The address of the owner, i.e. the Timelock contract, which can update parameters directly @@ -21,27 +27,37 @@ contract JumpRateModelV2 is InterestRateModel { /** * @notice The approximate number of blocks per year that is assumed by the interest rate model */ - uint public constant blocksPerYear = 2102400; + uint256 public constant blocksPerYear = 2102400; + + /** + * @notice The minimum roof value used for calculating borrow rate. + */ + uint256 internal constant minRoofValue = 1e18; /** * @notice The multiplier of utilization rate that gives the slope of the interest rate */ - uint public multiplierPerBlock; + uint256 public multiplierPerBlock; /** * @notice The base interest rate which is the y-intercept when utilization rate is 0 */ - uint public baseRatePerBlock; + uint256 public baseRatePerBlock; /** * @notice The multiplierPerBlock after hitting a specified utilization point */ - uint public jumpMultiplierPerBlock; + uint256 public jumpMultiplierPerBlock; /** * @notice The utilization point at which the jump multiplier is applied */ - uint public kink; + uint256 public kink; + + /** + * @notice The utilization point at which the rate is fixed + */ + uint256 public roof; /** * @notice Construct an interest rate model @@ -49,12 +65,20 @@ contract JumpRateModelV2 is InterestRateModel { * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied + * @param roof_ The utilization point at which the borrow rate is fixed * @param owner_ The address of the owner, i.e. the Timelock contract (which has the ability to update parameters directly) */ - constructor(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_, address owner_) public { + constructor( + uint256 baseRatePerYear, + uint256 multiplierPerYear, + uint256 jumpMultiplierPerYear, + uint256 kink_, + uint256 roof_, + address owner_ + ) public { owner = owner_; - updateJumpRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_); + updateJumpRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_, roof_); } /** @@ -63,11 +87,18 @@ contract JumpRateModelV2 is InterestRateModel { * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied + * @param roof_ The utilization point at which the borrow rate is fixed */ - function updateJumpRateModel(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) external { + function updateJumpRateModel( + uint256 baseRatePerYear, + uint256 multiplierPerYear, + uint256 jumpMultiplierPerYear, + uint256 kink_, + uint256 roof_ + ) external { require(msg.sender == owner, "only the owner may call this function."); - updateJumpRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_); + updateJumpRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_, roof_); } /** @@ -77,13 +108,22 @@ contract JumpRateModelV2 is InterestRateModel { * @param reserves The amount of reserves in the market (currently unused) * @return The utilization rate as a mantissa between [0, 1e18] */ - function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) { + function utilizationRate( + uint256 cash, + uint256 borrows, + uint256 reserves + ) public view returns (uint256) { // Utilization rate is 0 when there are no borrows if (borrows == 0) { return 0; } - return borrows.mul(1e18).div(cash.add(borrows).sub(reserves)); + uint256 util = borrows.mul(1e18).div(cash.add(borrows).sub(reserves)); + // If the utilization is above the roof, cap it. + if (util > roof) { + util = roof; + } + return util; } /** @@ -93,14 +133,18 @@ contract JumpRateModelV2 is InterestRateModel { * @param reserves The amount of reserves in the market * @return The borrow rate percentage per block as a mantissa (scaled by 1e18) */ - function getBorrowRate(uint cash, uint borrows, uint reserves) public view returns (uint) { - uint util = utilizationRate(cash, borrows, reserves); + function getBorrowRate( + uint256 cash, + uint256 borrows, + uint256 reserves + ) public view returns (uint256) { + uint256 util = utilizationRate(cash, borrows, reserves); if (util <= kink) { return util.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); } else { - uint normalRate = kink.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); - uint excessUtil = util.sub(kink); + uint256 normalRate = kink.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); + uint256 excessUtil = util.sub(kink); return excessUtil.mul(jumpMultiplierPerBlock).div(1e18).add(normalRate); } } @@ -113,10 +157,15 @@ contract JumpRateModelV2 is InterestRateModel { * @param reserveFactorMantissa The current reserve factor for the market * @return The supply rate percentage per block as a mantissa (scaled by 1e18) */ - function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) public view returns (uint) { - uint oneMinusReserveFactor = uint(1e18).sub(reserveFactorMantissa); - uint borrowRate = getBorrowRate(cash, borrows, reserves); - uint rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18); + function getSupplyRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 reserveFactorMantissa + ) public view returns (uint256) { + uint256 oneMinusReserveFactor = uint256(1e18).sub(reserveFactorMantissa); + uint256 borrowRate = getBorrowRate(cash, borrows, reserves); + uint256 rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18); return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18); } @@ -126,13 +175,23 @@ contract JumpRateModelV2 is InterestRateModel { * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied + * @param roof_ The utilization point at which the borrow rate is fixed */ - function updateJumpRateModelInternal(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) internal { + function updateJumpRateModelInternal( + uint256 baseRatePerYear, + uint256 multiplierPerYear, + uint256 jumpMultiplierPerYear, + uint256 kink_, + uint256 roof_ + ) internal { + require(roof_ >= minRoofValue, "invalid roof value"); + baseRatePerBlock = baseRatePerYear.div(blocksPerYear); multiplierPerBlock = (multiplierPerYear.mul(1e18)).div(blocksPerYear.mul(kink_)); jumpMultiplierPerBlock = jumpMultiplierPerYear.div(blocksPerYear); kink = kink_; + roof = roof_; - emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); + emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink, roof); } } diff --git a/contracts/Lens/CompoundLens.sol b/contracts/Lens/CompoundLens.sol index 4a48875..09086b5 100644 --- a/contracts/Lens/CompoundLens.sol +++ b/contracts/Lens/CompoundLens.sol @@ -2,48 +2,59 @@ pragma solidity ^0.5.16; pragma experimental ABIEncoderV2; import "../CErc20.sol"; +import "../Comptroller.sol"; import "../CToken.sol"; import "../PriceOracle.sol"; import "../EIP20Interface.sol"; -import "../Governance/Comp.sol"; - -interface ComptrollerLensInterface { - function markets(address) external view returns (bool, uint); - function oracle() external view returns (PriceOracle); - function getAccountLiquidity(address) external view returns (uint, uint, uint); - function getAssetsIn(address) external view returns (CToken[] memory); - function claimComp(address) external; - function compAccrued(address) external view returns (uint); -} +import "../Exponential.sol"; interface CSLPInterface { - function claimSushi(address) external returns (uint); + function claimSushi(address) external returns (uint256); +} + +interface CCTokenInterface { + function claimComp(address) external returns (uint256); } -contract CompoundLens { +contract CompoundLens is Exponential { struct CTokenMetadata { address cToken; - uint exchangeRateCurrent; - uint supplyRatePerBlock; - uint borrowRatePerBlock; - uint reserveFactorMantissa; - uint totalBorrows; - uint totalReserves; - uint totalSupply; - uint totalCash; + uint256 exchangeRateCurrent; + uint256 supplyRatePerBlock; + uint256 borrowRatePerBlock; + uint256 reserveFactorMantissa; + uint256 totalBorrows; + uint256 totalReserves; + uint256 totalSupply; + uint256 totalCash; + uint256 totalCollateralTokens; bool isListed; - uint collateralFactorMantissa; + uint256 collateralFactorMantissa; address underlyingAssetAddress; - uint cTokenDecimals; - uint underlyingDecimals; - } - - function cTokenMetadata(CToken cToken) public returns (CTokenMetadata memory) { - uint exchangeRateCurrent = cToken.exchangeRateCurrent(); - ComptrollerLensInterface comptroller = ComptrollerLensInterface(address(cToken.comptroller())); - (bool isListed, uint collateralFactorMantissa) = comptroller.markets(address(cToken)); + uint256 cTokenDecimals; + uint256 underlyingDecimals; + ComptrollerV2Storage.Version version; + uint256 collateralCap; + uint256 underlyingPrice; + bool supplyPaused; + bool borrowPaused; + uint256 supplyCap; + uint256 borrowCap; + } + + function cTokenMetadataInternal( + CToken cToken, + Comptroller comptroller, + PriceOracle priceOracle + ) internal returns (CTokenMetadata memory) { + uint256 exchangeRateCurrent = cToken.exchangeRateCurrent(); + (bool isListed, uint256 collateralFactorMantissa, , ComptrollerV2Storage.Version version) = comptroller.markets( + address(cToken) + ); address underlyingAssetAddress; - uint underlyingDecimals; + uint256 underlyingDecimals; + uint256 collateralCap; + uint256 totalCollateralTokens; if (compareStrings(cToken.symbol(), "crETH")) { underlyingAssetAddress = address(0); @@ -54,48 +65,75 @@ contract CompoundLens { underlyingDecimals = EIP20Interface(cErc20.underlying()).decimals(); } - return CTokenMetadata({ - cToken: address(cToken), - exchangeRateCurrent: exchangeRateCurrent, - supplyRatePerBlock: cToken.supplyRatePerBlock(), - borrowRatePerBlock: cToken.borrowRatePerBlock(), - reserveFactorMantissa: cToken.reserveFactorMantissa(), - totalBorrows: cToken.totalBorrows(), - totalReserves: cToken.totalReserves(), - totalSupply: cToken.totalSupply(), - totalCash: cToken.getCash(), - isListed: isListed, - collateralFactorMantissa: collateralFactorMantissa, - underlyingAssetAddress: underlyingAssetAddress, - cTokenDecimals: cToken.decimals(), - underlyingDecimals: underlyingDecimals - }); + if (version == ComptrollerV2Storage.Version.COLLATERALCAP) { + collateralCap = CCollateralCapErc20Interface(address(cToken)).collateralCap(); + totalCollateralTokens = CCollateralCapErc20Interface(address(cToken)).totalCollateralTokens(); + } + + return + CTokenMetadata({ + cToken: address(cToken), + exchangeRateCurrent: exchangeRateCurrent, + supplyRatePerBlock: cToken.supplyRatePerBlock(), + borrowRatePerBlock: cToken.borrowRatePerBlock(), + reserveFactorMantissa: cToken.reserveFactorMantissa(), + totalBorrows: cToken.totalBorrows(), + totalReserves: cToken.totalReserves(), + totalSupply: cToken.totalSupply(), + totalCash: cToken.getCash(), + totalCollateralTokens: totalCollateralTokens, + isListed: isListed, + collateralFactorMantissa: collateralFactorMantissa, + underlyingAssetAddress: underlyingAssetAddress, + cTokenDecimals: cToken.decimals(), + underlyingDecimals: underlyingDecimals, + version: version, + collateralCap: collateralCap, + underlyingPrice: priceOracle.getUnderlyingPrice(cToken), + supplyPaused: comptroller.mintGuardianPaused(address(cToken)), + borrowPaused: comptroller.borrowGuardianPaused(address(cToken)), + supplyCap: comptroller.supplyCaps(address(cToken)), + borrowCap: comptroller.borrowCaps(address(cToken)) + }); + } + + function cTokenMetadata(CToken cToken) public returns (CTokenMetadata memory) { + Comptroller comptroller = Comptroller(address(cToken.comptroller())); + PriceOracle priceOracle = comptroller.oracle(); + return cTokenMetadataInternal(cToken, comptroller, priceOracle); } function cTokenMetadataAll(CToken[] calldata cTokens) external returns (CTokenMetadata[] memory) { - uint cTokenCount = cTokens.length; + uint256 cTokenCount = cTokens.length; + require(cTokenCount > 0, "invalid input"); CTokenMetadata[] memory res = new CTokenMetadata[](cTokenCount); - for (uint i = 0; i < cTokenCount; i++) { - res[i] = cTokenMetadata(cTokens[i]); + Comptroller comptroller = Comptroller(address(cTokens[0].comptroller())); + PriceOracle priceOracle = comptroller.oracle(); + for (uint256 i = 0; i < cTokenCount; i++) { + require(address(comptroller) == address(cTokens[i].comptroller()), "mismatch comptroller"); + res[i] = cTokenMetadataInternal(cTokens[i], comptroller, priceOracle); } return res; } struct CTokenBalances { address cToken; - uint balanceOf; - uint borrowBalanceCurrent; - uint balanceOfUnderlying; - uint tokenBalance; - uint tokenAllowance; + uint256 balanceOf; + uint256 borrowBalanceCurrent; + uint256 balanceOfUnderlying; + uint256 tokenBalance; + uint256 tokenAllowance; + bool collateralEnabled; + uint256 collateralBalance; + uint256 nativeTokenBalance; } function cTokenBalances(CToken cToken, address payable account) public returns (CTokenBalances memory) { - uint balanceOf = cToken.balanceOf(account); - uint borrowBalanceCurrent = cToken.borrowBalanceCurrent(account); - uint balanceOfUnderlying = cToken.balanceOfUnderlying(account); - uint tokenBalance; - uint tokenAllowance; + address comptroller = address(cToken.comptroller()); + bool collateralEnabled = Comptroller(comptroller).checkMembership(account, cToken); + uint256 tokenBalance; + uint256 tokenAllowance; + uint256 collateralBalance; if (compareStrings(cToken.symbol(), "crETH")) { tokenBalance = account.balance; @@ -107,127 +145,77 @@ contract CompoundLens { tokenAllowance = underlying.allowance(account, address(cToken)); } - return CTokenBalances({ - cToken: address(cToken), - balanceOf: balanceOf, - borrowBalanceCurrent: borrowBalanceCurrent, - balanceOfUnderlying: balanceOfUnderlying, - tokenBalance: tokenBalance, - tokenAllowance: tokenAllowance - }); - } - - function cTokenBalancesAll(CToken[] calldata cTokens, address payable account) external returns (CTokenBalances[] memory) { - uint cTokenCount = cTokens.length; - CTokenBalances[] memory res = new CTokenBalances[](cTokenCount); - for (uint i = 0; i < cTokenCount; i++) { - res[i] = cTokenBalances(cTokens[i], account); + if (collateralEnabled) { + (, collateralBalance, , ) = cToken.getAccountSnapshot(account); } - return res; - } - - struct CTokenUnderlyingPrice { - address cToken; - uint underlyingPrice; - } - function cTokenUnderlyingPrice(CToken cToken) public returns (CTokenUnderlyingPrice memory) { - ComptrollerLensInterface comptroller = ComptrollerLensInterface(address(cToken.comptroller())); - PriceOracle priceOracle = comptroller.oracle(); - - return CTokenUnderlyingPrice({ - cToken: address(cToken), - underlyingPrice: priceOracle.getUnderlyingPrice(cToken) - }); + return + CTokenBalances({ + cToken: address(cToken), + balanceOf: cToken.balanceOf(account), + borrowBalanceCurrent: cToken.borrowBalanceCurrent(account), + balanceOfUnderlying: cToken.balanceOfUnderlying(account), + tokenBalance: tokenBalance, + tokenAllowance: tokenAllowance, + collateralEnabled: collateralEnabled, + collateralBalance: collateralBalance, + nativeTokenBalance: account.balance + }); } - function cTokenUnderlyingPriceAll(CToken[] calldata cTokens) external returns (CTokenUnderlyingPrice[] memory) { - uint cTokenCount = cTokens.length; - CTokenUnderlyingPrice[] memory res = new CTokenUnderlyingPrice[](cTokenCount); - for (uint i = 0; i < cTokenCount; i++) { - res[i] = cTokenUnderlyingPrice(cTokens[i]); + function cTokenBalancesAll(CToken[] calldata cTokens, address payable account) + external + returns (CTokenBalances[] memory) + { + uint256 cTokenCount = cTokens.length; + CTokenBalances[] memory res = new CTokenBalances[](cTokenCount); + for (uint256 i = 0; i < cTokenCount; i++) { + res[i] = cTokenBalances(cTokens[i], account); } return res; } struct AccountLimits { CToken[] markets; - uint liquidity; - uint shortfall; + uint256 liquidity; + uint256 shortfall; } - function getAccountLimits(ComptrollerLensInterface comptroller, address account) public returns (AccountLimits memory) { - (uint errorCode, uint liquidity, uint shortfall) = comptroller.getAccountLiquidity(account); + function getAccountLimits(Comptroller comptroller, address account) public returns (AccountLimits memory) { + (uint256 errorCode, uint256 liquidity, uint256 shortfall) = comptroller.getAccountLiquidity(account); require(errorCode == 0); - return AccountLimits({ - markets: comptroller.getAssetsIn(account), - liquidity: liquidity, - shortfall: shortfall - }); - } - - struct CompBalanceMetadata { - uint balance; - uint votes; - address delegate; - } - - function getCompBalanceMetadata(Comp comp, address account) external view returns (CompBalanceMetadata memory) { - return CompBalanceMetadata({ - balance: comp.balanceOf(account), - votes: uint256(comp.getCurrentVotes(account)), - delegate: comp.delegates(account) - }); - } - - struct CompBalanceMetadataExt { - uint balance; - uint votes; - address delegate; - uint allocated; - } - - function getCompBalanceMetadataExt(Comp comp, ComptrollerLensInterface comptroller, address account) external returns (CompBalanceMetadataExt memory) { - uint balance = comp.balanceOf(account); - comptroller.claimComp(account); - uint newBalance = comp.balanceOf(account); - uint accrued = comptroller.compAccrued(account); - uint total = add(accrued, newBalance, "sum comp total"); - uint allocated = sub(total, balance, "sub allocated"); - - return CompBalanceMetadataExt({ - balance: balance, - votes: uint256(comp.getCurrentVotes(account)), - delegate: comp.delegates(account), - allocated: allocated - }); + return AccountLimits({markets: comptroller.getAssetsIn(account), liquidity: liquidity, shortfall: shortfall}); } - struct CompVotes { - uint blockNumber; - uint votes; - } - - function getCompVotes(Comp comp, address account, uint32[] calldata blockNumbers) external view returns (CompVotes[] memory) { - CompVotes[] memory res = new CompVotes[](blockNumbers.length); - for (uint i = 0; i < blockNumbers.length; i++) { - res[i] = CompVotes({ - blockNumber: uint256(blockNumbers[i]), - votes: uint256(comp.getPriorVotes(account, blockNumbers[i])) - }); + function getClaimableSushiRewards( + CSLPInterface[] calldata cTokens, + address sushi, + address account + ) external returns (uint256[] memory) { + uint256 cTokenCount = cTokens.length; + uint256[] memory rewards = new uint256[](cTokenCount); + for (uint256 i = 0; i < cTokenCount; i++) { + uint256 balanceBefore = EIP20Interface(sushi).balanceOf(account); + cTokens[i].claimSushi(account); + uint256 balanceAfter = EIP20Interface(sushi).balanceOf(account); + rewards[i] = sub_(balanceAfter, balanceBefore); } - return res; + return rewards; } - function getClaimableSushiRewards(CSLPInterface[] calldata cTokens, address sushi, address account) external returns (uint[] memory) { - uint cTokenCount = cTokens.length; - uint[] memory rewards = new uint[](cTokenCount); - for (uint i = 0; i < cTokenCount; i++) { - uint balanceBefore = EIP20Interface(sushi).balanceOf(account); - cTokens[i].claimSushi(account); - uint balanceAfter = EIP20Interface(sushi).balanceOf(account); - rewards[i] = sub(balanceAfter, balanceBefore, "subtraction underflow"); + function getClaimableCompRewards( + CCTokenInterface[] calldata cTokens, + address comp, + address account + ) external returns (uint256[] memory) { + uint256 cTokenCount = cTokens.length; + uint256[] memory rewards = new uint256[](cTokenCount); + for (uint256 i = 0; i < cTokenCount; i++) { + uint256 balanceBefore = EIP20Interface(comp).balanceOf(account); + cTokens[i].claimComp(account); + uint256 balanceAfter = EIP20Interface(comp).balanceOf(account); + rewards[i] = sub_(balanceAfter, balanceBefore); } return rewards; } @@ -235,16 +223,4 @@ contract CompoundLens { function compareStrings(string memory a, string memory b) internal pure returns (bool) { return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); } - - function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) { - uint c = a + b; - require(c >= a, errorMessage); - return c; - } - - function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { - require(b <= a, errorMessage); - uint c = a - b; - return c; - } } diff --git a/contracts/LiquidityMiningInterface.sol b/contracts/LiquidityMiningInterface.sol new file mode 100644 index 0000000..93446ed --- /dev/null +++ b/contracts/LiquidityMiningInterface.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.5.16; + +contract LiquidityMiningInterface { + function comptroller() external view returns (address); + + function updateSupplyIndex(address cToken, address[] calldata accounts) external; + + function updateBorrowIndex(address cToken, address[] calldata accounts) external; +} diff --git a/contracts/PriceOracle.sol b/contracts/PriceOracle.sol index 27bcc99..825fd8d 100644 --- a/contracts/PriceOracle.sol +++ b/contracts/PriceOracle.sol @@ -7,10 +7,10 @@ contract PriceOracle { bool public constant isPriceOracle = true; /** - * @notice Get the underlying price of a cToken asset - * @param cToken The cToken to get the underlying price of - * @return The underlying asset price mantissa (scaled by 1e18). - * Zero means the price is unavailable. - */ - function getUnderlyingPrice(CToken cToken) external view returns (uint); + * @notice Get the underlying price of a cToken asset + * @param cToken The cToken to get the underlying price of + * @return The underlying asset price mantissa (scaled by 1e18). + * Zero means the price is unavailable. + */ + function getUnderlyingPrice(CToken cToken) external view returns (uint256); } diff --git a/contracts/PriceOracleProxy.sol b/contracts/PriceOracleProxy.sol index 5584362..f892671 100644 --- a/contracts/PriceOracleProxy.sol +++ b/contracts/PriceOracleProxy.sol @@ -7,46 +7,66 @@ import "./Exponential.sol"; import "./EIP20Interface.sol"; interface V1PriceOracleInterface { - function assetPrices(address asset) external view returns (uint); + function assetPrices(address asset) external view returns (uint256); } interface CurveSwapInterface { function get_virtual_price() external view returns (uint256); } -interface YVaultInterface { +interface CurveTokenV3Interface { + function minter() external view returns (address); +} + +interface YVaultV1Interface { + function token() external view returns (address); + function getPricePerFullShare() external view returns (uint256); } +interface YVaultV2Interface { + function token() external view returns (address); + + function pricePerShare() external view returns (uint256); +} + interface AggregatorV3Interface { function decimals() external view returns (uint8); + function description() external view returns (string memory); + function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. - function getRoundData(uint80 _roundId) external view returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ); + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); - function latestRoundData() external view returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ); + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); } // Ref: https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/interfaces/IUniswapV2Pair.sol interface IUniswapV2Pair { - event Approval(address indexed owner, address indexed spender, uint value); - event Transfer(address indexed from, address indexed to, uint value); + event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); @@ -54,51 +74,51 @@ interface IUniswapV2Pair { function decimals() external pure returns (uint8); - function totalSupply() external view returns (uint); + function totalSupply() external view returns (uint256); - function balanceOf(address owner) external view returns (uint); + function balanceOf(address owner) external view returns (uint256); - function allowance(address owner, address spender) external view returns (uint); + function allowance(address owner, address spender) external view returns (uint256); - function approve(address spender, uint value) external returns (bool); + function approve(address spender, uint256 value) external returns (bool); - function transfer(address to, uint value) external returns (bool); + function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, - uint value + uint256 value ) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); - function nonces(address owner) external view returns (uint); + function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, - uint value, - uint deadline, + uint256 value, + uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; - event Mint(address indexed sender, uint amount0, uint amount1); - event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); + event Mint(address indexed sender, uint256 amount0, uint256 amount1); + event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); event Swap( address indexed sender, - uint amount0In, - uint amount1In, - uint amount0Out, - uint amount1Out, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); - function MINIMUM_LIQUIDITY() external pure returns (uint); + function MINIMUM_LIQUIDITY() external pure returns (uint256); function factory() external view returns (address); @@ -115,19 +135,19 @@ interface IUniswapV2Pair { uint32 blockTimestampLast ); - function price0CumulativeLast() external view returns (uint); + function price0CumulativeLast() external view returns (uint256); - function price1CumulativeLast() external view returns (uint); + function price1CumulativeLast() external view returns (uint256); - function kLast() external view returns (uint); + function kLast() external view returns (uint256); - function mint(address to) external returns (uint liquidity); + function mint(address to) external returns (uint256 liquidity); - function burn(address to) external returns (uint amount0, uint amount1); + function burn(address to) external returns (uint256 amount0, uint256 amount1); function swap( - uint amount0Out, - uint amount1Out, + uint256 amount0Out, + uint256 amount1Out, address to, bytes calldata data ) external; @@ -140,29 +160,83 @@ interface IUniswapV2Pair { } interface IXSushiExchangeRate { - function getExchangeRate() external view returns (uint); + function getExchangeRate() external view returns (uint256); } contract PriceOracleProxy is PriceOracle, Exponential { + /// @notice Yvault token version, currently support v1 and v2 + enum YvTokenVersion { + V1, + V2 + } + + /// @notice Curve token version, currently support v1, v2 and v3 + enum CurveTokenVersion { + V1, + V2, + V3 + } + + /// @notice Curve pool type, currently support ETH and USD base + enum CurvePoolType { + ETH, + USD + } + + /// @notice ChainLink aggregator base, currently support ETH and USD + enum AggregatorBase { + ETH, + USD + } + + struct YvTokenInfo { + /// @notice Check if this token is a Yvault token + bool isYvToken; + /// @notice The version of Yvault + YvTokenVersion version; + } + + struct CrvTokenInfo { + /// @notice Check if this token is a curve pool token + bool isCrvToken; + /// @notice The curve pool type + CurvePoolType poolType; + /// @notice The curve swap contract address + address curveSwap; + } + + struct AggregatorInfo { + /// @notice The source address of the aggregator + AggregatorV3Interface source; + /// @notice The aggregator base + AggregatorBase base; + } + /// @notice Admin address address public admin; + /// @notice Guardian address + address public guardian; + /// @notice Indicator that this is a PriceOracle contract (for inspection) bool public constant isPriceOracle = true; /// @notice The v1 price oracle, which will continue to serve prices for v1 assets V1PriceOracleInterface public v1PriceOracle; - /// @notice Chainlink Aggregators - mapping(address => AggregatorV3Interface) public aggregators; + /// @notice ChainLink aggregators + mapping(address => AggregatorInfo) public aggregators; /// @notice Check if the underlying address is Uniswap or SushiSwap LP mapping(address => bool) public areUnderlyingLPs; + /// @notice Yvault token data + mapping(address => YvTokenInfo) public yvTokens; + + /// @notice Curve pool token data + mapping(address => CrvTokenInfo) public crvTokens; + address public cEthAddress; - address public cYcrvAddress; - address public cYusdAddress; - address public cYethAddress; address public cXSushiAddress; address public constant usdcAddress = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; @@ -171,27 +245,20 @@ contract PriceOracleProxy is PriceOracle, Exponential { address public constant xSushiExRateAddress = 0x851a040fC0Dcbb13a272EBC272F2bC2Ce1e11C4d; /** - * @param admin_ The address of admin to set aggregators + * @param admin_ The address of admin to set aggregators, LPs, curve tokens, or Yvault tokens * @param v1PriceOracle_ The address of the v1 price oracle, which will continue to operate and hold prices for collateral assets * @param cEthAddress_ The address of cETH, which will return a constant 1e18, since all prices relative to ether - * @param cYcrvAddress_ The address of cYcrv - * @param cYusdAddress_ The address of cYusd - * @param cYethAddress_ The address of cYeth * @param cXSushiAddress_ The address of cXSushi */ - constructor(address admin_, - address v1PriceOracle_, - address cEthAddress_, - address cYcrvAddress_, - address cYusdAddress_, - address cYethAddress_, - address cXSushiAddress_) public { + constructor( + address admin_, + address v1PriceOracle_, + address cEthAddress_, + address cXSushiAddress_ + ) public { admin = admin_; v1PriceOracle = V1PriceOracleInterface(v1PriceOracle_); cEthAddress = cEthAddress_; - cYcrvAddress = cYcrvAddress_; - cYusdAddress = cYusdAddress_; - cYethAddress = cYethAddress_; cXSushiAddress = cXSushiAddress_; } @@ -200,47 +267,36 @@ contract PriceOracleProxy is PriceOracle, Exponential { * @param cToken The cToken to get the underlying price of * @return The underlying asset price mantissa (scaled by 1e18) */ - function getUnderlyingPrice(CToken cToken) public view returns (uint) { + function getUnderlyingPrice(CToken cToken) public view returns (uint256) { address cTokenAddress = address(cToken); if (cTokenAddress == cEthAddress) { // ether always worth 1 return 1e18; } - if (cTokenAddress == cYethAddress) { - return YVaultInterface(0xe1237aA7f535b0CC33Fd973D66cBf830354D16c7).getPricePerFullShare(); - } - - if (cTokenAddress == cYcrvAddress || cTokenAddress == cYusdAddress) { - // USD/ETH (treat USDC as USD) - uint usdEthPrice = getTokenPrice(usdcAddress) / 1e12; - - // YCRV/USD - uint virtualPrice = CurveSwapInterface(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51).get_virtual_price(); - - // YCRV/ETH = USD/ETH * YCRV/USD - uint yCrvEthPrice = mul_(usdEthPrice, Exp({mantissa: virtualPrice})); - - if (cTokenAddress == cYusdAddress) { - // YUSD/YCRV - uint yVaultPrice = YVaultInterface(0x5dbcF33D8c2E976c6b560249878e6F1491Bca25c).getPricePerFullShare(); - - // YUSD/ETH = YCRV/ETH * YUSD/YCRV - return mul_(yCrvEthPrice, Exp({mantissa: yVaultPrice})); - } - return yCrvEthPrice; - } - + // Handle xSUSHI. if (cTokenAddress == cXSushiAddress) { - uint exchangeRate = IXSushiExchangeRate(xSushiExRateAddress).getExchangeRate(); + uint256 exchangeRate = IXSushiExchangeRate(xSushiExRateAddress).getExchangeRate(); return mul_(getTokenPrice(sushiAddress), Exp({mantissa: exchangeRate})); } address underlying = CErc20(cTokenAddress).underlying(); + + // Handle LP tokens. if (areUnderlyingLPs[cTokenAddress]) { return getLPFairPrice(underlying); } + // Handle Yvault tokens. + if (yvTokens[underlying].isYvToken) { + return getYvTokenPrice(underlying); + } + + // Handle curve pool tokens. + if (crvTokens[underlying].isCrvToken) { + return getCrvTokenPrice(underlying); + } + return getTokenPrice(underlying); } @@ -251,16 +307,20 @@ contract PriceOracleProxy is PriceOracle, Exponential { * @param token The token to get the price of * @return The price */ - function getTokenPrice(address token) internal view returns (uint) { + function getTokenPrice(address token) internal view returns (uint256) { if (token == wethAddress) { // weth always worth 1 return 1e18; } - AggregatorV3Interface aggregator = aggregators[token]; - if (address(aggregator) != address(0)) { - uint price = getPriceFromChainlink(aggregator); - uint underlyingDecimals = EIP20Interface(token).decimals(); + AggregatorInfo memory aggregatorInfo = aggregators[token]; + if (address(aggregatorInfo.source) != address(0)) { + uint256 price = getPriceFromChainlink(aggregatorInfo.source); + if (aggregatorInfo.base == AggregatorBase.USD) { + // Convert the price to ETH based if it's USD based. + price = mul_(price, Exp({mantissa: getUsdcEthPrice()})); + } + uint256 underlyingDecimals = EIP20Interface(token).decimals(); return mul_(price, 10**(18 - underlyingDecimals)); } return getPriceFromV1(token); @@ -271,12 +331,12 @@ contract PriceOracleProxy is PriceOracle, Exponential { * @param aggregator The ChainLink aggregator to get the price of * @return The price */ - function getPriceFromChainlink(AggregatorV3Interface aggregator) internal view returns (uint) { - ( , int price, , , ) = aggregator.latestRoundData(); + function getPriceFromChainlink(AggregatorV3Interface aggregator) internal view returns (uint256) { + (, int256 price, , , ) = aggregator.latestRoundData(); require(price > 0, "invalid price"); // Extend the decimals to 1e18. - return mul_(uint(price), 10**(18 - uint(aggregator.decimals()))); + return mul_(uint256(price), 10**(18 - uint256(aggregator.decimals()))); } /** @@ -285,50 +345,198 @@ contract PriceOracleProxy is PriceOracle, Exponential { * @param pair The pair of AMM (Uniswap or SushiSwap) * @return The price */ - function getLPFairPrice(address pair) internal view returns (uint) { + function getLPFairPrice(address pair) internal view returns (uint256) { address token0 = IUniswapV2Pair(pair).token0(); address token1 = IUniswapV2Pair(pair).token1(); - uint totalSupply = IUniswapV2Pair(pair).totalSupply(); - (uint r0, uint r1, ) = IUniswapV2Pair(pair).getReserves(); - uint sqrtR = sqrt(mul_(r0, r1)); - uint p0 = getTokenPrice(token0); - uint p1 = getTokenPrice(token1); - uint sqrtP = sqrt(mul_(p0, p1)); + uint256 totalSupply = IUniswapV2Pair(pair).totalSupply(); + (uint256 r0, uint256 r1, ) = IUniswapV2Pair(pair).getReserves(); + uint256 sqrtR = sqrt(mul_(r0, r1)); + uint256 p0 = getTokenPrice(token0); + uint256 p1 = getTokenPrice(token1); + uint256 sqrtP = sqrt(mul_(p0, p1)); return div_(mul_(2, mul_(sqrtR, sqrtP)), totalSupply); } + /** + * @notice Get price for Yvault tokens + * @param token The Yvault token + * @return The price + */ + function getYvTokenPrice(address token) internal view returns (uint256) { + YvTokenInfo memory yvTokenInfo = yvTokens[token]; + require(yvTokenInfo.isYvToken, "not a Yvault token"); + + uint256 pricePerShare; + address underlying; + if (yvTokenInfo.version == YvTokenVersion.V1) { + pricePerShare = YVaultV1Interface(token).getPricePerFullShare(); + underlying = YVaultV1Interface(token).token(); + } else { + pricePerShare = YVaultV2Interface(token).pricePerShare(); + underlying = YVaultV2Interface(token).token(); + } + + uint256 underlyingPrice; + if (crvTokens[underlying].isCrvToken) { + underlyingPrice = getCrvTokenPrice(underlying); + } else { + underlyingPrice = getTokenPrice(underlying); + } + return mul_(underlyingPrice, Exp({mantissa: pricePerShare})); + } + + /** + * @notice Get price for curve pool tokens + * @param token The curve pool token + * @return The price + */ + function getCrvTokenPrice(address token) internal view returns (uint256) { + CrvTokenInfo memory crvTokenInfo = crvTokens[token]; + require(crvTokenInfo.isCrvToken, "not a curve pool token"); + + uint256 virtualPrice = CurveSwapInterface(crvTokenInfo.curveSwap).get_virtual_price(); + if (crvTokenInfo.poolType == CurvePoolType.ETH) { + return virtualPrice; + } + + // We treat USDC as USD and convert the price to ETH base. + return mul_(getUsdcEthPrice(), Exp({mantissa: virtualPrice})); + } + + /** + * @notice Get USDC price + * @dev We treat USDC as USD for convenience + * @return The USDC price + */ + function getUsdcEthPrice() internal view returns (uint256) { + return getTokenPrice(usdcAddress) / 1e12; + } + /** * @notice Get price from v1 price oracle * @param token The token to get the price of * @return The price */ - function getPriceFromV1(address token) internal view returns (uint) { + function getPriceFromV1(address token) internal view returns (uint256) { return v1PriceOracle.assetPrices(token); } - event AggregatorUpdated(address tokenAddress, address source); + /*** Admin or guardian functions ***/ + + event AggregatorUpdated(address tokenAddress, address source, AggregatorBase base); event IsLPUpdated(address tokenAddress, bool isLP); + event SetYVaultToken(address token, YvTokenVersion version); + event SetCurveToken(address token, CurvePoolType poolType, address swap); + event SetGuardian(address guardian); + event SetAdmin(address admin); - function _setAggregators(address[] calldata tokenAddresses, address[] calldata sources) external { - require(msg.sender == admin, "only the admin may set the aggregators"); - require(tokenAddresses.length == sources.length, "mismatched data"); - for (uint i = 0; i < tokenAddresses.length; i++) { - aggregators[tokenAddresses[i]] = AggregatorV3Interface(sources[i]); - emit AggregatorUpdated(tokenAddresses[i], sources[i]); + /** + * @notice Set ChainLink aggregators for multiple cTokens + * @param tokenAddresses The list of underlying tokens + * @param sources The list of ChainLink aggregator sources + * @param bases The list of ChainLink aggregator bases + */ + function _setAggregators( + address[] calldata tokenAddresses, + address[] calldata sources, + AggregatorBase[] calldata bases + ) external { + require(msg.sender == admin || msg.sender == guardian, "only the admin or guardian may set the aggregators"); + require(tokenAddresses.length == sources.length && tokenAddresses.length == bases.length, "mismatched data"); + for (uint256 i = 0; i < tokenAddresses.length; i++) { + if (sources[i] != address(0)) { + require(msg.sender == admin, "guardian may only clear the aggregator"); + } + aggregators[tokenAddresses[i]] = AggregatorInfo({ + source: AggregatorV3Interface(sources[i]), + base: bases[i] + }); + emit AggregatorUpdated(tokenAddresses[i], sources[i], bases[i]); } } + /** + * @notice See assets as LP tokens for multiple cTokens + * @param cTokenAddresses The list of cTokens + * @param isLP The list of cToken properties (it's LP or not) + */ function _setLPs(address[] calldata cTokenAddresses, bool[] calldata isLP) external { require(msg.sender == admin, "only the admin may set LPs"); require(cTokenAddresses.length == isLP.length, "mismatched data"); - for (uint i = 0; i < cTokenAddresses.length; i++) { + for (uint256 i = 0; i < cTokenAddresses.length; i++) { areUnderlyingLPs[cTokenAddresses[i]] = isLP[i]; emit IsLPUpdated(cTokenAddresses[i], isLP[i]); } } + /** + * @notice See assets as Yvault tokens for multiple cTokens + * @param tokenAddresses The list of underlying tokens + * @param version The list of vault version + */ + function _setYVaultTokens(address[] calldata tokenAddresses, YvTokenVersion[] calldata version) external { + require(msg.sender == admin, "only the admin may set Yvault tokens"); + require(tokenAddresses.length == version.length, "mismatched data"); + for (uint256 i = 0; i < tokenAddresses.length; i++) { + // Sanity check to make sure version is right. + if (version[i] == YvTokenVersion.V1) { + YVaultV1Interface(tokenAddresses[i]).getPricePerFullShare(); + } else { + YVaultV2Interface(tokenAddresses[i]).pricePerShare(); + } + + yvTokens[tokenAddresses[i]] = YvTokenInfo({isYvToken: true, version: version[i]}); + emit SetYVaultToken(tokenAddresses[i], version[i]); + } + } + + /** + * @notice See assets as curve pool tokens for multiple cTokens + * @param tokenAddresses The list of underlying tokens + * @param poolType The list of curve pool type (ETH or USD base only) + * @param swap The list of curve swap address + */ + function _setCurveTokens( + address[] calldata tokenAddresses, + CurveTokenVersion[] calldata version, + CurvePoolType[] calldata poolType, + address[] calldata swap + ) external { + require(msg.sender == admin, "only the admin may set curve pool tokens"); + require( + tokenAddresses.length == version.length && + tokenAddresses.length == poolType.length && + tokenAddresses.length == swap.length, + "mismatched data" + ); + for (uint256 i = 0; i < tokenAddresses.length; i++) { + if (version[i] == CurveTokenVersion.V3) { + // Sanity check to make sure the token minter is right. + require(CurveTokenV3Interface(tokenAddresses[i]).minter() == swap[i], "incorrect pool"); + } + + crvTokens[tokenAddresses[i]] = CrvTokenInfo({isCrvToken: true, poolType: poolType[i], curveSwap: swap[i]}); + emit SetCurveToken(tokenAddresses[i], poolType[i], swap[i]); + } + } + + /** + * @notice Set guardian for price oracle proxy + * @param _guardian The new guardian + */ + function _setGuardian(address _guardian) external { + require(msg.sender == admin, "only the admin may set new guardian"); + guardian = _guardian; + emit SetGuardian(guardian); + } + + /** + * @notice Set admin for price oracle proxy + * @param _admin The new admin + */ function _setAdmin(address _admin) external { require(msg.sender == admin, "only the admin may set new admin"); admin = _admin; + emit SetAdmin(admin); } } diff --git a/contracts/SafeMath.sol b/contracts/SafeMath.sol index b27c996..294315d 100644 --- a/contracts/SafeMath.sol +++ b/contracts/SafeMath.sol @@ -40,7 +40,11 @@ library SafeMath { * Requirements: * - Addition cannot overflow. */ - function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + function add( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, errorMessage); @@ -67,7 +71,11 @@ library SafeMath { * Requirements: * - Subtraction cannot underflow. */ - function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; @@ -104,7 +112,11 @@ library SafeMath { * Requirements: * - Multiplication cannot overflow. */ - function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + function mul( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 @@ -144,7 +156,11 @@ library SafeMath { * Requirements: * - The divisor cannot be zero. */ - function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; @@ -179,7 +195,11 @@ library SafeMath { * Requirements: * - The divisor cannot be zero. */ - function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } diff --git a/contracts/SimplePriceOracle.sol b/contracts/SimplePriceOracle.sol index 3060704..3ad8e74 100644 --- a/contracts/SimplePriceOracle.sol +++ b/contracts/SimplePriceOracle.sol @@ -4,10 +4,15 @@ import "./PriceOracle.sol"; import "./CErc20.sol"; contract SimplePriceOracle is PriceOracle { - mapping(address => uint) prices; - event PricePosted(address asset, uint previousPriceMantissa, uint requestedPriceMantissa, uint newPriceMantissa); + mapping(address => uint256) prices; + event PricePosted( + address asset, + uint256 previousPriceMantissa, + uint256 requestedPriceMantissa, + uint256 newPriceMantissa + ); - function getUnderlyingPrice(CToken cToken) public view returns (uint) { + function getUnderlyingPrice(CToken cToken) public view returns (uint256) { if (compareStrings(cToken.symbol(), "crETH")) { return 1e18; } else { @@ -15,19 +20,19 @@ contract SimplePriceOracle is PriceOracle { } } - function setUnderlyingPrice(CToken cToken, uint underlyingPriceMantissa) public { + function setUnderlyingPrice(CToken cToken, uint256 underlyingPriceMantissa) public { address asset = address(CErc20(address(cToken)).underlying()); emit PricePosted(asset, prices[asset], underlyingPriceMantissa, underlyingPriceMantissa); prices[asset] = underlyingPriceMantissa; } - function setDirectPrice(address asset, uint price) public { + function setDirectPrice(address asset, uint256 price) public { emit PricePosted(asset, prices[asset], price, price); prices[asset] = price; } // v1 price oracle interface for use as backing of proxy - function assetPrices(address asset) external view returns (uint) { + function assetPrices(address asset) external view returns (uint256) { return prices[asset]; } diff --git a/contracts/TripleSlopeRateModel.sol b/contracts/TripleSlopeRateModel.sol new file mode 100644 index 0000000..bdf8df1 --- /dev/null +++ b/contracts/TripleSlopeRateModel.sol @@ -0,0 +1,212 @@ +pragma solidity ^0.5.16; + +import "./InterestRateModel.sol"; +import "./SafeMath.sol"; + +/** + * @title CREAM's TripleSlopeRateModel Contract + * @author C.R.E.A.M. Finance + */ +contract TripleSlopeRateModel is InterestRateModel { + using SafeMath for uint256; + + event NewInterestParams( + uint256 baseRatePerBlock, + uint256 multiplierPerBlock, + uint256 jumpMultiplierPerBlock, + uint256 kink1, + uint256 kink2, + uint256 roof + ); + + /** + * @notice The address of the owner, i.e. the Timelock contract, which can update parameters directly + */ + address public owner; + + /** + * @notice The approximate number of blocks per year that is assumed by the interest rate model + */ + uint256 public constant blocksPerYear = 2102400; + + /** + * @notice The minimum roof value used for calculating borrow rate. + */ + uint256 internal constant minRoofValue = 1e18; + + /** + * @notice The multiplier of utilization rate that gives the slope of the interest rate + */ + uint256 public multiplierPerBlock; + + /** + * @notice The base interest rate which is the y-intercept when utilization rate is 0 + */ + uint256 public baseRatePerBlock; + + /** + * @notice The multiplierPerBlock after hitting a specified utilization point + */ + uint256 public jumpMultiplierPerBlock; + + /** + * @notice The utilization point at which the interest rate is fixed + */ + uint256 public kink1; + + /** + * @notice The utilization point at which the jump multiplier is applied + */ + uint256 public kink2; + + /** + * @notice The utilization point at which the rate is fixed + */ + uint256 public roof; + + /** + * @notice Construct an interest rate model + * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18) + * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) + * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point + * @param kink1_ The utilization point at which the interest rate is fixed + * @param kink2_ The utilization point at which the jump multiplier is applied + * @param roof_ The utilization point at which the borrow rate is fixed + * @param owner_ The address of the owner, i.e. the Timelock contract (which has the ability to update parameters directly) + */ + constructor( + uint256 baseRatePerYear, + uint256 multiplierPerYear, + uint256 jumpMultiplierPerYear, + uint256 kink1_, + uint256 kink2_, + uint256 roof_, + address owner_ + ) public { + owner = owner_; + + updateTripleRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink1_, kink2_, roof_); + } + + /** + * @notice Update the parameters of the interest rate model (only callable by owner, i.e. Timelock) + * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18) + * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) + * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point + * @param kink1_ The utilization point at which the interest rate is fixed + * @param kink2_ The utilization point at which the jump multiplier is applied + * @param roof_ The utilization point at which the borrow rate is fixed + */ + function updateTripleRateModel( + uint256 baseRatePerYear, + uint256 multiplierPerYear, + uint256 jumpMultiplierPerYear, + uint256 kink1_, + uint256 kink2_, + uint256 roof_ + ) external { + require(msg.sender == owner, "only the owner may call this function."); + + updateTripleRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink1_, kink2_, roof_); + } + + /** + * @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)` + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market (currently unused) + * @return The utilization rate as a mantissa between [0, 1e18] + */ + function utilizationRate( + uint256 cash, + uint256 borrows, + uint256 reserves + ) public view returns (uint256) { + // Utilization rate is 0 when there are no borrows + if (borrows == 0) { + return 0; + } + + uint256 util = borrows.mul(1e18).div(cash.add(borrows).sub(reserves)); + // If the utilization is above the roof, cap it. + if (util > roof) { + util = roof; + } + return util; + } + + /** + * @notice Calculates the current borrow rate per block, with the error code expected by the market + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @return The borrow rate percentage per block as a mantissa (scaled by 1e18) + */ + function getBorrowRate( + uint256 cash, + uint256 borrows, + uint256 reserves + ) public view returns (uint256) { + uint256 util = utilizationRate(cash, borrows, reserves); + + if (util <= kink1) { + return util.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); + } else if (util <= kink2) { + return kink1.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); + } else { + uint256 normalRate = kink1.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); + uint256 excessUtil = util.sub(kink2); + return excessUtil.mul(jumpMultiplierPerBlock).div(1e18).add(normalRate); + } + } + + /** + * @notice Calculates the current supply rate per block + * @param cash The amount of cash in the market + * @param borrows The amount of borrows in the market + * @param reserves The amount of reserves in the market + * @param reserveFactorMantissa The current reserve factor for the market + * @return The supply rate percentage per block as a mantissa (scaled by 1e18) + */ + function getSupplyRate( + uint256 cash, + uint256 borrows, + uint256 reserves, + uint256 reserveFactorMantissa + ) public view returns (uint256) { + uint256 oneMinusReserveFactor = uint256(1e18).sub(reserveFactorMantissa); + uint256 borrowRate = getBorrowRate(cash, borrows, reserves); + uint256 rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18); + return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18); + } + + /** + * @notice Internal function to update the parameters of the interest rate model + * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18) + * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) + * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point + * @param kink1_ The utilization point at which the interest rate is fixed + * @param kink2_ The utilization point at which the jump multiplier is applied + * @param roof_ The utilization point at which the borrow rate is fixed + */ + function updateTripleRateModelInternal( + uint256 baseRatePerYear, + uint256 multiplierPerYear, + uint256 jumpMultiplierPerYear, + uint256 kink1_, + uint256 kink2_, + uint256 roof_ + ) internal { + require(kink1_ <= kink2_, "kink1 must less than or equal to kink2"); + require(roof_ >= minRoofValue, "invalid roof value"); + + baseRatePerBlock = baseRatePerYear.div(blocksPerYear); + multiplierPerBlock = (multiplierPerYear.mul(1e18)).div(blocksPerYear.mul(kink1_)); + jumpMultiplierPerBlock = jumpMultiplierPerYear.div(blocksPerYear); + kink1 = kink1_; + kink2 = kink2_; + roof = roof_; + + emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink1, kink2, roof); + } +} diff --git a/contracts/Unitroller.sol b/contracts/Unitroller.sol index 9476586..451b640 100644 --- a/contracts/Unitroller.sol +++ b/contracts/Unitroller.sol @@ -2,31 +2,31 @@ pragma solidity ^0.5.16; import "./ErrorReporter.sol"; import "./ComptrollerStorage.sol"; + /** * @title ComptrollerCore * @dev Storage for the comptroller is at this address, while execution is delegated to the `comptrollerImplementation`. * CTokens should reference this contract as their comptroller. */ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { - /** - * @notice Emitted when pendingComptrollerImplementation is changed - */ + * @notice Emitted when pendingComptrollerImplementation is changed + */ event NewPendingImplementation(address oldPendingImplementation, address newPendingImplementation); /** - * @notice Emitted when pendingComptrollerImplementation is accepted, which means comptroller implementation is updated - */ + * @notice Emitted when pendingComptrollerImplementation is accepted, which means comptroller implementation is updated + */ event NewImplementation(address oldImplementation, address newImplementation); /** - * @notice Emitted when pendingAdmin is changed - */ + * @notice Emitted when pendingAdmin is changed + */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** - * @notice Emitted when pendingAdmin is accepted, which means admin is updated - */ + * @notice Emitted when pendingAdmin is accepted, which means admin is updated + */ event NewAdmin(address oldAdmin, address newAdmin); constructor() public { @@ -35,8 +35,7 @@ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { } /*** Admin Functions ***/ - function _setPendingImplementation(address newPendingImplementation) public returns (uint) { - + function _setPendingImplementation(address newPendingImplementation) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_IMPLEMENTATION_OWNER_CHECK); } @@ -47,15 +46,15 @@ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { emit NewPendingImplementation(oldPendingImplementation, pendingComptrollerImplementation); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Accepts new implementation of comptroller. msg.sender must be pendingImplementation - * @dev Admin function for new implementation to accept it's role as implementation - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _acceptImplementation() public returns (uint) { + * @notice Accepts new implementation of comptroller. msg.sender must be pendingImplementation + * @dev Admin function for new implementation to accept it's role as implementation + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _acceptImplementation() public returns (uint256) { // Check caller is pendingImplementation and pendingImplementation ≠ address(0) if (msg.sender != pendingComptrollerImplementation || pendingComptrollerImplementation == address(0)) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK); @@ -72,17 +71,16 @@ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { emit NewImplementation(oldImplementation, comptrollerImplementation); emit NewPendingImplementation(oldPendingImplementation, pendingComptrollerImplementation); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } - /** - * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @param newPendingAdmin New pending admin. - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _setPendingAdmin(address newPendingAdmin) public returns (uint) { + * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @param newPendingAdmin New pending admin. + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPendingAdmin(address newPendingAdmin) public returns (uint256) { // Check caller = admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); @@ -97,15 +95,15 @@ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin - * @dev Admin function for pending admin to accept role and update admin - * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) - */ - function _acceptAdmin() public returns (uint) { + * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + * @dev Admin function for pending admin to accept role and update admin + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _acceptAdmin() public returns (uint256) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin || msg.sender == address(0)) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); @@ -124,7 +122,7 @@ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** @@ -132,17 +130,21 @@ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { * It returns to the external caller whatever the implementation returns * or forwards reverts. */ - function () payable external { + function() external payable { // delegate all other functions to current implementation (bool success, ) = comptrollerImplementation.delegatecall(msg.data); assembly { - let free_mem_ptr := mload(0x40) - returndatacopy(free_mem_ptr, 0, returndatasize) - - switch success - case 0 { revert(free_mem_ptr, returndatasize) } - default { return(free_mem_ptr, returndatasize) } + let free_mem_ptr := mload(0x40) + returndatacopy(free_mem_ptr, 0, returndatasize) + + switch success + case 0 { + revert(free_mem_ptr, returndatasize) + } + default { + return(free_mem_ptr, returndatasize) + } } } } diff --git a/contracts/v1PriceOracle.sol b/contracts/v1PriceOracle.sol index 93c7aa9..5267b95 100644 --- a/contracts/v1PriceOracle.sol +++ b/contracts/v1PriceOracle.sol @@ -1,11 +1,11 @@ pragma solidity ^0.5.16; -contract ErrorReporter { +contract ErrorReporter { /** - * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary - * contract-specific code that enables us to report opaque error codes from upgradeable contracts. - **/ - event Failure(uint error, uint info, uint detail); + * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary + * contract-specific code that enables us to report opaque error codes from upgradeable contracts. + **/ + event Failure(uint256 error, uint256 info, uint256 detail); enum Error { NO_ERROR, @@ -122,38 +122,35 @@ contract ErrorReporter { WITHDRAW_TRANSFER_OUT_NOT_POSSIBLE } - /** - * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator - */ - function fail(Error err, FailureInfo info) internal returns (uint) { - emit Failure(uint(err), uint(info), 0); + * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator + */ + function fail(Error err, FailureInfo info) internal returns (uint256) { + emit Failure(uint256(err), uint256(info), 0); - return uint(err); + return uint256(err); } - /** - * @dev use this when reporting an opaque error from an upgradeable collaborator contract - */ - function failOpaque(FailureInfo info, uint opaqueError) internal returns (uint) { - emit Failure(uint(Error.OPAQUE_ERROR), uint(info), opaqueError); + * @dev use this when reporting an opaque error from an upgradeable collaborator contract + */ + function failOpaque(FailureInfo info, uint256 opaqueError) internal returns (uint256) { + emit Failure(uint256(Error.OPAQUE_ERROR), uint256(info), opaqueError); - return uint(Error.OPAQUE_ERROR); + return uint256(Error.OPAQUE_ERROR); } - } -contract CarefulMath is ErrorReporter { +contract CarefulMath is ErrorReporter { /** - * @dev Multiplies two numbers, returns an error on overflow. - */ - function mul(uint a, uint b) internal pure returns (Error, uint) { + * @dev Multiplies two numbers, returns an error on overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (Error, uint256) { if (a == 0) { return (Error.NO_ERROR, 0); } - uint c = a * b; + uint256 c = a * b; if (c / a != b) { return (Error.INTEGER_OVERFLOW, 0); @@ -163,9 +160,9 @@ contract CarefulMath is ErrorReporter { } /** - * @dev Integer division of two numbers, truncating the quotient. - */ - function div(uint a, uint b) internal pure returns (Error, uint) { + * @dev Integer division of two numbers, truncating the quotient. + */ + function div(uint256 a, uint256 b) internal pure returns (Error, uint256) { if (b == 0) { return (Error.DIVISION_BY_ZERO, 0); } @@ -174,9 +171,9 @@ contract CarefulMath is ErrorReporter { } /** - * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). - */ - function sub(uint a, uint b) internal pure returns (Error, uint) { + * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint256 a, uint256 b) internal pure returns (Error, uint256) { if (b <= a) { return (Error.NO_ERROR, a - b); } else { @@ -185,10 +182,10 @@ contract CarefulMath is ErrorReporter { } /** - * @dev Adds two numbers, returns an error on overflow. - */ - function add(uint a, uint b) internal pure returns (Error, uint) { - uint c = a + b; + * @dev Adds two numbers, returns an error on overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (Error, uint256) { + uint256 c = a + b; if (c >= a) { return (Error.NO_ERROR, c); @@ -198,10 +195,14 @@ contract CarefulMath is ErrorReporter { } /** - * @dev add a and b and then subtract c - */ - function addThenSub(uint a, uint b, uint c) internal pure returns (Error, uint) { - (Error err0, uint sum) = add(a, b); + * @dev add a and b and then subtract c + */ + function addThenSub( + uint256 a, + uint256 b, + uint256 c + ) internal pure returns (Error, uint256) { + (Error err0, uint256 sum) = add(a, b); if (err0 != Error.NO_ERROR) { return (err0, 0); @@ -210,35 +211,35 @@ contract CarefulMath is ErrorReporter { return sub(sum, c); } } -contract Exponential is ErrorReporter, CarefulMath { +contract Exponential is ErrorReporter, CarefulMath { // TODO: We may wish to put the result of 10**18 here instead of the expression. // Per https://solidity.readthedocs.io/en/latest/contracts.html#constant-state-variables // the optimizer MAY replace the expression 10**18 with its calculated value. - uint constant expScale = 10**18; + uint256 constant expScale = 10**18; // See TODO on expScale - uint constant halfExpScale = expScale/2; + uint256 constant halfExpScale = expScale / 2; struct Exp { - uint mantissa; + uint256 mantissa; } - uint constant mantissaOne = 10**18; - uint constant mantissaOneTenth = 10**17; + uint256 constant mantissaOne = 10**18; + uint256 constant mantissaOneTenth = 10**17; /** - * @dev Creates an exponential from numerator and denominator values. - * Note: Returns an error if (`num` * 10e18) > MAX_INT, - * or if `denom` is zero. - */ - function getExp(uint num, uint denom) pure internal returns (Error, Exp memory) { - (Error err0, uint scaledNumerator) = mul(num, expScale); + * @dev Creates an exponential from numerator and denominator values. + * Note: Returns an error if (`num` * 10e18) > MAX_INT, + * or if `denom` is zero. + */ + function getExp(uint256 num, uint256 denom) internal pure returns (Error, Exp memory) { + (Error err0, uint256 scaledNumerator) = mul(num, expScale); if (err0 != Error.NO_ERROR) { return (err0, Exp({mantissa: 0})); } - (Error err1, uint rational) = div(scaledNumerator, denom); + (Error err1, uint256 rational) = div(scaledNumerator, denom); if (err1 != Error.NO_ERROR) { return (err1, Exp({mantissa: 0})); } @@ -247,28 +248,28 @@ contract Exponential is ErrorReporter, CarefulMath { } /** - * @dev Adds two exponentials, returning a new exponential. - */ - function addExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) { - (Error error, uint result) = add(a.mantissa, b.mantissa); + * @dev Adds two exponentials, returning a new exponential. + */ + function addExp(Exp memory a, Exp memory b) internal pure returns (Error, Exp memory) { + (Error error, uint256 result) = add(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } /** - * @dev Subtracts two exponentials, returning a new exponential. - */ - function subExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) { - (Error error, uint result) = sub(a.mantissa, b.mantissa); + * @dev Subtracts two exponentials, returning a new exponential. + */ + function subExp(Exp memory a, Exp memory b) internal pure returns (Error, Exp memory) { + (Error error, uint256 result) = sub(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } /** - * @dev Multiply an Exp by a scalar, returning a new Exp. - */ - function mulScalar(Exp memory a, uint scalar) pure internal returns (Error, Exp memory) { - (Error err0, uint scaledMantissa) = mul(a.mantissa, scalar); + * @dev Multiply an Exp by a scalar, returning a new Exp. + */ + function mulScalar(Exp memory a, uint256 scalar) internal pure returns (Error, Exp memory) { + (Error err0, uint256 scaledMantissa) = mul(a.mantissa, scalar); if (err0 != Error.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -277,10 +278,10 @@ contract Exponential is ErrorReporter, CarefulMath { } /** - * @dev Divide an Exp by a scalar, returning a new Exp. - */ - function divScalar(Exp memory a, uint scalar) pure internal returns (Error, Exp memory) { - (Error err0, uint descaledMantissa) = div(a.mantissa, scalar); + * @dev Divide an Exp by a scalar, returning a new Exp. + */ + function divScalar(Exp memory a, uint256 scalar) internal pure returns (Error, Exp memory) { + (Error err0, uint256 descaledMantissa) = div(a.mantissa, scalar); if (err0 != Error.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -289,9 +290,9 @@ contract Exponential is ErrorReporter, CarefulMath { } /** - * @dev Divide a scalar by an Exp, returning a new Exp. - */ - function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (Error, Exp memory) { + * @dev Divide a scalar by an Exp, returning a new Exp. + */ + function divScalarByExp(uint256 scalar, Exp memory divisor) internal pure returns (Error, Exp memory) { /* We are doing this as: getExp(mul(expScale, scalar), divisor.mantissa) @@ -301,7 +302,7 @@ contract Exponential is ErrorReporter, CarefulMath { Scalar = s; `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` */ - (Error err0, uint numerator) = mul(expScale, scalar); + (Error err0, uint256 numerator) = mul(expScale, scalar); if (err0 != Error.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -309,11 +310,10 @@ contract Exponential is ErrorReporter, CarefulMath { } /** - * @dev Multiplies two exponentials, returning a new exponential. - */ - function mulExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) { - - (Error err0, uint doubleScaledProduct) = mul(a.mantissa, b.mantissa); + * @dev Multiplies two exponentials, returning a new exponential. + */ + function mulExp(Exp memory a, Exp memory b) internal pure returns (Error, Exp memory) { + (Error err0, uint256 doubleScaledProduct) = mul(a.mantissa, b.mantissa); if (err0 != Error.NO_ERROR) { return (err0, Exp({mantissa: 0})); } @@ -321,12 +321,12 @@ contract Exponential is ErrorReporter, CarefulMath { // We add half the scale before dividing so that we get rounding instead of truncation. // See "Listing 6" and text above it at https://accu.org/index.php/journals/1717 // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18. - (Error err1, uint doubleScaledProductWithHalfScale) = add(halfExpScale, doubleScaledProduct); + (Error err1, uint256 doubleScaledProductWithHalfScale) = add(halfExpScale, doubleScaledProduct); if (err1 != Error.NO_ERROR) { return (err1, Exp({mantissa: 0})); } - (Error err2, uint product) = div(doubleScaledProductWithHalfScale, expScale); + (Error err2, uint256 product) = div(doubleScaledProductWithHalfScale, expScale); // The only error `div` can return is Error.DIVISION_BY_ZERO but we control `expScale` and it is not zero. assert(err2 == Error.NO_ERROR); @@ -334,81 +334,81 @@ contract Exponential is ErrorReporter, CarefulMath { } /** - * @dev Divides two exponentials, returning a new exponential. - * (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b, - * which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa) - */ - function divExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) { + * @dev Divides two exponentials, returning a new exponential. + * (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b, + * which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa) + */ + function divExp(Exp memory a, Exp memory b) internal pure returns (Error, Exp memory) { return getExp(a.mantissa, b.mantissa); } /** - * @dev Truncates the given exp to a whole number value. - * For example, truncate(Exp{mantissa: 15 * (10**18)}) = 15 - */ - function truncate(Exp memory exp) pure internal returns (uint) { + * @dev Truncates the given exp to a whole number value. + * For example, truncate(Exp{mantissa: 15 * (10**18)}) = 15 + */ + function truncate(Exp memory exp) internal pure returns (uint256) { // Note: We are not using careful math here as we're performing a division that cannot fail return exp.mantissa / 10**18; } /** - * @dev Checks if first Exp is less than second Exp. - */ - function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { + * @dev Checks if first Exp is less than second Exp. + */ + function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa < right.mantissa; //TODO: Add some simple tests and this in another PR yo. } /** - * @dev Checks if left Exp <= right Exp. - */ - function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) { + * @dev Checks if left Exp <= right Exp. + */ + function lessThanOrEqualExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa <= right.mantissa; } /** - * @dev Checks if first Exp is greater than second Exp. - */ - function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { + * @dev Checks if first Exp is greater than second Exp. + */ + function greaterThanExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa > right.mantissa; } /** - * @dev returns true if Exp is exactly zero - */ - function isZeroExp(Exp memory value) pure internal returns (bool) { + * @dev returns true if Exp is exactly zero + */ + function isZeroExp(Exp memory value) internal pure returns (bool) { return value.mantissa == 0; } } -contract PriceOracle is Exponential { +contract PriceOracle is Exponential { /** - * @dev flag for whether or not contract is paused - * - */ + * @dev flag for whether or not contract is paused + * + */ bool public paused; - uint public constant numBlocksPerPeriod = 240; // approximately 1 hour: 60 seconds/minute * 60 minutes/hour * 1 block/15 seconds + uint256 public constant numBlocksPerPeriod = 240; // approximately 1 hour: 60 seconds/minute * 60 minutes/hour * 1 block/15 seconds - uint public constant maxSwingMantissa = (10 ** 17); // 0.1 + uint256 public constant maxSwingMantissa = (10**17); // 0.1 /** - * @dev Mapping of asset addresses and their corresponding price in terms of Eth-Wei - * which is simply equal to AssetWeiPrice * 10e18. For instance, if OMG token was - * worth 5x Eth then the price for OMG would be 5*10e18 or Exp({mantissa: 5000000000000000000}). - * map: assetAddress -> Exp - */ + * @dev Mapping of asset addresses and their corresponding price in terms of Eth-Wei + * which is simply equal to AssetWeiPrice * 10e18. For instance, if OMG token was + * worth 5x Eth then the price for OMG would be 5*10e18 or Exp({mantissa: 5000000000000000000}). + * map: assetAddress -> Exp + */ mapping(address => Exp) public _assetPrices; constructor(address _poster) public { anchorAdmin = msg.sender; poster = _poster; - maxSwing = Exp({mantissa : maxSwingMantissa}); + maxSwing = Exp({mantissa: maxSwingMantissa}); } /** - * @notice Do not pay into PriceOracle - */ - function() payable external { + * @notice Do not pay into PriceOracle + */ + function() external payable { revert(); } @@ -433,132 +433,150 @@ contract PriceOracle is Exponential { } /** - * @dev `msgSender` is msg.sender; `error` corresponds to enum OracleError; `info` corresponds to enum OracleFailureInfo, and `detail` is an arbitrary - * contract-specific code that enables us to report opaque error codes from upgradeable contracts. - **/ - event OracleFailure(address msgSender, address asset, uint error, uint info, uint detail); + * @dev `msgSender` is msg.sender; `error` corresponds to enum OracleError; `info` corresponds to enum OracleFailureInfo, and `detail` is an arbitrary + * contract-specific code that enables us to report opaque error codes from upgradeable contracts. + **/ + event OracleFailure(address msgSender, address asset, uint256 error, uint256 info, uint256 detail); /** - * @dev use this when reporting a known error from the price oracle or a non-upgradeable collaborator - * Using Oracle in name because we already inherit a `fail` function from ErrorReporter.sol via Exponential.sol - */ - function failOracle(address asset, OracleError err, OracleFailureInfo info) internal returns (uint) { - emit OracleFailure(msg.sender, asset, uint(err), uint(info), 0); + * @dev use this when reporting a known error from the price oracle or a non-upgradeable collaborator + * Using Oracle in name because we already inherit a `fail` function from ErrorReporter.sol via Exponential.sol + */ + function failOracle( + address asset, + OracleError err, + OracleFailureInfo info + ) internal returns (uint256) { + emit OracleFailure(msg.sender, asset, uint256(err), uint256(info), 0); - return uint(err); + return uint256(err); } /** - * @dev Use this when reporting an error from the money market. Give the money market result as `details` - */ - function failOracleWithDetails(address asset, OracleError err, OracleFailureInfo info, uint details) internal returns (uint) { - emit OracleFailure(msg.sender, asset, uint(err), uint(info), details); + * @dev Use this when reporting an error from the money market. Give the money market result as `details` + */ + function failOracleWithDetails( + address asset, + OracleError err, + OracleFailureInfo info, + uint256 details + ) internal returns (uint256) { + emit OracleFailure(msg.sender, asset, uint256(err), uint256(info), details); - return uint(err); + return uint256(err); } /** - * @dev An administrator who can set the pending anchor value for assets. - * Set in the constructor. - */ + * @dev An administrator who can set the pending anchor value for assets. + * Set in the constructor. + */ address public anchorAdmin; /** - * @dev pending anchor administrator for this contract. - */ + * @dev pending anchor administrator for this contract. + */ address public pendingAnchorAdmin; /** - * @dev Address of the price poster. - * Set in the constructor. - */ + * @dev Address of the price poster. + * Set in the constructor. + */ address public poster; /** - * @dev maxSwing the maximum allowed percentage difference between a new price and the anchor's price - * Set only in the constructor - */ + * @dev maxSwing the maximum allowed percentage difference between a new price and the anchor's price + * Set only in the constructor + */ Exp public maxSwing; struct Anchor { // floor(block.number / numBlocksPerPeriod) + 1 - uint period; - + uint256 period; // Price in ETH, scaled by 10**18 - uint priceMantissa; + uint256 priceMantissa; } /** - * @dev anchors by asset - */ + * @dev anchors by asset + */ mapping(address => Anchor) public anchors; /** - * @dev pending anchor prices by asset - */ - mapping(address => uint) public pendingAnchors; + * @dev pending anchor prices by asset + */ + mapping(address => uint256) public pendingAnchors; /** - * @dev emitted when a pending anchor is set - * @param asset Asset for which to set a pending anchor - * @param oldScaledPrice if an unused pending anchor was present, its value; otherwise 0. - * @param newScaledPrice the new scaled pending anchor price - */ - event NewPendingAnchor(address anchorAdmin, address asset, uint oldScaledPrice, uint newScaledPrice); + * @dev emitted when a pending anchor is set + * @param asset Asset for which to set a pending anchor + * @param oldScaledPrice if an unused pending anchor was present, its value; otherwise 0. + * @param newScaledPrice the new scaled pending anchor price + */ + event NewPendingAnchor(address anchorAdmin, address asset, uint256 oldScaledPrice, uint256 newScaledPrice); /** - * @notice provides ability to override the anchor price for an asset - * @dev Admin function to set the anchor price for an asset - * @param asset Asset for which to override the anchor price - * @param newScaledPrice New anchor price - * @return uint 0=success, otherwise a failure (see enum OracleError for details) - */ - function _setPendingAnchor(address asset, uint newScaledPrice) public returns (uint) { + * @notice provides ability to override the anchor price for an asset + * @dev Admin function to set the anchor price for an asset + * @param asset Asset for which to override the anchor price + * @param newScaledPrice New anchor price + * @return uint 0=success, otherwise a failure (see enum OracleError for details) + */ + function _setPendingAnchor(address asset, uint256 newScaledPrice) public returns (uint256) { // Check caller = anchorAdmin. Note: Deliberately not allowing admin. They can just change anchorAdmin if desired. if (msg.sender != anchorAdmin) { return failOracle(asset, OracleError.UNAUTHORIZED, OracleFailureInfo.SET_PENDING_ANCHOR_PERMISSION_CHECK); } - uint oldScaledPrice = pendingAnchors[asset]; + uint256 oldScaledPrice = pendingAnchors[asset]; pendingAnchors[asset] = newScaledPrice; emit NewPendingAnchor(msg.sender, asset, oldScaledPrice, newScaledPrice); - return uint(OracleError.NO_ERROR); + return uint256(OracleError.NO_ERROR); } /** - * @dev emitted for all price changes - */ - event PricePosted(address asset, uint previousPriceMantissa, uint requestedPriceMantissa, uint newPriceMantissa); + * @dev emitted for all price changes + */ + event PricePosted( + address asset, + uint256 previousPriceMantissa, + uint256 requestedPriceMantissa, + uint256 newPriceMantissa + ); /** - * @dev emitted if this contract successfully posts a capped-to-max price to the money market - */ - event CappedPricePosted(address asset, uint requestedPriceMantissa, uint anchorPriceMantissa, uint cappedPriceMantissa); + * @dev emitted if this contract successfully posts a capped-to-max price to the money market + */ + event CappedPricePosted( + address asset, + uint256 requestedPriceMantissa, + uint256 anchorPriceMantissa, + uint256 cappedPriceMantissa + ); /** - * @dev emitted when admin either pauses or resumes the contract; newState is the resulting state - */ + * @dev emitted when admin either pauses or resumes the contract; newState is the resulting state + */ event SetPaused(bool newState); /** - * @dev emitted when pendingAnchorAdmin is changed - */ + * @dev emitted when pendingAnchorAdmin is changed + */ event NewPendingAnchorAdmin(address oldPendingAnchorAdmin, address newPendingAnchorAdmin); /** - * @dev emitted when pendingAnchorAdmin is accepted, which means anchor admin is updated - */ + * @dev emitted when pendingAnchorAdmin is accepted, which means anchor admin is updated + */ event NewAnchorAdmin(address oldAnchorAdmin, address newAnchorAdmin); /** - * @notice set `paused` to the specified state - * @dev Admin function to pause or resume the market - * @param requestedState value to assign to `paused` - * @return uint 0=success, otherwise a failure - */ - function _setPaused(bool requestedState) public returns (uint) { + * @notice set `paused` to the specified state + * @dev Admin function to pause or resume the market + * @param requestedState value to assign to `paused` + * @return uint 0=success, otherwise a failure + */ + function _setPaused(bool requestedState) public returns (uint256) { // Check caller = anchorAdmin if (msg.sender != anchorAdmin) { return failOracle(address(0), OracleError.UNAUTHORIZED, OracleFailureInfo.SET_PAUSED_OWNER_CHECK); @@ -567,21 +585,26 @@ contract PriceOracle is Exponential { paused = requestedState; emit SetPaused(requestedState); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Begins transfer of anchor admin rights. The newPendingAnchorAdmin must call `_acceptAnchorAdmin` to finalize the transfer. - * @dev Admin function to begin change of anchor admin. The newPendingAnchorAdmin must call `_acceptAnchorAdmin` to finalize the transfer. - * @param newPendingAnchorAdmin New pending anchor admin. - * @return uint 0=success, otherwise a failure - * - * TODO: Should we add a second arg to verify, like a checksum of `newAnchorAdmin` address? - */ - function _setPendingAnchorAdmin(address newPendingAnchorAdmin) public returns (uint) { + * @notice Begins transfer of anchor admin rights. The newPendingAnchorAdmin must call `_acceptAnchorAdmin` to finalize the transfer. + * @dev Admin function to begin change of anchor admin. The newPendingAnchorAdmin must call `_acceptAnchorAdmin` to finalize the transfer. + * @param newPendingAnchorAdmin New pending anchor admin. + * @return uint 0=success, otherwise a failure + * + * TODO: Should we add a second arg to verify, like a checksum of `newAnchorAdmin` address? + */ + function _setPendingAnchorAdmin(address newPendingAnchorAdmin) public returns (uint256) { // Check caller = anchorAdmin if (msg.sender != anchorAdmin) { - return failOracle(address(0), OracleError.UNAUTHORIZED, OracleFailureInfo.SET_PENDING_ANCHOR_ADMIN_OWNER_CHECK); + return + failOracle( + address(0), + OracleError.UNAUTHORIZED, + OracleFailureInfo.SET_PENDING_ANCHOR_ADMIN_OWNER_CHECK + ); } // save current value, if any, for inclusion in log @@ -591,19 +614,24 @@ contract PriceOracle is Exponential { emit NewPendingAnchorAdmin(oldPendingAnchorAdmin, newPendingAnchorAdmin); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice Accepts transfer of anchor admin rights. msg.sender must be pendingAnchorAdmin - * @dev Admin function for pending anchor admin to accept role and update anchor admin - * @return uint 0=success, otherwise a failure - */ - function _acceptAnchorAdmin() public returns (uint) { + * @notice Accepts transfer of anchor admin rights. msg.sender must be pendingAnchorAdmin + * @dev Admin function for pending anchor admin to accept role and update anchor admin + * @return uint 0=success, otherwise a failure + */ + function _acceptAnchorAdmin() public returns (uint256) { // Check caller = pendingAnchorAdmin // msg.sender can't be zero if (msg.sender != pendingAnchorAdmin) { - return failOracle(address(0), OracleError.UNAUTHORIZED, OracleFailureInfo.ACCEPT_ANCHOR_ADMIN_PENDING_ANCHOR_ADMIN_CHECK); + return + failOracle( + address(0), + OracleError.UNAUTHORIZED, + OracleFailureInfo.ACCEPT_ANCHOR_ADMIN_PENDING_ANCHOR_ADMIN_CHECK + ); } // Save current value for inclusion in log @@ -615,16 +643,16 @@ contract PriceOracle is Exponential { emit NewAnchorAdmin(oldAnchorAdmin, msg.sender); - return uint(Error.NO_ERROR); + return uint256(Error.NO_ERROR); } /** - * @notice retrieves price of an asset - * @dev function to get price for an asset - * @param asset Asset for which to get the price - * @return uint mantissa of asset price (scaled by 1e18) or zero if unset or contract paused - */ - function assetPrices(address asset) public view returns (uint) { + * @notice retrieves price of an asset + * @dev function to get price for an asset + * @param asset Asset for which to get the price + * @return uint mantissa of asset price (scaled by 1e18) or zero if unset or contract paused + */ + function assetPrices(address asset) public view returns (uint256) { // Note: zero is treated by the money market as an invalid // price and will cease operations with that asset // when zero. @@ -641,12 +669,12 @@ contract PriceOracle is Exponential { } /** - * @notice retrieves price of an asset - * @dev function to get price for an asset - * @param asset Asset for which to get the price - * @return uint mantissa of asset price (scaled by 1e18) or zero if unset or contract paused - */ - function getPrice(address asset) public view returns (uint) { + * @notice retrieves price of an asset + * @dev function to get price for an asset + * @param asset Asset for which to get the price + * @return uint mantissa of asset price (scaled by 1e18) or zero if unset or contract paused + */ + function getPrice(address asset) public view returns (uint256) { return assetPrices(asset); } @@ -654,21 +682,21 @@ contract PriceOracle is Exponential { Exp price; Exp swing; Exp anchorPrice; - uint anchorPeriod; - uint currentPeriod; + uint256 anchorPeriod; + uint256 currentPeriod; bool priceCapped; - uint cappingAnchorPriceMantissa; - uint pendingAnchorMantissa; + uint256 cappingAnchorPriceMantissa; + uint256 pendingAnchorMantissa; } /** - * @notice entry point for updating prices - * @dev function to set price for an asset - * @param asset Asset for which to set the price - * @param requestedPriceMantissa requested new price, scaled by 10**18 - * @return uint 0=success, otherwise a failure (see enum OracleError for details) - */ - function setPrice(address asset, uint requestedPriceMantissa) public returns (uint) { + * @notice entry point for updating prices + * @dev function to set price for an asset + * @param asset Asset for which to set the price + * @param requestedPriceMantissa requested new price, scaled by 10**18 + * @return uint 0=success, otherwise a failure (see enum OracleError for details) + */ + function setPrice(address asset, uint256 requestedPriceMantissa) public returns (uint256) { // Fail when msg.sender is not poster if (msg.sender != poster) { return failOracle(asset, OracleError.UNAUTHORIZED, OracleFailureInfo.SET_PRICE_PERMISSION_CHECK); @@ -677,7 +705,7 @@ contract PriceOracle is Exponential { return setPriceInternal(asset, requestedPriceMantissa); } - function setPriceInternal(address asset, uint requestedPriceMantissa) internal returns (uint) { + function setPriceInternal(address asset, uint256 requestedPriceMantissa) internal returns (uint256) { // re-used for intermediate errors Error err; SetPriceLocalVars memory localVars; @@ -685,31 +713,49 @@ contract PriceOracle is Exponential { // (It can be a problem in tests with low block numbers.) localVars.currentPeriod = (block.number / numBlocksPerPeriod) + 1; localVars.pendingAnchorMantissa = pendingAnchors[asset]; - localVars.price = Exp({mantissa : requestedPriceMantissa}); + localVars.price = Exp({mantissa: requestedPriceMantissa}); if (localVars.pendingAnchorMantissa != 0) { // let's explicitly set to 0 rather than relying on default of declaration localVars.anchorPeriod = 0; - localVars.anchorPrice = Exp({mantissa : localVars.pendingAnchorMantissa}); + localVars.anchorPrice = Exp({mantissa: localVars.pendingAnchorMantissa}); // Verify movement is within max swing of pending anchor (currently: 10%) (err, localVars.swing) = calculateSwing(localVars.anchorPrice, localVars.price); if (err != Error.NO_ERROR) { - return failOracleWithDetails(asset, OracleError.FAILED_TO_SET_PRICE, OracleFailureInfo.SET_PRICE_CALCULATE_SWING, uint(err)); + return + failOracleWithDetails( + asset, + OracleError.FAILED_TO_SET_PRICE, + OracleFailureInfo.SET_PRICE_CALCULATE_SWING, + uint256(err) + ); } // Fail when swing > maxSwing if (greaterThanExp(localVars.swing, maxSwing)) { - return failOracleWithDetails(asset, OracleError.FAILED_TO_SET_PRICE, OracleFailureInfo.SET_PRICE_MAX_SWING_CHECK, localVars.swing.mantissa); + return + failOracleWithDetails( + asset, + OracleError.FAILED_TO_SET_PRICE, + OracleFailureInfo.SET_PRICE_MAX_SWING_CHECK, + localVars.swing.mantissa + ); } } else { localVars.anchorPeriod = anchors[asset].period; - localVars.anchorPrice = Exp({mantissa : anchors[asset].priceMantissa}); + localVars.anchorPrice = Exp({mantissa: anchors[asset].priceMantissa}); if (localVars.anchorPeriod != 0) { (err, localVars.priceCapped, localVars.price) = capToMax(localVars.anchorPrice, localVars.price); if (err != Error.NO_ERROR) { - return failOracleWithDetails(asset, OracleError.FAILED_TO_SET_PRICE, OracleFailureInfo.SET_PRICE_CAP_TO_MAX, uint(err)); + return + failOracleWithDetails( + asset, + OracleError.FAILED_TO_SET_PRICE, + OracleFailureInfo.SET_PRICE_CAP_TO_MAX, + uint256(err) + ); } if (localVars.priceCapped) { // save for use in log @@ -717,7 +763,7 @@ contract PriceOracle is Exponential { } } else { // Setting first price. Accept as is (already assigned above from requestedPriceMantissa) and use as anchor - localVars.anchorPrice = Exp({mantissa : requestedPriceMantissa}); + localVars.anchorPrice = Exp({mantissa: requestedPriceMantissa}); } } @@ -726,7 +772,12 @@ contract PriceOracle is Exponential { // zero price is more likely as the result of bad input from the caller of this function if (isZeroExp(localVars.anchorPrice)) { // If we get here price could also be zero, but it does not seem worthwhile to distinguish the 3rd case - return failOracle(asset, OracleError.FAILED_TO_SET_PRICE, OracleFailureInfo.SET_PRICE_NO_ANCHOR_PRICE_OR_INITIAL_PRICE_ZERO); + return + failOracle( + asset, + OracleError.FAILED_TO_SET_PRICE, + OracleFailureInfo.SET_PRICE_NO_ANCHOR_PRICE_OR_INITIAL_PRICE_ZERO + ); } if (isZeroExp(localVars.price)) { @@ -745,10 +796,10 @@ contract PriceOracle is Exponential { // Set anchors[asset] = (currentPeriod, price) // The new anchor is if we're in a new period or we had a pending anchor, then we become the new anchor if (localVars.currentPeriod > localVars.anchorPeriod) { - anchors[asset] = Anchor({period : localVars.currentPeriod, priceMantissa : localVars.price.mantissa}); + anchors[asset] = Anchor({period: localVars.currentPeriod, priceMantissa: localVars.price.mantissa}); } - uint previousPrice = _assetPrices[asset].mantissa; + uint256 previousPrice = _assetPrices[asset].mantissa; setPriceStorageInternal(asset, localVars.price.mantissa); @@ -756,10 +807,15 @@ contract PriceOracle is Exponential { if (localVars.priceCapped) { // We have set a capped price. Log it so we can detect the situation and investigate. - emit CappedPricePosted(asset, requestedPriceMantissa, localVars.cappingAnchorPriceMantissa, localVars.price.mantissa); + emit CappedPricePosted( + asset, + requestedPriceMantissa, + localVars.cappingAnchorPriceMantissa, + localVars.price.mantissa + ); } - return uint(OracleError.NO_ERROR); + return uint256(OracleError.NO_ERROR); } // As a function to allow harness overrides @@ -768,7 +824,7 @@ contract PriceOracle is Exponential { } // abs(price - anchorPrice) / anchorPrice - function calculateSwing(Exp memory anchorPrice, Exp memory price) pure internal returns (Error, Exp memory) { + function calculateSwing(Exp memory anchorPrice, Exp memory price) internal pure returns (Error, Exp memory) { Exp memory numerator; Error err; @@ -785,8 +841,16 @@ contract PriceOracle is Exponential { return divExp(numerator, anchorPrice); } - function capToMax(Exp memory anchorPrice, Exp memory price) view internal returns (Error, bool, Exp memory) { - Exp memory one = Exp({mantissa : mantissaOne}); + function capToMax(Exp memory anchorPrice, Exp memory price) + internal + view + returns ( + Error, + bool, + Exp memory + ) + { + Exp memory one = Exp({mantissa: mantissaOne}); Exp memory onePlusMaxSwing; Exp memory oneMinusMaxSwing; Exp memory max; @@ -796,13 +860,13 @@ contract PriceOracle is Exponential { (err, onePlusMaxSwing) = addExp(one, maxSwing); if (err != Error.NO_ERROR) { - return (err, false, Exp({mantissa : 0})); + return (err, false, Exp({mantissa: 0})); } // max = anchorPrice * (1 + maxSwing) (err, max) = mulExp(anchorPrice, onePlusMaxSwing); if (err != Error.NO_ERROR) { - return (err, false, Exp({mantissa : 0})); + return (err, false, Exp({mantissa: 0})); } // If price > anchorPrice * (1 + maxSwing) @@ -813,7 +877,7 @@ contract PriceOracle is Exponential { (err, oneMinusMaxSwing) = subExp(one, maxSwing); if (err != Error.NO_ERROR) { - return (err, false, Exp({mantissa : 0})); + return (err, false, Exp({mantissa: 0})); } // min = anchorPrice * (1 - maxSwing) @@ -831,33 +895,40 @@ contract PriceOracle is Exponential { } /** - * @notice entry point for updating multiple prices - * @dev function to set prices for a variable number of assets. - * @param assets a list of up to assets for which to set a price. required: 0 < assets.length == requestedPriceMantissas.length - * @param requestedPriceMantissas requested new prices for the assets, scaled by 10**18. required: 0 < assets.length == requestedPriceMantissas.length - * @return uint values in same order as inputs. For each: 0=success, otherwise a failure (see enum OracleError for details) - */ - function setPrices(address[] memory assets, uint[] memory requestedPriceMantissas) public returns (uint[] memory) { - uint numAssets = assets.length; - uint numPrices = requestedPriceMantissas.length; - uint[] memory result; + * @notice entry point for updating multiple prices + * @dev function to set prices for a variable number of assets. + * @param assets a list of up to assets for which to set a price. required: 0 < assets.length == requestedPriceMantissas.length + * @param requestedPriceMantissas requested new prices for the assets, scaled by 10**18. required: 0 < assets.length == requestedPriceMantissas.length + * @return uint values in same order as inputs. For each: 0=success, otherwise a failure (see enum OracleError for details) + */ + function setPrices(address[] memory assets, uint256[] memory requestedPriceMantissas) + public + returns (uint256[] memory) + { + uint256 numAssets = assets.length; + uint256 numPrices = requestedPriceMantissas.length; + uint256[] memory result; // Fail when msg.sender is not poster if (msg.sender != poster) { - result = new uint[](1); + result = new uint256[](1); result[0] = failOracle(address(0), OracleError.UNAUTHORIZED, OracleFailureInfo.SET_PRICE_PERMISSION_CHECK); return result; } if ((numAssets == 0) || (numPrices != numAssets)) { - result = new uint[](1); - result[0] = failOracle(address(0), OracleError.FAILED_TO_SET_PRICE, OracleFailureInfo.SET_PRICES_PARAM_VALIDATION); + result = new uint256[](1); + result[0] = failOracle( + address(0), + OracleError.FAILED_TO_SET_PRICE, + OracleFailureInfo.SET_PRICES_PARAM_VALIDATION + ); return result; } - result = new uint[](numAssets); + result = new uint256[](numAssets); - for (uint i = 0; i < numAssets; i++) { + for (uint256 i = 0; i < numAssets; i++) { result[i] = setPriceInternal(assets[i], requestedPriceMantissas[i]); } diff --git a/package-lock.json b/package-lock.json index 1352d0c..a5306bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -296,6 +296,133 @@ "postinstall-postinstall": "^2.1.0" } }, + "@ethereumjs/block": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-3.4.0.tgz", + "integrity": "sha512-umKAoTX32yXzErpIksPHodFc/5y8bmZMnOl6hWy5Vd8xId4+HKFUOyEiN16Y97zMwFRysRpcrR6wBejfqc6Bmg==", + "dev": true, + "requires": { + "@ethereumjs/common": "^2.4.0", + "@ethereumjs/tx": "^3.3.0", + "ethereumjs-util": "^7.1.0", + "merkle-patricia-tree": "^4.2.0" + } + }, + "@ethereumjs/blockchain": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/blockchain/-/blockchain-5.4.0.tgz", + "integrity": "sha512-wAuKLaew6PL52kH8YPXO7PbjjKV12jivRSyHQehkESw4slSLLfYA6Jv7n5YxyT2ajD7KNMPVh7oyF/MU6HcOvg==", + "dev": true, + "requires": { + "@ethereumjs/block": "^3.4.0", + "@ethereumjs/common": "^2.4.0", + "@ethereumjs/ethash": "^1.0.0", + "debug": "^2.2.0", + "ethereumjs-util": "^7.1.0", + "level-mem": "^5.0.1", + "lru-cache": "^5.1.1", + "rlp": "^2.2.4", + "semaphore-async-await": "^1.5.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "@ethereumjs/common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.4.0.tgz", + "integrity": "sha512-UdkhFWzWcJCZVsj1O/H8/oqj/0RVYjLc1OhPjBrQdALAkQHpCp8xXI4WLnuGTADqTdJZww0NtgwG+TRPkXt27w==", + "dev": true, + "requires": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.0" + } + }, + "@ethereumjs/ethash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/ethash/-/ethash-1.0.0.tgz", + "integrity": "sha512-iIqnGG6NMKesyOxv2YctB2guOVX18qMAWlj3QlZyrc+GqfzLqoihti+cVNQnyNxr7eYuPdqwLQOFuPe6g/uKjw==", + "dev": true, + "requires": { + "@types/levelup": "^4.3.0", + "buffer-xor": "^2.0.1", + "ethereumjs-util": "^7.0.7", + "miller-rabin": "^4.0.0" + }, + "dependencies": { + "buffer-xor": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", + "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "@ethereumjs/tx": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.0.tgz", + "integrity": "sha512-yTwEj2lVzSMgE6Hjw9Oa1DZks/nKTWM8Wn4ykDNapBPua2f4nXO3qKnni86O6lgDj5fVNRqbDsD0yy7/XNGDEA==", + "dev": true, + "requires": { + "@ethereumjs/common": "^2.4.0", + "ethereumjs-util": "^7.1.0" + } + }, + "@ethereumjs/vm": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/vm/-/vm-5.5.2.tgz", + "integrity": "sha512-AydZ4wfvZAsBuFzs3xVSA2iU0hxhL8anXco3UW3oh9maVC34kTEytOfjHf06LTEfN0MF9LDQ4ciLa7If6ZN/sg==", + "dev": true, + "requires": { + "@ethereumjs/block": "^3.4.0", + "@ethereumjs/blockchain": "^5.4.0", + "@ethereumjs/common": "^2.4.0", + "@ethereumjs/tx": "^3.3.0", + "async-eventemitter": "^0.2.4", + "core-js-pure": "^3.0.1", + "debug": "^2.2.0", + "ethereumjs-util": "^7.1.0", + "functional-red-black-tree": "^1.0.1", + "mcl-wasm": "^0.7.1", + "merkle-patricia-tree": "^4.2.0", + "rustbn.js": "~0.2.0", + "util.promisify": "^1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "@ethersproject/abi": { "version": "5.0.9", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.9.tgz", @@ -719,116 +846,6 @@ "@ethersproject/strings": "^5.0.4" } }, - "@nomiclabs/ethereumjs-vm": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@nomiclabs/ethereumjs-vm/-/ethereumjs-vm-4.2.0.tgz", - "integrity": "sha512-+XwqoO941bILTO4KDLIUJ37U42ySxw6it7jyoi0tKv0/VUcOrWKF1TCQWMv6dBDRlxpPQd273n9o5SVlYYLRWQ==", - "dev": true, - "requires": { - "async": "^2.1.2", - "async-eventemitter": "^0.2.2", - "core-js-pure": "^3.0.1", - "ethereumjs-account": "^3.0.0", - "ethereumjs-block": "^2.2.2", - "ethereumjs-blockchain": "^4.0.3", - "ethereumjs-common": "^1.5.0", - "ethereumjs-tx": "^2.1.2", - "ethereumjs-util": "^6.2.0", - "fake-merkle-patricia-tree": "^1.0.1", - "functional-red-black-tree": "^1.0.1", - "merkle-patricia-tree": "^2.3.2", - "rustbn.js": "~0.2.0", - "safe-buffer": "^5.1.1", - "util.promisify": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "merkle-patricia-tree": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz", - "integrity": "sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==", - "dev": true, - "requires": { - "async": "^1.4.2", - "ethereumjs-util": "^5.0.0", - "level-ws": "0.0.0", - "levelup": "^1.2.1", - "memdown": "^1.0.0", - "readable-stream": "^2.0.0", - "rlp": "^2.0.0", - "semaphore": ">=1.0.1" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - } - } - }, "@nomiclabs/hardhat-ethers": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.1.tgz", @@ -935,51 +952,51 @@ } }, "@sentry/core": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.2.tgz", - "integrity": "sha512-7WYkoxB5IdlNEbwOwqSU64erUKH4laavPsM0/yQ+jojM76ErxlgEF0u//p5WaLPRzh3iDSt6BH+9TL45oNZeZw==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", "dev": true, "requires": { - "@sentry/hub": "5.29.2", - "@sentry/minimal": "5.29.2", - "@sentry/types": "5.29.2", - "@sentry/utils": "5.29.2", + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.2.tgz", - "integrity": "sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", "dev": true, "requires": { - "@sentry/types": "5.29.2", - "@sentry/utils": "5.29.2", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.2.tgz", - "integrity": "sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", "dev": true, "requires": { - "@sentry/hub": "5.29.2", - "@sentry/types": "5.29.2", + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", "tslib": "^1.9.3" } }, "@sentry/node": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.29.2.tgz", - "integrity": "sha512-98m1ZejmJgA+eiz6jEFyYYfp6kJZQnx6d6KrJDMxGfss4YTmmJY57bE4xStnjjk7WINDGzlCiHuk+wJFMBjuoA==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", "dev": true, "requires": { - "@sentry/core": "5.29.2", - "@sentry/hub": "5.29.2", - "@sentry/tracing": "5.29.2", - "@sentry/types": "5.29.2", - "@sentry/utils": "5.29.2", + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", @@ -987,38 +1004,44 @@ } }, "@sentry/tracing": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.29.2.tgz", - "integrity": "sha512-iumYbVRpvoU3BUuIooxibydeaOOjl5ysc+mzsqhRs2NGW/C3uKAsFXdvyNfqt3bxtRQwJEhwJByLP2u3pLThpw==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", "dev": true, "requires": { - "@sentry/hub": "5.29.2", - "@sentry/minimal": "5.29.2", - "@sentry/types": "5.29.2", - "@sentry/utils": "5.29.2", + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.2.tgz", - "integrity": "sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", "dev": true }, "@sentry/utils": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.2.tgz", - "integrity": "sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", "dev": true, "requires": { - "@sentry/types": "5.29.2", + "@sentry/types": "5.30.0", "tslib": "^1.9.3" } }, "@solidity-parser/parser": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.7.1.tgz", - "integrity": "sha512-5ma2uuwPAEX1TPl2rAPAAuGlBkKnn2oUKQvnhTFlDIB8U/KDWX77FpHtL6Rcz+OwqSCWx9IClxACgyIEJ/GhIw==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.11.1.tgz", + "integrity": "sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ==", + "dev": true + }, + "@types/abstract-leveldown": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-5.0.2.tgz", + "integrity": "sha512-+jA1XXF3jsz+Z7FcuiNqgK53hTa/luglT2TyTpKPqoYbxVY+mCPF22Rm+q3KPBrMHJwNXFrTViHszBOfU4vftQ==", "dev": true }, "@types/bn.js": { @@ -1036,10 +1059,27 @@ "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", "dev": true }, + "@types/level-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/level-errors/-/level-errors-3.0.0.tgz", + "integrity": "sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ==", + "dev": true + }, + "@types/levelup": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/levelup/-/levelup-4.3.3.tgz", + "integrity": "sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA==", + "dev": true, + "requires": { + "@types/abstract-leveldown": "*", + "@types/level-errors": "*", + "@types/node": "*" + } + }, "@types/lru-cache": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.0.tgz", - "integrity": "sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", "dev": true }, "@types/mkdirp": { @@ -1090,9 +1130,9 @@ } }, "@types/secp256k1": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.1.tgz", - "integrity": "sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", "dev": true, "requires": { "@types/node": "*" @@ -1155,20 +1195,16 @@ } }, "abstract-leveldown": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", - "integrity": "sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz", + "integrity": "sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==", "dev": true, "requires": { + "buffer": "^5.5.0", + "immediate": "^3.2.3", + "level-concat-iterator": "~2.0.0", + "level-supports": "~1.0.0", "xtend": "~4.0.0" - }, - "dependencies": { - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } } }, "adm-zip": { @@ -1211,12 +1247,12 @@ "dev": true }, "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { - "type-fest": "^0.11.0" + "type-fest": "^0.21.3" } }, "ansi-regex": { @@ -1235,9 +1271,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -1446,15 +1482,15 @@ "dev": true }, "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "blakejs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", - "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==", "dev": true }, "bluebird": { @@ -1545,9 +1581,9 @@ } }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "buffer-to-arraybuffer": { @@ -1586,13 +1622,13 @@ } }, "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dev": true, "requires": { "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" + "get-intrinsic": "^1.0.2" } }, "camelcase": { @@ -1638,29 +1674,20 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, - "checkpoint-store": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", - "integrity": "sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY=", - "dev": true, - "requires": { - "functional-red-black-tree": "^1.0.1" - } - }, "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "ci-info": { @@ -1818,9 +1845,9 @@ "dev": true }, "core-js-pure": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.1.tgz", - "integrity": "sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g==", + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.16.1.tgz", + "integrity": "sha512-TyofCdMzx0KMhi84mVRS8rL1XsRk2SPUNz2azmth53iRN0/08Uim9fdhQTaZTG1LqaXHYVci4RDHka6WrXfnvg==", "dev": true }, "core-util-is": { @@ -1829,6 +1856,16 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "crc-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", + "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", + "dev": true, + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + } + }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -1887,9 +1924,9 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -1926,12 +1963,28 @@ } }, "deferred-leveldown": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", - "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", + "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", "dev": true, "requires": { - "abstract-leveldown": "~2.6.0" + "abstract-leveldown": "~6.2.1", + "inherits": "^2.0.3" + }, + "dependencies": { + "abstract-leveldown": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", + "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "immediate": "^3.2.3", + "level-concat-iterator": "~2.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + } + } } }, "define-properties": { @@ -1941,14 +1994,6 @@ "dev": true, "requires": { "object-keys": "^1.0.12" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } } }, "define-property": { @@ -2048,51 +2093,15 @@ "dev": true }, "encoding-down": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-5.0.4.tgz", - "integrity": "sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", + "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", "dev": true, "requires": { - "abstract-leveldown": "^5.0.0", + "abstract-leveldown": "^6.2.1", "inherits": "^2.0.3", "level-codec": "^9.0.0", - "level-errors": "^2.0.0", - "xtend": "^4.0.1" - }, - "dependencies": { - "abstract-leveldown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", - "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } - }, - "level-codec": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", - "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", - "dev": true, - "requires": { - "buffer": "^5.6.0" - } - }, - "level-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", - "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", - "dev": true, - "requires": { - "errno": "~0.1.1" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } + "level-errors": "^2.0.0" } }, "enquirer": { @@ -2105,9 +2114,9 @@ } }, "env-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true }, "errno": { @@ -2129,30 +2138,28 @@ } }, "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", + "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", "dev": true, "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.11.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" } }, "es-to-primitive": { @@ -2208,44 +2215,17 @@ } }, "eth-sig-util": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-2.5.2.tgz", - "integrity": "sha512-xvDojS/4reXsw8Pz/+p/qcM5rVB61FOdPbEtMZ8FQ0YHnPEzPy5F8zAAaZ+zj5ud0SwRLWPfor2Cacjm7EzMIw==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-2.5.4.tgz", + "integrity": "sha512-aCMBwp8q/4wrW4QLsF/HYBOSA7TpLKmkVwP3pYQNkEEseW2Rr8Z5Uxc9/h6HX+OG3tuHo+2bINVSihIeBfym6A==", "dev": true, "requires": { - "buffer": "^5.2.1", - "elliptic": "^6.4.0", - "ethereumjs-abi": "0.6.5", + "ethereumjs-abi": "0.6.8", "ethereumjs-util": "^5.1.1", - "tweetnacl": "^1.0.0", + "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.0" }, "dependencies": { - "ethereumjs-abi": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz", - "integrity": "sha1-WmN+8Wq0NHP6cqKa2QhxQFs/UkE=", - "dev": true, - "requires": { - "bn.js": "^4.10.0", - "ethereumjs-util": "^4.3.0" - }, - "dependencies": { - "ethereumjs-util": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.5.1.tgz", - "integrity": "sha512-WrckOZ7uBnei4+AKimpuF1B3Fv25OmoRgmYCpGsP7u8PFxXAmAgiJSYT2kRWnt6fVIlKaQlZvuwXp7PIrmn3/w==", - "dev": true, - "requires": { - "bn.js": "^4.8.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.0.0" - } - } - } - }, "ethereumjs-util": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", @@ -2263,49 +2243,6 @@ } } }, - "ethashjs": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ethashjs/-/ethashjs-0.0.8.tgz", - "integrity": "sha512-/MSbf/r2/Ld8o0l15AymjOTlPqpN8Cr4ByUEA9GtR4x0yAh3TdtDzEg29zMjXCNPI7u6E5fOQdj/Cf9Tc7oVNw==", - "dev": true, - "requires": { - "async": "^2.1.2", - "buffer-xor": "^2.0.1", - "ethereumjs-util": "^7.0.2", - "miller-rabin": "^4.0.0" - }, - "dependencies": { - "bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", - "dev": true - }, - "buffer-xor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", - "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "ethereumjs-util": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.7.tgz", - "integrity": "sha512-vU5rtZBlZsgkTw3o6PDKyB8li2EgLavnAbsKcfsH2YhHH1Le+PP8vEiMnAnvgc1B6uMoaM5GDCrVztBw0Q5K9g==", - "dev": true, - "requires": { - "@types/bn.js": "^4.11.3", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.4" - } - } - } - }, "ethereum-bloom-filters": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.7.tgz", @@ -2359,166 +2296,54 @@ "requires": { "bn.js": "^4.11.8", "ethereumjs-util": "^6.0.0" - } - }, - "ethereumjs-account": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-3.0.0.tgz", - "integrity": "sha512-WP6BdscjiiPkQfF9PVfMcwx/rDvfZTjFKY0Uwc09zSQr9JfIVH87dYIJu0gNhBhpmovV4yq295fdllS925fnBA==", - "dev": true, - "requires": { - "ethereumjs-util": "^6.0.0", - "rlp": "^2.2.1", - "safe-buffer": "^5.1.1" - } - }, - "ethereumjs-block": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz", - "integrity": "sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg==", - "dev": true, - "requires": { - "async": "^2.0.1", - "ethereumjs-common": "^1.5.0", - "ethereumjs-tx": "^2.1.1", - "ethereumjs-util": "^5.0.0", - "merkle-patricia-tree": "^2.1.2" }, "dependencies": { "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", "dev": true, "requires": { + "@types/bn.js": "^4.11.3", "bn.js": "^4.11.0", "create-hash": "^1.1.2", "elliptic": "^6.5.2", "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "merkle-patricia-tree": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz", - "integrity": "sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==", - "dev": true, - "requires": { - "async": "^1.4.2", - "ethereumjs-util": "^5.0.0", - "level-ws": "0.0.0", - "levelup": "^1.2.1", - "memdown": "^1.0.0", - "readable-stream": "^2.0.0", - "rlp": "^2.0.0", - "semaphore": ">=1.0.1" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" } } } }, - "ethereumjs-blockchain": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/ethereumjs-blockchain/-/ethereumjs-blockchain-4.0.4.tgz", - "integrity": "sha512-zCxaRMUOzzjvX78DTGiKjA+4h2/sF0OYL1QuPux0DHpyq8XiNoF5GYHtb++GUxVlMsMfZV7AVyzbtgcRdIcEPQ==", - "dev": true, - "requires": { - "async": "^2.6.1", - "ethashjs": "~0.0.7", - "ethereumjs-block": "~2.2.2", - "ethereumjs-common": "^1.5.0", - "ethereumjs-util": "^6.1.0", - "flow-stoplight": "^1.0.0", - "level-mem": "^3.0.1", - "lru-cache": "^5.1.1", - "rlp": "^2.2.2", - "semaphore": "^1.1.0" - } - }, - "ethereumjs-common": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz", - "integrity": "sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA==", - "dev": true - }, - "ethereumjs-tx": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", - "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", - "dev": true, - "requires": { - "ethereumjs-common": "^1.5.0", - "ethereumjs-util": "^6.0.0" - } - }, "ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.0.tgz", + "integrity": "sha512-kR+vhu++mUDARrsMMhsjjzPduRVAeundLGXucGRHF3B4oEltOUspfgCVco4kckucj3FMlLaZHUl9n7/kdmr6Tw==", "dev": true, "requires": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", "create-hash": "^1.1.2", - "elliptic": "^6.5.2", "ethereum-cryptography": "^0.1.3", "ethjs-util": "0.1.6", - "rlp": "^2.2.3" + "rlp": "^2.2.4" + }, + "dependencies": { + "@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "dev": true + } } }, "ethers": { @@ -2603,6 +2428,12 @@ "safe-buffer": "^5.1.1" } }, + "exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==", + "dev": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -2751,15 +2582,6 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, - "fake-merkle-patricia-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz", - "integrity": "sha1-S4w6z7Ugr635hgsfFM2M40As3dM=", - "dev": true, - "requires": { - "checkpoint-store": "^1.1.0" - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2822,18 +2644,21 @@ "is-buffer": "~2.0.3" } }, - "flow-stoplight": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flow-stoplight/-/flow-stoplight-1.0.0.tgz", - "integrity": "sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s=", - "dev": true - }, "follow-redirects": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", - "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", "dev": true }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2890,9 +2715,9 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -11754,9 +11579,9 @@ "dev": true }, "get-intrinsic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", - "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -11794,9 +11619,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -11841,15 +11666,20 @@ } }, "hardhat": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.0.6.tgz", - "integrity": "sha512-wFTamcSUBMGwpgFxWu7ptg5o07FQyr8Ce8072rzE+QjpwM0uD3GRdhlqn+O1w+awQZ7AhK44njpAoRPUelOf/Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.6.0.tgz", + "integrity": "sha512-NEM2pe11QXWXB7k49heOLQA9vxihG4DJ0712KjMT9NYSZgLOMcWswJ3tvn+/ND6vzLn6Z4pqr2x/kWSfllWFuw==", "dev": true, "requires": { - "@nomiclabs/ethereumjs-vm": "^4.1.1", + "@ethereumjs/block": "^3.4.0", + "@ethereumjs/blockchain": "^5.4.0", + "@ethereumjs/common": "^2.4.0", + "@ethereumjs/tx": "^3.3.0", + "@ethereumjs/vm": "^5.5.2", + "@ethersproject/abi": "^5.1.2", "@sentry/node": "^5.18.1", - "@solidity-parser/parser": "^0.7.1", - "@types/bn.js": "^4.11.5", + "@solidity-parser/parser": "^0.11.0", + "@types/bn.js": "^5.1.0", "@types/lru-cache": "^5.1.0", "abort-controller": "^3.0.0", "adm-zip": "^0.4.16", @@ -11863,19 +11693,17 @@ "eth-sig-util": "^2.5.2", "ethereum-cryptography": "^0.1.2", "ethereumjs-abi": "^0.6.8", - "ethereumjs-account": "^3.0.0", - "ethereumjs-block": "^2.2.0", - "ethereumjs-common": "^1.5.0", - "ethereumjs-tx": "^2.1.1", - "ethereumjs-util": "^6.1.0", + "ethereumjs-util": "^7.1.0", "find-up": "^2.1.0", "fp-ts": "1.19.3", "fs-extra": "^7.0.1", "glob": "^7.1.3", + "https-proxy-agent": "^5.0.0", "immutable": "^4.0.0-rc.12", "io-ts": "1.10.4", "lodash": "^4.17.11", - "merkle-patricia-tree": "^3.0.0", + "merkle-patricia-tree": "^4.2.0", + "mnemonist": "^0.38.0", "mocha": "^7.1.2", "node-fetch": "^2.6.0", "qs": "^6.7.0", @@ -11889,79 +11717,337 @@ "true-case-path": "^2.2.1", "tsort": "0.0.1", "uuid": "^3.3.2", - "ws": "^7.2.1" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "ws": "^7.4.6" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "@ethersproject/abi": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.0.tgz", + "integrity": "sha512-9gU2H+/yK1j2eVMdzm6xvHSnMxk8waIHQGYCZg5uvAyH0rsAzxkModzBSpbAkAuhKFEovC2S9hM4nPuLym8IZw==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "@ethersproject/address": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/constants": "^5.4.0", + "@ethersproject/hash": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/strings": "^5.4.0" } }, - "kind-of": { - "version": "4.0.0", + "@ethersproject/abstract-provider": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz", + "integrity": "sha512-3EedfKI3LVpjSKgAxoUaI+gB27frKsxzm+r21w9G60Ugk+3wVLQwhi1LsEJAKNV7WoZc8CIpNrATlL1QFABjtQ==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/networks": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/transactions": "^5.4.0", + "@ethersproject/web": "^5.4.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.4.1.tgz", + "integrity": "sha512-SkkFL5HVq1k4/25dM+NWP9MILgohJCgGv5xT5AcRruGz4ILpfHeBtO/y6j+Z3UN/PAjDeb4P7E51Yh8wcGNLGA==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0" + } + }, + "@ethersproject/address": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.4.0.tgz", + "integrity": "sha512-SD0VgOEkcACEG/C6xavlU1Hy3m5DGSXW3CUHkaaEHbAPPsgi0coP5oNPsxau8eTlZOk/bpa/hKeCNoK5IzVI2Q==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/rlp": "^5.4.0" + } + }, + "@ethersproject/base64": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.4.0.tgz", + "integrity": "sha512-CjQw6E17QDSSC5jiM9YpF7N1aSCHmYGMt9bWD8PWv6YPMxjsys2/Q8xLrROKI3IWJ7sFfZ8B3flKDTM5wlWuZQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.4.1.tgz", + "integrity": "sha512-fJhdxqoQNuDOk6epfM7yD6J8Pol4NUCy1vkaGAkuujZm0+lNow//MKu1hLhRiYV4BsOHyBv5/lsTjF+7hWwhJg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.4.0.tgz", + "integrity": "sha512-H60ceqgTHbhzOj4uRc/83SCN9d+BSUnOkrr2intevqdtEMO1JFVZ1XL84OEZV+QjV36OaZYxtnt4lGmxcGsPfA==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/constants": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.4.0.tgz", + "integrity": "sha512-tzjn6S7sj9+DIIeKTJLjK9WGN2Tj0P++Z8ONEIlZjyoTkBuODN+0VfhAyYksKi43l1Sx9tX2VlFfzjfmr5Wl3Q==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.4.0" + } + }, + "@ethersproject/hash": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.4.0.tgz", + "integrity": "sha512-xymAM9tmikKgbktOCjW60Z5sdouiIIurkZUr9oW5NOex5uwxrbsYG09kb5bMcNjlVeJD3yPivTNzViIs1GCbqA==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.4.0", + "@ethersproject/address": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/strings": "^5.4.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.4.0.tgz", + "integrity": "sha512-FBI1plWet+dPUvAzPAeHzRKiPpETQzqSUWR1wXJGHVWi4i8bOSrpC3NwpkPjgeXG7MnugVc1B42VbfnQikyC/A==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "js-sha3": "0.5.7" + } + }, + "@ethersproject/logger": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.0.tgz", + "integrity": "sha512-xYdWGGQ9P2cxBayt64d8LC8aPFJk6yWCawQi/4eJ4+oJdMMjEBMrIcIMZ9AxhwpPVmnBPrsB10PcXGmGAqgUEQ==", + "dev": true + }, + "@ethersproject/networks": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.2.tgz", + "integrity": "sha512-eekOhvJyBnuibfJnhtK46b8HimBc5+4gqpvd1/H9LEl7Q7/qhsIhM81dI9Fcnjpk3jB1aTy6bj0hz3cifhNeYw==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/properties": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.0.tgz", + "integrity": "sha512-7jczalGVRAJ+XSRvNA6D5sAwT4gavLq3OXPuV/74o3Rd2wuzSL035IMpIMgei4CYyBdialJMrTqkOnzccLHn4A==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/rlp": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.4.0.tgz", + "integrity": "sha512-0I7MZKfi+T5+G8atId9QaQKHRvvasM/kqLyAH4XxBCBchAooH2EX5rL9kYZWwcm3awYV+XC7VF6nLhfeQFKVPg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.4.0.tgz", + "integrity": "sha512-q8POUeywx6AKg2/jX9qBYZIAmKSB4ubGXdQ88l40hmATj29JnG5pp331nAWwwxPn2Qao4JpWHNZsQN+bPiSW9A==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.4.0.tgz", + "integrity": "sha512-k/9DkH5UGDhv7aReXLluFG5ExurwtIpUfnDNhQA29w896Dw3i4uDTz01Quaptbks1Uj9kI8wo9tmW73wcIEaWA==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/constants": "^5.4.0", + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/transactions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.4.0.tgz", + "integrity": "sha512-s3EjZZt7xa4BkLknJZ98QGoIza94rVjaEed0rzZ/jB9WrIuu/1+tjvYCWzVrystXtDswy7TPBeIepyXwSYa4WQ==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/constants": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/rlp": "^5.4.0", + "@ethersproject/signing-key": "^5.4.0" + } + }, + "@ethersproject/web": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.4.0.tgz", + "integrity": "sha512-1bUusGmcoRLYgMn6c1BLk1tOKUIFuTg8j+6N8lYlbMpDesnle+i3pGSagGNvwjaiLo4Y5gBibwctpPRmjrh4Og==", + "dev": true, + "requires": { + "@ethersproject/base64": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/strings": "^5.4.0" + } + }, + "@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, @@ -12080,9 +12166,9 @@ "dev": true }, "immutable": { - "version": "4.0.0-rc.12", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz", - "integrity": "sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==", + "version": "4.0.0-rc.14", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.14.tgz", + "integrity": "sha512-pfkvmRKJSoW7JFx0QeYlAmT+kNYvn5j0u7bnpNq4N2RCvHSTlLT208G8jgaquNe+Q8kCPHKOSpxJkyvLDpYq0w==", "dev": true }, "inflight": { @@ -12101,6 +12187,17 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -12148,6 +12245,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -12157,6 +12263,16 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-buffer": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", @@ -12164,9 +12280,9 @@ "dev": true }, "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true }, "is-ci": { @@ -12205,10 +12321,13 @@ } }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-descriptor": { "version": "0.1.6", @@ -12280,6 +12399,15 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -12290,21 +12418,31 @@ } }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" } }, "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2" } }, "is-typedarray": { @@ -12331,12 +12469,6 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -12460,273 +12592,91 @@ } }, "level-codec": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", - "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==", - "dev": true - }, - "level-errors": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", - "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", - "dev": true, - "requires": { - "errno": "~0.1.1" - } - }, - "level-iterator-stream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz", - "integrity": "sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", + "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", "dev": true, "requires": { - "inherits": "^2.0.1", - "level-errors": "^1.0.3", - "readable-stream": "^1.0.33", - "xtend": "^4.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } + "buffer": "^5.6.0" } }, - "level-mem": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/level-mem/-/level-mem-3.0.1.tgz", - "integrity": "sha512-LbtfK9+3Ug1UmvvhR2DqLqXiPW1OJ5jEh0a3m9ZgAipiwpSxGj/qaVVy54RG5vAQN1nCuXqjvprCuKSCxcJHBg==", - "dev": true, - "requires": { - "level-packager": "~4.0.0", - "memdown": "~3.0.0" - }, - "dependencies": { - "abstract-leveldown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", - "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } - }, - "immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", - "dev": true - }, - "memdown": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-3.0.0.tgz", - "integrity": "sha512-tbV02LfZMWLcHcq4tw++NuqMO+FZX8tNJEiD2aNRm48ZZusVg5N8NART+dmBkepJVye986oixErf7jfXboMGMA==", - "dev": true, - "requires": { - "abstract-leveldown": "~5.0.0", - "functional-red-black-tree": "~1.0.1", - "immediate": "~3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } - } + "level-concat-iterator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", + "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", + "dev": true }, - "level-packager": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-4.0.1.tgz", - "integrity": "sha512-svCRKfYLn9/4CoFfi+d8krOtrp6RoX8+xm0Na5cgXMqSyRru0AnDYdLl+YI8u1FyS6gGZ94ILLZDE5dh2but3Q==", - "dev": true, - "requires": { - "encoding-down": "~5.0.0", - "levelup": "^3.0.0" - }, - "dependencies": { - "abstract-leveldown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", - "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } - }, - "deferred-leveldown": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz", - "integrity": "sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww==", - "dev": true, - "requires": { - "abstract-leveldown": "~5.0.0", - "inherits": "^2.0.3" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "level-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", - "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", - "dev": true, - "requires": { - "errno": "~0.1.1" - } - }, - "level-iterator-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz", - "integrity": "sha512-nEIQvxEED9yRThxvOrq8Aqziy4EGzrxSZK+QzEFAVuJvQ8glfyZ96GB6BoI4sBbLfjMXm2w4vu3Tkcm9obcY0g==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "xtend": "^4.0.0" - } - }, - "levelup": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-3.1.1.tgz", - "integrity": "sha512-9N10xRkUU4dShSRRFTBdNaBxofz+PGaIZO962ckboJZiNmLuhVT6FZ6ZKAsICKfUBO76ySaYU6fJWX/jnj3Lcg==", - "dev": true, - "requires": { - "deferred-leveldown": "~4.0.0", - "level-errors": "~2.0.0", - "level-iterator-stream": "~3.0.0", - "xtend": "~4.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } + "level-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", + "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", + "dev": true, + "requires": { + "errno": "~0.1.1" + } + }, + "level-iterator-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", + "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.4.0", + "xtend": "^4.0.2" + } + }, + "level-mem": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/level-mem/-/level-mem-5.0.1.tgz", + "integrity": "sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg==", + "dev": true, + "requires": { + "level-packager": "^5.0.3", + "memdown": "^5.0.0" + } + }, + "level-packager": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", + "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", + "dev": true, + "requires": { + "encoding-down": "^6.3.0", + "levelup": "^4.3.2" + } + }, + "level-supports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", + "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", + "dev": true, + "requires": { + "xtend": "^4.0.2" } }, "level-ws": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz", - "integrity": "sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-2.0.0.tgz", + "integrity": "sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA==", "dev": true, "requires": { - "readable-stream": "~1.0.15", - "xtend": "~2.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } + "inherits": "^2.0.3", + "readable-stream": "^3.1.0", + "xtend": "^4.0.1" } }, "levelup": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", - "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", + "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", "dev": true, "requires": { - "deferred-leveldown": "~1.2.1", - "level-codec": "~7.0.0", - "level-errors": "~1.0.3", - "level-iterator-stream": "~1.3.0", - "prr": "~1.0.1", - "semver": "~5.4.1", + "deferred-leveldown": "~5.3.0", + "level-errors": "~2.0.0", + "level-iterator-stream": "~4.0.0", + "level-supports": "~1.0.0", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } } }, "load-json-file": { @@ -12753,9 +12703,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.assign": { @@ -12809,6 +12759,15 @@ "object-visit": "^1.0.0" } }, + "mcl-wasm": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.8.tgz", + "integrity": "sha512-qNHlYO6wuEtSoH5A8TcZfCEHtw8gGPqF6hLZpQn2SVd/Mck0ELIKOkmj072D98S9B9CI/jZybTUC96q1P2/ZDw==", + "dev": true, + "requires": { + "typescript": "^4.3.4" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -12821,38 +12780,36 @@ } }, "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-5.1.0.tgz", + "integrity": "sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw==", "dev": true, "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", + "abstract-leveldown": "~6.2.1", + "functional-red-black-tree": "~1.0.1", + "immediate": "~3.2.3", "inherits": "~2.0.1", "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" + "safe-buffer": "~5.2.0" }, "dependencies": { "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", + "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", "dev": true, "requires": { + "buffer": "^5.5.0", + "immediate": "^3.2.3", + "level-concat-iterator": "~2.0.0", + "level-supports": "~1.0.0", "xtend": "~4.0.0" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "immediate": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", + "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", "dev": true } } @@ -12864,98 +12821,18 @@ "dev": true }, "merkle-patricia-tree": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz", - "integrity": "sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-4.2.0.tgz", + "integrity": "sha512-0sBVXs7z1Q1/kxzWZ3nPnxSPiaHKF/f497UQzt9O7isRcS10tel9jM/4TivF6Jv7V1yFq4bWyoATxbDUOen5vQ==", "dev": true, "requires": { - "async": "^2.6.1", - "ethereumjs-util": "^5.2.0", - "level-mem": "^3.0.1", - "level-ws": "^1.0.0", - "readable-stream": "^3.0.6", - "rlp": "^2.0.0", - "semaphore": ">=1.0.1" - }, - "dependencies": { - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "level-ws": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-1.0.0.tgz", - "integrity": "sha512-RXEfCmkd6WWFlArh3X8ONvQPm8jNpfA0s/36M4QzLqrLEIt1iJE9WBHLZ5vZJK6haMjJPJGJCQWfjMNnRcq/9Q==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.8", - "xtend": "^4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } + "@types/levelup": "^4.3.0", + "ethereumjs-util": "^7.0.10", + "level-mem": "^5.0.1", + "level-ws": "^2.0.0", + "readable-stream": "^3.6.0", + "rlp": "^2.2.4", + "semaphore-async-await": "^1.5.1" } }, "micromatch": { @@ -13166,6 +13043,15 @@ "minimist": "^1.2.5" } }, + "mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "dev": true, + "requires": { + "obliterator": "^1.6.1" + } + }, "mocha": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", @@ -13238,6 +13124,13 @@ "locate-path": "^3.0.0" } }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -13268,12 +13161,6 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, "object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", @@ -13497,15 +13384,15 @@ } }, "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", "dev": true }, "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, "object-visit": { @@ -13527,53 +13414,17 @@ "define-properties": "^1.1.3", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } } }, "object.getownpropertydescriptors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", - "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } + "es-abstract": "^1.18.0-next.2" } }, "object.pick": { @@ -13585,6 +13436,12 @@ "isobject": "^3.0.1" } }, + "obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -13736,9 +13593,9 @@ "dev": true }, "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -13755,9 +13612,9 @@ "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "pify": { @@ -13793,18 +13650,18 @@ "integrity": "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==", "dev": true }, + "printj": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", "dev": true }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -13824,10 +13681,13 @@ "dev": true }, "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", - "dev": true + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } }, "query-string": { "version": "5.1.1", @@ -13921,9 +13781,9 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -14098,10 +13958,10 @@ "node-gyp-build": "^4.2.0" } }, - "semaphore": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", - "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", + "semaphore-async-await": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz", + "integrity": "sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo=", "dev": true }, "semver": { @@ -14176,6 +14036,17 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -14548,22 +14419,22 @@ } }, "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, @@ -14756,11 +14627,29 @@ "dev": true }, "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", @@ -14895,15 +14784,16 @@ "dev": true }, "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.1.tgz", + "integrity": "sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", + "for-each": "^0.3.3", "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" + "object.getownpropertydescriptors": "^2.1.1" } }, "uuid": { @@ -14958,6 +14848,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -15025,9 +14928,9 @@ "dev": true }, "ws": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", - "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", "dev": true }, "xhr": { @@ -15075,18 +14978,15 @@ } }, "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "requires": { - "object-keys": "~0.4.0" - } + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yallist": { diff --git a/package.json b/package.json index 4b81ec0..59edf59 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,6 @@ "chai": "^4.2.0", "ethereum-waffle": "^3.2.1", "ethers": "^5.0.24", - "hardhat": "^2.0.6" + "hardhat": "^2.6.0" } } diff --git a/test/upgradeFlashLoan.js b/test/upgradeFlashLoan.js new file mode 100644 index 0000000..7610916 --- /dev/null +++ b/test/upgradeFlashLoan.js @@ -0,0 +1,153 @@ +const { expect } = require("chai"); +const { ethers, waffle } = require("hardhat"); +const unitrollerAbi = require('../abi/unitroller'); +const comptrollerAbi = require("../abi/comptroller"); +const erc20Abi = require("../abi/erc20"); +const cCollateralCapDelegatorAbi = require("../abi/cCollateralCapDelegator"); +const cTokenAdminAbi = require("../abi/cTokenAdmin"); +crTokens = [ + "0x250Fb308199FE8C5220509C1bf83D21d60b7f74A", //crLON + "0x2A867fd776B83e1bd4e13C6611AFd2F6af07EA6D", //crIBBTC + "0x1241B10E7EA55b22f5b2d007e8fECDF73DCff999", //crPAXG + "0xD7394428536F63d5659cc869EF69d10f9E66314B", //crPAX + "0x766175eaC1A99C969dDd1EBDBe7e270D508d8FFF", //crEURT + "0x81E346729723C4D15d0FB1c5679b9f2926Ff13C6", //crBNT + "0xE585c76573D7593ABF21537B607091F76c996E73", //crWOO + "0x8C3B7a4320ba70f8239F83770c4015B5bc4e6F91", //crFEI + "0x98E329eB5aae2125af273102f3440DE19094b77c", //crSWAP + "0x523EFFC8bFEfC2948211A05A905F761CBA5E8e9E", //crGNO + "0x21011BC93d9E515B9511A817A1eD1D6d468f49Fc", //crCOVER + "0x1A122348B73B58eA39F822A89e6ec67950c2bBD0", //crVVSP + "0x71cEFCd324B732d4E058AfAcBA040d908c441847", //crVSP + "0xDbb5e3081dEf4b6cdD8864aC2aeDA4cBf778feCf", //crMLN + "0xdFFf11DFe6436e42a17B86e7F419Ac8292990393", //crARNXM + "0xab10586C918612BA440482db77549d26B7ABF8f7", //crARMOR + "0x28526Bb33d7230E65E735dB64296413731C5402e", //crSFI + "0x081FE64df6dc6fc70043aedF3713a3ce6F190a21", //crRARI + "0x7C3297cFB4c4bbd5f44b450c0872E0ADA5203112", //crOCEAN + "0x299e254A8a165bBeB76D9D69305013329Eea3a3B", //crPERP + "0xf8445C529D363cE114148662387eba5E62016e20", //crRAI + "0x8379BAA817c5c5aB929b03ee8E3c48e45018Ae41", //crRUNE + "0xc36080892c64821fa8e396bc1bD8678fA3b82b17", //crFTM + "0x51F48b638F82e8765F7a26373A2Cb4CcB10C07af", //crUST + "0x1d0986Fb43985c88Ffa9aD959CC24e6a087C7e35", //crALPHA + "0xb092b4601850E23903A42EaCBc9D8A0EeC26A4d5", //crFRAX + "0x2Db6c82CE72C8d7D770ba1b5F5Ed0b6E075066d6", //crAMP + "0x59089279987DD76fC65Bf94Cb40E186b96e03cB3", //crOGN + "0x65883978aDA0e707c3b2BE2A6825b1C4BDF76A90", //crAKRO + "0xc68251421eDDa00a10815E273fA4b1191fAC651b", //crPICKLE + "0x25555933a8246Ab67cbf907CE3d1949884E82B55", //crSUSD + "0xC25EAE724f189Ba9030B2556a1533E7c8A732E14", //crSNX + "0x197070723CE0D3810a0E47F06E935c30a480D4Fc", //crWBTC + "0x7Aaa323D7e398be4128c7042d197a2545f0f1fea", //crOMG + "0x85759961b116f1D36fD697855c57A6ae40793D9B", //cr1INCH + "0x3C6C553A95910F9FC81c98784736bd628636D296", //crESD + "0x10a3da2BB0Fae4D591476fd97D6636fd172923a8", //crHEGIC + "0x92B767185fB3B04F881e3aC8e5B0662a027A1D9f", //crDAI + "0xD692ac3245bb82319A31068D6B8412796eE85d2c", //crHUSD + "0xfd609a03B393F1A1cFcAcEdaBf068CAD09a924E2", //crCRETH2 + "0xd5103AfcD0B3fA865997Ef2984C66742c51b2a8b", //crHFIL + "0x054B7ed3F45714d3091e82aAd64A1588dC4096Ed", //crHBTC + "0x903560b1CcE601794C584F58898dA8a8b789Fc5d", //crKP3R + "0x3225E3C669B39C7c8B3e204a8614bB218c5e31BC", //crAAVE + "0xf55BbE0255f7f4E70f63837Ff72A577fbDDbE924", //crBOND + "0x7ea9C63E216D5565c3940A2B3d150e59C2907Db3", //crBBTC + "0x2A537Fa9FFaea8C1A41D3C2B68a9cb791529366D", //crDPI + "0x8b3FF1ed4F36C2c2be675AFb13CC3AA5d73685a5", //crCEL + "0xeFF039C3c1D668f408d09dD7B63008622a77532C", //crWNXM + "0xef58b2d5A1b8D3cDE67b8aB054dC5C831E9Bc025", //crSRM + "0xe89a6D0509faF730BD707bf868d9A2A744a363C7", //crUNI + "0x10FDBD1e48eE2fD9336a482D746138AE19e649Db", //crFTT + "0x338286C0BC081891A4Bda39C7667ae150bf5D206", //crSUSHI + "0x3623387773010d9214B10C551d6e7fc375D31F58", //crMTA + "0x1FF8CDB51219a8838b52E9cAc09b71e591BC998e", //crBUSD + "0x17107f40d70f4470d20CB3f138a052cAE8EbD4bE", //crRENBTC + "0xc7Fd8Dcee4697ceef5a2fd4608a7BD6A94C77480", //crCRV + "0x697256CAA3cCaFD62BB6d3Aa1C7C5671786A5fD9", //crLINK + "0x19D1666f543D42ef17F66E376944A22aEa1a8E46", //crCOMP + "0xcE4Fe9b4b8Ff61949DCfeB7e03bc9FAca59D2Eb3", //crBAL + "0xCbaE0A83f4f9926997c8339545fb8eE32eDc6b76", //crYFI + "0x44fbeBd2F576670a6C33f6Fc0B00aA8c5753b322", //crUSDC + "0x797AAB1ce7c01eB727ab980762bA88e7133d2157", //crUSDT +] + +describe('upgrade', () => { + for (i=0; i { + accounts = await ethers.getSigners(); + admin = accounts[0]; + adminAddress = await admin.getAddress(); + + creamMultisig = await ethers.provider.getSigner(creamMultisigAddress); + + const comptrollerFactory = await ethers.getContractFactory('Comptroller'); + newComptroller = await comptrollerFactory.deploy(); + + const flashloanReceiverFactory = await ethers.getContractFactory('FlashloanReceiver'); + flashloanReceiver = await flashloanReceiverFactory.deploy() + + const flashloanLenderFactory = await ethers.getContractFactory('FlashloanLender'); + flashloanLender = await flashloanLenderFactory.deploy(unitrollerAddress, creamMultisigAddress) + + cCollateralCapErc20DelegateFactory = await ethers.getContractFactory('CCollateralCapErc20Delegate'); + + unitroller = new ethers.Contract(unitrollerAddress, unitrollerAbi, provider); + cTokenAdmin = new ethers.Contract(cTokenAdminAddress, cTokenAdminAbi, provider); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [creamMultisigAddress] + }); + // 1. upgrade comptroller + await unitroller.connect(creamMultisig)._setPendingImplementation(newComptroller.address); + await newComptroller.connect(creamMultisig)._become(unitroller.address); + expect(await unitroller.comptrollerImplementation()).to.equal(newComptroller.address); + unitroller = new ethers.Contract(unitrollerAddress, comptrollerAbi, provider); + + + }); + + it('flash loan', async () => { + allMarkets = await unitroller.getAllMarkets() + var flashloanRevertMessage; + for (i=0; i < allMarkets.length; i++){ + crTokenAddress = allMarkets[i].toLowerCase() + if (crTokens.includes(crTokenAddress)){ + // need to upgrade to new ccollateralcap + newCrToken = await cCollateralCapErc20DelegateFactory.deploy() + await cTokenAdmin.connect(creamMultisig)._setImplementation(crTokenAddress, newCrToken.address, true, "0x"); + await unitroller.connect(creamMultisig)._setFlashloanPaused(crTokenAddress, false); + crToken = new ethers.Contract(crTokenAddress, cCollateralCapDelegatorAbi, provider); + console.log('update') + expect(await crToken.implementation()).to.equal(newCrToken.address); + expect(await unitroller.flashloanGuardianPaused(crTokenAddress)).to.be.false + } else { + await unitroller.connect(creamMultisig)._setFlashloanPaused(crTokenAddress, true); + expect(await unitroller.flashloanGuardianPaused(crTokenAddress)).to.be.true + console.log('no need to update') + } + } + }); +});