This nametag was submitted by Kleros Curate.
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 558,629 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Exit Market | 41968615 | 248 days ago | IN | 0 ETH | 0.00001411 | ||||
Enter Markets | 38977151 | 283 days ago | IN | 0 ETH | 0.00000894 | ||||
Enter Markets | 37533597 | 300 days ago | IN | 0 ETH | 0.00000426 | ||||
Enter Markets | 37525185 | 301 days ago | IN | 0 ETH | 0.00000548 | ||||
Enter Markets | 37523203 | 301 days ago | IN | 0 ETH | 0.00000565 | ||||
Enter Markets | 37513218 | 301 days ago | IN | 0 ETH | 0.00000573 | ||||
Enter Markets | 37512934 | 301 days ago | IN | 0 ETH | 0.00000569 | ||||
Enter Markets | 37510664 | 301 days ago | IN | 0 ETH | 0.00000572 | ||||
Enter Markets | 37509301 | 301 days ago | IN | 0 ETH | 0.00000601 | ||||
Enter Markets | 37502843 | 301 days ago | IN | 0 ETH | 0.00000507 | ||||
Enter Markets | 37502670 | 301 days ago | IN | 0 ETH | 0.00000595 | ||||
Enter Markets | 37502235 | 301 days ago | IN | 0 ETH | 0.00000551 | ||||
Enter Markets | 37499175 | 301 days ago | IN | 0 ETH | 0.00000546 | ||||
Enter Markets | 37498966 | 301 days ago | IN | 0 ETH | 0.00000547 | ||||
Enter Markets | 37498731 | 301 days ago | IN | 0 ETH | 0.00000637 | ||||
Enter Markets | 37494441 | 301 days ago | IN | 0 ETH | 0.0000052 | ||||
Enter Markets | 37492451 | 301 days ago | IN | 0 ETH | 0.00000766 | ||||
Enter Markets | 37485404 | 301 days ago | IN | 0 ETH | 0.00000793 | ||||
Enter Markets | 37484795 | 301 days ago | IN | 0 ETH | 0.00000759 | ||||
Enter Markets | 37484404 | 301 days ago | IN | 0 ETH | 0.00000541 | ||||
Enter Markets | 37484132 | 301 days ago | IN | 0 ETH | 0.00000477 | ||||
Enter Markets | 37483596 | 301 days ago | IN | 0 ETH | 0.00000781 | ||||
Enter Markets | 37483065 | 301 days ago | IN | 0 ETH | 0.00000767 | ||||
Enter Markets | 37482725 | 301 days ago | IN | 0 ETH | 0.00000863 | ||||
Enter Markets | 37481781 | 301 days ago | IN | 0 ETH | 0.00000934 |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
Unitroller
Compiler Version
v0.8.12+commit.f00d7308
ZkSolc Version
v1.3.6
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import {ComptrollerErrorReporter} from "./ErrorReporter.sol"; import {UnitrollerAdminStorage} from "./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 */ event NewPendingImplementation( address oldPendingImplementation, address newPendingImplementation ); /** * @notice Emitted when pendingComptrollerImplementation is accepted, which means comptroller implementation is updated */ event NewImplementation( address oldImplementation, address newImplementation ); /** * @notice Emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); constructor() { // Set admin to caller admin = msg.sender; } /*** Admin Functions ***/ function _setPendingImplementation( address newPendingImplementation ) public returns (uint) { if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_PENDING_IMPLEMENTATION_OWNER_CHECK ); } address oldPendingImplementation = pendingComptrollerImplementation; pendingComptrollerImplementation = newPendingImplementation; emit NewPendingImplementation( oldPendingImplementation, pendingComptrollerImplementation ); return uint(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) { // 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 ); } // Save current values for inclusion in log address oldImplementation = comptrollerImplementation; address oldPendingImplementation = pendingComptrollerImplementation; comptrollerImplementation = pendingComptrollerImplementation; pendingComptrollerImplementation = address(0); emit NewImplementation(oldImplementation, comptrollerImplementation); emit NewPendingImplementation( oldPendingImplementation, pendingComptrollerImplementation ); return uint(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) { // 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 uint(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) { // 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 uint(Error.NO_ERROR); } /** * @dev Delegates execution to an implementation contract. * It returns to the external caller whatever the implementation returns * or forwards reverts. */ fallback() 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()) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import {EIP20Interface} from "./EIP20Interface.sol"; import {TokenErrorReporter} from "./ErrorReporter.sol"; import {InterestRateModel} from "./InterestRateModel.sol"; import {ExponentialNoError} from "./ExponentialNoError.sol"; import {ComptrollerInterface} from "./ComptrollerInterface.sol"; import {CTokenStorage, CTokenInterface} from "./CTokenInterfaces.sol"; /** * @title Compound's CToken Contract * @notice Abstract base for CTokens * @author Compound */ abstract contract CToken is ExponentialNoError, TokenErrorReporter, CTokenStorage { /** * @notice Indicator that this is a CToken contract (for inspection) */ bool public constant isCToken = true; /** * @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 == NO_ERROR, "setting comptroller failed"); // Initialize block number and borrow index (block number mocks depend on comptroller being set) accrualBlockNumber = getBlockNumber(); borrowIndex = MANTISSA_ONE; // Set the interest rate model (depends on block number / borrow index) err = _setInterestRateModelFresh(interestRateModel_); require(err == 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) { revert TransferComptrollerRejection(allowed); } /* Do not allow self-transfers */ if (src == dst) { revert TransferNotAllowed(); } /* Get the allowance, infinite for the account owner */ uint256 startingAllowance = 0; if (spender == src) { startingAllowance = type(uint256).max; } else { startingAllowance = transferAllowances[src][spender]; } /* Do not allow if spender has less allowance or source account doesn't have sufficient tokens */ if (accountTokens[src] < tokens || startingAllowance < tokens) { revert TransferNotEnough(); } uint256 allowanceNew = startingAllowance - tokens; uint256 srcTokensNew = accountTokens[src] - tokens; uint256 dstTokensNew = 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 != type(uint256).max) { transferAllowances[src][spender] = allowanceNew; } /* We emit a Transfer event */ emit Transfer(src, dst, tokens); return 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 override nonReentrant returns (bool) { return transferTokens(msg.sender, msg.sender, dst, amount) == 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 override nonReentrant returns (bool) { return transferTokens(msg.sender, src, dst, amount) == 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 override 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 override 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 override 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 override 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 override returns (uint256, uint256, uint256, uint256) { return ( NO_ERROR, accountTokens[account], borrowBalanceStoredInternal(account), exchangeRateStoredInternal() ); } /** * @dev Function to simply retrieve block number * This exists mainly for inheriting test contracts to stub this result. */ function getBlockNumber() internal view virtual 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 override 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 override 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 override nonReentrant returns (uint256) { require(accrueInterest() == 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 override nonReentrant returns (uint256) { accrueInterest(); 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 override 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 (error code, 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 */ uint principalTimesIndex = borrowSnapshot.principal * borrowIndex; return principalTimesIndex / borrowSnapshot.interestIndex; } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() public override nonReentrant returns (uint256) { accrueInterest(); 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 override 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 (error code, calculated exchange rate scaled by 1e18) */ function exchangeRateStoredInternal() internal view virtual 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(); uint cashPlusBorrowsMinusReserves = (totalCash + totalBorrows) - totalReserves; uint exchangeRate = (cashPlusBorrowsMinusReserves * EXP_SCALE) / _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 override 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 virtual override returns (uint256) { /* Remember the initial block number */ uint256 currentBlockNumber = getBlockNumber(); uint256 accrualBlockNumberPrior = accrualBlockNumber; /* Short-circuit accumulating 0 interest */ if (accrualBlockNumberPrior == currentBlockNumber) { return 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 <= BORROW_RATE_MAX_MANTISSA, "borrow rate is absurdly high" ); /* Calculate the number of blocks elapsed since the last accrual */ uint256 blockDelta = 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 ); uint interestAccumulated = mul_ScalarTruncate( simpleInterestFactor, borrowsPrior ); uint totalBorrowsNew = interestAccumulated + borrowsPrior; uint totalReservesNew = mul_ScalarTruncateAddUInt( Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior ); uint 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 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 */ function mintInternal(uint256 mintAmount) internal nonReentrant { accrueInterest(); // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to mintFresh(msg.sender, mintAmount); } /** * @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 */ function mintFresh(address minter, uint256 mintAmount) internal { /* Fail if mint not allowed */ uint256 allowed = comptroller.mintAllowed( address(this), minter, mintAmount ); if (allowed != 0) { revert MintComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert MintFreshnessCheck(); } Exp memory exchangeRate = Exp({mantissa: 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. */ uint actualMintAmount = doTransferIn(minter, mintAmount); /* * We get the current exchange rate and calculate the number of cTokens to be minted: * mintTokens = actualMintAmount / exchangeRate */ uint mintTokens = div_(actualMintAmount, exchangeRate); /* * We calculate the new total supply of cTokens and minter token balance, checking for overflow: * totalSupplyNew = totalSupply + mintTokens * accountTokensNew = accountTokens[minter] + mintTokens */ totalSupply = totalSupply + mintTokens; accountTokens[minter] = accountTokens[minter] + mintTokens; /* We emit a Mint event, and a Transfer event */ emit Mint(minter, actualMintAmount, mintTokens); emit Transfer(address(0), minter, mintTokens); } /** * @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 */ function redeemInternal(uint256 redeemTokens) internal nonReentrant { accrueInterest(); // redeemFresh emits redeem-specific logs on errors, so we don't need to redeemFresh(payable(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 */ function redeemUnderlyingInternal( uint256 redeemAmount ) internal nonReentrant { accrueInterest(); // redeemFresh emits redeem-specific logs on errors, so we don't need to redeemFresh(payable(msg.sender), 0, redeemAmount); } /** * @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) */ function redeemFresh( address payable redeemer, uint256 redeemTokensIn, uint256 redeemAmountIn ) internal { require( redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero" ); /* exchangeRate = invoke Exchange Rate Stored() */ Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()}); uint redeemTokens; uint redeemAmount; /* 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 */ redeemTokens = redeemTokensIn; redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn); } else { /* * We get the current exchange rate and calculate the amount to be redeemed: * redeemTokens = redeemAmountIn / exchangeRate * redeemAmount = redeemAmountIn */ redeemTokens = div_(redeemAmountIn, exchangeRate); redeemAmount = redeemAmountIn; } /* Fail if redeem not allowed */ uint256 allowed = comptroller.redeemAllowed( address(this), redeemer, redeemTokens ); if (allowed != 0) { revert RedeemComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert RedeemFreshnessCheck(); } /* Fail gracefully if protocol has insufficient cash */ if (getCashPrior() < redeemAmount) { revert RedeemTransferOutNotPossible(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We calculate the new total supply and redeemer balance, checking for underflow: * totalSupplyNew = totalSupply - redeemTokens * accountTokensNew = accountTokens[redeemer] - redeemTokens */ totalSupply = totalSupply - redeemTokens; accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens; /* * 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, redeemAmount); /* We emit a Transfer event, and a Redeem event */ emit Transfer(redeemer, address(this), redeemTokens); emit Redeem(redeemer, redeemAmount, redeemTokens); /* We call the defense hook */ comptroller.redeemVerify( address(this), redeemer, redeemAmount, redeemTokens ); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowInternal(uint256 borrowAmount) internal nonReentrant { accrueInterest(); // borrowFresh emits borrow-specific logs on errors, so we don't need to borrowFresh(payable(msg.sender), borrowAmount); } /** * @notice Users borrow assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowFresh( address payable borrower, uint256 borrowAmount ) internal { /* Fail if borrow not allowed */ uint256 allowed = comptroller.borrowAllowed( address(this), borrower, borrowAmount ); if (allowed != 0) { revert BorrowComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert BorrowFreshnessCheck(); } /* Fail gracefully if protocol has insufficient underlying cash */ if (getCashPrior() < borrowAmount) { revert BorrowCashNotAvailable(); } /* * We calculate the new borrower and total borrow balances, failing on overflow: * accountBorrowsNew = accountBorrows + borrowAmount * totalBorrowsNew = totalBorrows + borrowAmount */ uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower); uint accountBorrowsNew = accountBorrowsPrev + borrowAmount; uint totalBorrowsNew = totalBorrows + borrowAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We write the previously calculated values into storage. * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer. `*/ accountBorrows[borrower].principal = accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = totalBorrowsNew; /* * 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 emit a Borrow event */ emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay */ function repayBorrowInternal(uint256 repayAmount) internal nonReentrant { accrueInterest(); // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to 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 */ function repayBorrowBehalfInternal( address borrower, uint256 repayAmount ) internal nonReentrant { accrueInterest(); // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to repayBorrowFresh(msg.sender, borrower, repayAmount); } /** * @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) { /* Fail if repayBorrow not allowed */ uint256 allowed = comptroller.repayBorrowAllowed( address(this), payer, borrower, repayAmount ); if (allowed != 0) { revert RepayBorrowComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert RepayBorrowFreshnessCheck(); } /* We fetch the amount the borrower owes, with accumulated interest */ uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower); /* If repayAmount >= accountBorrows, repayAmount = accountBorrows */ uint repayAmountFinal = repayAmount; if (repayAmount >= accountBorrowsPrev) { repayAmountFinal = accountBorrowsPrev; } ///////////////////////// // 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. */ uint actualRepayAmount = doTransferIn(payer, repayAmountFinal); /* * We calculate the new borrower and total borrow balances, failing on underflow: * accountBorrowsNew = accountBorrows - actualRepayAmount * totalBorrowsNew = totalBorrows - actualRepayAmount */ uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount; uint totalBorrowsNew = totalBorrows - actualRepayAmount; /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = totalBorrowsNew; /* We emit a RepayBorrow event */ emit RepayBorrow( payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew ); return 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 */ function liquidateBorrowInternal( address borrower, uint256 repayAmount, CTokenInterface cTokenCollateral ) internal nonReentrant { accrueInterest(); uint error = cTokenCollateral.accrueInterest(); if (error != NO_ERROR) { revert LiquidateAccrueCollateralInterestFailed(error); } // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to 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 */ function liquidateBorrowFresh( address liquidator, address borrower, uint256 repayAmount, CTokenInterface cTokenCollateral ) internal { /* Fail if liquidate not allowed */ uint256 allowed = comptroller.liquidateBorrowAllowed( address(this), address(cTokenCollateral), liquidator, borrower, repayAmount ); if (allowed != 0) { revert LiquidateComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert LiquidateFreshnessCheck(); } /* Verify cTokenCollateral market's block number equals current block number */ if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) { revert LiquidateCollateralFreshnessCheck(); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert LiquidateLiquidatorIsBorrower(); } /* Fail if repayAmount = 0 */ if (repayAmount == 0) { revert LiquidateCloseAmountIsZero(); } /* Fail if repayAmount = -1 */ if (repayAmount == type(uint256).max) { revert LiquidateCloseAmountIsUintMax(); } /* Fail if repayBorrow fails */ uint256 actualRepayAmount = repayBorrowFresh( liquidator, borrower, repayAmount ); ///////////////////////// // 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 == 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 if (address(cTokenCollateral) == address(this)) { seizeInternal(address(this), liquidator, borrower, seizeTokens); } else { require( cTokenCollateral.seize(liquidator, borrower, seizeTokens) == NO_ERROR, "token seizure failed" ); } /* We emit a LiquidateBorrow event */ emit LiquidateBorrow( liquidator, borrower, actualRepayAmount, address(cTokenCollateral), seizeTokens ); } /** * @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 override nonReentrant returns (uint256) { seizeInternal(msg.sender, liquidator, borrower, seizeTokens); return 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 */ function seizeInternal( address seizerToken, address liquidator, address borrower, uint256 seizeTokens ) internal { /* Fail if seize not allowed */ uint256 allowed = comptroller.seizeAllowed( address(this), seizerToken, liquidator, borrower, seizeTokens ); if (allowed != 0) { revert LiquidateSeizeComptrollerRejection(allowed); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert LiquidateSeizeLiquidatorIsBorrower(); } /* * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: * borrowerTokensNew = accountTokens[borrower] - seizeTokens * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens */ uint protocolSeizeTokens = mul_( seizeTokens, Exp({mantissa: protocolSeizeShareMantissa}) ); uint liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens; Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()}); uint protocolSeizeAmount = mul_ScalarTruncate( exchangeRate, protocolSeizeTokens ); uint totalReservesNew = totalReserves + protocolSeizeAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ totalReserves = totalReservesNew; totalSupply = totalSupply - protocolSeizeTokens; accountTokens[borrower] = accountTokens[borrower] - seizeTokens; accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens; /* Emit a Transfer event */ emit Transfer(borrower, liquidator, liquidatorSeizeTokens); emit Transfer(borrower, address(this), protocolSeizeTokens); emit ReservesAdded( address(this), protocolSeizeAmount, totalReservesNew ); } /*** 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 override returns (uint256) { // Check caller = admin if (msg.sender != admin) { revert SetPendingAdminOwnerCheck(); } // 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 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 override returns (uint256) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin) { revert AcceptAdminPendingAdminCheck(); } // 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 = payable(address(0)); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return 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 override returns (uint256) { // Check caller is admin if (msg.sender != admin) { revert SetComptrollerOwnerCheck(); } 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 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 override nonReentrant returns (uint256) { accrueInterest(); // _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) { revert SetReserveFactorAdminCheck(); } require( newReserveFactorMantissa <= 5e17, "reserve factor can't exceed 50%" ); // Verify market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert SetReserveFactorFreshCheck(); } // Check newReserveFactor ≤ maxReserveFactor if (newReserveFactorMantissa > RESERVE_FACTOR_MAX_MANTISSA) { revert SetReserveFactorBoundsCheck(); } uint256 oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor( oldReserveFactorMantissa, newReserveFactorMantissa ); return 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) { accrueInterest(); // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to. _addReservesFresh(addAmount); return NO_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) { uint256 actualAddAmount; // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert AddReservesFactorFreshCheck(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); totalReserves = totalReserves + actualAddAmount; /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */ emit ReservesAdded(msg.sender, actualAddAmount, totalReserves); /* Return (NO_ERROR, actualAddAmount) */ return (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 override nonReentrant returns (uint256) { accrueInterest(); // _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) { revert ReduceReservesAdminCheck(); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert ReduceReservesFreshCheck(); } // Fail gracefully if protocol has insufficient underlying cash if (getCashPrior() < reduceAmount) { revert ReduceReservesCashNotAvailable(); } // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { revert ReduceReservesCashValidation(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) totalReservesNew = totalReserves - reduceAmount; // We checked reduceAmount <= totalReserves above, so this should never revert. require( totalReservesNew <= totalReserves, "reduce reserves unexpected underflow" ); // 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 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 override returns (uint256) { accrueInterest(); // _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) { revert SetInterestRateModelOwnerCheck(); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert SetInterestRateModelFreshCheck(); } // 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 NO_ERROR; } /** * @notice Sets a new protocol seize share (taken from liquidation) for the protocol. * @dev Admin function to set a new protocol seize share * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setProtocolSeizeShare( uint256 newProtocolSeizeShareMantissa ) external override nonReentrant returns (uint256) { // Check caller is admin if (msg.sender != admin) { revert setProtocolSeizeShareAdminCheck(); } uint256 oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa; require( newProtocolSeizeShareMantissa <= 5e17, "protocol seize share can't exceed 50%" ); protocolSeizeShareMantissa = newProtocolSeizeShareMantissa; emit NewProtocolSeizeShare( oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa ); return 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 virtual 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 virtual 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 virtual; /*** 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 } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import {InterestRateModel} from "./InterestRateModel.sol"; import {ComptrollerInterface} from "./ComptrollerInterface.sol"; import {EIP20NonStandardInterface} from "./EIP20NonStandardInterface.sol"; interface CTokenInterface { function isCToken() external returns (bool); /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ event AccrueInterest( uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows ); /** * @notice Event emitted when tokens are minted */ event Mint(address minter, uint256 mintAmount, uint256 mintTokens); /** * @notice Event emitted when tokens are redeemed */ event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens); /** * @notice Event emitted when underlying is borrowed */ event Borrow( address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows ); /** * @notice Event emitted when a borrow is repaid */ 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, uint256 repayAmount, address cTokenCollateral, uint256 seizeTokens ); /*** Admin Events ***/ /** * @notice Event emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Event emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); /** * @notice Event emitted when comptroller is changed */ event NewComptroller( ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller ); /** * @notice Event emitted when interestRateModel is changed */ event NewMarketInterestRateModel( InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel ); /** * @notice Event emitted when the reserve factor is changed */ event NewReserveFactor( uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa ); /** * @notice Event emitted when the reserves are added */ event ReservesAdded( address benefactor, uint256 addAmount, uint256 newTotalReserves ); /** * @notice Event emitted when the reserves are reduced */ event ReservesReduced( address admin, uint256 reduceAmount, uint256 newTotalReserves ); /** * @notice EIP20 Transfer event */ event Transfer(address indexed from, address indexed to, uint256 amount); /** * @notice EIP20 Approval event */ event Approval( address indexed owner, address indexed spender, uint256 amount ); /** * @notice Event emitted when protocol seize share is changed */ event NewProtocolSeizeShare( uint256 oldProtocolSeizeShare, uint256 newProtocolSeizeShare ); // /** // * @notice Failure event // */ // event Failure(uint256 error, uint256 info, uint256 detail); /*** User Interface ***/ 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 ) external view returns (uint256); function exchangeRateCurrent() external returns (uint256); function exchangeRateStored() external view returns (uint256); function getCash() external view returns (uint256); function accrueInterest() external returns (uint256); function accrualBlockNumber() external returns (uint256); function seize( address liquidator, address borrower, uint256 seizeTokens ) external returns (uint256); /*** Admin Functions ***/ function _setPendingAdmin( address payable newPendingAdmin ) external returns (uint256); function _acceptAdmin() external returns (uint256); function _setComptroller( ComptrollerInterface newComptroller ) external returns (uint256); function _setReserveFactor( uint256 newReserveFactorMantissa ) external returns (uint256); function _reduceReserves(uint256 reduceAmount) external returns (uint256); function _setInterestRateModel( InterestRateModel newInterestRateModel ) external returns (uint256); function _setProtocolSeizeShare( uint256 newProtocolSeizeShareMantissa ) external returns (uint256); } abstract contract CTokenStorage is CTokenInterface { /** * @dev Guard variable for re-entrancy checks */ bool internal _notEntered; /** * @notice EIP-20 token name for this token */ string public name; /** * @notice EIP-20 token symbol for this token */ string public symbol; /** * @notice EIP-20 token decimals for this token */ uint8 public decimals; /** * @notice Maximum borrow rate that can ever be applied (.0005% / block) */ uint256 internal constant BORROW_RATE_MAX_MANTISSA = 0.0005e16; /** * @notice Maximum fraction of interest that can be set aside for reserves */ uint256 internal constant RESERVE_FACTOR_MAX_MANTISSA = 1e18; /** * @notice Administrator for this contract */ address payable public admin; /** * @notice Pending administrator for this contract */ address payable public pendingAdmin; /** * @notice Contract which oversees inter-cToken operations */ ComptrollerInterface public comptroller; /** * @notice Model which tells what the current interest rate should be */ InterestRateModel public interestRateModel; /** * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) */ uint256 internal initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves * It can range from 0% to 50%. */ uint256 public reserveFactorMantissa; /** * @notice Block number that interest was last accrued at */ uint256 public accrualBlockNumber; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ uint256 public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ uint256 public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ uint256 public totalReserves; /** * @notice Total number of tokens in circulation */ uint256 public totalSupply; /** * @notice Official record of token balances for each account */ mapping(address => uint256) internal accountTokens; /** * @notice Approved token transfer amounts on behalf of others */ mapping(address => mapping(address => uint256)) internal transferAllowances; /** * @notice Container for borrow balance information * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action * @member interestIndex Global borrowIndex as of the most recent balance-changing action */ struct BorrowSnapshot { uint256 principal; uint256 interestIndex; } /** * @notice Mapping of account addresses to outstanding borrow balances */ mapping(address => BorrowSnapshot) internal accountBorrows; /** * @notice Share of seized collateral that is added to reserves * It can range from 0% to 50%. */ uint256 public protocolSeizeShareMantissa = 3e16; // 3% } abstract contract CErc20Storage { /** * @notice Underlying asset for this CToken */ address public underlying; } interface CErc20Interface { /*** User Interface ***/ function mint(uint256 mintAmount) external returns (uint256); function redeem(uint256 redeemTokens) external returns (uint256); function redeemUnderlying(uint256 redeemAmount) external returns (uint256); function borrow(uint256 borrowAmount) external returns (uint256); 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 sweepToken(EIP20NonStandardInterface token) external; /*** Admin Functions ***/ function _addReserves(uint256 addAmount) external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; interface ComptrollerInterface { /// @notice Indicator that this is a Comptroller contract (for inspection) function isComptroller() external view returns (bool); /*** Assets You Are In ***/ function enterMarkets( address[] calldata cTokens ) external returns (uint256[] memory); function exitMarket(address cToken) external returns (uint256); /*** Policy Hooks ***/ function mintAllowed( address cToken, address minter, uint256 mintAmount ) external returns (uint256); function redeemAllowed( address cToken, address redeemer, uint256 redeemTokens ) external returns (uint256); function redeemVerify( address cToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens ) external; function borrowAllowed( address cToken, address borrower, uint256 borrowAmount ) external returns (uint256); function repayBorrowAllowed( address cToken, address payer, address borrower, uint256 repayAmount ) external returns (uint256); function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint256 repayAmount ) external returns (uint256); function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external returns (uint256); function transferAllowed( address cToken, address src, address dst, uint256 transferTokens ) external returns (uint256); /*** Liquidity/Liquidation Calculations ***/ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, uint256 repayAmount ) external view returns (uint256, uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import {CToken} from "./CToken.sol"; import {IPriceOracle} from "./IPriceOracle.sol"; contract UnitrollerAdminStorage { /** * @notice Administrator for this contract */ address public admin; /** * @notice Pending administrator for this contract */ address public pendingAdmin; /** * @notice Active brains of Unitroller */ address public comptrollerImplementation; /** * @notice Pending brains of Unitroller */ address public pendingComptrollerImplementation; } contract ComptrollerV1Storage is UnitrollerAdminStorage { /** * @notice Oracle which gives the price of any given asset */ IPriceOracle public oracle; /** * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow */ uint public closeFactorMantissa; /** * @notice Multiplier representing the discount on collateral that a liquidator receives */ uint public liquidationIncentiveMantissa; /** * @notice Max number of assets a single account can participate in (borrow or use as collateral) */ uint public maxAssets; /** * @notice Per-account mapping of "assets you are in", capped by maxAssets */ mapping(address => CToken[]) public accountAssets; } contract ComptrollerV2Storage is ComptrollerV1Storage { 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; /// @notice Per-market mapping of "accounts in this asset" mapping(address => bool) accountMembership; /// @notice Whether or not this market receives COMP bool isComped; } /** * @notice Official mapping of cTokens -> Market metadata * @dev Used e.g. to determine if a market is supported */ 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. * Liquidation / seizing / transfer can only be paused globally, not by market. */ address public pauseGuardian; bool public transferGuardianPaused; bool public seizeGuardianPaused; mapping(address => bool) public mintGuardianPaused; mapping(address => bool) public borrowGuardianPaused; } 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; } /// @notice A list of all markets CToken[] public allMarkets; /** * @dev Maps borrowers to booleans indicating if they have entered any markets */ mapping(address => bool) internal borrowers; /// @notice A list of all borrowers who have entered markets address[] internal allBorrowers; /// @notice Indexes of borrower account addresses in the `allBorrowers` array mapping(address => uint256) internal borrowerIndexes; /// @notice The rate at which the flywheel distributes COMP, per block uint public compRate; /// @notice The portion of compRate that each market currently receives mapping(address => uint) public compSpeeds; /// @notice The COMP market supply state for each market mapping(address => CompMarketState) public compSupplyState; /// @notice The COMP market borrow state for each market 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; /// @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; /// @notice The COMP accrued but not yet transferred to each user mapping(address => uint) public compAccrued; } contract ComptrollerV4Storage is ComptrollerV3Storage { // @notice The capGuardian can set borrowCaps or supplyCaps to any number for any market. address public capGuardian; // @notice Borrow caps enforced by borrowAllowed for each cToken address. Defaults to zero which corresponds to unlimited borrowing. mapping(address => uint) public borrowCaps; // @notice Supply caps enforced by supplyAllowed for each cToken address. Defaults to zero which corresponds to unlimited supplying. mapping(address => uint) public supplyCaps; } contract ComptrollerV5Storage is ComptrollerV4Storage { /// @notice The portion of COMP that each contributor receives per block mapping(address => uint) public compContributorSpeeds; /// @notice Last block at which a contributor's COMP rewards have been allocated mapping(address => uint) public lastContributorBlock; } contract ComptrollerV6Storage is ComptrollerV5Storage { /// @notice The rate at which comp is distributed to the corresponding borrow market (per block) mapping(address => uint) public compBorrowSpeeds; /// @notice The rate at which comp is distributed to the corresponding supply market (per block) mapping(address => uint) public compSupplySpeeds; /// @notice The comp token address address internal compAddress; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; /** * @title ERC 20 Token Standard Interface * https://eips.ethereum.org/EIPS/eip-20 */ 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 */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return balance The balance */ 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 success 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 success 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 success 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 remaining 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); event Approval( address indexed owner, address indexed spender, uint256 amount ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; /** * @title EIP20NonStandardInterface * @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 */ interface EIP20NonStandardInterface { /** * @notice Get the total number of tokens in circulation * @return The supply of tokens */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return balance The balance */ function balanceOf(address owner) external view returns (uint256 balance); /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @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; /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @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 success 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 remaining 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); event Approval( address indexed owner, address indexed spender, uint256 amount ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; contract ComptrollerErrorReporter { enum Error { NO_ERROR, UNAUTHORIZED, COMPTROLLER_MISMATCH, INSUFFICIENT_SHORTFALL, INSUFFICIENT_LIQUIDITY, INVALID_CLOSE_FACTOR, INVALID_COLLATERAL_FACTOR, INVALID_LIQUIDATION_INCENTIVE, MARKET_NOT_ENTERED, // no longer possible MARKET_NOT_LISTED, MARKET_ALREADY_LISTED, MATH_ERROR, NONZERO_BORROW_BALANCE, PRICE_ERROR, REJECTION, SNAPSHOT_ERROR, TOO_MANY_ASSETS, TOO_MUCH_REPAY } enum FailureInfo { ACCEPT_ADMIN_PENDING_ADMIN_CHECK, ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK, EXIT_MARKET_BALANCE_OWED, EXIT_MARKET_REJECTION, SET_CLOSE_FACTOR_OWNER_CHECK, SET_CLOSE_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_OWNER_CHECK, SET_COLLATERAL_FACTOR_NO_EXISTS, SET_COLLATERAL_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_WITHOUT_PRICE, SET_IMPLEMENTATION_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_VALIDATION, SET_MAX_ASSETS_OWNER_CHECK, SET_PENDING_ADMIN_OWNER_CHECK, SET_PENDING_IMPLEMENTATION_OWNER_CHECK, SET_PRICE_ORACLE_OWNER_CHECK, SUPPORT_MARKET_EXISTS, SUPPORT_MARKET_OWNER_CHECK, SET_PAUSE_GUARDIAN_OWNER_CHECK } /** * @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 (uint256) { emit Failure(uint256(err), uint256(info), 0); return uint256(err); } /** * @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 uint256(err); } } contract TokenErrorReporter { uint public constant NO_ERROR = 0; // support legacy return codes error TransferComptrollerRejection(uint256 errorCode); error TransferNotAllowed(); error TransferNotEnough(); error TransferTooMuch(); error MintComptrollerRejection(uint256 errorCode); error MintFreshnessCheck(); error RedeemComptrollerRejection(uint256 errorCode); error RedeemFreshnessCheck(); error RedeemTransferOutNotPossible(); error BorrowComptrollerRejection(uint256 errorCode); error BorrowFreshnessCheck(); error BorrowCashNotAvailable(); error RepayBorrowComptrollerRejection(uint256 errorCode); error RepayBorrowFreshnessCheck(); error LiquidateComptrollerRejection(uint256 errorCode); error LiquidateFreshnessCheck(); error LiquidateCollateralFreshnessCheck(); error LiquidateAccrueBorrowInterestFailed(uint256 errorCode); error LiquidateAccrueCollateralInterestFailed(uint256 errorCode); error LiquidateLiquidatorIsBorrower(); error LiquidateCloseAmountIsZero(); error LiquidateCloseAmountIsUintMax(); error LiquidateRepayBorrowFreshFailed(uint256 errorCode); error LiquidateSeizeComptrollerRejection(uint256 errorCode); error LiquidateSeizeLiquidatorIsBorrower(); error AcceptAdminPendingAdminCheck(); error SetComptrollerOwnerCheck(); error SetPendingAdminOwnerCheck(); error SetReserveFactorAdminCheck(); error SetReserveFactorFreshCheck(); error SetReserveFactorBoundsCheck(); error AddReservesFactorFreshCheck(uint256 actualAddAmount); error ReduceReservesAdminCheck(); error ReduceReservesFreshCheck(); error ReduceReservesCashNotAvailable(); error ReduceReservesCashValidation(); error SetInterestRateModelOwnerCheck(); error SetInterestRateModelFreshCheck(); error setProtocolSeizeShareAdminCheck(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; /** * @title Exponential module for storing fixed-precision decimals * @author Compound * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places. * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is: * `Exp({mantissa: 5100000000000000000})`. */ contract ExponentialNoError { uint constant EXP_SCALE = 1e18; uint constant DOUBLE_SCALE = 1e36; uint constant HALF_EXP_SCALE = EXP_SCALE / 2; uint constant MANTISSA_ONE = EXP_SCALE; struct Exp { uint mantissa; } struct Double { uint mantissa; } /** * @dev Truncates the given exp to a whole number value. * For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15 */ function truncate(Exp memory exp) internal pure returns (uint) { // Note: We are not using careful math here as we're performing a division that cannot fail return exp.mantissa / EXP_SCALE; } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mul_ScalarTruncate( Exp memory a, uint scalar ) internal pure returns (uint) { Exp memory product = mul_(a, scalar); return truncate(product); } /** * @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 ) internal pure returns (uint) { Exp memory product = mul_(a, scalar); return add_(truncate(product), addend); } /** * @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; } /** * @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 left Exp > right 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) internal pure returns (bool) { return value.mantissa == 0; } function safe224( uint n, string memory errorMessage ) internal pure returns (uint224) { require(n < 2 ** 224, errorMessage); return uint224(n); } function safe32( uint n, string memory errorMessage ) internal pure returns (uint32) { require(n < 2 ** 32, errorMessage); return uint32(n); } 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 ) internal pure returns (Double memory) { return Double({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(uint a, uint b) internal pure returns (uint) { return a + b; } 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 ) internal pure returns (Double memory) { return Double({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(uint a, uint b) internal pure returns (uint) { return a - b; } function mul_( Exp memory a, Exp memory b ) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b.mantissa) / EXP_SCALE}); } function mul_(Exp memory a, uint b) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b)}); } function mul_(uint a, Exp memory b) internal pure returns (uint) { return mul_(a, b.mantissa) / EXP_SCALE; } function mul_( Double memory a, Double memory b ) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE}); } function mul_( Double memory a, uint b ) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b)}); } function mul_(uint a, Double memory b) internal pure returns (uint) { return mul_(a, b.mantissa) / DOUBLE_SCALE; } function mul_(uint a, uint b) internal pure returns (uint) { return a * b; } function div_( Exp memory a, Exp memory b ) internal pure returns (Exp memory) { return Exp({mantissa: div_(mul_(a.mantissa, EXP_SCALE), b.mantissa)}); } function div_(Exp memory a, uint b) internal pure returns (Exp memory) { return Exp({mantissa: div_(a.mantissa, b)}); } function div_(uint a, Exp memory b) internal pure returns (uint) { return div_(mul_(a, EXP_SCALE), b.mantissa); } function div_( Double memory a, Double memory b ) internal pure returns (Double memory) { return Double({ mantissa: div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa) }); } function div_( Double memory a, uint b ) internal pure returns (Double memory) { return Double({mantissa: div_(a.mantissa, b)}); } function div_(uint a, Double memory b) internal pure returns (uint) { return div_(mul_(a, DOUBLE_SCALE), b.mantissa); } function div_(uint a, uint b) internal pure returns (uint) { return a / b; } function fraction(uint a, uint b) internal pure returns (Double memory) { return Double({mantissa: div_(mul_(a, DOUBLE_SCALE), b)}); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import {CToken} from "./CToken.sol"; interface IPriceOracle { /// @notice Indicator that this is a PriceOracle contract (for inspection) function isPriceOracle() external returns (bool); /** * @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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.12; /** * @title Compound's InterestRateModel Interface * @author Compound */ interface InterestRateModel { function isInterestRateModel() external view returns (bool); /** * @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 amount 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 amount 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); }
{ "optimizer": { "enabled": true } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":false,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingImplementation","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingImplementation","type":"address"}],"name":"NewPendingImplementation","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_acceptImplementation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingImplementation","type":"address"}],"name":"_setPendingImplementation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"comptrollerImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingComptrollerImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
9c4d535b0000000000000000000000000000000000000000000000000000000000000000010000d7f891695bb518145dc60f91ed0082d5a5507724681a2717c6ea6b816600000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x000400000000000200000000030100190000006003300270000000b80430019700030000004103550002000000010355000000b80030019d000100000000001f0000008001000039000000400010043f0000000101200190000000280000c13d00000002010003670000000003000031000000040230008c000000860000413d000000000201043b000000e002200270000000bb0420009c000000350000213d000000c10420009c000000490000213d000000c40420009c0000011d0000613d000000c50220009c000000860000c13d0000000001000416000000000110004c0000017d0000c13d000000000100003102db01880000040f02db023a0000040f000000400200043d0000000000120435000000b801000041000000b80320009c00000000010240190000004001100210000000c8011001c7000002dc0001042e0000000001000416000000000110004c0000017d0000c13d000000000100041a000000b9011001970000000002000411000000000121019f000000000010041b000000200100003900000100001004430000012000000443000000ba01000041000002dc0001042e000000bc0420009c000000670000213d000000bf0420009c000001340000613d000000c00220009c000000860000c13d0000000001000416000000000110004c0000017d0000c13d000000000100003102db01880000040f02db019d0000040f000000400200043d0000000000120435000000b801000041000000b80320009c00000000010240190000004001100210000000c8011001c7000002dc0001042e000000c20420009c000001500000613d000000c30220009c000000860000c13d0000000001000416000000000110004c0000017d0000c13d000000040100008a0000000001100031000000c602000041000000000310004c00000000030000190000000003024019000000c601100197000000000410004c000000000200a019000000c60110009c00000000010300190000000001026019000000000110004c0000017d0000c13d02db01dc0000040f000000400200043d0000000000120435000000b801000041000000b80320009c00000000010240190000004001100210000000c8011001c7000002dc0001042e000000bd0420009c0000016c0000613d000000be0220009c000000860000c13d0000000001000416000000000110004c0000017d0000c13d000000040100008a0000000001100031000000c602000041000000000310004c00000000030000190000000003024019000000c601100197000000000410004c000000000200a019000000c60110009c00000000010300190000000001026019000000000110004c0000017d0000c13d000000000100041a000000c701100197000000400200043d0000000000120435000000b801000041000000b80320009c00000000010240190000004001100210000000c8011001c7000002dc0001042e0000001f0430018f0000000202000039000000000202041a000000c7022001970000000503300272000000950000613d00000000050000190000000506500210000000000761034f000000000707043b000000800660003900000000007604350000000105500039000000000635004b0000008d0000413d000000000540004c000000a40000613d0000000503300210000000000131034f00000003044002100000008003300039000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000000001000031000000800310003900000000000304350000000003000414000000040420008c000000ad0000c13d00000001020000390000000101000031000000ba0000013d000000b804000041000000b80530009c0000000003048019000000c00330021000000060011002100000000001130019000000ca011001c702db02d60000040f000000010220018f00030000000103550000006001100270000100b80010019d000000b801100197000000000310004c0000000005000019000000ed0000613d000000cb0310009c000001160000813d0000001f03100039000000200400008a000000000343016f0000003f03300039000000000443016f000000400300043d0000000004430019000000000534004b00000000050000190000000105004039000000cc0640009c000001160000213d0000000105500190000001160000c13d000000400040043f0000000001130436000000030300036700000001050000310000001f0450018f0000000505500272000000dd0000613d000000000600001900000005076002100000000008710019000000000773034f000000000707043b00000000007804350000000106600039000000000756004b000000d50000413d000000000640004c000000ec0000613d0000000505500210000000000353034f00000000015100190000000304400210000000000501043300000000054501cf000000000545022f000000000303043b0000010004400089000000000343022f00000000034301cf000000000353019f000000000031043500000001050000310000000303000367000000400100043d0000001f0450018f0000000505500272000000fb0000613d000000000600001900000005076002100000000008710019000000000773034f000000000707043b00000000007804350000000106600039000000000756004b000000f30000413d000000000640004c0000010a0000613d0000000505500210000000000353034f00000000055100190000000304400210000000000605043300000000064601cf000000000646022f000000000303043b0000010004400089000000000343022f00000000034301cf000000000363019f0000000000350435000000b8030000410000000104000031000000b80540009c0000000004038019000000b80510009c000000000103801900000040011002100000006003400210000000000113019f000000000220004c0000011c0000c13d000002dd00010430000000cd0100004100000000001004350000004101000039000000040010043f000000ce01000041000002dd00010430000002dc0001042e0000000001000416000000000110004c0000017d0000c13d000000040100008a0000000001100031000000c602000041000000000310004c00000000030000190000000003024019000000c601100197000000000410004c000000000200a019000000c60110009c00000000010300190000000001026019000000000110004c0000017d0000c13d0000000101000039000000000101041a000000c701100197000000800010043f000000c901000041000002dc0001042e0000000001000416000000000110004c0000017d0000c13d000000040100008a0000000001100031000000c602000041000000000310004c00000000030000190000000003024019000000c601100197000000000410004c000000000200a019000000c60110009c00000000010300190000000001026019000000000110004c0000017d0000c13d0000000301000039000000000101041a000000c701100197000000400200043d0000000000120435000000b801000041000000b80320009c00000000010240190000004001100210000000c8011001c7000002dc0001042e0000000001000416000000000110004c0000017d0000c13d000000040100008a0000000001100031000000c602000041000000000310004c00000000030000190000000003024019000000c601100197000000000410004c000000000200a019000000c60110009c00000000010300190000000001026019000000000110004c0000017d0000c13d0000000201000039000000000101041a000000c701100197000000400200043d0000000000120435000000b801000041000000b80320009c00000000010240190000004001100210000000c8011001c7000002dc0001042e0000000001000416000000000110004c0000017d0000c13d000000040100008a0000000001100031000000c602000041000000000310004c00000000030000190000000003024019000000c601100197000000000410004c000000000200a019000000c60110009c00000000010300190000000001026019000000000110004c0000017f0000613d0000000001000019000002dd0001043002db02780000040f000000400200043d0000000000120435000000b801000041000000b80320009c00000000010240190000004001100210000000c8011001c7000002dc0001042e000000040110008a000000c6020000410000001f0310008c00000000030000190000000003022019000000c601100197000000000410004c0000000002008019000000c60110009c00000000010300190000000001026019000000000110004c0000019b0000613d00000004010000390000000201100367000000000101043b000000c70210009c0000019b0000213d000000000001042d0000000001000019000002dd000104300001000000000002000000000200041a000000c7022001970000000003000411000000000223004b000001c10000c13d000000c7011001970000000302000039000000000302041a000000b904300197000000000414019f000000000042041b000000400200043d00000020042000390000000000140435000000c7013001970000000000120435000000b8010000410000000003000414000000b80430009c0000000003018019000000b80420009c00000000010240190000004001100210000000c002300210000000000112019f000000d1011001c70000800d020000390000000103000039000000d20400004102db02d10000040f00000001012001900000000001000019000001db0000c13d0000000001000019000002dd00010430000000400100043d00000020021000390000000f03000039000000000032043500000040021000390000000000020435000000010200003900000000002104350000000003020019000100000003001d000000b8020000410000000005000414000000b80450009c0000000005028019000000b80410009c00000000010280190000004001100210000000c002500210000000000112019f000000cf011001c70000800d02000039000000d00400004102db02d10000040f00000001012001900000000101000029000001bf0000613d000000000001042d00020000000000020000000305000039000000000105041a000000c7041001970000000001000411000000000141004b000002210000c13d000000000140004c000002210000613d0000000201000039000000000201041a000000b903200197000200000004001d000000000343019f000000000031041b000000000305041a000000b903300197000000000035041b000000000101041a000000c701100197000000400300043d00000020043000390000000000140435000000c7012001970000000000130435000000b8010000410000000002000414000000b80420009c0000000002018019000000b80430009c00000000010340190000004001100210000000c002200210000000000112019f000000d1011001c70000800d020000390000000103000039000000d304000041000100000005001d02db02d10000040f00000001012001900000021f0000613d0000000101000029000000000101041a000000c701100197000000400200043d0000002003200039000000000013043500000002010000290000000000120435000000b8010000410000000003000414000000b80430009c0000000003018019000000b80420009c00000000010240190000004001100210000000c002300210000000000112019f000000d1011001c70000800d020000390000000103000039000000d20400004102db02d10000040f00000001012001900000000001000019000002390000c13d0000000001000019000002dd00010430000000400100043d000000200210003900000001030000390000000000320435000000400210003900000000000204350000000000310435000200000003001d000000b8020000410000000005000414000000b80450009c0000000005028019000000b80410009c00000000010280190000004001100210000000c002500210000000000112019f000000cf011001c70000800d02000039000000d00400004102db02d10000040f000000010120019000000002010000290000021f0000613d000000000001042d0001000000000002000000000200041a000000c7022001970000000003000411000000000223004b0000025d0000c13d000000c7011001970000000103000039000000000203041a000000b904200197000000000414019f000000000043041b000000400400043d00000020054000390000000000150435000000c7012001970000000000140435000000b8010000410000000002000414000000b80520009c0000000002018019000000b80540009c00000000010440190000004001100210000000c002200210000000000112019f000000d1011001c70000800d02000039000000d40400004102db02d10000040f00000001012001900000000001000019000002770000c13d0000000001000019000002dd00010430000000400100043d00000020021000390000000e03000039000000000032043500000040021000390000000000020435000000010200003900000000002104350000000003020019000100000003001d000000b8020000410000000005000414000000b80450009c0000000005028019000000b80410009c00000000010280190000004001100210000000c002500210000000000112019f000000cf011001c70000800d02000039000000d00400004102db02d10000040f000000010120019000000001010000290000025b0000613d000000000001042d00020000000000020000000103000039000000000103041a000000c7041001970000000001000411000000000241004b000200000003001d000002ba0000c13d000000000110004c000002ba0000613d000000000100041a000000b902100197000000000242019f000000000020041b000000000203041a000000b902200197000000000023041b000000000200041a000000c702200197000000400500043d000100000004001d00000020045000390000000000240435000000c7011001970000000000150435000000b8010000410000000002000414000000b80420009c0000000002018019000000b80450009c00000000010540190000004001100210000000c002200210000000000112019f000000d1011001c70000800d02000039000000d50400004102db02d10000040f00000002030000290000000101200190000002b80000613d000000000103041a000000c701100197000000400200043d0000002004200039000000000014043500000001010000290000000000120435000000b8010000410000000005000414000000b80450009c0000000005018019000000b80420009c00000000010240190000004001100210000000c002500210000000000112019f000000d1011001c70000800d02000039000000d40400004102db02d10000040f00000001012001900000000001000019000002d00000c13d0000000001000019000002dd00010430000000400100043d00000040021000390000000000020435000000200210003900000000000204350000000000310435000000b8020000410000000005000414000000b80450009c0000000005028019000000b80410009c00000000010280190000004001100210000000c002500210000000000112019f000000cf011001c70000800d02000039000000d00400004102db02d10000040f00000002010000290000000102200190000002b80000613d000000000001042d000002d4002104210000000102000039000000000001042d0000000002000019000000000001042d000002d9002104250000000102000039000000000001042d0000000002000019000000000001042d000002db00000432000002dc0001042e000002dd000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000020000000000000000000000000000004000000100000000000000000000000000000000000000000000000000000000000000000000000000dcfbc0c600000000000000000000000000000000000000000000000000000000e9c714f100000000000000000000000000000000000000000000000000000000e9c714f200000000000000000000000000000000000000000000000000000000f851a44000000000000000000000000000000000000000000000000000000000dcfbc0c700000000000000000000000000000000000000000000000000000000e992a04100000000000000000000000000000000000000000000000000000000bb82aa5d00000000000000000000000000000000000000000000000000000000bb82aa5e00000000000000000000000000000000000000000000000000000000c1e80334000000000000000000000000000000000000000000000000000000002678224700000000000000000000000000000000000000000000000000000000b71d1a0c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff4e487b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000020000000000000000000000000000000000006000000000000000000000000045b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa00200000000000000000000000000000000000040000000000000000000000000e945ccee5d701fc83f9b8aa8ca94ea4219ec1fcbd4f4cab4f0ea57c5c3e1d815d604de94d45953f9138079ec1b82d533cb2160c906d1076d1f7ed54befbca97aca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9f9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dcfca3c0fb3ffaf59f191d62d14da0d22d9aa594e07589bf5d9e482d77d0fd7db0
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.