diff --git a/.circleci/config.yml b/.circleci/config.yml index 3c75a1184..8912109c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -140,7 +140,7 @@ jobs: lint: docker: - image: circleci/node:11 - - image: trufflesuite/ganache-cli:v6.2.5 + - image: trufflesuite/ganache-cli:v6.7.0 working_directory: ~/repo steps: - checkout diff --git a/contracts/CErc20.sol b/contracts/CErc20.sol index b86ce526c..0fd05d7cc 100644 --- a/contracts/CErc20.sol +++ b/contracts/CErc20.sol @@ -30,8 +30,9 @@ contract CErc20 is CToken { uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, - uint decimals_) public - CToken(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_) { + uint8 decimals_, + address payable admin_) public + CToken(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_, admin_) { // Set underlying underlying = underlying_; EIP20Interface(underlying).totalSupply(); // Sanity check the underlying diff --git a/contracts/CEther.sol b/contracts/CEther.sol index 6f5cd09c3..adf148c4d 100644 --- a/contracts/CEther.sol +++ b/contracts/CEther.sol @@ -22,8 +22,9 @@ contract CEther is CToken { uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, - uint decimals_) public - CToken(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_) {} + uint8 decimals_, + address payable admin_) public + CToken(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_, admin_) {} /*** User Interface ***/ diff --git a/contracts/CToken.sol b/contracts/CToken.sol index df653280f..fd5e88b31 100644 --- a/contracts/CToken.sol +++ b/contracts/CToken.sol @@ -32,17 +32,18 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu /** * @notice EIP-20 token decimals for this token */ - uint public decimals; + uint8 public decimals; /** * @notice Maximum borrow rate that can ever be applied (.0005% / block) */ - uint constant borrowRateMaxMantissa = 5e14; + + uint internal constant borrowRateMaxMantissa = 0.0005e16; /** * @notice Maximum fraction of interest that can be set aside for reserves */ - uint constant reserveFactorMaxMantissa = 1e18; + uint internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract @@ -80,7 +81,7 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu uint public accrualBlockNumber; /** - * @notice Accumulator of total earned interest since the opening of the market + * @notice Accumulator of the total earned interest rate since the opening of the market */ uint public borrowIndex; @@ -102,12 +103,12 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu /** * @notice Official record of token balances for each account */ - mapping (address => uint256) accountTokens; + mapping (address => uint256) internal accountTokens; /** * @notice Approved token transfer amounts on behalf of others */ - mapping (address => mapping (address => uint256)) transferAllowances; + mapping (address => mapping (address => uint256)) internal transferAllowances; /** * @notice Container for borrow balance information @@ -122,7 +123,7 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu /** * @notice Mapping of account addresses to outstanding borrow balances */ - mapping(address => BorrowSnapshot) accountBorrows; + mapping(address => BorrowSnapshot) internal accountBorrows; /*** Market Events ***/ @@ -199,20 +200,21 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu * @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 + * @param admin_ Administrator of this token */ constructor(ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, - uint decimals_) internal { - // Set admin to msg.sender - admin = msg.sender; - + uint8 decimals_, + address payable admin_) internal { // Set initial exchange rate initialExchangeRateMantissa = initialExchangeRateMantissa_; require(initialExchangeRateMantissa > 0, "Initial exchange rate must be greater than zero."); + // Temporarily set msg.sender to admin to set comptroller and interest rate model + admin = msg.sender; // Set the comptroller uint err = _setComptroller(comptroller_); require(err == uint(Error.NO_ERROR), "Setting comptroller failed"); @@ -228,6 +230,9 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu name = name_; symbol = symbol_; decimals = decimals_; + + // Set the proper admin now that initialization is done + admin = admin_; } /** @@ -295,7 +300,6 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu /* We emit a Transfer event */ emit Transfer(src, dst, tokens); - /* We call the defense hook (which checks for under-collateralization) */ comptroller.transferVerify(address(this), src, dst, tokens); return uint(Error.NO_ERROR); @@ -790,7 +794,7 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted - * @param redeemAmount The amount of underlying to redeem + * @param redeemAmount The amount of underlying to receive from redeeming cTokens * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant returns (uint) { @@ -817,8 +821,8 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu * @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 zero) - * @param redeemAmountIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be zero) + * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero) + * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal returns (uint) { @@ -1302,8 +1306,6 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu * @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) - * - * TODO: Should we add a second arg to verify, like a checksum of `newAdmin` address? */ function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) { // Check caller = admin @@ -1402,7 +1404,6 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu // Verify market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { - // TODO: static_assert + no error code? return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK); } @@ -1452,7 +1453,6 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { - // TODO: static_assert + no error code? return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK); } @@ -1462,7 +1462,6 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu } // Check reduceAmount ≤ reserves[n] (totalReserves) - // TODO: I'm following the spec literally here but I think we should we just use SafeMath instead and fail on an error (which would be underflow) if (reduceAmount > totalReserves) { return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION); } @@ -1522,7 +1521,6 @@ contract CToken is EIP20Interface, Exponential, TokenErrorReporter, ReentrancyGu // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { - // TODO: static_assert + no error code? return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK); } diff --git a/contracts/Comptroller.sol b/contracts/Comptroller.sol index 2991b77cf..516b53bbb 100644 --- a/contracts/Comptroller.sol +++ b/contracts/Comptroller.sol @@ -12,32 +12,7 @@ import "./Unitroller.sol"; * @title Compound's Comptroller Contract * @author Compound */ -contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { - 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 Official mapping of cTokens -> Market metadata - * @dev Used e.g. to determine if a market is supported - */ - mapping(address => Market) public markets; - +contract Comptroller is ComptrollerV2Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { /** * @notice Emitted when an admin supports a market */ @@ -78,25 +53,35 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE */ event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); + /** + * @notice Emitted when pause guardian is changed + */ + event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); + + /** + * @notice Emitted when an action is paused + */ + event ActionPaused(string action, bool pauseState); + /** * @notice Indicator that this is a Comptroller contract (for inspection) */ bool public constant isComptroller = true; // closeFactorMantissa must be strictly greater than this value - uint constant closeFactorMinMantissa = 5e16; // 0.05 + uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value - uint constant closeFactorMaxMantissa = 9e17; // 0.9 + uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value - uint constant collateralFactorMaxMantissa = 9e17; // 0.9 + uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 // liquidationIncentiveMantissa must be no less than this value - uint constant liquidationIncentiveMinMantissa = mantissaOne; + uint internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 // liquidationIncentiveMantissa must be no greater than this value - uint constant liquidationIncentiveMaxMantissa = 15e17; // 1.5 + uint internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 constructor() public { admin = msg.sender; @@ -136,40 +121,48 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE uint[] memory results = new uint[](len); for (uint i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); - Market storage marketToJoin = markets[address(cToken)]; - if (!marketToJoin.isListed) { - // if market is not listed, cannot join move along - results[i] = uint(Error.MARKET_NOT_LISTED); - continue; - } + results[i] = uint(addToMarketInternal(cToken, msg.sender)); + } - if (marketToJoin.accountMembership[msg.sender] == true) { - // if already joined, move along - results[i] = uint(Error.NO_ERROR); - continue; - } + return results; + } - if (accountAssets[msg.sender].length >= maxAssets) { - // if no space, cannot join, move along - results[i] = uint(Error.TOO_MANY_ASSETS); - continue; - } + /** + * @notice Add the market to the borrower's "assets in" for liquidity calculations + * @param cToken The market to enter + * @param borrower The address of the account to modify + * @return Success indicator for whether the market was entered + */ + function addToMarketInternal(CToken cToken, address borrower) internal returns (Error) { + Market storage marketToJoin = markets[address(cToken)]; - // survived the gauntlet, add to list - // NOTE: we store these somewhat redundantly as a significant optimization - // this avoids having to iterate through the list for the most common use cases - // that is, only when we need to perform liquidity checks - // and not whenever we want to check if an account is in a particular market - marketToJoin.accountMembership[msg.sender] = true; - accountAssets[msg.sender].push(cToken); + if (!marketToJoin.isListed) { + // market is not listed, cannot join + return Error.MARKET_NOT_LISTED; + } - emit MarketEntered(cToken, msg.sender); + if (marketToJoin.accountMembership[borrower] == true) { + // already joined + return Error.NO_ERROR; + } - results[i] = uint(Error.NO_ERROR); + if (accountAssets[borrower].length >= maxAssets) { + // no space, cannot join + return Error.TOO_MANY_ASSETS; } - return results; + // survived the gauntlet, add to list + // NOTE: we store these somewhat redundantly as a significant optimization + // this avoids having to iterate through the list for the most common use cases + // that is, only when we need to perform liquidity checks + // and not whenever we want to check if an account is in a particular market + marketToJoin.accountMembership[borrower] = true; + accountAssets[borrower].push(cToken); + + emit MarketEntered(cToken, borrower); + + return Error.NO_ERROR; } /** @@ -241,8 +234,12 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { - minter; // currently unused - mintAmount; // currently unused + // Pausing is a very serious situation - we revert to sound the alarms + require(!mintGuardianPaused, "mint is paused"); + + // Shh - currently unused + minter; + mintAmount; if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); @@ -261,13 +258,15 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @param mintTokens The number of tokens being minted */ function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external { - cToken; // currently unused - minter; // currently unused - mintAmount; // currently unused - mintTokens; // currently unused + // Shh - currently unused + cToken; + minter; + mintAmount; + mintTokens; + // Shh - we don't ever want this hook to be marked pure if (false) { - maxAssets = maxAssets; // not pure + maxAssets = maxAssets; } } @@ -314,10 +313,9 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @param redeemTokens The number of tokens being redeemed */ function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { - cToken; // currently unused - redeemer; // currently unused - redeemAmount; // currently unused - redeemTokens; // currently unused + // Shh - currently unused + cToken; + redeemer; // Require tokens is zero or amount is also zero if (redeemTokens == 0 && redeemAmount > 0) { @@ -333,6 +331,9 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + // Pausing is a very serious situation - we revert to sound the alarms + require(!borrowGuardianPaused, "borrow is paused"); + if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); } @@ -340,7 +341,17 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE // *may include Policy Hook-type checks if (!markets[cToken].accountMembership[borrower]) { - return uint(Error.MARKET_NOT_ENTERED); + // only cTokens may call borrowAllowed if borrower not in market + require(msg.sender == cToken, "sender must be cToken"); + + // attempt to add borrower to the market + Error err = addToMarketInternal(CToken(msg.sender), borrower); + if (err != Error.NO_ERROR) { + return uint(err); + } + + // it should be impossible to break the important invariant + assert(markets[cToken].accountMembership[borrower]); } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { @@ -365,12 +376,14 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @param borrowAmount The amount of the underlying asset requested to borrow */ function borrowVerify(address cToken, address borrower, uint borrowAmount) external { - cToken; // currently unused - borrower; // currently unused - borrowAmount; // currently unused + // Shh - currently unused + cToken; + borrower; + borrowAmount; + // Shh - we don't ever want this hook to be marked pure if (false) { - maxAssets = maxAssets; // not pure + maxAssets = maxAssets; } } @@ -387,9 +400,10 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE address payer, address borrower, uint repayAmount) external returns (uint) { - payer; // currently unused - borrower; // currently unused - repayAmount; // currently unused + // Shh - currently unused + payer; + borrower; + repayAmount; if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); @@ -413,14 +427,16 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE address borrower, uint repayAmount, uint borrowerIndex) external { - cToken; // currently unused - payer; // currently unused - borrower; // currently unused - repayAmount; // currently unused - borrowerIndex; // currently unused - + // Shh - currently unused + cToken; + payer; + borrower; + repayAmount; + borrowerIndex; + + // Shh - we don't ever want this hook to be marked pure if (false) { - maxAssets = maxAssets; // not pure + maxAssets = maxAssets; } } @@ -438,9 +454,8 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE address liquidator, address borrower, uint repayAmount) external returns (uint) { - liquidator; // currently unused - borrower; // currently unused - repayAmount; // currently unused + // Shh - currently unused + liquidator; if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { return uint(Error.MARKET_NOT_LISTED); @@ -485,15 +500,17 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE address borrower, uint repayAmount, uint seizeTokens) external { - cTokenBorrowed; // currently unused - cTokenCollateral; // currently unused - liquidator; // currently unused - borrower; // currently unused - repayAmount; // currently unused - seizeTokens; // currently unused - + // Shh - currently unused + cTokenBorrowed; + cTokenCollateral; + liquidator; + borrower; + repayAmount; + seizeTokens; + + // Shh - we don't ever want this hook to be marked pure if (false) { - maxAssets = maxAssets; // not pure + maxAssets = maxAssets; } } @@ -511,9 +528,13 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE address liquidator, address borrower, uint seizeTokens) external returns (uint) { - liquidator; // currently unused - borrower; // currently unused - seizeTokens; // currently unused + // Pausing is a very serious situation - we revert to sound the alarms + require(!seizeGuardianPaused, "seize is paused"); + + // Shh - currently unused + liquidator; + borrower; + seizeTokens; if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { return uint(Error.MARKET_NOT_LISTED); @@ -542,14 +563,16 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE address liquidator, address borrower, uint seizeTokens) external { - cTokenCollateral; // currently unused - cTokenBorrowed; // currently unused - liquidator; // currently unused - borrower; // currently unused - seizeTokens; // currently unused - + // Shh - currently unused + cTokenCollateral; + cTokenBorrowed; + liquidator; + borrower; + seizeTokens; + + // Shh - we don't ever want this hook to be marked pure if (false) { - maxAssets = maxAssets; // not pure + maxAssets = maxAssets; } } @@ -562,10 +585,11 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { - cToken; // currently unused - src; // currently unused - dst; // currently unused - transferTokens; // currently unused + // Pausing is a very serious situation - we revert to sound the alarms + require(!transferGuardianPaused, "transfer is paused"); + + // Shh - currently unused + dst; // *may include Policy Hook-type checks @@ -582,13 +606,15 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @param transferTokens The number of cTokens to transfer */ function transferVerify(address cToken, address src, address dst, uint transferTokens) external { - cToken; // currently unused - src; // currently unused - dst; // currently unused - transferTokens; // currently unused + // Shh - currently unused + cToken; + src; + dst; + transferTokens; + // Shh - we don't ever want this hook to be marked pure if (false) { - maxAssets = maxAssets; // not pure + maxAssets = maxAssets; } } @@ -780,17 +806,14 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPriceOracle(PriceOracle newOracle) public returns (uint) { - // Check caller is admin OR currently initialzing as new unitroller implementation - if (!adminOrInitializing()) { + // Check caller is admin + if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); } // Track the old oracle for the comptroller PriceOracle oldOracle = oracle; - // Ensure invoke newOracle.isPriceOracle() returns true - // require(newOracle.isPriceOracle(), "oracle method isPriceOracle returned false"); - // Set comptroller's oracle to newOracle oracle = newOracle; @@ -807,8 +830,8 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint256) { - // Check caller is admin OR currently initialzing as new unitroller implementation - if (!adminOrInitializing()) { + // Check caller is admin + if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); } @@ -879,8 +902,8 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setMaxAssets(uint newMaxAssets) external returns (uint) { - // Check caller is admin OR currently initialzing as new unitroller implementation - if (!adminOrInitializing()) { + // Check caller is admin + if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); } @@ -898,12 +921,12 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { - // Check caller is admin OR currently initialzing as new unitroller implementation - if (!adminOrInitializing()) { + // Check caller is admin + if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); } - // Check de-scaled 1 <= newLiquidationDiscount <= 1.5 + // Check de-scaled min <= newLiquidationIncentive <= max Exp memory newLiquidationIncentive = Exp({mantissa: newLiquidationIncentiveMantissa}); Exp memory minLiquidationIncentive = Exp({mantissa: liquidationIncentiveMinMantissa}); if (lessThanExp(newLiquidationIncentive, minLiquidationIncentive)) { @@ -950,47 +973,68 @@ contract Comptroller is ComptrollerV1Storage, ComptrollerInterface, ComptrollerE return uint(Error.NO_ERROR); } - function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool reinitializing) public { - require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); - uint changeStatus = unitroller._acceptImplementation(); + /** + * @notice Admin function to change the Pause Guardian + * @param newPauseGuardian The address of the new Pause Guardian + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _setPauseGuardian(address newPauseGuardian) public returns (uint) { + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); + } - require(changeStatus == 0, "change not authorized"); + // Save current value for inclusion in log + address oldPauseGuardian = pauseGuardian; - if (!reinitializing) { - Comptroller freshBrainedComptroller = Comptroller(address(unitroller)); + // Store pauseGuardian with value newPauseGuardian + pauseGuardian = newPauseGuardian; - // Ensure invoke _setPriceOracle() = 0 - uint err = freshBrainedComptroller._setPriceOracle(_oracle); - require (err == uint(Error.NO_ERROR), "set price oracle error"); + // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) + emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); - // Ensure invoke _setCloseFactor() = 0 - err = freshBrainedComptroller._setCloseFactor(_closeFactorMantissa); - require (err == uint(Error.NO_ERROR), "set close factor error"); + return uint(Error.NO_ERROR); + } - // Ensure invoke _setMaxAssets() = 0 - err = freshBrainedComptroller._setMaxAssets(_maxAssets); - require (err == uint(Error.NO_ERROR), "set max asssets error"); + function _setMintPaused(bool state) public returns (bool) { + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); - // Ensure invoke _setLiquidationIncentive(liquidationIncentiveMinMantissa) = 0 - err = freshBrainedComptroller._setLiquidationIncentive(liquidationIncentiveMinMantissa); - require (err == uint(Error.NO_ERROR), "set liquidation incentive error"); - } + mintGuardianPaused = state; + emit ActionPaused("Mint", state); + return state; } - /** - * @dev Check that caller is admin or this contract is initializing itself as - * the new implementation. - * There should be no way to satisfy msg.sender == comptrollerImplementaiton - * without tx.origin also being admin, but both are included for extra safety - */ - function adminOrInitializing() internal view returns (bool) { - bool initializing = ( - msg.sender == comptrollerImplementation - && - //solium-disable-next-line security/no-tx-origin - tx.origin == admin - ); - bool isAdmin = msg.sender == admin; - return isAdmin || initializing; + function _setBorrowPaused(bool state) public returns (bool) { + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + borrowGuardianPaused = state; + emit ActionPaused("Borrow", state); + return state; + } + + function _setTransferPaused(bool state) public returns (bool) { + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + transferGuardianPaused = state; + emit ActionPaused("Transfer", state); + return state; + } + + function _setSeizePaused(bool state) public returns (bool) { + require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); + require(msg.sender == admin || state == true, "only admin can unpause"); + + seizeGuardianPaused = state; + emit ActionPaused("Seize", state); + return state; + } + + function _become(Unitroller unitroller) public { + require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); + + uint changeStatus = unitroller._acceptImplementation(); + require(changeStatus == 0, "change not authorized"); } } diff --git a/contracts/ComptrollerG1.sol b/contracts/ComptrollerG1.sol new file mode 100644 index 000000000..4fcc32dc9 --- /dev/null +++ b/contracts/ComptrollerG1.sol @@ -0,0 +1,998 @@ +pragma solidity ^0.5.8; + +import "./CToken.sol"; +import "./ErrorReporter.sol"; +import "./Exponential.sol"; +import "./PriceOracle.sol"; +import "./ComptrollerInterface.sol"; +import "./ComptrollerStorage.sol"; +import "./Unitroller.sol"; + +/** + * @title Compound's Comptroller Contract + * @author Compound + * @dev This was the first version of the Comptroller brains. + * We keep it so our tests can continue to do the real-life behavior of upgrading from this logic forward. + */ +contract ComptrollerG1 is ComptrollerV1Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { + 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 Official mapping of cTokens -> Market metadata + * @dev Used e.g. to determine if a market is supported + */ + mapping(address => Market) public markets; + + /** + * @notice Emitted when an admin supports a market + */ + event MarketListed(CToken cToken); + + /** + * @notice Emitted when an account enters a market + */ + event MarketEntered(CToken cToken, address account); + + /** + * @notice Emitted when an account exits a market + */ + event MarketExited(CToken cToken, address account); + + /** + * @notice Emitted when close factor is changed by admin + */ + event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); + + /** + * @notice Emitted when a collateral factor is changed by admin + */ + event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); + + /** + * @notice Emitted when liquidation incentive is changed by admin + */ + event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); + + /** + * @notice Emitted when maxAssets is changed by admin + */ + event NewMaxAssets(uint oldMaxAssets, uint newMaxAssets); + + /** + * @notice Emitted when price oracle is changed + */ + event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); + + /** + * @notice Indicator that this is a Comptroller contract (for inspection) + */ + bool public constant isComptroller = true; + + // closeFactorMantissa must be strictly greater than this value + uint constant closeFactorMinMantissa = 5e16; // 0.05 + + // closeFactorMantissa must not exceed this value + uint constant closeFactorMaxMantissa = 9e17; // 0.9 + + // No collateralFactorMantissa may exceed this value + uint constant collateralFactorMaxMantissa = 9e17; // 0.9 + + // liquidationIncentiveMantissa must be no less than this value + uint constant liquidationIncentiveMinMantissa = mantissaOne; + + // liquidationIncentiveMantissa must be no greater than this value + uint constant liquidationIncentiveMaxMantissa = 15e17; // 1.5 + + constructor() public { + admin = msg.sender; + } + + /*** Assets You Are In ***/ + + /** + * @notice Returns the assets an account has entered + * @param account The address of the account to pull assets for + * @return A dynamic list with the assets the account has entered + */ + function getAssetsIn(address account) external view returns (CToken[] memory) { + CToken[] memory assetsIn = accountAssets[account]; + + return assetsIn; + } + + /** + * @notice Returns whether the given account is entered in the given asset + * @param account The address of the account to check + * @param cToken The cToken to check + * @return True if the account is in the asset, otherwise false. + */ + function checkMembership(address account, CToken cToken) external view returns (bool) { + return markets[address(cToken)].accountMembership[account]; + } + + /** + * @notice Add assets to be included in account liquidity calculation + * @param cTokens The list of addresses of the cToken markets to be enabled + * @return Success indicator for whether each corresponding market was entered + */ + function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { + uint len = cTokens.length; + + uint[] memory results = new uint[](len); + for (uint i = 0; i < len; i++) { + CToken cToken = CToken(cTokens[i]); + Market storage marketToJoin = markets[address(cToken)]; + + if (!marketToJoin.isListed) { + // if market is not listed, cannot join move along + results[i] = uint(Error.MARKET_NOT_LISTED); + continue; + } + + if (marketToJoin.accountMembership[msg.sender] == true) { + // if already joined, move along + results[i] = uint(Error.NO_ERROR); + continue; + } + + if (accountAssets[msg.sender].length >= maxAssets) { + // if no space, cannot join, move along + results[i] = uint(Error.TOO_MANY_ASSETS); + continue; + } + + // survived the gauntlet, add to list + // NOTE: we store these somewhat redundantly as a significant optimization + // this avoids having to iterate through the list for the most common use cases + // that is, only when we need to perform liquidity checks + // and not whenever we want to check if an account is in a particular market + marketToJoin.accountMembership[msg.sender] = true; + accountAssets[msg.sender].push(cToken); + + emit MarketEntered(cToken, msg.sender); + + results[i] = uint(Error.NO_ERROR); + } + + return results; + } + + /** + * @notice Removes asset from sender's account liquidity calculation + * @dev Sender must not have an outstanding borrow balance in the asset, + * or be providing neccessary collateral for an outstanding borrow. + * @param cTokenAddress The address of the asset to be removed + * @return Whether or not the account successfully exited the market + */ + function exitMarket(address cTokenAddress) external returns (uint) { + CToken cToken = CToken(cTokenAddress); + /* Get sender tokensHeld and amountOwed underlying from the cToken */ + (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); + require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code + + /* Fail if the sender has a borrow balance */ + if (amountOwed != 0) { + return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED); + } + + /* Fail if the sender is not permitted to redeem all of their tokens */ + uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); + if (allowed != 0) { + return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); + } + + Market storage marketToExit = markets[address(cToken)]; + + /* Return true if the sender is not already ‘in’ the market */ + if (!marketToExit.accountMembership[msg.sender]) { + return uint(Error.NO_ERROR); + } + + /* Set cToken account membership to false */ + delete marketToExit.accountMembership[msg.sender]; + + /* Delete cToken from the account’s list of assets */ + // load into memory for faster iteration + CToken[] memory userAssetList = accountAssets[msg.sender]; + uint len = userAssetList.length; + uint assetIndex = len; + for (uint i = 0; i < len; i++) { + if (userAssetList[i] == cToken) { + assetIndex = i; + break; + } + } + + // We *must* have found the asset in the list or our redundant data structure is broken + assert(assetIndex < len); + + // copy last item in list to location of item to be removed, reduce length by 1 + CToken[] storage storedList = accountAssets[msg.sender]; + storedList[assetIndex] = storedList[storedList.length - 1]; + storedList.length--; + + emit MarketExited(cToken, msg.sender); + + return uint(Error.NO_ERROR); + } + + /*** Policy Hooks ***/ + + /** + * @notice Checks if the account should be allowed to mint tokens in the given market + * @param cToken The market to verify the mint against + * @param minter The account which would get the minted tokens + * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens + * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { + minter; // currently unused + mintAmount; // currently unused + + if (!markets[cToken].isListed) { + return uint(Error.MARKET_NOT_LISTED); + } + + // *may include Policy Hook-type checks + + return uint(Error.NO_ERROR); + } + + /** + * @notice Validates mint and reverts on rejection. May emit logs. + * @param cToken Asset being minted + * @param minter The address minting the tokens + * @param mintAmount The amount of the underlying asset being minted + * @param mintTokens The number of tokens being minted + */ + function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external { + cToken; // currently unused + minter; // currently unused + mintAmount; // currently unused + mintTokens; // currently unused + + if (false) { + maxAssets = maxAssets; // not pure + } + } + + /** + * @notice Checks if the account should be allowed to redeem tokens in the given market + * @param cToken The market to verify the redeem against + * @param redeemer The account which would redeem the tokens + * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market + * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { + return redeemAllowedInternal(cToken, redeemer, redeemTokens); + } + + function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { + if (!markets[cToken].isListed) { + return uint(Error.MARKET_NOT_LISTED); + } + + // *may include Policy Hook-type checks + + /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ + if (!markets[cToken].accountMembership[redeemer]) { + return uint(Error.NO_ERROR); + } + + /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ + (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); + if (err != Error.NO_ERROR) { + return uint(err); + } + if (shortfall > 0) { + return uint(Error.INSUFFICIENT_LIQUIDITY); + } + + return uint(Error.NO_ERROR); + } + + /** + * @notice Validates redeem and reverts on rejection. May emit logs. + * @param cToken Asset being redeemed + * @param redeemer The address redeeming the tokens + * @param redeemAmount The amount of the underlying asset being redeemed + * @param redeemTokens The number of tokens being redeemed + */ + function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { + cToken; // currently unused + redeemer; // currently unused + redeemAmount; // currently unused + redeemTokens; // currently unused + + // Require tokens is zero or amount is also zero + if (redeemTokens == 0 && redeemAmount > 0) { + revert("redeemTokens zero"); + } + } + + /** + * @notice Checks if the account should be allowed to borrow the underlying asset of the given market + * @param cToken The market to verify the borrow against + * @param borrower The account which would borrow the asset + * @param borrowAmount The amount of underlying the account would borrow + * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { + if (!markets[cToken].isListed) { + return uint(Error.MARKET_NOT_LISTED); + } + + // *may include Policy Hook-type checks + + if (!markets[cToken].accountMembership[borrower]) { + return uint(Error.MARKET_NOT_ENTERED); + } + + if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { + return uint(Error.PRICE_ERROR); + } + + (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); + if (err != Error.NO_ERROR) { + return uint(err); + } + if (shortfall > 0) { + return uint(Error.INSUFFICIENT_LIQUIDITY); + } + + return uint(Error.NO_ERROR); + } + + /** + * @notice Validates borrow and reverts on rejection. May emit logs. + * @param cToken Asset whose underlying is being borrowed + * @param borrower The address borrowing the underlying + * @param borrowAmount The amount of the underlying asset requested to borrow + */ + function borrowVerify(address cToken, address borrower, uint borrowAmount) external { + cToken; // currently unused + borrower; // currently unused + borrowAmount; // currently unused + + if (false) { + maxAssets = maxAssets; // not pure + } + } + + /** + * @notice Checks if the account should be allowed to repay a borrow in the given market + * @param cToken The market to verify the repay against + * @param payer The account which would repay the asset + * @param borrower The account which would borrowed the asset + * @param repayAmount The amount of the underlying asset the account would repay + * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function repayBorrowAllowed( + address cToken, + address payer, + address borrower, + uint repayAmount) external returns (uint) { + payer; // currently unused + borrower; // currently unused + repayAmount; // currently unused + + if (!markets[cToken].isListed) { + return uint(Error.MARKET_NOT_LISTED); + } + + // *may include Policy Hook-type checks + + return uint(Error.NO_ERROR); + } + + /** + * @notice Validates repayBorrow and reverts on rejection. May emit logs. + * @param cToken Asset being repaid + * @param payer The address repaying the borrow + * @param borrower The address of the borrower + * @param repayAmount The amount of underlying being repaid + */ + function repayBorrowVerify( + address cToken, + address payer, + address borrower, + uint repayAmount, + uint borrowerIndex) external { + cToken; // currently unused + payer; // currently unused + borrower; // currently unused + repayAmount; // currently unused + borrowerIndex; // currently unused + + if (false) { + maxAssets = maxAssets; // not pure + } + } + + /** + * @notice Checks if the liquidation should be allowed to occur + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param repayAmount The amount of underlying being repaid + */ + function liquidateBorrowAllowed( + address cTokenBorrowed, + address cTokenCollateral, + address liquidator, + address borrower, + uint repayAmount) external returns (uint) { + liquidator; // currently unused + borrower; // currently unused + repayAmount; // currently unused + + if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { + return uint(Error.MARKET_NOT_LISTED); + } + + // *may include Policy Hook-type checks + + /* The borrower must have shortfall in order to be liquidatable */ + (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); + if (err != Error.NO_ERROR) { + return uint(err); + } + if (shortfall == 0) { + return uint(Error.INSUFFICIENT_SHORTFALL); + } + + /* The liquidator may not repay more than what is allowed by the closeFactor */ + uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); + (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); + if (mathErr != MathError.NO_ERROR) { + return uint(Error.MATH_ERROR); + } + if (repayAmount > maxClose) { + return uint(Error.TOO_MUCH_REPAY); + } + + return uint(Error.NO_ERROR); + } + + /** + * @notice Validates liquidateBorrow and reverts on rejection. May emit logs. + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param repayAmount The amount of underlying being repaid + */ + function liquidateBorrowVerify( + address cTokenBorrowed, + address cTokenCollateral, + address liquidator, + address borrower, + uint repayAmount, + uint seizeTokens) external { + cTokenBorrowed; // currently unused + cTokenCollateral; // currently unused + liquidator; // currently unused + borrower; // currently unused + repayAmount; // currently unused + seizeTokens; // currently unused + + if (false) { + maxAssets = maxAssets; // not pure + } + } + + /** + * @notice Checks if the seizing of assets should be allowed to occur + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param seizeTokens The number of collateral tokens to seize + */ + function seizeAllowed( + address cTokenCollateral, + address cTokenBorrowed, + address liquidator, + address borrower, + uint seizeTokens) external returns (uint) { + liquidator; // currently unused + borrower; // currently unused + seizeTokens; // currently unused + + if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { + return uint(Error.MARKET_NOT_LISTED); + } + + if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { + return uint(Error.COMPTROLLER_MISMATCH); + } + + // *may include Policy Hook-type checks + + return uint(Error.NO_ERROR); + } + + /** + * @notice Validates seize and reverts on rejection. May emit logs. + * @param cTokenCollateral Asset which was used as collateral and will be seized + * @param cTokenBorrowed Asset which was borrowed by the borrower + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @param seizeTokens The number of collateral tokens to seize + */ + function seizeVerify( + address cTokenCollateral, + address cTokenBorrowed, + address liquidator, + address borrower, + uint seizeTokens) external { + cTokenCollateral; // currently unused + cTokenBorrowed; // currently unused + liquidator; // currently unused + borrower; // currently unused + seizeTokens; // currently unused + + if (false) { + maxAssets = maxAssets; // not pure + } + } + + /** + * @notice Checks if the account should be allowed to transfer tokens in the given market + * @param cToken The market to verify the transfer against + * @param src The account which sources the tokens + * @param dst The account which receives the tokens + * @param transferTokens The number of cTokens to transfer + * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) + */ + function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { + cToken; // currently unused + src; // currently unused + dst; // currently unused + transferTokens; // currently unused + + // *may include Policy Hook-type checks + + // Currently the only consideration is whether or not + // the src is allowed to redeem this many tokens + return redeemAllowedInternal(cToken, src, transferTokens); + } + + /** + * @notice Validates transfer and reverts on rejection. May emit logs. + * @param cToken Asset being transferred + * @param src The account which sources the tokens + * @param dst The account which receives the tokens + * @param transferTokens The number of cTokens to transfer + */ + function transferVerify(address cToken, address src, address dst, uint transferTokens) external { + cToken; // currently unused + src; // currently unused + dst; // currently unused + transferTokens; // currently unused + + if (false) { + maxAssets = maxAssets; // not pure + } + } + + /*** Liquidity/Liquidation Calculations ***/ + + /** + * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. + * Note that `cTokenBalance` is the number of cTokens the account owns in the market, + * whereas `borrowBalance` is the amount of underlying that the account has borrowed. + */ + struct AccountLiquidityLocalVars { + uint sumCollateral; + uint sumBorrowPlusEffects; + uint cTokenBalance; + uint borrowBalance; + uint exchangeRateMantissa; + uint oraclePriceMantissa; + Exp collateralFactor; + Exp exchangeRate; + Exp oraclePrice; + Exp tokensToEther; + } + + /** + * @notice Determine the current account liquidity wrt collateral requirements + * @return (possible error code (semi-opaque), + account liquidity in excess of collateral requirements, + * account shortfall below collateral requirements) + */ + function getAccountLiquidity(address account) public view returns (uint, uint, uint) { + (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); + + return (uint(err), liquidity, shortfall); + } + + /** + * @notice Determine the current account liquidity wrt collateral requirements + * @return (possible error code, + account liquidity in excess of collateral requirements, + * account shortfall below collateral requirements) + */ + function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { + return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); + } + + /** + * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed + * @param cTokenModify The market to hypothetically redeem/borrow in + * @param account The account to determine liquidity for + * @param redeemTokens The number of tokens to hypothetically redeem + * @param borrowAmount The amount of underlying to hypothetically borrow + * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data, + * without calculating accumulated interest. + * @return (possible error code, + hypothetical account liquidity in excess of collateral requirements, + * hypothetical account shortfall below collateral requirements) + */ + function getHypotheticalAccountLiquidityInternal( + address account, + CToken cTokenModify, + uint redeemTokens, + uint borrowAmount) internal view returns (Error, uint, uint) { + + AccountLiquidityLocalVars memory vars; // Holds all our calculation results + uint oErr; + MathError mErr; + + // For each asset the account is in + CToken[] memory assets = accountAssets[account]; + for (uint i = 0; i < assets.length; i++) { + CToken asset = assets[i]; + + // Read the balances and exchange rate from the cToken + (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); + if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades + return (Error.SNAPSHOT_ERROR, 0, 0); + } + vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); + vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); + + // Get the normalized price of the asset + vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); + if (vars.oraclePriceMantissa == 0) { + return (Error.PRICE_ERROR, 0, 0); + } + vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); + + // Pre-compute a conversion factor from tokens -> ether (normalized price value) + (mErr, vars.tokensToEther) = mulExp3(vars.collateralFactor, vars.exchangeRate, vars.oraclePrice); + if (mErr != MathError.NO_ERROR) { + return (Error.MATH_ERROR, 0, 0); + } + + // sumCollateral += tokensToEther * cTokenBalance + (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToEther, vars.cTokenBalance, vars.sumCollateral); + if (mErr != MathError.NO_ERROR) { + return (Error.MATH_ERROR, 0, 0); + } + + // sumBorrowPlusEffects += oraclePrice * borrowBalance + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); + if (mErr != MathError.NO_ERROR) { + return (Error.MATH_ERROR, 0, 0); + } + + // Calculate effects of interacting with cTokenModify + if (asset == cTokenModify) { + // redeem effect + // sumBorrowPlusEffects += tokensToEther * redeemTokens + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToEther, redeemTokens, vars.sumBorrowPlusEffects); + if (mErr != MathError.NO_ERROR) { + return (Error.MATH_ERROR, 0, 0); + } + + // borrow effect + // sumBorrowPlusEffects += oraclePrice * borrowAmount + (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); + if (mErr != MathError.NO_ERROR) { + return (Error.MATH_ERROR, 0, 0); + } + } + } + + // These are safe, as the underflow condition is checked first + if (vars.sumCollateral > vars.sumBorrowPlusEffects) { + return (Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0); + } else { + return (Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral); + } + } + + /** + * @notice Calculate number of tokens of collateral asset to seize given an underlying amount + * @dev Used in liquidation (called in cToken.liquidateBorrowFresh) + * @param cTokenBorrowed The address of the borrowed cToken + * @param cTokenCollateral The address of the collateral cToken + * @param repayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens + * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) + */ + function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint repayAmount) external view returns (uint, uint) { + /* Read oracle prices for borrowed and collateral markets */ + uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); + uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); + if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { + return (uint(Error.PRICE_ERROR), 0); + } + + /* + * Get the exchange rate and calculate the number of collateral tokens to seize: + * seizeAmount = repayAmount * liquidationIncentive * priceBorrowed / priceCollateral + * seizeTokens = seizeAmount / exchangeRate + * = repayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) + */ + uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error + uint seizeTokens; + Exp memory numerator; + Exp memory denominator; + Exp memory ratio; + MathError mathErr; + + (mathErr, numerator) = mulExp(liquidationIncentiveMantissa, priceBorrowedMantissa); + if (mathErr != MathError.NO_ERROR) { + return (uint(Error.MATH_ERROR), 0); + } + + (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); + if (mathErr != MathError.NO_ERROR) { + return (uint(Error.MATH_ERROR), 0); + } + + (mathErr, ratio) = divExp(numerator, denominator); + if (mathErr != MathError.NO_ERROR) { + return (uint(Error.MATH_ERROR), 0); + } + + (mathErr, seizeTokens) = mulScalarTruncate(ratio, repayAmount); + if (mathErr != MathError.NO_ERROR) { + return (uint(Error.MATH_ERROR), 0); + } + + return (uint(Error.NO_ERROR), seizeTokens); + } + + /*** Admin Functions ***/ + + /** + * @notice Sets a new price oracle for the comptroller + * @dev Admin function to set a new price oracle + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function _setPriceOracle(PriceOracle newOracle) public returns (uint) { + // Check caller is admin OR currently initialzing as new unitroller implementation + if (!adminOrInitializing()) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); + } + + // Track the old oracle for the comptroller + PriceOracle oldOracle = oracle; + + // Ensure invoke newOracle.isPriceOracle() returns true + // require(newOracle.isPriceOracle(), "oracle method isPriceOracle returned false"); + + // Set comptroller's oracle to newOracle + oracle = newOracle; + + // Emit NewPriceOracle(oldOracle, newOracle) + emit NewPriceOracle(oldOracle, newOracle); + + return uint(Error.NO_ERROR); + } + + /** + * @notice Sets the closeFactor used when liquidating borrows + * @dev Admin function to set closeFactor + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint256) { + // Check caller is admin OR currently initialzing as new unitroller implementation + if (!adminOrInitializing()) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); + } + + Exp memory newCloseFactorExp = Exp({mantissa: newCloseFactorMantissa}); + Exp memory lowLimit = Exp({mantissa: closeFactorMinMantissa}); + if (lessThanOrEqualExp(newCloseFactorExp, lowLimit)) { + return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); + } + + Exp memory highLimit = Exp({mantissa: closeFactorMaxMantissa}); + if (lessThanExp(highLimit, newCloseFactorExp)) { + return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); + } + + uint oldCloseFactorMantissa = closeFactorMantissa; + closeFactorMantissa = newCloseFactorMantissa; + emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); + + return uint(Error.NO_ERROR); + } + + /** + * @notice Sets the collateralFactor for a market + * @dev Admin function to set per-market collateralFactor + * @param cToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint256) { + // Check caller is admin + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); + } + + // Verify market is listed + Market storage market = markets[address(cToken)]; + if (!market.isListed) { + return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS); + } + + Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa}); + + // Check collateral factor <= 0.9 + Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa}); + if (lessThanExp(highLimit, newCollateralFactorExp)) { + return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION); + } + + // If collateral factor != 0, fail if price == 0 + if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(cToken) == 0) { + return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); + } + + // Set market's collateral factor to new collateral factor, remember old value + uint oldCollateralFactorMantissa = market.collateralFactorMantissa; + market.collateralFactorMantissa = newCollateralFactorMantissa; + + // Emit event with asset, old collateral factor, and new collateral factor + emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); + + return uint(Error.NO_ERROR); + } + + /** + * @notice Sets maxAssets which controls how many markets can be entered + * @dev Admin function to set maxAssets + * @param newMaxAssets New max assets + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setMaxAssets(uint newMaxAssets) external returns (uint) { + // Check caller is admin OR currently initialzing as new unitroller implementation + if (!adminOrInitializing()) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); + } + + uint oldMaxAssets = maxAssets; + maxAssets = newMaxAssets; + emit NewMaxAssets(oldMaxAssets, newMaxAssets); + + return uint(Error.NO_ERROR); + } + + /** + * @notice Sets liquidationIncentive + * @dev Admin function to set liquidationIncentive + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { + // Check caller is admin OR currently initialzing as new unitroller implementation + if (!adminOrInitializing()) { + return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); + } + + // Check de-scaled 1 <= newLiquidationDiscount <= 1.5 + Exp memory newLiquidationIncentive = Exp({mantissa: newLiquidationIncentiveMantissa}); + Exp memory minLiquidationIncentive = Exp({mantissa: liquidationIncentiveMinMantissa}); + if (lessThanExp(newLiquidationIncentive, minLiquidationIncentive)) { + return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION); + } + + Exp memory maxLiquidationIncentive = Exp({mantissa: liquidationIncentiveMaxMantissa}); + if (lessThanExp(maxLiquidationIncentive, newLiquidationIncentive)) { + return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION); + } + + // Save current value for use in log + uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + + // Set liquidation incentive to new incentive + liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; + + // Emit event with old incentive, new incentive + emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); + + return uint(Error.NO_ERROR); + } + + /** + * @notice Add the market to the markets mapping and set it as listed + * @dev Admin function to set isListed and add support for the market + * @param cToken The address of the market (token) to list + * @return uint 0=success, otherwise a failure. (See enum Error for details) + */ + function _supportMarket(CToken cToken) external returns (uint) { + if (msg.sender != admin) { + return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); + } + + if (markets[address(cToken)].isListed) { + return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); + } + + cToken.isCToken(); // Sanity check to make sure its really a CToken + + markets[address(cToken)] = Market({isListed: true, collateralFactorMantissa: 0}); + emit MarketListed(cToken); + + return uint(Error.NO_ERROR); + } + + function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool reinitializing) public { + require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); + uint changeStatus = unitroller._acceptImplementation(); + + require(changeStatus == 0, "change not authorized"); + + if (!reinitializing) { + ComptrollerG1 freshBrainedComptroller = ComptrollerG1(address(unitroller)); + + // Ensure invoke _setPriceOracle() = 0 + uint err = freshBrainedComptroller._setPriceOracle(_oracle); + require (err == uint(Error.NO_ERROR), "set price oracle error"); + + // Ensure invoke _setCloseFactor() = 0 + err = freshBrainedComptroller._setCloseFactor(_closeFactorMantissa); + require (err == uint(Error.NO_ERROR), "set close factor error"); + + // Ensure invoke _setMaxAssets() = 0 + err = freshBrainedComptroller._setMaxAssets(_maxAssets); + require (err == uint(Error.NO_ERROR), "set max asssets error"); + + // Ensure invoke _setLiquidationIncentive(liquidationIncentiveMinMantissa) = 0 + err = freshBrainedComptroller._setLiquidationIncentive(liquidationIncentiveMinMantissa); + require (err == uint(Error.NO_ERROR), "set liquidation incentive error"); + } + } + + /** + * @dev Check that caller is admin or this contract is initializing itself as + * the new implementation. + * There should be no way to satisfy msg.sender == comptrollerImplementaiton + * without tx.origin also being admin, but both are included for extra safety + */ + function adminOrInitializing() internal view returns (bool) { + bool initializing = ( + msg.sender == comptrollerImplementation + && + //solium-disable-next-line security/no-tx-origin + tx.origin == admin + ); + bool isAdmin = msg.sender == admin; + return isAdmin || initializing; + } +} \ No newline at end of file diff --git a/contracts/ComptrollerStorage.sol b/contracts/ComptrollerStorage.sol index 1218d3729..b2e61c857 100644 --- a/contracts/ComptrollerStorage.sol +++ b/contracts/ComptrollerStorage.sol @@ -53,3 +53,40 @@ contract ComptrollerV1Storage is UnitrollerAdminStorage { 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 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. + */ + address public pauseGuardian; + bool public mintGuardianPaused; + bool public borrowGuardianPaused; + bool public transferGuardianPaused; + bool public seizeGuardianPaused; +} diff --git a/contracts/ErrorReporter.sol b/contracts/ErrorReporter.sol index ab646813f..4edbe4623 100644 --- a/contracts/ErrorReporter.sol +++ b/contracts/ErrorReporter.sol @@ -10,7 +10,7 @@ contract ComptrollerErrorReporter { INVALID_CLOSE_FACTOR, INVALID_COLLATERAL_FACTOR, INVALID_LIQUIDATION_INCENTIVE, - MARKET_NOT_ENTERED, + MARKET_NOT_ENTERED, // no longer possible MARKET_NOT_LISTED, MARKET_ALREADY_LISTED, MATH_ERROR, @@ -42,7 +42,7 @@ contract ComptrollerErrorReporter { SET_PRICE_ORACLE_OWNER_CHECK, SUPPORT_MARKET_EXISTS, SUPPORT_MARKET_OWNER_CHECK, - ZUNUSED + SET_PAUSE_GUARDIAN_OWNER_CHECK } /** diff --git a/contracts/Exponential.sol b/contracts/Exponential.sol index 4320ca01d..ce9ff490a 100644 --- a/contracts/Exponential.sol +++ b/contracts/Exponential.sol @@ -3,7 +3,7 @@ pragma solidity ^0.5.8; import "./CarefulMath.sol"; /** - * @title Exponential module for storing fixed-decision decimals + * @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: @@ -200,7 +200,7 @@ contract Exponential is CarefulMath { * @dev Checks if first Exp is less than second Exp. */ function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { - return left.mantissa < right.mantissa; //TODO: Add some simple tests and this in another PR yo. + return left.mantissa < right.mantissa; } /** diff --git a/contracts/Timelock.sol b/contracts/Timelock.sol new file mode 100644 index 000000000..c86e46bb6 --- /dev/null +++ b/contracts/Timelock.sol @@ -0,0 +1,111 @@ +pragma solidity ^0.5.8; + +import "./SafeMath.sol"; + +contract Timelock { + using SafeMath for uint; + + event NewAdmin(address indexed newAdmin); + event NewPendingAdmin(address indexed newPendingAdmin); + event NewDelay(uint indexed newDelay); + event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); + event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); + event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); + + uint public constant GRACE_PERIOD = 14 days; + uint public constant MINIMUM_DELAY = 2 days; + uint public constant MAXIMUM_DELAY = 30 days; + + address public admin; + address public pendingAdmin; + uint public delay; + + mapping (bytes32 => bool) public queuedTransactions; + + + constructor(address admin_, uint delay_) public { + require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + + admin = admin_; + delay = delay_; + } + + function() external payable { } + + function setDelay(uint delay_) public { + require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); + require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + delay = delay_; + + emit NewDelay(delay); + } + + function acceptAdmin() public { + require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); + admin = msg.sender; + pendingAdmin = address(0); + + emit NewAdmin(admin); + } + + function setPendingAdmin(address pendingAdmin_) public { + require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); + pendingAdmin = pendingAdmin_; + + emit NewPendingAdmin(pendingAdmin); + } + + function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) { + require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); + require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = true; + + emit QueueTransaction(txHash, target, value, signature, data, eta); + return txHash; + } + + function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public { + require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = false; + + emit CancelTransaction(txHash, target, value, signature, data, eta); + } + + function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) { + require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); + require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); + require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale."); + + queuedTransactions[txHash] = false; + + bytes memory callData; + + if (bytes(signature).length == 0) { + callData = data; + } else { + callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); + } + + // solium-disable-next-line security/no-call-value + (bool success, bytes memory returnData) = target.call.value(value)(callData); + require(success, "Timelock::executeTransaction: Transaction execution reverted."); + + emit ExecuteTransaction(txHash, target, value, signature, data, eta); + + return returnData; + } + + function getBlockTimestamp() internal view returns (uint) { + // solium-disable-next-line security/no-block-members + return block.timestamp; + } +} \ No newline at end of file diff --git a/contracts/Unitroller.sol b/contracts/Unitroller.sol index 1bb0d7368..1badc09c7 100644 --- a/contracts/Unitroller.sol +++ b/contracts/Unitroller.sol @@ -4,9 +4,8 @@ import "./ErrorReporter.sol"; import "./ComptrollerStorage.sol"; /** * @title ComptrollerCore - * @dev storage for the comptroller will be at this address, and - * cTokens should reference this contract rather than a deployed implementation if - * + * @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 { @@ -82,8 +81,6 @@ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { * @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) - * - * TODO: Should we add a second arg to verify, like a checksum of `newAdmin` address? */ function _setPendingAdmin(address newPendingAdmin) public returns (uint) { // Check caller = admin diff --git a/networks/mainnet-abi.json b/networks/mainnet-abi.json index 81ea7cfb9..a3637252b 100644 --- a/networks/mainnet-abi.json +++ b/networks/mainnet-abi.json @@ -8747,180 +8747,154 @@ "signature": "constructor" } ], - "Base200bps_Slope3000bps": [ + "Timelock": [ { - "constant": true, + "constant": false, "inputs": [ { - "name": "cash", - "type": "uint256" + "name": "target", + "type": "address" }, { - "name": "borrows", + "name": "value", "type": "uint256" }, { - "name": "_reserves", - "type": "uint256" - } - ], - "name": "getBorrowRate", - "outputs": [ - { - "name": "", - "type": "uint256" + "name": "signature", + "type": "string" }, { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x15f24053" - }, - { - "constant": true, - "inputs": [], - "name": "multiplier", - "outputs": [ + "name": "data", + "type": "bytes" + }, { - "name": "", + "name": "eta", "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x1b3ed722" - }, - { - "constant": true, - "inputs": [], - "name": "baseRate", + "name": "executeTransaction", "outputs": [ { "name": "", - "type": "uint256" + "type": "bytes" } ], - "payable": false, - "stateMutability": "view", + "payable": true, + "stateMutability": "payable", "type": "function", - "signature": "0x1f68f20a" + "signature": "0x0825f38f" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "isInterestRateModel", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], + "name": "acceptAdmin", + "outputs": [], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x2191f92a" + "signature": "0x0e18b681" }, { "constant": true, "inputs": [], - "name": "blocksPerYear", + "name": "pendingAdmin", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xa385fb96" + "signature": "0x26782247" }, { + "constant": false, "inputs": [ { - "name": "baseRate_", + "name": "target", + "type": "address" + }, + { + "name": "value", "type": "uint256" }, { - "name": "multiplier_", + "name": "signature", + "type": "string" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "eta", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" - } - ], - "cREP": [ - { - "constant": true, - "inputs": [], - "name": "name", + "name": "queueTransaction", "outputs": [ { "name": "", - "type": "string" + "type": "bytes32" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x06fdde03" + "signature": "0x3a66f901" }, { "constant": false, "inputs": [ { - "name": "spender", + "name": "pendingAdmin_", "type": "address" - }, - { - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "name": "", - "type": "bool" } ], + "name": "setPendingAdmin", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x095ea7b3" + "signature": "0x4dd18bf5" }, { "constant": false, "inputs": [ { - "name": "repayAmount", + "name": "target", + "type": "address" + }, + { + "name": "value", "type": "uint256" - } - ], - "name": "repayBorrow", - "outputs": [ + }, { - "name": "", + "name": "signature", + "type": "string" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "eta", "type": "uint256" } ], + "name": "cancelTransaction", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x0e752702" + "signature": "0x591fcdfe" }, { "constant": true, "inputs": [], - "name": "reserveFactorMantissa", + "name": "delay", "outputs": [ { "name": "", @@ -8930,17 +8904,12 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x173b9904" + "signature": "0x6a42b8f8" }, { - "constant": false, - "inputs": [ - { - "name": "account", - "type": "address" - } - ], - "name": "borrowBalanceCurrent", + "constant": true, + "inputs": [], + "name": "MAXIMUM_DELAY", "outputs": [ { "name": "", @@ -8948,14 +8917,14 @@ } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0x17bfdfbc" + "signature": "0x7d645fab" }, { "constant": true, "inputs": [], - "name": "totalSupply", + "name": "MINIMUM_DELAY", "outputs": [ { "name": "", @@ -8965,12 +8934,12 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x18160ddd" + "signature": "0xb1b43ae5" }, { "constant": true, "inputs": [], - "name": "exchangeRateStored", + "name": "GRACE_PERIOD", "outputs": [ { "name": "", @@ -8980,200 +8949,256 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x182df0f5" + "signature": "0xc1a287e2" }, { "constant": false, "inputs": [ { - "name": "src", - "type": "address" - }, - { - "name": "dst", - "type": "address" - }, - { - "name": "amount", + "name": "delay_", "type": "uint256" } ], - "name": "transferFrom", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], + "name": "setDelay", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x23b872dd" + "signature": "0xe177246e" }, { - "constant": false, + "constant": true, "inputs": [ - { - "name": "borrower", - "type": "address" - }, - { - "name": "repayAmount", - "type": "uint256" - } - ], - "name": "repayBorrowBehalf", - "outputs": [ { "name": "", - "type": "uint256" + "type": "bytes32" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x2608f818" - }, - { - "constant": true, - "inputs": [], - "name": "pendingAdmin", + "name": "queuedTransactions", "outputs": [ { "name": "", - "type": "address" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x26782247" + "signature": "0xf2b06537" }, { "constant": true, "inputs": [], - "name": "decimals", + "name": "admin", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x313ce567" + "signature": "0xf851a440" }, { - "constant": false, "inputs": [ { - "name": "owner", + "name": "admin_", "type": "address" - } - ], - "name": "balanceOfUnderlying", - "outputs": [ + }, { - "name": "", + "name": "delay_", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", - "type": "function", - "signature": "0x3af9e669" + "type": "constructor", + "signature": "constructor" }, { - "constant": true, - "inputs": [], - "name": "getCash", - "outputs": [ + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ { - "name": "", - "type": "uint256" + "indexed": true, + "name": "newAdmin", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x3b1d21a2" + "name": "NewAdmin", + "type": "event", + "signature": "0x71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "newComptroller", + "indexed": true, + "name": "newPendingAdmin", "type": "address" } ], - "name": "_setComptroller", - "outputs": [ + "name": "NewPendingAdmin", + "type": "event", + "signature": "0x69d78e38a01985fbb1462961809b4b2d65531bc93b2b94037f3334b82ca4a756" + }, + { + "anonymous": false, + "inputs": [ { - "name": "", + "indexed": true, + "name": "newDelay", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x4576b5db" + "name": "NewDelay", + "type": "event", + "signature": "0x948b1f6a42ee138b7e34058ba85a37f716d55ff25ff05a763f15bed6a04c8d2c" }, { - "constant": true, - "inputs": [], - "name": "totalBorrows", - "outputs": [ + "anonymous": false, + "inputs": [ { - "name": "", - "type": "uint256" - } + "indexed": true, + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "signature", + "type": "string" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "name": "eta", + "type": "uint256" + } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x47bd3718" + "name": "CancelTransaction", + "type": "event", + "signature": "0x2fffc091a501fd91bfbff27141450d3acb40fb8e6d8382b243ec7a812a3aaf87" }, { - "constant": true, - "inputs": [], - "name": "comptroller", - "outputs": [ + "anonymous": false, + "inputs": [ { - "name": "", + "indexed": true, + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": true, + "name": "target", "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "signature", + "type": "string" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "name": "eta", + "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x5fe3b567" + "name": "ExecuteTransaction", + "type": "event", + "signature": "0xa560e3198060a2f10670c1ec5b403077ea6ae93ca8de1c32b451dc1a943cd6e7" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "reduceAmount", + "indexed": true, + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": false, + "name": "value", "type": "uint256" - } - ], - "name": "_reduceReserves", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "signature", + "type": "string" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "name": "eta", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x601a0bf1" - }, + "name": "QueueTransaction", + "type": "event", + "signature": "0x76e2796dc3a81d57b0e8504b647febcbeeb5f4af818e164f11eef8131a6a763f" + } + ], + "Base200bps_Slope3000bps": [ { "constant": true, - "inputs": [], - "name": "initialExchangeRateMantissa", + "inputs": [ + { + "name": "cash", + "type": "uint256" + }, + { + "name": "borrows", + "type": "uint256" + }, + { + "name": "_reserves", + "type": "uint256" + } + ], + "name": "getBorrowRate", "outputs": [ + { + "name": "", + "type": "uint256" + }, { "name": "", "type": "uint256" @@ -9182,12 +9207,12 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x675d972c" + "signature": "0x15f24053" }, { "constant": true, "inputs": [], - "name": "accrualBlockNumber", + "name": "multiplier", "outputs": [ { "name": "", @@ -9197,47 +9222,42 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x6c540baf" + "signature": "0x1b3ed722" }, { "constant": true, "inputs": [], - "name": "underlying", + "name": "baseRate", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x6f307dc3" + "signature": "0x1f68f20a" }, { "constant": true, - "inputs": [ - { - "name": "owner", - "type": "address" - } - ], - "name": "balanceOf", + "inputs": [], + "name": "isInterestRateModel", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x70a08231" + "signature": "0x2191f92a" }, { - "constant": false, + "constant": true, "inputs": [], - "name": "totalBorrowsCurrent", + "name": "blocksPerYear", "outputs": [ { "name": "", @@ -9245,49 +9265,32 @@ } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0x73acee98" + "signature": "0xa385fb96" }, { - "constant": false, "inputs": [ { - "name": "redeemAmount", + "name": "baseRate_", "type": "uint256" - } - ], - "name": "redeemUnderlying", - "outputs": [ + }, { - "name": "", + "name": "multiplier_", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", - "type": "function", - "signature": "0x852a12e3" - }, - { - "constant": true, - "inputs": [], - "name": "totalReserves", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x8f840ddd" - }, + "type": "constructor", + "signature": "constructor" + } + ], + "cREP": [ { "constant": true, "inputs": [], - "name": "symbol", + "name": "name", "outputs": [ { "name": "", @@ -9297,37 +9300,41 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95d89b41" + "signature": "0x06fdde03" }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "account", + "name": "spender", "type": "address" + }, + { + "name": "amount", + "type": "uint256" } ], - "name": "borrowBalanceStored", + "name": "approve", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x95dd9193" + "signature": "0x095ea7b3" }, { "constant": false, "inputs": [ { - "name": "mintAmount", + "name": "repayAmount", "type": "uint256" } ], - "name": "mint", + "name": "repayBorrow", "outputs": [ { "name": "", @@ -9337,12 +9344,12 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa0712d68" + "signature": "0x0e752702" }, { - "constant": false, + "constant": true, "inputs": [], - "name": "accrueInterest", + "name": "reserveFactorMantissa", "outputs": [ { "name": "", @@ -9350,38 +9357,34 @@ } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xa6afed95" + "signature": "0x173b9904" }, { "constant": false, "inputs": [ { - "name": "dst", + "name": "account", "type": "address" - }, - { - "name": "amount", - "type": "uint256" } ], - "name": "transfer", + "name": "borrowBalanceCurrent", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa9059cbb" + "signature": "0x17bfdfbc" }, { "constant": true, "inputs": [], - "name": "borrowIndex", + "name": "totalSupply", "outputs": [ { "name": "", @@ -9391,12 +9394,12 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xaa5af0fd" + "signature": "0x18160ddd" }, { "constant": true, "inputs": [], - "name": "supplyRatePerBlock", + "name": "exchangeRateStored", "outputs": [ { "name": "", @@ -9406,45 +9409,49 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xae9d70b0" + "signature": "0x182df0f5" }, { "constant": false, "inputs": [ { - "name": "liquidator", + "name": "src", "type": "address" }, { - "name": "borrower", + "name": "dst", "type": "address" }, { - "name": "seizeTokens", + "name": "amount", "type": "uint256" } ], - "name": "seize", + "name": "transferFrom", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xb2a02ff1" + "signature": "0x23b872dd" }, { "constant": false, "inputs": [ { - "name": "newPendingAdmin", + "name": "borrower", "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" } ], - "name": "_setPendingAdmin", + "name": "repayBorrowBehalf", "outputs": [ { "name": "", @@ -9454,45 +9461,63 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xb71d1a0c" + "signature": "0x2608f818" }, { - "constant": false, + "constant": true, "inputs": [], - "name": "exchangeRateCurrent", + "name": "pendingAdmin", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xbd6d894d" + "signature": "0x26782247" }, { "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" + }, + { + "constant": false, "inputs": [ { - "name": "account", + "name": "owner", "type": "address" } ], - "name": "getAccountSnapshot", + "name": "balanceOfUnderlying", "outputs": [ { "name": "", "type": "uint256" - }, - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" - }, + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x3af9e669" + }, + { + "constant": true, + "inputs": [], + "name": "getCash", + "outputs": [ { "name": "", "type": "uint256" @@ -9501,17 +9526,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xc37f68e2" + "signature": "0x3b1d21a2" }, { "constant": false, "inputs": [ { - "name": "borrowAmount", - "type": "uint256" + "name": "newComptroller", + "type": "address" } ], - "name": "borrow", + "name": "_setComptroller", "outputs": [ { "name": "", @@ -9521,17 +9546,47 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xc5ebeaec" + "signature": "0x4576b5db" + }, + { + "constant": true, + "inputs": [], + "name": "totalBorrows", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x47bd3718" + }, + { + "constant": true, + "inputs": [], + "name": "comptroller", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x5fe3b567" }, { "constant": false, "inputs": [ { - "name": "redeemTokens", + "name": "reduceAmount", "type": "uint256" } ], - "name": "redeem", + "name": "_reduceReserves", "outputs": [ { "name": "", @@ -9541,21 +9596,27 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xdb006a75" + "signature": "0x601a0bf1" }, { "constant": true, - "inputs": [ - { - "name": "owner", - "type": "address" - }, + "inputs": [], + "name": "initialExchangeRateMantissa", + "outputs": [ { - "name": "spender", - "type": "address" + "name": "", + "type": "uint256" } ], - "name": "allowance", + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x675d972c" + }, + { + "constant": true, + "inputs": [], + "name": "accrualBlockNumber", "outputs": [ { "name": "", @@ -9565,32 +9626,32 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xdd62ed3e" + "signature": "0x6c540baf" }, { - "constant": false, + "constant": true, "inputs": [], - "name": "_acceptAdmin", + "name": "underlying", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xe9c714f2" + "signature": "0x6f307dc3" }, { - "constant": false, + "constant": true, "inputs": [ { - "name": "newInterestRateModel", + "name": "owner", "type": "address" } ], - "name": "_setInterestRateModel", + "name": "balanceOf", "outputs": [ { "name": "", @@ -9598,42 +9659,34 @@ } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xf2b3abbd" + "signature": "0x70a08231" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "interestRateModel", + "name": "totalBorrowsCurrent", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0xf3fdb15a" + "signature": "0x73acee98" }, { "constant": false, "inputs": [ { - "name": "borrower", - "type": "address" - }, - { - "name": "repayAmount", + "name": "redeemAmount", "type": "uint256" - }, - { - "name": "cTokenCollateral", - "type": "address" } ], - "name": "liquidateBorrow", + "name": "redeemUnderlying", "outputs": [ { "name": "", @@ -9643,27 +9696,47 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xf5e3c462" + "signature": "0x852a12e3" }, { "constant": true, "inputs": [], - "name": "admin", + "name": "totalReserves", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xf851a440" + "signature": "0x8f840ddd" }, { "constant": true, "inputs": [], - "name": "borrowRatePerBlock", + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95d89b41" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", "outputs": [ { "name": "", @@ -9673,17 +9746,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xf8f9da28" + "signature": "0x95dd9193" }, { "constant": false, "inputs": [ { - "name": "newReserveFactorMantissa", + "name": "mintAmount", "type": "uint256" } ], - "name": "_setReserveFactor", + "name": "mint", "outputs": [ { "name": "", @@ -9693,403 +9766,2033 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xfca7820b" + "signature": "0xa0712d68" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "isCToken", + "name": "accrueInterest", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0xfe9c44ae" + "signature": "0xa6afed95" }, { + "constant": false, "inputs": [ { - "name": "underlying_", - "type": "address" - }, - { - "name": "comptroller_", - "type": "address" - }, - { - "name": "interestRateModel_", + "name": "dst", "type": "address" }, { - "name": "initialExchangeRateMantissa_", + "name": "amount", "type": "uint256" - }, - { - "name": "name_", - "type": "string" - }, - { - "name": "symbol_", - "type": "string" - }, + } + ], + "name": "transfer", + "outputs": [ { - "name": "decimals_", - "type": "uint256" + "name": "", + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" + "type": "function", + "signature": "0xa9059cbb" }, { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "interestAccumulated", - "type": "uint256" - }, + "constant": true, + "inputs": [], + "name": "borrowIndex", + "outputs": [ { - "indexed": false, - "name": "borrowIndex", + "name": "", "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xaa5af0fd" + }, + { + "constant": true, + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xae9d70b0" + }, + { + "constant": false, + "inputs": [ + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seize", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xb2a02ff1" + }, + { + "constant": false, + "inputs": [ + { + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "_setPendingAdmin", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xb71d1a0c" + }, + { + "constant": false, + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xbd6d894d" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xc37f68e2" + }, + { + "constant": false, + "inputs": [ + { + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xc5ebeaec" + }, + { + "constant": false, + "inputs": [ + { + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xdb006a75" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdd62ed3e" + }, + { + "constant": false, + "inputs": [], + "name": "_acceptAdmin", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xe9c714f2" + }, + { + "constant": false, + "inputs": [ + { + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "_setInterestRateModel", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xf2b3abbd" + }, + { + "constant": true, + "inputs": [], + "name": "interestRateModel", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf3fdb15a" + }, + { + "constant": false, + "inputs": [ + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + }, + { + "name": "cTokenCollateral", + "type": "address" + } + ], + "name": "liquidateBorrow", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xf5e3c462" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf851a440" + }, + { + "constant": true, + "inputs": [], + "name": "borrowRatePerBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf8f9da28" + }, + { + "constant": false, + "inputs": [ + { + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "_setReserveFactor", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xfca7820b" + }, + { + "constant": true, + "inputs": [], + "name": "isCToken", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xfe9c44ae" + }, + { + "inputs": [ + { + "name": "underlying_", + "type": "address" + }, + { + "name": "comptroller_", + "type": "address" + }, + { + "name": "interestRateModel_", + "type": "address" + }, + { + "name": "initialExchangeRateMantissa_", + "type": "uint256" + }, + { + "name": "name_", + "type": "string" + }, + { + "name": "symbol_", + "type": "string" + }, + { + "name": "decimals_", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "interestAccumulated", + "type": "uint256" + }, + { + "indexed": false, + "name": "borrowIndex", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "AccrueInterest", + "type": "event", + "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "name": "mintAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "mintTokens", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event", + "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "redeemer", + "type": "address" + }, + { + "indexed": false, + "name": "redeemAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event", + "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "borrowAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "Borrow", + "type": "event", + "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "payer", + "type": "address" + }, + { + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "RepayBorrow", + "type": "event", + "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "liquidator", + "type": "address" + }, + { + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "cTokenCollateral", + "type": "address" + }, + { + "indexed": false, + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "LiquidateBorrow", + "type": "event", + "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldPendingAdmin", + "type": "address" + }, + { + "indexed": false, + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "NewPendingAdmin", + "type": "event", + "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldAdmin", + "type": "address" + }, + { + "indexed": false, + "name": "newAdmin", + "type": "address" + } + ], + "name": "NewAdmin", + "type": "event", + "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": false, + "name": "newComptroller", + "type": "address" + } + ], + "name": "NewComptroller", + "type": "event", + "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": false, + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event", + "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldReserveFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "NewReserveFactor", + "type": "event", + "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "admin", + "type": "address" + }, + { + "indexed": false, + "name": "reduceAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesReduced", + "type": "event", + "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "error", + "type": "uint256" + }, + { + "indexed": false, + "name": "info", + "type": "uint256" + }, + { + "indexed": false, + "name": "detail", + "type": "uint256" + } + ], + "name": "Failure", + "type": "event", + "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event", + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + } + ], + "ComptrollerG2": [ + { + "constant": true, + "inputs": [], + "name": "isComptroller", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x007e3dd2" + }, + { + "constant": false, + "inputs": [ + { + "name": "unitroller", + "type": "address" + } + ], + "name": "_become", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x1d504dc6" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "payer", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + }, + { + "name": "borrowerIndex", + "type": "uint256" + } + ], + "name": "repayBorrowVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x1ededc91" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "payer", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrowAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x24008a62" + }, + { + "constant": true, + "inputs": [], + "name": "pauseGuardian", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x24a3d622" + }, + { + "constant": true, + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x26782247" + }, + { + "constant": false, + "inputs": [ + { + "name": "state", + "type": "bool" + } + ], + "name": "_setSeizePaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x2d70db78" + }, + { + "constant": false, + "inputs": [ + { + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "_setCloseFactor", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x317b0b77" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "minter", + "type": "address" + }, + { + "name": "mintAmount", + "type": "uint256" + }, + { + "name": "mintTokens", + "type": "uint256" + } + ], + "name": "mintVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x41c728b9" + }, + { + "constant": false, + "inputs": [ + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + }, + { + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "liquidateBorrowVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x47ef3b3b" + }, + { + "constant": true, + "inputs": [], + "name": "liquidationIncentiveMantissa", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x4ada90af" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "minter", + "type": "address" + }, + { + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x4ef4c3e1" + }, + { + "constant": false, + "inputs": [ + { + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "_setLiquidationIncentive", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x4fd42e17" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "redeemer", + "type": "address" + }, + { + "name": "redeemAmount", + "type": "uint256" + }, + { + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x51dff989" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOracle", + "type": "address" + } + ], + "name": "_setPriceOracle", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x55ee1fe1" + }, + { + "constant": false, + "inputs": [ + { + "name": "state", + "type": "bool" + } + ], + "name": "_setBorrowPaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x56133fc8" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x5c778605" + }, + { + "constant": true, + "inputs": [], + "name": "mintGuardianPaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x5dce0515" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getAccountLiquidity", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x5ec88c79" + }, + { + "constant": false, + "inputs": [ + { + "name": "newPauseGuardian", + "type": "address" + } + ], + "name": "_setPauseGuardian", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x5f5af1aa" + }, + { + "constant": false, + "inputs": [ + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "liquidateBorrowAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x5fc7e71e" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "transferTokens", + "type": "uint256" + } + ], + "name": "transferVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x6a56947e" + }, + { + "constant": false, + "inputs": [ + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seizeVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x6d35bf91" + }, + { + "constant": true, + "inputs": [], + "name": "oracle", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x7dc0d1d0" + }, + { + "constant": true, + "inputs": [], + "name": "transferGuardianPaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x87f76303" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "markets", + "outputs": [ + { + "name": "isListed", + "type": "bool" + }, + { + "name": "collateralFactorMantissa", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x8e8f294b" + }, + { + "constant": false, + "inputs": [ + { + "name": "state", + "type": "bool" + } + ], + "name": "_setTransferPaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x8ebf6364" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + }, + { + "name": "cToken", + "type": "address" + } + ], + "name": "checkMembership", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x929fe9a1" + }, + { + "constant": true, + "inputs": [], + "name": "maxAssets", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x94b2294b" + }, + { + "constant": true, + "inputs": [], + "name": "borrowGuardianPaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x9530f644" + }, + { + "constant": false, + "inputs": [ + { + "name": "state", + "type": "bool" + } + ], + "name": "_setMintPaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x9845f280" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + } + ], + "name": "_supportMarket", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa76b3fda" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getAssetsIn", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xabfceffc" + }, + { + "constant": true, + "inputs": [], + "name": "seizeGuardianPaused", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xac0b0bb7" + }, + { + "constant": true, + "inputs": [], + "name": "comptrollerImplementation", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xbb82aa5e" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" }, { - "indexed": false, - "name": "totalBorrows", + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "transferTokens", "type": "uint256" } ], - "name": "AccrueInterest", - "type": "event", - "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" + "name": "transferAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xbdcdc258" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": false, - "name": "minter", + "name": "cTokens", + "type": "address[]" + } + ], + "name": "enterMarkets", + "outputs": [ + { + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xc2998238" + }, + { + "constant": true, + "inputs": [ + { + "name": "cTokenBorrowed", "type": "address" }, { - "indexed": false, - "name": "mintAmount", + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "liquidateCalculateSeizeTokens", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xc488847b" + }, + { + "constant": false, + "inputs": [ + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seizeAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xd02f7351" + }, + { + "constant": false, + "inputs": [ + { + "name": "newMaxAssets", + "type": "uint256" + } + ], + "name": "_setMaxAssets", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xd9226ced" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xda3d454c" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", "type": "uint256" + } + ], + "name": "accountAssets", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdce15449" + }, + { + "constant": true, + "inputs": [], + "name": "pendingComptrollerImplementation", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdcfbc0c7" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" }, { - "indexed": false, - "name": "mintTokens", + "name": "newCollateralFactorMantissa", + "type": "uint256" + } + ], + "name": "_setCollateralFactor", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xe4028eee" + }, + { + "constant": true, + "inputs": [], + "name": "closeFactorMantissa", + "outputs": [ + { + "name": "", "type": "uint256" } ], - "name": "Mint", - "type": "event", - "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xe8755446" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": false, - "name": "redeemer", + "name": "cToken", "type": "address" }, { - "indexed": false, - "name": "redeemAmount", - "type": "uint256" + "name": "redeemer", + "type": "address" }, { - "indexed": false, "name": "redeemTokens", "type": "uint256" } ], - "name": "Redeem", - "type": "event", - "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" + "name": "redeemAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xeabe7d91" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": false, - "name": "borrower", + "name": "cTokenAddress", "type": "address" - }, - { - "indexed": false, - "name": "borrowAmount", - "type": "uint256" - }, + } + ], + "name": "exitMarket", + "outputs": [ { - "indexed": false, - "name": "accountBorrows", + "name": "", "type": "uint256" - }, + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xede4edd0" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ { - "indexed": false, - "name": "totalBorrows", - "type": "uint256" + "name": "", + "type": "address" } ], - "name": "Borrow", - "type": "event", - "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf851a440" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "payer", - "type": "address" - }, - { - "indexed": false, - "name": "borrower", + "name": "cToken", "type": "address" - }, - { - "indexed": false, - "name": "repayAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "accountBorrows", - "type": "uint256" - }, - { - "indexed": false, - "name": "totalBorrows", - "type": "uint256" } ], - "name": "RepayBorrow", + "name": "MarketListed", "type": "event", - "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" + "signature": "0xcf583bb0c569eb967f806b11601c4cb93c10310485c67add5f8362c2f212321f" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "liquidator", - "type": "address" - }, - { - "indexed": false, - "name": "borrower", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "repayAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "cTokenCollateral", + "name": "account", "type": "address" - }, - { - "indexed": false, - "name": "seizeTokens", - "type": "uint256" } ], - "name": "LiquidateBorrow", + "name": "MarketEntered", "type": "event", - "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" + "signature": "0x3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a5" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldPendingAdmin", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "newPendingAdmin", + "name": "account", "type": "address" } ], - "name": "NewPendingAdmin", + "name": "MarketExited", "type": "event", - "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" + "signature": "0xe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldAdmin", - "type": "address" + "name": "oldCloseFactorMantissa", + "type": "uint256" }, { "indexed": false, - "name": "newAdmin", - "type": "address" + "name": "newCloseFactorMantissa", + "type": "uint256" } ], - "name": "NewAdmin", + "name": "NewCloseFactor", "type": "event", - "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" + "signature": "0x3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd9" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldComptroller", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "newComptroller", - "type": "address" + "name": "oldCollateralFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "name": "newCollateralFactorMantissa", + "type": "uint256" } ], - "name": "NewComptroller", + "name": "NewCollateralFactor", "type": "event", - "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" + "signature": "0x70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc5" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldInterestRateModel", - "type": "address" + "name": "oldLiquidationIncentiveMantissa", + "type": "uint256" }, { "indexed": false, - "name": "newInterestRateModel", - "type": "address" + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" } ], - "name": "NewMarketInterestRateModel", + "name": "NewLiquidationIncentive", "type": "event", - "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" + "signature": "0xaeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec1316" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldReserveFactorMantissa", + "name": "oldMaxAssets", "type": "uint256" }, { "indexed": false, - "name": "newReserveFactorMantissa", + "name": "newMaxAssets", "type": "uint256" } ], - "name": "NewReserveFactor", + "name": "NewMaxAssets", "type": "event", - "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" + "signature": "0x7093cf1eb653f749c3ff531d6df7f92764536a7fa0d13530cd26e070780c32ea" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "admin", + "name": "oldPriceOracle", "type": "address" }, { "indexed": false, - "name": "reduceAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "newTotalReserves", - "type": "uint256" + "name": "newPriceOracle", + "type": "address" } ], - "name": "ReservesReduced", + "name": "NewPriceOracle", "type": "event", - "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" + "signature": "0xd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e22" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "error", - "type": "uint256" - }, - { - "indexed": false, - "name": "info", - "type": "uint256" + "name": "oldPauseGuardian", + "type": "address" }, { "indexed": false, - "name": "detail", - "type": "uint256" + "name": "newPauseGuardian", + "type": "address" } ], - "name": "Failure", + "name": "NewPauseGuardian", "type": "event", - "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" + "signature": "0x0613b6ee6a04f0d09f390e4d9318894b9f6ac7fd83897cd8d18896ba579c401e" }, { "anonymous": false, "inputs": [ { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" + "indexed": false, + "name": "action", + "type": "string" }, { "indexed": false, - "name": "amount", - "type": "uint256" + "name": "pauseState", + "type": "bool" } ], - "name": "Transfer", + "name": "ActionPaused", "type": "event", - "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + "signature": "0xef159d9a32b2472e32b098f954f3ce62d232939f1c207070b584df1814de2de0" }, { "anonymous": false, "inputs": [ { - "indexed": true, - "name": "owner", - "type": "address" + "indexed": false, + "name": "error", + "type": "uint256" }, { - "indexed": true, - "name": "spender", - "type": "address" + "indexed": false, + "name": "info", + "type": "uint256" }, { "indexed": false, - "name": "amount", + "name": "detail", "type": "uint256" } ], - "name": "Approval", + "name": "Failure", "type": "event", - "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" } ], "MoneyMarket": [ diff --git a/networks/mainnet.json b/networks/mainnet.json index ad4bd5269..aa361b3f2 100644 --- a/networks/mainnet.json +++ b/networks/mainnet.json @@ -16,8 +16,10 @@ "BAT": "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "cETH": "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", "Base500bps_Slope1200bps": "0xa1046abfc2598F48C44Fb320d281d3F3c0733c9a", + "Timelock": "0x6d903f6003cca6255D85CcA4D3B5E5146dC33925", "Base200bps_Slope3000bps": "0xBAE04CbF96391086dC643e842b517734E214D698", "cREP": "0x158079Ee67Fce2f58472A96584A73C7Ab9AC95c1", + "ComptrollerG2": "0xf592eF673057a451c49c9433E278c5d59b56132c", "WBTC": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", "REP": "0x1985365e9f78359a9B6AD760e32412f4a445E862", "cZRX": "0xB3319f5D18Bc0D84dD1b4825Dcde5d5f7266d407", @@ -36,8 +38,10 @@ "Base0bps_Slope2000bps": 7710727, "cETH": 7710758, "Base500bps_Slope1200bps": 7710726, + "Timelock": 8722895, "Base200bps_Slope3000bps": 7710728, "cREP": 7710755, + "ComptrollerG2": 8722898, "cZRX": 7710733, "cWBTC": 8163813 }, @@ -62,8 +66,18 @@ "address": "0x62F18C451af964197341d3c86D27e98C41BB8fcC", "contract": "Comptroller", "description": "Standard Comptroller Impl" + }, + "ComptrollerG2": { + "address": "0xf592eF673057a451c49c9433E278c5d59b56132c", + "contract": "Comptroller", + "description": "Standard Comptroller Impl" } }, + "Timelock": { + "address": "0x6d903f6003cca6255D85CcA4D3B5E5146dC33925", + "contract": "Timelock", + "description": "Standard Timelock" + }, "Constructors": { "cUSDC": "0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000003d9819210a31b4961b30ef54be2aed79b9c9cd3b000000000000000000000000c64c4cba055efa614ce01f4bad8a9f519c4f8fab0000000000000000000000000000000000000000000000000000b5e620f4800000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000011436f6d706f756e642055534420436f696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056355534443000000000000000000000000000000000000000000000000000000", "PriceOracleProxy": "0x0000000000000000000000003d9819210a31b4961b30ef54be2aed79b9c9cd3b00000000000000000000000002557a5e05defeffd4cae6d83ea3d173b272c9040000000000000000000000004ddc2d193948926d02f9b1fe9e1daa0718270ed500000000000000000000000039aa39c021dfbae8fac545936693ac917d5e7563000000000000000000000000f5dce57282a584d2746faf1593d3121fcac444dc", @@ -75,8 +89,10 @@ "Base0bps_Slope2000bps": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c68af0bb140000", "cETH": "0x0000000000000000000000003d9819210a31b4961b30ef54be2aed79b9c9cd3b000000000000000000000000c64c4cba055efa614ce01f4bad8a9f519c4f8fab000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e436f6d706f756e6420457468657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046345544800000000000000000000000000000000000000000000000000000000", "Base500bps_Slope1200bps": "0x00000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000001aa535d3d0c0000", + "Timelock": "0x0000000000000000000000008b8592e9570e96166336603a1b4bd1e8db20fa20000000000000000000000000000000000000000000000000000000000002a300", "Base200bps_Slope3000bps": "0x00000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000429d069189e0000", "cREP": "0x0000000000000000000000001985365e9f78359a9b6ad760e32412f4a445e8620000000000000000000000003d9819210a31b4961b30ef54be2aed79b9c9cd3b000000000000000000000000bae04cbf96391086dc643e842b517734e214d698000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e436f6d706f756e6420417567757200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046352455000000000000000000000000000000000000000000000000000000000", + "ComptrollerG2": "0x", "cZRX": "0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000003d9819210a31b4961b30ef54be2aed79b9c9cd3b000000000000000000000000bae04cbf96391086dc643e842b517734e214d698000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000b436f6d706f756e642030780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004635a525800000000000000000000000000000000000000000000000000000000", "cWBTC": "0x0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000003d9819210a31b4961b30ef54be2aed79b9c9cd3b000000000000000000000000bae04cbf96391086dc643e842b517734e214d69800000000000000000000000000000000000000000000000000470de4df82000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000014436f6d706f756e6420577261707065642042544300000000000000000000000000000000000000000000000000000000000000000000000000000000000000056357425443000000000000000000000000000000000000000000000000000000" }, diff --git a/networks/rinkeby-abi.json b/networks/rinkeby-abi.json index d0539d787..e38902685 100644 --- a/networks/rinkeby-abi.json +++ b/networks/rinkeby-abi.json @@ -8724,57 +8724,89 @@ "name": "Failure", "type": "event", "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" - } - ], - "cBAT": [ + }, { "constant": true, "inputs": [], - "name": "name", + "name": "isComptroller", "outputs": [ { "name": "", - "type": "string" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x06fdde03" + "signature": "0x007e3dd2" }, { "constant": false, "inputs": [ { - "name": "spender", + "name": "unitroller", + "type": "address" + } + ], + "name": "_become", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x1d504dc6" + }, + { + "constant": false, + "inputs": [ + { + "name": "cToken", "type": "address" }, { - "name": "amount", + "name": "payer", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", "type": "uint256" - } - ], - "name": "approve", - "outputs": [ + }, { - "name": "", - "type": "bool" + "name": "borrowerIndex", + "type": "uint256" } ], + "name": "repayBorrowVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x095ea7b3" + "signature": "0x1ededc91" }, { "constant": false, "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "payer", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, { "name": "repayAmount", "type": "uint256" } ], - "name": "repayBorrow", + "name": "repayBorrowAllowed", "outputs": [ { "name": "", @@ -8784,62 +8816,67 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x0e752702" + "signature": "0x24008a62" }, { "constant": true, "inputs": [], - "name": "reserveFactorMantissa", + "name": "pauseGuardian", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x173b9904" + "signature": "0x24a3d622" + }, + { + "constant": true, + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x26782247" }, { "constant": false, "inputs": [ { - "name": "account", - "type": "address" + "name": "state", + "type": "bool" } ], - "name": "borrowBalanceCurrent", + "name": "_setSeizePaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x17bfdfbc" + "signature": "0x2d70db78" }, { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", + "name": "newCloseFactorMantissa", "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x18160ddd" - }, - { - "constant": true, - "inputs": [], - "name": "exchangeRateStored", + "name": "_setCloseFactor", "outputs": [ { "name": "", @@ -8847,41 +8884,52 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x182df0f5" + "signature": "0x317b0b77" }, { "constant": false, "inputs": [ { - "name": "src", + "name": "cToken", "type": "address" }, { - "name": "dst", + "name": "minter", "type": "address" }, { - "name": "amount", + "name": "mintAmount", "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ + }, { - "name": "", - "type": "bool" + "name": "mintTokens", + "type": "uint256" } ], + "name": "mintVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x23b872dd" + "signature": "0x41c728b9" }, { "constant": false, "inputs": [ + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, { "name": "borrower", "type": "address" @@ -8889,39 +8937,51 @@ { "name": "repayAmount", "type": "uint256" - } - ], - "name": "repayBorrowBehalf", - "outputs": [ + }, { - "name": "", + "name": "seizeTokens", "type": "uint256" } ], + "name": "liquidateBorrowVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x2608f818" + "signature": "0x47ef3b3b" }, { "constant": true, "inputs": [], - "name": "pendingAdmin", + "name": "liquidationIncentiveMantissa", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x26782247" + "signature": "0x4ada90af" }, { - "constant": true, - "inputs": [], - "name": "decimals", + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "minter", + "type": "address" + }, + { + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintAllowed", "outputs": [ { "name": "", @@ -8929,19 +8989,19 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x313ce567" + "signature": "0x4ef4c3e1" }, { "constant": false, "inputs": [ { - "name": "owner", - "type": "address" + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" } ], - "name": "balanceOfUnderlying", + "name": "_setLiquidationIncentive", "outputs": [ { "name": "", @@ -8951,32 +9011,44 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x3af9e669" + "signature": "0x4fd42e17" }, { - "constant": true, - "inputs": [], - "name": "getCash", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", + "name": "cToken", + "type": "address" + }, + { + "name": "redeemer", + "type": "address" + }, + { + "name": "redeemAmount", + "type": "uint256" + }, + { + "name": "redeemTokens", "type": "uint256" } ], + "name": "redeemVerify", + "outputs": [], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x3b1d21a2" + "signature": "0x51dff989" }, { "constant": false, "inputs": [ { - "name": "newComptroller", + "name": "newOracle", "type": "address" } ], - "name": "_setComptroller", + "name": "_setPriceOracle", "outputs": [ { "name": "", @@ -8986,78 +9058,84 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x4576b5db" + "signature": "0x55ee1fe1" }, { - "constant": true, - "inputs": [], - "name": "totalBorrows", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", - "type": "uint256" + "name": "state", + "type": "bool" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x47bd3718" - }, - { - "constant": true, - "inputs": [], - "name": "comptroller", + "name": "_setBorrowPaused", "outputs": [ { "name": "", - "type": "address" + "type": "bool" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x5fe3b567" + "signature": "0x56133fc8" }, { "constant": false, "inputs": [ { - "name": "reduceAmount", - "type": "uint256" - } - ], - "name": "_reduceReserves", - "outputs": [ + "name": "cToken", + "type": "address" + }, { - "name": "", + "name": "borrower", + "type": "address" + }, + { + "name": "borrowAmount", "type": "uint256" } ], + "name": "borrowVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x601a0bf1" + "signature": "0x5c778605" }, { "constant": true, "inputs": [], - "name": "initialExchangeRateMantissa", + "name": "mintGuardianPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x675d972c" + "signature": "0x5dce0515" }, { "constant": true, - "inputs": [], - "name": "accrualBlockNumber", + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getAccountLiquidity", "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, { "name": "", "type": "uint256" @@ -9066,32 +9144,53 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x6c540baf" + "signature": "0x5ec88c79" }, { - "constant": true, - "inputs": [], - "name": "underlying", + "constant": false, + "inputs": [ + { + "name": "newPauseGuardian", + "type": "address" + } + ], + "name": "_setPauseGuardian", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x6f307dc3" + "signature": "0x5f5af1aa" }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "owner", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" } ], - "name": "balanceOf", + "name": "liquidateBorrowAllowed", "outputs": [ { "name": "", @@ -9099,143 +9198,155 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x70a08231" + "signature": "0x5fc7e71e" }, { "constant": false, - "inputs": [], - "name": "totalBorrowsCurrent", - "outputs": [ + "inputs": [ { - "name": "", + "name": "cToken", + "type": "address" + }, + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "transferTokens", "type": "uint256" } ], + "name": "transferVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x73acee98" + "signature": "0x6a56947e" }, { "constant": false, "inputs": [ { - "name": "redeemAmount", - "type": "uint256" - } - ], - "name": "redeemUnderlying", - "outputs": [ + "name": "cTokenCollateral", + "type": "address" + }, { - "name": "", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", "type": "uint256" } ], + "name": "seizeVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x852a12e3" + "signature": "0x6d35bf91" }, { "constant": true, "inputs": [], - "name": "totalReserves", + "name": "oracle", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x8f840ddd" + "signature": "0x7dc0d1d0" }, { "constant": true, "inputs": [], - "name": "symbol", + "name": "transferGuardianPaused", "outputs": [ { "name": "", - "type": "string" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95d89b41" + "signature": "0x87f76303" }, { "constant": true, "inputs": [ { - "name": "account", + "name": "", "type": "address" } ], - "name": "borrowBalanceStored", + "name": "markets", "outputs": [ { - "name": "", + "name": "isListed", + "type": "bool" + }, + { + "name": "collateralFactorMantissa", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95dd9193" + "signature": "0x8e8f294b" }, { "constant": false, "inputs": [ { - "name": "mintAmount", - "type": "uint256" - } - ], - "name": "mint", - "outputs": [ - { - "name": "", - "type": "uint256" + "name": "state", + "type": "bool" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xa0712d68" - }, - { - "constant": false, - "inputs": [], - "name": "accrueInterest", + "name": "_setTransferPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa6afed95" + "signature": "0x8ebf6364" }, { - "constant": false, + "constant": true, "inputs": [ { - "name": "dst", + "name": "account", "type": "address" }, { - "name": "amount", - "type": "uint256" + "name": "cToken", + "type": "address" } ], - "name": "transfer", + "name": "checkMembership", "outputs": [ { "name": "", @@ -9243,14 +9354,14 @@ } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xa9059cbb" + "signature": "0x929fe9a1" }, { "constant": true, "inputs": [], - "name": "borrowIndex", + "name": "maxAssets", "outputs": [ { "name": "", @@ -9260,75 +9371,52 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xaa5af0fd" + "signature": "0x94b2294b" }, { "constant": true, "inputs": [], - "name": "supplyRatePerBlock", + "name": "borrowGuardianPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xae9d70b0" + "signature": "0x9530f644" }, { "constant": false, "inputs": [ { - "name": "liquidator", - "type": "address" - }, - { - "name": "borrower", - "type": "address" - }, - { - "name": "seizeTokens", - "type": "uint256" + "name": "state", + "type": "bool" } ], - "name": "seize", + "name": "_setMintPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xb2a02ff1" + "signature": "0x9845f280" }, { "constant": false, "inputs": [ { - "name": "newPendingAdmin", + "name": "cToken", "type": "address" } ], - "name": "_setPendingAdmin", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xb71d1a0c" - }, - { - "constant": false, - "inputs": [], - "name": "exchangeRateCurrent", + "name": "_supportMarket", "outputs": [ { "name": "", @@ -9338,7 +9426,7 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xbd6d894d" + "signature": "0xa76b3fda" }, { "constant": true, @@ -9348,59 +9436,69 @@ "type": "address" } ], - "name": "getAccountSnapshot", + "name": "getAssetsIn", "outputs": [ { "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" + "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xc37f68e2" + "signature": "0xabfceffc" }, { - "constant": false, - "inputs": [ - { - "name": "borrowAmount", - "type": "uint256" - } - ], - "name": "borrow", + "constant": true, + "inputs": [], + "name": "seizeGuardianPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xc5ebeaec" + "signature": "0xac0b0bb7" + }, + { + "constant": true, + "inputs": [], + "name": "comptrollerImplementation", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xbb82aa5e" }, { "constant": false, "inputs": [ { - "name": "redeemTokens", + "name": "cToken", + "type": "address" + }, + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "transferTokens", "type": "uint256" } ], - "name": "redeem", + "name": "transferAllowed", "outputs": [ { "name": "", @@ -9410,56 +9508,85 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xdb006a75" + "signature": "0xbdcdc258" }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "owner", - "type": "address" - }, - { - "name": "spender", - "type": "address" + "name": "cTokens", + "type": "address[]" } ], - "name": "allowance", + "name": "enterMarkets", "outputs": [ { "name": "", - "type": "uint256" + "type": "uint256[]" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0xdd62ed3e" + "signature": "0xc2998238" }, { - "constant": false, - "inputs": [], - "name": "_acceptAdmin", + "constant": true, + "inputs": [ + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "liquidateCalculateSeizeTokens", "outputs": [ + { + "name": "", + "type": "uint256" + }, { "name": "", "type": "uint256" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xe9c714f2" + "signature": "0xc488847b" }, { "constant": false, "inputs": [ { - "name": "newInterestRateModel", + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", "type": "address" + }, + { + "name": "seizeTokens", + "type": "uint256" } ], - "name": "_setInterestRateModel", + "name": "seizeAllowed", "outputs": [ { "name": "", @@ -9469,40 +9596,45 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xf2b3abbd" + "signature": "0xd02f7351" }, { - "constant": true, - "inputs": [], - "name": "interestRateModel", + "constant": false, + "inputs": [ + { + "name": "newMaxAssets", + "type": "uint256" + } + ], + "name": "_setMaxAssets", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0xf3fdb15a" + "signature": "0xd9226ced" }, { "constant": false, "inputs": [ { - "name": "borrower", + "name": "cToken", "type": "address" }, { - "name": "repayAmount", - "type": "uint256" + "name": "borrower", + "type": "address" }, { - "name": "cTokenCollateral", - "type": "address" + "name": "borrowAmount", + "type": "uint256" } ], - "name": "liquidateBorrow", + "name": "borrowAllowed", "outputs": [ { "name": "", @@ -9512,12 +9644,21 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xf5e3c462" + "signature": "0xda3d454c" }, { "constant": true, - "inputs": [], - "name": "admin", + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "accountAssets", "outputs": [ { "name": "", @@ -9527,32 +9668,36 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xf851a440" + "signature": "0xdce15449" }, { "constant": true, "inputs": [], - "name": "borrowRatePerBlock", + "name": "pendingComptrollerImplementation", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xf8f9da28" + "signature": "0xdcfbc0c7" }, { "constant": false, "inputs": [ { - "name": "newReserveFactorMantissa", + "name": "cToken", + "type": "address" + }, + { + "name": "newCollateralFactorMantissa", "type": "uint256" } ], - "name": "_setReserveFactor", + "name": "_setCollateralFactor", "outputs": [ { "name": "", @@ -9562,56 +9707,90 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xfca7820b" + "signature": "0xe4028eee" }, { "constant": true, "inputs": [], - "name": "isCToken", + "name": "closeFactorMantissa", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xfe9c44ae" + "signature": "0xe8755446" }, { + "constant": false, "inputs": [ { - "name": "underlying_", - "type": "address" - }, - { - "name": "comptroller_", + "name": "cToken", "type": "address" }, { - "name": "interestRateModel_", + "name": "redeemer", "type": "address" }, { - "name": "initialExchangeRateMantissa_", + "name": "redeemTokens", "type": "uint256" - }, + } + ], + "name": "redeemAllowed", + "outputs": [ { - "name": "name_", - "type": "string" - }, + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xeabe7d91" + }, + { + "constant": false, + "inputs": [ { - "name": "symbol_", - "type": "string" - }, + "name": "cTokenAddress", + "type": "address" + } + ], + "name": "exitMarket", + "outputs": [ { - "name": "decimals_", + "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", + "type": "function", + "signature": "0xede4edd0" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf851a440" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", "type": "constructor", "signature": "constructor" }, @@ -9620,276 +9799,180 @@ "inputs": [ { "indexed": false, - "name": "interestAccumulated", - "type": "uint256" - }, - { - "indexed": false, - "name": "borrowIndex", - "type": "uint256" - }, - { - "indexed": false, - "name": "totalBorrows", - "type": "uint256" + "name": "cToken", + "type": "address" } ], - "name": "AccrueInterest", + "name": "MarketListed", "type": "event", - "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" + "signature": "0xcf583bb0c569eb967f806b11601c4cb93c10310485c67add5f8362c2f212321f" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "minter", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "mintAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "mintTokens", - "type": "uint256" + "name": "account", + "type": "address" } ], - "name": "Mint", + "name": "MarketEntered", "type": "event", - "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" + "signature": "0x3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a5" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "redeemer", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "redeemAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "redeemTokens", - "type": "uint256" + "name": "account", + "type": "address" } ], - "name": "Redeem", + "name": "MarketExited", "type": "event", - "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" + "signature": "0xe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "borrower", - "type": "address" - }, - { - "indexed": false, - "name": "borrowAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "accountBorrows", + "name": "oldCloseFactorMantissa", "type": "uint256" }, { "indexed": false, - "name": "totalBorrows", + "name": "newCloseFactorMantissa", "type": "uint256" } ], - "name": "Borrow", + "name": "NewCloseFactor", "type": "event", - "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" + "signature": "0x3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd9" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "payer", - "type": "address" - }, - { - "indexed": false, - "name": "borrower", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "repayAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "accountBorrows", + "name": "oldCollateralFactorMantissa", "type": "uint256" }, { "indexed": false, - "name": "totalBorrows", + "name": "newCollateralFactorMantissa", "type": "uint256" } ], - "name": "RepayBorrow", + "name": "NewCollateralFactor", "type": "event", - "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" + "signature": "0x70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc5" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "liquidator", - "type": "address" - }, - { - "indexed": false, - "name": "borrower", - "type": "address" - }, - { - "indexed": false, - "name": "repayAmount", + "name": "oldLiquidationIncentiveMantissa", "type": "uint256" }, { "indexed": false, - "name": "cTokenCollateral", - "type": "address" - }, - { - "indexed": false, - "name": "seizeTokens", + "name": "newLiquidationIncentiveMantissa", "type": "uint256" } ], - "name": "LiquidateBorrow", - "type": "event", - "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "oldPendingAdmin", - "type": "address" - }, - { - "indexed": false, - "name": "newPendingAdmin", - "type": "address" - } - ], - "name": "NewPendingAdmin", + "name": "NewLiquidationIncentive", "type": "event", - "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" + "signature": "0xaeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec1316" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldAdmin", - "type": "address" + "name": "oldMaxAssets", + "type": "uint256" }, { "indexed": false, - "name": "newAdmin", - "type": "address" + "name": "newMaxAssets", + "type": "uint256" } ], - "name": "NewAdmin", + "name": "NewMaxAssets", "type": "event", - "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" + "signature": "0x7093cf1eb653f749c3ff531d6df7f92764536a7fa0d13530cd26e070780c32ea" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldComptroller", + "name": "oldPriceOracle", "type": "address" }, { "indexed": false, - "name": "newComptroller", + "name": "newPriceOracle", "type": "address" } ], - "name": "NewComptroller", + "name": "NewPriceOracle", "type": "event", - "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" + "signature": "0xd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e22" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldInterestRateModel", + "name": "oldPauseGuardian", "type": "address" }, { "indexed": false, - "name": "newInterestRateModel", + "name": "newPauseGuardian", "type": "address" } ], - "name": "NewMarketInterestRateModel", - "type": "event", - "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "oldReserveFactorMantissa", - "type": "uint256" - }, - { - "indexed": false, - "name": "newReserveFactorMantissa", - "type": "uint256" - } - ], - "name": "NewReserveFactor", + "name": "NewPauseGuardian", "type": "event", - "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" + "signature": "0x0613b6ee6a04f0d09f390e4d9318894b9f6ac7fd83897cd8d18896ba579c401e" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "admin", - "type": "address" - }, - { - "indexed": false, - "name": "reduceAmount", - "type": "uint256" + "name": "action", + "type": "string" }, { "indexed": false, - "name": "newTotalReserves", - "type": "uint256" + "name": "pauseState", + "type": "bool" } ], - "name": "ReservesReduced", + "name": "ActionPaused", "type": "event", - "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" + "signature": "0xef159d9a32b2472e32b098f954f3ce62d232939f1c207070b584df1814de2de0" }, { "anonymous": false, @@ -9913,91 +9996,72 @@ "name": "Failure", "type": "event", "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" - }, + } + ], + "cBAT": [ { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" - }, + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ { - "indexed": false, - "name": "amount", - "type": "uint256" + "name": "", + "type": "string" } ], - "name": "Transfer", - "type": "event", - "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x06fdde03" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": true, - "name": "owner", - "type": "address" - }, - { - "indexed": true, "name": "spender", "type": "address" }, { - "indexed": false, "name": "amount", "type": "uint256" } ], - "name": "Approval", - "type": "event", - "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" - } - ], - "Base0bps_Slope2000bps": [ + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "cash", - "type": "uint256" - }, - { - "name": "borrows", - "type": "uint256" - }, - { - "name": "_reserves", + "name": "repayAmount", "type": "uint256" } ], - "name": "getBorrowRate", + "name": "repayBorrow", "outputs": [ - { - "name": "", - "type": "uint256" - }, { "name": "", "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x15f24053" + "signature": "0x0e752702" }, { "constant": true, "inputs": [], - "name": "multiplier", + "name": "reserveFactorMantissa", "outputs": [ { "name": "", @@ -10007,12 +10071,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x1b3ed722" + "signature": "0x173b9904" }, { - "constant": true, - "inputs": [], - "name": "baseRate", + "constant": false, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceCurrent", "outputs": [ { "name": "", @@ -10020,29 +10089,29 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x1f68f20a" + "signature": "0x17bfdfbc" }, { "constant": true, "inputs": [], - "name": "isInterestRateModel", + "name": "totalSupply", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x2191f92a" + "signature": "0x18160ddd" }, { "constant": true, "inputs": [], - "name": "blocksPerYear", + "name": "exchangeRateStored", "outputs": [ { "name": "", @@ -10052,69 +10121,79 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xa385fb96" + "signature": "0x182df0f5" }, { + "constant": false, "inputs": [ { - "name": "baseRate_", - "type": "uint256" + "name": "src", + "type": "address" }, { - "name": "multiplier_", + "name": "dst", + "type": "address" + }, + { + "name": "amount", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" - } - ], - "BAT": [ - { - "constant": true, - "inputs": [], - "name": "name", + "name": "transferFrom", "outputs": [ { "name": "", - "type": "string" + "type": "bool" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x06fdde03" + "signature": "0x23b872dd" }, { "constant": false, "inputs": [ { - "name": "_spender", + "name": "borrower", "type": "address" }, { - "name": "_value", + "name": "repayAmount", "type": "uint256" } ], - "name": "approve", + "name": "repayBorrowBehalf", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x095ea7b3" + "signature": "0x2608f818" }, { "constant": true, "inputs": [], - "name": "totalSupply", + "name": "pendingAdmin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x26782247" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", "outputs": [ { "name": "", @@ -10124,79 +10203,67 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x18160ddd" + "signature": "0x313ce567" }, { "constant": false, "inputs": [ { - "name": "_from", - "type": "address" - }, - { - "name": "_to", + "name": "owner", "type": "address" - }, + } + ], + "name": "balanceOfUnderlying", + "outputs": [ { - "name": "_value", + "name": "", "type": "uint256" } ], - "name": "transferFrom", - "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x23b872dd" + "signature": "0x3af9e669" }, { "constant": true, "inputs": [], - "name": "decimals", + "name": "getCash", "outputs": [ { "name": "", - "type": "uint8" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x313ce567" + "signature": "0x3b1d21a2" }, { "constant": false, "inputs": [ { - "name": "_spender", + "name": "newComptroller", "type": "address" - }, - { - "name": "_subtractedValue", - "type": "uint256" } ], - "name": "decreaseApproval", + "name": "_setComptroller", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x66188463" + "signature": "0x4576b5db" }, { "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", + "inputs": [], + "name": "totalBorrows", "outputs": [ { "name": "", @@ -10206,79 +10273,97 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x70a08231" + "signature": "0x47bd3718" }, { "constant": true, "inputs": [], - "name": "symbol", + "name": "comptroller", "outputs": [ { "name": "", - "type": "string" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95d89b41" + "signature": "0x5fe3b567" }, { "constant": false, "inputs": [ { - "name": "_to", - "type": "address" - }, + "name": "reduceAmount", + "type": "uint256" + } + ], + "name": "_reduceReserves", + "outputs": [ { - "name": "_value", + "name": "", "type": "uint256" } ], - "name": "transfer", - "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa9059cbb" + "signature": "0x601a0bf1" }, { - "constant": false, - "inputs": [ - { - "name": "_spender", - "type": "address" - }, + "constant": true, + "inputs": [], + "name": "initialExchangeRateMantissa", + "outputs": [ { - "name": "_addedValue", + "name": "", "type": "uint256" } ], - "name": "increaseApproval", + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x675d972c" + }, + { + "constant": true, + "inputs": [], + "name": "accrualBlockNumber", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xd73dd623" + "signature": "0x6c540baf" }, { "constant": true, - "inputs": [ + "inputs": [], + "name": "underlying", + "outputs": [ { - "name": "_owner", + "name": "", "type": "address" - }, + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6f307dc3" + }, + { + "constant": true, + "inputs": [ { - "name": "_spender", + "name": "owner", "type": "address" } ], - "name": "allowance", + "name": "balanceOf", "outputs": [ { "name": "", @@ -10288,103 +10373,62 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xdd62ed3e" + "signature": "0x70a08231" }, { - "inputs": [ + "constant": false, + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [ { - "name": "_initialAmount", + "name": "", "type": "uint256" - }, - { - "name": "_tokenName", - "type": "string" - }, - { - "name": "_decimalUnits", - "type": "uint8" - }, - { - "name": "_tokenSymbol", - "type": "string" } ], "payable": false, "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" + "type": "function", + "signature": "0x73acee98" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": true, - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "name": "value", + "name": "redeemAmount", "type": "uint256" } ], - "name": "Approval", - "type": "event", - "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" - }, + "name": "redeemUnderlying", + "outputs": [ { - "indexed": false, - "name": "value", + "name": "", "type": "uint256" } ], - "name": "Transfer", - "type": "event", - "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x852a12e3" }, { - "constant": false, - "inputs": [ - { - "name": "_owner", - "type": "address" - }, + "constant": true, + "inputs": [], + "name": "totalReserves", + "outputs": [ { - "name": "value", + "name": "", "type": "uint256" } ], - "name": "allocateTo", - "outputs": [], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0x08bca566" - } - ], - "cETH": [ + "signature": "0x8f840ddd" + }, { "constant": true, "inputs": [], - "name": "name", + "name": "symbol", "outputs": [ { "name": "", @@ -10394,46 +10438,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x06fdde03" + "signature": "0x95d89b41" }, { - "constant": false, + "constant": true, "inputs": [ { - "name": "spender", + "name": "account", "type": "address" - }, - { - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "name": "", - "type": "bool" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x095ea7b3" - }, - { - "constant": false, - "inputs": [], - "name": "mint", - "outputs": [], - "payable": true, - "stateMutability": "payable", - "type": "function", - "signature": "0x1249c58b" - }, - { - "constant": true, - "inputs": [], - "name": "reserveFactorMantissa", + "name": "borrowBalanceStored", "outputs": [ { "name": "", @@ -10443,32 +10458,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x173b9904" + "signature": "0x95dd9193" }, { "constant": false, "inputs": [ { - "name": "account", - "type": "address" - } - ], - "name": "borrowBalanceCurrent", - "outputs": [ - { - "name": "", + "name": "mintAmount", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x17bfdfbc" - }, - { - "constant": true, - "inputs": [], - "name": "totalSupply", + "name": "mint", "outputs": [ { "name": "", @@ -10476,14 +10476,14 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x18160ddd" + "signature": "0xa0712d68" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "exchangeRateStored", + "name": "accrueInterest", "outputs": [ { "name": "", @@ -10491,17 +10491,13 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x182df0f5" + "signature": "0xa6afed95" }, { "constant": false, "inputs": [ - { - "name": "src", - "type": "address" - }, { "name": "dst", "type": "address" @@ -10511,7 +10507,7 @@ "type": "uint256" } ], - "name": "transferFrom", + "name": "transfer", "outputs": [ { "name": "", @@ -10521,27 +10517,27 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x23b872dd" + "signature": "0xa9059cbb" }, { "constant": true, "inputs": [], - "name": "pendingAdmin", + "name": "borrowIndex", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x26782247" + "signature": "0xaa5af0fd" }, { "constant": true, "inputs": [], - "name": "decimals", + "name": "supplyRatePerBlock", "outputs": [ { "name": "", @@ -10551,32 +10547,25 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x313ce567" + "signature": "0xae9d70b0" }, { "constant": false, "inputs": [ { - "name": "owner", + "name": "liquidator", "type": "address" - } - ], - "name": "balanceOfUnderlying", - "outputs": [ + }, { - "name": "", + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x3af9e669" - }, - { - "constant": true, - "inputs": [], - "name": "getCash", + "name": "seize", "outputs": [ { "name": "", @@ -10584,19 +10573,19 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x3b1d21a2" + "signature": "0xb2a02ff1" }, { "constant": false, "inputs": [ { - "name": "newComptroller", + "name": "newPendingAdmin", "type": "address" } ], - "name": "_setComptroller", + "name": "_setPendingAdmin", "outputs": [ { "name": "", @@ -10606,12 +10595,12 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x4576b5db" + "signature": "0xb71d1a0c" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "totalBorrows", + "name": "exchangeRateCurrent", "outputs": [ { "name": "", @@ -10619,44 +10608,51 @@ } ], "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x47bd3718" - }, - { - "constant": false, - "inputs": [], - "name": "repayBorrow", - "outputs": [], - "payable": true, - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x4e4d9fea" + "signature": "0xbd6d894d" }, { "constant": true, - "inputs": [], - "name": "comptroller", + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x5fe3b567" + "signature": "0xc37f68e2" }, { "constant": false, "inputs": [ { - "name": "reduceAmount", + "name": "borrowAmount", "type": "uint256" } ], - "name": "_reduceReserves", + "name": "borrow", "outputs": [ { "name": "", @@ -10666,27 +10662,17 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x601a0bf1" + "signature": "0xc5ebeaec" }, { - "constant": true, - "inputs": [], - "name": "initialExchangeRateMantissa", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", + "name": "redeemTokens", "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x675d972c" - }, - { - "constant": true, - "inputs": [], - "name": "accrualBlockNumber", + "name": "redeem", "outputs": [ { "name": "", @@ -10694,9 +10680,9 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x6c540baf" + "signature": "0xdb006a75" }, { "constant": true, @@ -10704,9 +10690,13 @@ { "name": "owner", "type": "address" + }, + { + "name": "spender", + "type": "address" } ], - "name": "balanceOf", + "name": "allowance", "outputs": [ { "name": "", @@ -10716,12 +10706,12 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x70a08231" + "signature": "0xdd62ed3e" }, { "constant": false, "inputs": [], - "name": "totalBorrowsCurrent", + "name": "_acceptAdmin", "outputs": [ { "name": "", @@ -10731,17 +10721,17 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x73acee98" + "signature": "0xe9c714f2" }, { "constant": false, "inputs": [ { - "name": "redeemAmount", - "type": "uint256" + "name": "newInterestRateModel", + "type": "address" } ], - "name": "redeemUnderlying", + "name": "_setInterestRateModel", "outputs": [ { "name": "", @@ -10751,62 +10741,40 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x852a12e3" + "signature": "0xf2b3abbd" }, { "constant": true, "inputs": [], - "name": "totalReserves", + "name": "interestRateModel", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x8f840ddd" + "signature": "0xf3fdb15a" }, { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x95d89b41" - }, - { - "constant": true, - "inputs": [ - { - "name": "account", + "name": "borrower", "type": "address" - } - ], - "name": "borrowBalanceStored", - "outputs": [ + }, { - "name": "", + "name": "repayAmount", "type": "uint256" + }, + { + "name": "cTokenCollateral", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x95dd9193" - }, - { - "constant": false, - "inputs": [], - "name": "accrueInterest", + "name": "liquidateBorrow", "outputs": [ { "name": "", @@ -10816,36 +10784,27 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa6afed95" + "signature": "0xf5e3c462" }, { - "constant": false, - "inputs": [ - { - "name": "dst", - "type": "address" - }, - { - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", + "constant": true, + "inputs": [], + "name": "admin", "outputs": [ { "name": "", - "type": "bool" + "type": "address" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xa9059cbb" + "signature": "0xf851a440" }, { "constant": true, "inputs": [], - "name": "borrowIndex", + "name": "borrowRatePerBlock", "outputs": [ { "name": "", @@ -10855,215 +10814,3385 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xaa5af0fd" + "signature": "0xf8f9da28" }, { "constant": false, "inputs": [ { - "name": "borrower", - "type": "address" - }, + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "_setReserveFactor", + "outputs": [ { - "name": "cTokenCollateral", - "type": "address" + "name": "", + "type": "uint256" } ], - "name": "liquidateBorrow", - "outputs": [], - "payable": true, - "stateMutability": "payable", + "payable": false, + "stateMutability": "nonpayable", "type": "function", - "signature": "0xaae40a2a" + "signature": "0xfca7820b" }, { "constant": true, "inputs": [], - "name": "supplyRatePerBlock", + "name": "isCToken", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xae9d70b0" + "signature": "0xfe9c44ae" }, { - "constant": false, "inputs": [ { - "name": "liquidator", + "name": "underlying_", "type": "address" }, { - "name": "borrower", + "name": "comptroller_", "type": "address" }, { - "name": "seizeTokens", + "name": "interestRateModel_", + "type": "address" + }, + { + "name": "initialExchangeRateMantissa_", "type": "uint256" - } - ], - "name": "seize", - "outputs": [ + }, { - "name": "", + "name": "name_", + "type": "string" + }, + { + "name": "symbol_", + "type": "string" + }, + { + "name": "decimals_", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", - "type": "function", - "signature": "0xb2a02ff1" + "type": "constructor", + "signature": "constructor" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "newPendingAdmin", - "type": "address" - } - ], - "name": "_setPendingAdmin", - "outputs": [ + "indexed": false, + "name": "interestAccumulated", + "type": "uint256" + }, { - "name": "", + "indexed": false, + "name": "borrowIndex", "type": "uint256" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xb71d1a0c" - }, - { - "constant": false, - "inputs": [], - "name": "exchangeRateCurrent", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "totalBorrows", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xbd6d894d" + "name": "AccrueInterest", + "type": "event", + "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" }, { - "constant": true, + "anonymous": false, "inputs": [ { - "name": "account", + "indexed": false, + "name": "minter", "type": "address" - } - ], - "name": "getAccountSnapshot", - "outputs": [ - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" }, { - "name": "", + "indexed": false, + "name": "mintAmount", "type": "uint256" }, { - "name": "", + "indexed": false, + "name": "mintTokens", "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xc37f68e2" + "name": "Mint", + "type": "event", + "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "borrowAmount", + "indexed": false, + "name": "redeemer", + "type": "address" + }, + { + "indexed": false, + "name": "redeemAmount", "type": "uint256" - } - ], - "name": "borrow", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "redeemTokens", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xc5ebeaec" + "name": "Redeem", + "type": "event", + "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "redeemTokens", + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "borrowAmount", "type": "uint256" - } - ], - "name": "redeem", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xdb006a75" + "name": "Borrow", + "type": "event", + "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" }, { - "constant": true, + "anonymous": false, "inputs": [ { - "name": "owner", + "indexed": false, + "name": "payer", "type": "address" }, { - "name": "spender", + "indexed": false, + "name": "borrower", "type": "address" - } - ], - "name": "allowance", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "repayAmount", "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xdd62ed3e" + }, + { + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "RepayBorrow", + "type": "event", + "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "liquidator", + "type": "address" + }, + { + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "cTokenCollateral", + "type": "address" + }, + { + "indexed": false, + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "LiquidateBorrow", + "type": "event", + "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldPendingAdmin", + "type": "address" + }, + { + "indexed": false, + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "NewPendingAdmin", + "type": "event", + "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldAdmin", + "type": "address" + }, + { + "indexed": false, + "name": "newAdmin", + "type": "address" + } + ], + "name": "NewAdmin", + "type": "event", + "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": false, + "name": "newComptroller", + "type": "address" + } + ], + "name": "NewComptroller", + "type": "event", + "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": false, + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event", + "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldReserveFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "NewReserveFactor", + "type": "event", + "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "admin", + "type": "address" + }, + { + "indexed": false, + "name": "reduceAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesReduced", + "type": "event", + "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "error", + "type": "uint256" + }, + { + "indexed": false, + "name": "info", + "type": "uint256" + }, + { + "indexed": false, + "name": "detail", + "type": "uint256" + } + ], + "name": "Failure", + "type": "event", + "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event", + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + } + ], + "Base0bps_Slope2000bps": [ + { + "constant": true, + "inputs": [ + { + "name": "cash", + "type": "uint256" + }, + { + "name": "borrows", + "type": "uint256" + }, + { + "name": "_reserves", + "type": "uint256" + } + ], + "name": "getBorrowRate", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x15f24053" + }, + { + "constant": true, + "inputs": [], + "name": "multiplier", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x1b3ed722" + }, + { + "constant": true, + "inputs": [], + "name": "baseRate", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x1f68f20a" + }, + { + "constant": true, + "inputs": [], + "name": "isInterestRateModel", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x2191f92a" + }, + { + "constant": true, + "inputs": [], + "name": "blocksPerYear", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xa385fb96" + }, + { + "inputs": [ + { + "name": "baseRate_", + "type": "uint256" + }, + { + "name": "multiplier_", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + } + ], + "BAT": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x06fdde03" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x18160ddd" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x23b872dd" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x66188463" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x70a08231" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95d89b41" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa9059cbb" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_addedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xd73dd623" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdd62ed3e" + }, + { + "inputs": [ + { + "name": "_initialAmount", + "type": "uint256" + }, + { + "name": "_tokenName", + "type": "string" + }, + { + "name": "_decimalUnits", + "type": "uint8" + }, + { + "name": "_tokenSymbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event", + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + }, + { + "constant": false, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "allocateTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x08bca566" + } + ], + "cETH": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x06fdde03" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, + { + "constant": false, + "inputs": [], + "name": "mint", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0x1249c58b" + }, + { + "constant": true, + "inputs": [], + "name": "reserveFactorMantissa", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x173b9904" + }, + { + "constant": false, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceCurrent", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x17bfdfbc" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x18160ddd" + }, + { + "constant": true, + "inputs": [], + "name": "exchangeRateStored", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x182df0f5" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x23b872dd" + }, + { + "constant": true, + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x26782247" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" + }, + { + "constant": false, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOfUnderlying", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x3af9e669" + }, + { + "constant": true, + "inputs": [], + "name": "getCash", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x3b1d21a2" + }, + { + "constant": false, + "inputs": [ + { + "name": "newComptroller", + "type": "address" + } + ], + "name": "_setComptroller", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x4576b5db" + }, + { + "constant": true, + "inputs": [], + "name": "totalBorrows", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x47bd3718" + }, + { + "constant": false, + "inputs": [], + "name": "repayBorrow", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0x4e4d9fea" + }, + { + "constant": true, + "inputs": [], + "name": "comptroller", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x5fe3b567" + }, + { + "constant": false, + "inputs": [ + { + "name": "reduceAmount", + "type": "uint256" + } + ], + "name": "_reduceReserves", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x601a0bf1" + }, + { + "constant": true, + "inputs": [], + "name": "initialExchangeRateMantissa", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x675d972c" + }, + { + "constant": true, + "inputs": [], + "name": "accrualBlockNumber", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6c540baf" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x70a08231" + }, + { + "constant": false, + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x73acee98" + }, + { + "constant": false, + "inputs": [ + { + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlying", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x852a12e3" + }, + { + "constant": true, + "inputs": [], + "name": "totalReserves", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x8f840ddd" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95d89b41" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95dd9193" + }, + { + "constant": false, + "inputs": [], + "name": "accrueInterest", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa6afed95" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa9059cbb" + }, + { + "constant": true, + "inputs": [], + "name": "borrowIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xaa5af0fd" + }, + { + "constant": false, + "inputs": [ + { + "name": "borrower", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + } + ], + "name": "liquidateBorrow", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0xaae40a2a" + }, + { + "constant": true, + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xae9d70b0" + }, + { + "constant": false, + "inputs": [ + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seize", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xb2a02ff1" + }, + { + "constant": false, + "inputs": [ + { + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "_setPendingAdmin", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xb71d1a0c" + }, + { + "constant": false, + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xbd6d894d" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xc37f68e2" + }, + { + "constant": false, + "inputs": [ + { + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xc5ebeaec" + }, + { + "constant": false, + "inputs": [ + { + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xdb006a75" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdd62ed3e" + }, + { + "constant": false, + "inputs": [ + { + "name": "borrower", + "type": "address" + } + ], + "name": "repayBorrowBehalf", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0xe5974619" + }, + { + "constant": false, + "inputs": [], + "name": "_acceptAdmin", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xe9c714f2" + }, + { + "constant": false, + "inputs": [ + { + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "_setInterestRateModel", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xf2b3abbd" + }, + { + "constant": true, + "inputs": [], + "name": "interestRateModel", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf3fdb15a" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf851a440" + }, + { + "constant": true, + "inputs": [], + "name": "borrowRatePerBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf8f9da28" + }, + { + "constant": false, + "inputs": [ + { + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "_setReserveFactor", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xfca7820b" + }, + { + "constant": true, + "inputs": [], + "name": "isCToken", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xfe9c44ae" + }, + { + "inputs": [ + { + "name": "comptroller_", + "type": "address" + }, + { + "name": "interestRateModel_", + "type": "address" + }, + { + "name": "initialExchangeRateMantissa_", + "type": "uint256" + }, + { + "name": "name_", + "type": "string" + }, + { + "name": "symbol_", + "type": "string" + }, + { + "name": "decimals_", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "interestAccumulated", + "type": "uint256" + }, + { + "indexed": false, + "name": "borrowIndex", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "AccrueInterest", + "type": "event", + "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "name": "mintAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "mintTokens", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event", + "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "redeemer", + "type": "address" + }, + { + "indexed": false, + "name": "redeemAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event", + "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "borrowAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "Borrow", + "type": "event", + "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "payer", + "type": "address" + }, + { + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "RepayBorrow", + "type": "event", + "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "liquidator", + "type": "address" + }, + { + "indexed": false, + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "cTokenCollateral", + "type": "address" + }, + { + "indexed": false, + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "LiquidateBorrow", + "type": "event", + "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldPendingAdmin", + "type": "address" + }, + { + "indexed": false, + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "NewPendingAdmin", + "type": "event", + "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldAdmin", + "type": "address" + }, + { + "indexed": false, + "name": "newAdmin", + "type": "address" + } + ], + "name": "NewAdmin", + "type": "event", + "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": false, + "name": "newComptroller", + "type": "address" + } + ], + "name": "NewComptroller", + "type": "event", + "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": false, + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event", + "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "oldReserveFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "NewReserveFactor", + "type": "event", + "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "admin", + "type": "address" + }, + { + "indexed": false, + "name": "reduceAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesReduced", + "type": "event", + "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "error", + "type": "uint256" + }, + { + "indexed": false, + "name": "info", + "type": "uint256" + }, + { + "indexed": false, + "name": "detail", + "type": "uint256" + } + ], + "name": "Failure", + "type": "event", + "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event", + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + } + ], + "Base500bps_Slope1200bps": [ + { + "constant": true, + "inputs": [ + { + "name": "cash", + "type": "uint256" + }, + { + "name": "borrows", + "type": "uint256" + }, + { + "name": "_reserves", + "type": "uint256" + } + ], + "name": "getBorrowRate", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x15f24053" + }, + { + "constant": true, + "inputs": [], + "name": "multiplier", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x1b3ed722" + }, + { + "constant": true, + "inputs": [], + "name": "baseRate", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x1f68f20a" + }, + { + "constant": true, + "inputs": [], + "name": "isInterestRateModel", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x2191f92a" + }, + { + "constant": true, + "inputs": [], + "name": "blocksPerYear", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xa385fb96" + }, + { + "inputs": [ + { + "name": "baseRate_", + "type": "uint256" + }, + { + "name": "multiplier_", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + } + ], + "Timelock": [ + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "signature", + "type": "string" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "eta", + "type": "uint256" + } + ], + "name": "executeTransaction", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0x0825f38f" + }, + { + "constant": false, + "inputs": [], + "name": "acceptAdmin", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x0e18b681" + }, + { + "constant": true, + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x26782247" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "signature", + "type": "string" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "eta", + "type": "uint256" + } + ], + "name": "queueTransaction", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x3a66f901" + }, + { + "constant": false, + "inputs": [ + { + "name": "pendingAdmin_", + "type": "address" + } + ], + "name": "setPendingAdmin", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x4dd18bf5" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "signature", + "type": "string" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "eta", + "type": "uint256" + } + ], + "name": "cancelTransaction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x591fcdfe" + }, + { + "constant": true, + "inputs": [], + "name": "delay", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6a42b8f8" + }, + { + "constant": true, + "inputs": [], + "name": "MAXIMUM_DELAY", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x7d645fab" + }, + { + "constant": true, + "inputs": [], + "name": "MINIMUM_DELAY", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xb1b43ae5" + }, + { + "constant": true, + "inputs": [], + "name": "GRACE_PERIOD", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xc1a287e2" + }, + { + "constant": false, + "inputs": [ + { + "name": "delay_", + "type": "uint256" + } + ], + "name": "setDelay", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xe177246e" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "queuedTransactions", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf2b06537" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xf851a440" + }, + { + "inputs": [ + { + "name": "admin_", + "type": "address" + }, + { + "name": "delay_", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "newAdmin", + "type": "address" + } + ], + "name": "NewAdmin", + "type": "event", + "signature": "0x71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "NewPendingAdmin", + "type": "event", + "signature": "0x69d78e38a01985fbb1462961809b4b2d65531bc93b2b94037f3334b82ca4a756" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "newDelay", + "type": "uint256" + } + ], + "name": "NewDelay", + "type": "event", + "signature": "0x948b1f6a42ee138b7e34058ba85a37f716d55ff25ff05a763f15bed6a04c8d2c" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "signature", + "type": "string" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "name": "eta", + "type": "uint256" + } + ], + "name": "CancelTransaction", + "type": "event", + "signature": "0x2fffc091a501fd91bfbff27141450d3acb40fb8e6d8382b243ec7a812a3aaf87" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "signature", + "type": "string" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "name": "eta", + "type": "uint256" + } + ], + "name": "ExecuteTransaction", + "type": "event", + "signature": "0xa560e3198060a2f10670c1ec5b403077ea6ae93ca8de1c32b451dc1a943cd6e7" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "signature", + "type": "string" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "name": "eta", + "type": "uint256" + } + ], + "name": "QueueTransaction", + "type": "event", + "signature": "0x76e2796dc3a81d57b0e8504b647febcbeeb5f4af818e164f11eef8131a6a763f" + } + ], + "Base200bps_Slope3000bps": [ + { + "constant": true, + "inputs": [ + { + "name": "cash", + "type": "uint256" + }, + { + "name": "borrows", + "type": "uint256" + }, + { + "name": "_reserves", + "type": "uint256" + } + ], + "name": "getBorrowRate", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x15f24053" + }, + { + "constant": true, + "inputs": [], + "name": "multiplier", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x1b3ed722" + }, + { + "constant": true, + "inputs": [], + "name": "baseRate", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x1f68f20a" + }, + { + "constant": true, + "inputs": [], + "name": "isInterestRateModel", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x2191f92a" + }, + { + "constant": true, + "inputs": [], + "name": "blocksPerYear", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xa385fb96" + }, + { + "inputs": [ + { + "name": "baseRate_", + "type": "uint256" + }, + { + "name": "multiplier_", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + } + ], + "cREP": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x06fdde03" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, + { + "constant": false, + "inputs": [ + { + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrow", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x0e752702" + }, + { + "constant": true, + "inputs": [], + "name": "reserveFactorMantissa", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x173b9904" + }, + { + "constant": false, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceCurrent", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x17bfdfbc" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x18160ddd" + }, + { + "constant": true, + "inputs": [], + "name": "exchangeRateStored", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x182df0f5" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x23b872dd" + }, + { + "constant": false, + "inputs": [ + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrowBehalf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x2608f818" + }, + { + "constant": true, + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x26782247" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" + }, + { + "constant": false, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOfUnderlying", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x3af9e669" + }, + { + "constant": true, + "inputs": [], + "name": "getCash", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x3b1d21a2" + }, + { + "constant": false, + "inputs": [ + { + "name": "newComptroller", + "type": "address" + } + ], + "name": "_setComptroller", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x4576b5db" + }, + { + "constant": true, + "inputs": [], + "name": "totalBorrows", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x47bd3718" + }, + { + "constant": true, + "inputs": [], + "name": "comptroller", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x5fe3b567" + }, + { + "constant": false, + "inputs": [ + { + "name": "reduceAmount", + "type": "uint256" + } + ], + "name": "_reduceReserves", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x601a0bf1" + }, + { + "constant": true, + "inputs": [], + "name": "initialExchangeRateMantissa", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x675d972c" + }, + { + "constant": true, + "inputs": [], + "name": "accrualBlockNumber", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6c540baf" + }, + { + "constant": true, + "inputs": [], + "name": "underlying", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6f307dc3" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x70a08231" + }, + { + "constant": false, + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x73acee98" + }, + { + "constant": false, + "inputs": [ + { + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlying", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x852a12e3" + }, + { + "constant": true, + "inputs": [], + "name": "totalReserves", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x8f840ddd" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95d89b41" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95dd9193" + }, + { + "constant": false, + "inputs": [ + { + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa0712d68" + }, + { + "constant": false, + "inputs": [], + "name": "accrueInterest", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa6afed95" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa9059cbb" + }, + { + "constant": true, + "inputs": [], + "name": "borrowIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xaa5af0fd" + }, + { + "constant": true, + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xae9d70b0" + }, + { + "constant": false, + "inputs": [ + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seize", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xb2a02ff1" }, { "constant": false, "inputs": [ { - "name": "borrower", + "name": "newPendingAdmin", "type": "address" } ], - "name": "repayBorrowBehalf", - "outputs": [], - "payable": true, - "stateMutability": "payable", + "name": "_setPendingAdmin", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", "type": "function", - "signature": "0xe5974619" + "signature": "0xb71d1a0c" + }, + { + "constant": false, + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xbd6d894d" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xc37f68e2" + }, + { + "constant": false, + "inputs": [ + { + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xc5ebeaec" + }, + { + "constant": false, + "inputs": [ + { + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xdb006a75" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdd62ed3e" }, { "constant": false, @@ -11115,6 +14244,34 @@ "type": "function", "signature": "0xf3fdb15a" }, + { + "constant": false, + "inputs": [ + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", + "type": "uint256" + }, + { + "name": "cTokenCollateral", + "type": "address" + } + ], + "name": "liquidateBorrow", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xf5e3c462" + }, { "constant": true, "inputs": [], @@ -11182,6 +14339,10 @@ }, { "inputs": [ + { + "name": "underlying_", + "type": "address" + }, { "name": "comptroller_", "type": "address" @@ -11212,11 +14373,6 @@ "type": "constructor", "signature": "constructor" }, - { - "payable": true, - "stateMutability": "payable", - "type": "fallback" - }, { "anonymous": false, "inputs": [ @@ -11554,82 +14710,20 @@ }, { "indexed": false, - "name": "amount", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event", - "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" - } - ], - "Base500bps_Slope1200bps": [ - { - "constant": true, - "inputs": [ - { - "name": "cash", - "type": "uint256" - }, - { - "name": "borrows", - "type": "uint256" - }, - { - "name": "_reserves", - "type": "uint256" - } - ], - "name": "getBorrowRate", - "outputs": [ - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x15f24053" - }, - { - "constant": true, - "inputs": [], - "name": "multiplier", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x1b3ed722" - }, - { - "constant": true, - "inputs": [], - "name": "baseRate", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x1f68f20a" - }, + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + } + ], + "ComptrollerG2": [ { "constant": true, "inputs": [], - "name": "isInterestRateModel", + "name": "isComptroller", "outputs": [ { "name": "", @@ -11639,214 +14733,222 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x2191f92a" + "signature": "0x007e3dd2" }, { - "constant": true, - "inputs": [], - "name": "blocksPerYear", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", - "type": "uint256" + "name": "unitroller", + "type": "address" } ], + "name": "_become", + "outputs": [], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0xa385fb96" + "signature": "0x1d504dc6" }, { + "constant": false, "inputs": [ { - "name": "baseRate_", + "name": "cToken", + "type": "address" + }, + { + "name": "payer", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", "type": "uint256" }, { - "name": "multiplier_", + "name": "borrowerIndex", "type": "uint256" } ], + "name": "repayBorrowVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" - } - ], - "Base200bps_Slope3000bps": [ + "type": "function", + "signature": "0x1ededc91" + }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "cash", - "type": "uint256" + "name": "cToken", + "type": "address" }, { - "name": "borrows", - "type": "uint256" + "name": "payer", + "type": "address" }, { - "name": "_reserves", + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", "type": "uint256" } ], - "name": "getBorrowRate", + "name": "repayBorrowAllowed", "outputs": [ - { - "name": "", - "type": "uint256" - }, { "name": "", "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x15f24053" + "signature": "0x24008a62" }, { "constant": true, "inputs": [], - "name": "multiplier", + "name": "pauseGuardian", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x1b3ed722" + "signature": "0x24a3d622" }, { "constant": true, "inputs": [], - "name": "baseRate", + "name": "pendingAdmin", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x1f68f20a" + "signature": "0x26782247" }, { - "constant": true, - "inputs": [], - "name": "isInterestRateModel", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", + "name": "state", "type": "bool" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x2191f92a" - }, - { - "constant": true, - "inputs": [], - "name": "blocksPerYear", + "name": "_setSeizePaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0xa385fb96" + "signature": "0x2d70db78" }, { + "constant": false, "inputs": [ { - "name": "baseRate_", - "type": "uint256" - }, - { - "name": "multiplier_", + "name": "newCloseFactorMantissa", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" - } - ], - "cREP": [ - { - "constant": true, - "inputs": [], - "name": "name", + "name": "_setCloseFactor", "outputs": [ { "name": "", - "type": "string" + "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x06fdde03" + "signature": "0x317b0b77" }, { "constant": false, "inputs": [ { - "name": "spender", + "name": "cToken", "type": "address" }, { - "name": "amount", + "name": "minter", + "type": "address" + }, + { + "name": "mintAmount", "type": "uint256" - } - ], - "name": "approve", - "outputs": [ + }, { - "name": "", - "type": "bool" + "name": "mintTokens", + "type": "uint256" } ], + "name": "mintVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x095ea7b3" + "signature": "0x41c728b9" }, { "constant": false, "inputs": [ + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, { "name": "repayAmount", "type": "uint256" - } - ], - "name": "repayBorrow", - "outputs": [ + }, { - "name": "", + "name": "seizeTokens", "type": "uint256" } ], + "name": "liquidateBorrowVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x0e752702" + "signature": "0x47ef3b3b" }, { "constant": true, "inputs": [], - "name": "reserveFactorMantissa", + "name": "liquidationIncentiveMantissa", "outputs": [ { "name": "", @@ -11856,17 +14958,25 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x173b9904" + "signature": "0x4ada90af" }, { "constant": false, "inputs": [ { - "name": "account", + "name": "cToken", + "type": "address" + }, + { + "name": "minter", "type": "address" + }, + { + "name": "mintAmount", + "type": "uint256" } ], - "name": "borrowBalanceCurrent", + "name": "mintAllowed", "outputs": [ { "name": "", @@ -11876,12 +14986,17 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x17bfdfbc" + "signature": "0x4ef4c3e1" }, { - "constant": true, - "inputs": [], - "name": "totalSupply", + "constant": false, + "inputs": [ + { + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "_setLiquidationIncentive", "outputs": [ { "name": "", @@ -11889,14 +15004,46 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x18160ddd" + "signature": "0x4fd42e17" }, { - "constant": true, - "inputs": [], - "name": "exchangeRateStored", + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "redeemer", + "type": "address" + }, + { + "name": "redeemAmount", + "type": "uint256" + }, + { + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x51dff989" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOracle", + "type": "address" + } + ], + "name": "_setPriceOracle", "outputs": [ { "name": "", @@ -11904,27 +15051,19 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x182df0f5" + "signature": "0x55ee1fe1" }, { "constant": false, "inputs": [ { - "name": "src", - "type": "address" - }, - { - "name": "dst", - "type": "address" - }, - { - "name": "amount", - "type": "uint256" + "name": "state", + "type": "bool" } ], - "name": "transferFrom", + "name": "_setBorrowPaused", "outputs": [ { "name": "", @@ -11934,87 +15073,64 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x23b872dd" + "signature": "0x56133fc8" }, { "constant": false, "inputs": [ { - "name": "borrower", + "name": "cToken", "type": "address" }, { - "name": "repayAmount", - "type": "uint256" - } - ], - "name": "repayBorrowBehalf", - "outputs": [ + "name": "borrower", + "type": "address" + }, { - "name": "", + "name": "borrowAmount", "type": "uint256" } ], + "name": "borrowVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x2608f818" + "signature": "0x5c778605" }, { "constant": true, "inputs": [], - "name": "pendingAdmin", + "name": "mintGuardianPaused", "outputs": [ { "name": "", - "type": "address" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x26782247" + "signature": "0x5dce0515" }, { "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x313ce567" - }, - { - "constant": false, "inputs": [ { - "name": "owner", + "name": "account", "type": "address" } ], - "name": "balanceOfUnderlying", + "name": "getAccountLiquidity", "outputs": [ { "name": "", "type": "uint256" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x3af9e669" - }, - { - "constant": true, - "inputs": [], - "name": "getCash", - "outputs": [ + }, + { + "name": "", + "type": "uint256" + }, { "name": "", "type": "uint256" @@ -12023,17 +15139,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x3b1d21a2" + "signature": "0x5ec88c79" }, { "constant": false, "inputs": [ { - "name": "newComptroller", + "name": "newPauseGuardian", "type": "address" } ], - "name": "_setComptroller", + "name": "_setPauseGuardian", "outputs": [ { "name": "", @@ -12043,232 +15159,259 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x4576b5db" + "signature": "0x5f5af1aa" }, { - "constant": true, - "inputs": [], - "name": "totalBorrows", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "repayAmount", "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x47bd3718" - }, - { - "constant": true, - "inputs": [], - "name": "comptroller", + "name": "liquidateBorrowAllowed", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x5fe3b567" + "signature": "0x5fc7e71e" }, { "constant": false, "inputs": [ { - "name": "reduceAmount", - "type": "uint256" - } - ], - "name": "_reduceReserves", - "outputs": [ + "name": "cToken", + "type": "address" + }, { - "name": "", + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "transferTokens", "type": "uint256" } ], + "name": "transferVerify", + "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x601a0bf1" + "signature": "0x6a56947e" }, { - "constant": true, - "inputs": [], - "name": "initialExchangeRateMantissa", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", "type": "uint256" } ], + "name": "seizeVerify", + "outputs": [], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x675d972c" + "signature": "0x6d35bf91" }, { "constant": true, "inputs": [], - "name": "accrualBlockNumber", + "name": "oracle", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x6c540baf" + "signature": "0x7dc0d1d0" }, { "constant": true, "inputs": [], - "name": "underlying", + "name": "transferGuardianPaused", "outputs": [ { "name": "", - "type": "address" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x6f307dc3" + "signature": "0x87f76303" }, { "constant": true, "inputs": [ { - "name": "owner", + "name": "", "type": "address" } ], - "name": "balanceOf", + "name": "markets", "outputs": [ { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x70a08231" - }, - { - "constant": false, - "inputs": [], - "name": "totalBorrowsCurrent", - "outputs": [ + "name": "isListed", + "type": "bool" + }, { - "name": "", + "name": "collateralFactorMantissa", "type": "uint256" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0x73acee98" + "signature": "0x8e8f294b" }, { "constant": false, "inputs": [ { - "name": "redeemAmount", - "type": "uint256" + "name": "state", + "type": "bool" } ], - "name": "redeemUnderlying", + "name": "_setTransferPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x852a12e3" + "signature": "0x8ebf6364" }, { "constant": true, - "inputs": [], - "name": "totalReserves", + "inputs": [ + { + "name": "account", + "type": "address" + }, + { + "name": "cToken", + "type": "address" + } + ], + "name": "checkMembership", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x8f840ddd" + "signature": "0x929fe9a1" }, { "constant": true, "inputs": [], - "name": "symbol", + "name": "maxAssets", "outputs": [ { "name": "", - "type": "string" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95d89b41" + "signature": "0x94b2294b" }, { "constant": true, - "inputs": [ - { - "name": "account", - "type": "address" - } - ], - "name": "borrowBalanceStored", + "inputs": [], + "name": "borrowGuardianPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95dd9193" + "signature": "0x9530f644" }, { "constant": false, "inputs": [ { - "name": "mintAmount", - "type": "uint256" + "name": "state", + "type": "bool" } ], - "name": "mint", + "name": "_setMintPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa0712d68" + "signature": "0x9845f280" }, { "constant": false, - "inputs": [], - "name": "accrueInterest", + "inputs": [ + { + "name": "cToken", + "type": "address" + } + ], + "name": "_supportMarket", "outputs": [ { "name": "", @@ -12278,79 +15421,79 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa6afed95" + "signature": "0xa76b3fda" }, { - "constant": false, + "constant": true, "inputs": [ { - "name": "dst", + "name": "account", "type": "address" - }, - { - "name": "amount", - "type": "uint256" } ], - "name": "transfer", + "name": "getAssetsIn", "outputs": [ { "name": "", - "type": "bool" + "type": "address[]" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xa9059cbb" + "signature": "0xabfceffc" }, { "constant": true, "inputs": [], - "name": "borrowIndex", + "name": "seizeGuardianPaused", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xaa5af0fd" + "signature": "0xac0b0bb7" }, { "constant": true, "inputs": [], - "name": "supplyRatePerBlock", + "name": "comptrollerImplementation", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xae9d70b0" + "signature": "0xbb82aa5e" }, { "constant": false, "inputs": [ { - "name": "liquidator", + "name": "cToken", "type": "address" }, { - "name": "borrower", + "name": "src", "type": "address" }, { - "name": "seizeTokens", + "name": "dst", + "type": "address" + }, + { + "name": "transferTokens", "type": "uint256" } ], - "name": "seize", + "name": "transferAllowed", "outputs": [ { "name": "", @@ -12360,61 +15503,46 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xb2a02ff1" + "signature": "0xbdcdc258" }, { "constant": false, "inputs": [ { - "name": "newPendingAdmin", - "type": "address" - } - ], - "name": "_setPendingAdmin", - "outputs": [ - { - "name": "", - "type": "uint256" + "name": "cTokens", + "type": "address[]" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xb71d1a0c" - }, - { - "constant": false, - "inputs": [], - "name": "exchangeRateCurrent", + "name": "enterMarkets", "outputs": [ { "name": "", - "type": "uint256" + "type": "uint256[]" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xbd6d894d" + "signature": "0xc2998238" }, { "constant": true, "inputs": [ { - "name": "account", + "name": "cTokenBorrowed", "type": "address" - } - ], - "name": "getAccountSnapshot", - "outputs": [ + }, { - "name": "", - "type": "uint256" + "name": "cTokenCollateral", + "type": "address" }, { - "name": "", + "name": "repayAmount", "type": "uint256" - }, + } + ], + "name": "liquidateCalculateSeizeTokens", + "outputs": [ { "name": "", "type": "uint256" @@ -12427,17 +15555,33 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xc37f68e2" + "signature": "0xc488847b" }, { "constant": false, "inputs": [ { - "name": "borrowAmount", + "name": "cTokenCollateral", + "type": "address" + }, + { + "name": "cTokenBorrowed", + "type": "address" + }, + { + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", "type": "uint256" } ], - "name": "borrow", + "name": "seizeAllowed", "outputs": [ { "name": "", @@ -12447,17 +15591,17 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xc5ebeaec" + "signature": "0xd02f7351" }, { "constant": false, "inputs": [ { - "name": "redeemTokens", + "name": "newMaxAssets", "type": "uint256" } ], - "name": "redeem", + "name": "_setMaxAssets", "outputs": [ { "name": "", @@ -12467,36 +15611,25 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xdb006a75" + "signature": "0xd9226ced" }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "owner", + "name": "cToken", "type": "address" }, { - "name": "spender", + "name": "borrower", "type": "address" - } - ], - "name": "allowance", - "outputs": [ + }, { - "name": "", + "name": "borrowAmount", "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xdd62ed3e" - }, - { - "constant": false, - "inputs": [], - "name": "_acceptAdmin", + "name": "borrowAllowed", "outputs": [ { "name": "", @@ -12506,32 +15639,36 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xe9c714f2" + "signature": "0xda3d454c" }, { - "constant": false, + "constant": true, "inputs": [ { - "name": "newInterestRateModel", + "name": "", "type": "address" + }, + { + "name": "", + "type": "uint256" } ], - "name": "_setInterestRateModel", + "name": "accountAssets", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xf2b3abbd" + "signature": "0xdce15449" }, { "constant": true, "inputs": [], - "name": "interestRateModel", + "name": "pendingComptrollerImplementation", "outputs": [ { "name": "", @@ -12541,25 +15678,21 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xf3fdb15a" + "signature": "0xdcfbc0c7" }, { "constant": false, "inputs": [ { - "name": "borrower", + "name": "cToken", "type": "address" }, { - "name": "repayAmount", + "name": "newCollateralFactorMantissa", "type": "uint256" - }, - { - "name": "cTokenCollateral", - "type": "address" } ], - "name": "liquidateBorrow", + "name": "_setCollateralFactor", "outputs": [ { "name": "", @@ -12569,27 +15702,40 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xf5e3c462" + "signature": "0xe4028eee" }, { "constant": true, "inputs": [], - "name": "admin", + "name": "closeFactorMantissa", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xf851a440" + "signature": "0xe8755446" }, { - "constant": true, - "inputs": [], - "name": "borrowRatePerBlock", + "constant": false, + "inputs": [ + { + "name": "cToken", + "type": "address" + }, + { + "name": "redeemer", + "type": "address" + }, + { + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemAllowed", "outputs": [ { "name": "", @@ -12597,19 +15743,19 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0xf8f9da28" + "signature": "0xeabe7d91" }, { "constant": false, "inputs": [ { - "name": "newReserveFactorMantissa", - "type": "uint256" + "name": "cTokenAddress", + "type": "address" } ], - "name": "_setReserveFactor", + "name": "exitMarket", "outputs": [ { "name": "", @@ -12619,54 +15765,25 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xfca7820b" + "signature": "0xede4edd0" }, { "constant": true, "inputs": [], - "name": "isCToken", + "name": "admin", "outputs": [ { "name": "", - "type": "bool" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xfe9c44ae" - }, - { - "inputs": [ - { - "name": "underlying_", - "type": "address" - }, - { - "name": "comptroller_", - "type": "address" - }, - { - "name": "interestRateModel_", - "type": "address" - }, - { - "name": "initialExchangeRateMantissa_", - "type": "uint256" - }, - { - "name": "name_", - "type": "string" - }, - { - "name": "symbol_", - "type": "string" - }, - { - "name": "decimals_", - "type": "uint256" - } - ], + "signature": "0xf851a440" + }, + { + "inputs": [], "payable": false, "stateMutability": "nonpayable", "type": "constructor", @@ -12677,276 +15794,180 @@ "inputs": [ { "indexed": false, - "name": "interestAccumulated", - "type": "uint256" - }, - { - "indexed": false, - "name": "borrowIndex", - "type": "uint256" - }, - { - "indexed": false, - "name": "totalBorrows", - "type": "uint256" + "name": "cToken", + "type": "address" } ], - "name": "AccrueInterest", + "name": "MarketListed", "type": "event", - "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" + "signature": "0xcf583bb0c569eb967f806b11601c4cb93c10310485c67add5f8362c2f212321f" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "minter", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "mintAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "mintTokens", - "type": "uint256" + "name": "account", + "type": "address" } ], - "name": "Mint", + "name": "MarketEntered", "type": "event", - "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" + "signature": "0x3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a5" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "redeemer", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "redeemAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "redeemTokens", - "type": "uint256" + "name": "account", + "type": "address" } ], - "name": "Redeem", + "name": "MarketExited", "type": "event", - "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" + "signature": "0xe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "borrower", - "type": "address" - }, - { - "indexed": false, - "name": "borrowAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "accountBorrows", + "name": "oldCloseFactorMantissa", "type": "uint256" }, { "indexed": false, - "name": "totalBorrows", + "name": "newCloseFactorMantissa", "type": "uint256" } ], - "name": "Borrow", + "name": "NewCloseFactor", "type": "event", - "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" + "signature": "0x3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd9" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "payer", - "type": "address" - }, - { - "indexed": false, - "name": "borrower", + "name": "cToken", "type": "address" }, { "indexed": false, - "name": "repayAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "accountBorrows", + "name": "oldCollateralFactorMantissa", "type": "uint256" }, { "indexed": false, - "name": "totalBorrows", + "name": "newCollateralFactorMantissa", "type": "uint256" } ], - "name": "RepayBorrow", + "name": "NewCollateralFactor", "type": "event", - "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" + "signature": "0x70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc5" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "liquidator", - "type": "address" - }, - { - "indexed": false, - "name": "borrower", - "type": "address" - }, - { - "indexed": false, - "name": "repayAmount", + "name": "oldLiquidationIncentiveMantissa", "type": "uint256" }, { "indexed": false, - "name": "cTokenCollateral", - "type": "address" - }, - { - "indexed": false, - "name": "seizeTokens", + "name": "newLiquidationIncentiveMantissa", "type": "uint256" } ], - "name": "LiquidateBorrow", - "type": "event", - "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "oldPendingAdmin", - "type": "address" - }, - { - "indexed": false, - "name": "newPendingAdmin", - "type": "address" - } - ], - "name": "NewPendingAdmin", + "name": "NewLiquidationIncentive", "type": "event", - "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" + "signature": "0xaeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec1316" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldAdmin", - "type": "address" + "name": "oldMaxAssets", + "type": "uint256" }, { "indexed": false, - "name": "newAdmin", - "type": "address" + "name": "newMaxAssets", + "type": "uint256" } ], - "name": "NewAdmin", + "name": "NewMaxAssets", "type": "event", - "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" + "signature": "0x7093cf1eb653f749c3ff531d6df7f92764536a7fa0d13530cd26e070780c32ea" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldComptroller", + "name": "oldPriceOracle", "type": "address" }, { "indexed": false, - "name": "newComptroller", + "name": "newPriceOracle", "type": "address" } ], - "name": "NewComptroller", + "name": "NewPriceOracle", "type": "event", - "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" + "signature": "0xd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e22" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "oldInterestRateModel", + "name": "oldPauseGuardian", "type": "address" }, { "indexed": false, - "name": "newInterestRateModel", + "name": "newPauseGuardian", "type": "address" } ], - "name": "NewMarketInterestRateModel", - "type": "event", - "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "oldReserveFactorMantissa", - "type": "uint256" - }, - { - "indexed": false, - "name": "newReserveFactorMantissa", - "type": "uint256" - } - ], - "name": "NewReserveFactor", + "name": "NewPauseGuardian", "type": "event", - "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" + "signature": "0x0613b6ee6a04f0d09f390e4d9318894b9f6ac7fd83897cd8d18896ba579c401e" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "admin", - "type": "address" - }, - { - "indexed": false, - "name": "reduceAmount", - "type": "uint256" + "name": "action", + "type": "string" }, { "indexed": false, - "name": "newTotalReserves", - "type": "uint256" + "name": "pauseState", + "type": "bool" } ], - "name": "ReservesReduced", + "name": "ActionPaused", "type": "event", - "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" + "signature": "0xef159d9a32b2472e32b098f954f3ce62d232939f1c207070b584df1814de2de0" }, { "anonymous": false, @@ -12970,52 +15991,6 @@ "name": "Failure", "type": "event", "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" - }, - { - "indexed": false, - "name": "amount", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event", - "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "name": "amount", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event", - "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" } ], "REP": [ @@ -14572,7 +17547,7 @@ "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" } ], - "USDC": [ + "cWBTC": [ { "constant": true, "inputs": [], @@ -14592,117 +17567,85 @@ "constant": false, "inputs": [ { - "name": "_spender", + "name": "spender", "type": "address" }, { - "name": "_value", + "name": "amount", "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0x095ea7b3" - }, - { - "constant": true, - "inputs": [], - "name": "totalSupply", + } + ], + "name": "approve", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x18160ddd" + "signature": "0x095ea7b3" }, { "constant": false, "inputs": [ { - "name": "_from", - "type": "address" - }, - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", + "name": "repayAmount", "type": "uint256" } ], - "name": "transferFrom", + "name": "repayBorrow", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x23b872dd" + "signature": "0x0e752702" }, { "constant": true, "inputs": [], - "name": "decimals", + "name": "reserveFactorMantissa", "outputs": [ { "name": "", - "type": "uint8" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x313ce567" + "signature": "0x173b9904" }, { "constant": false, "inputs": [ { - "name": "_spender", + "name": "account", "type": "address" - }, - { - "name": "_subtractedValue", - "type": "uint256" } ], - "name": "decreaseApproval", + "name": "borrowBalanceCurrent", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x66188463" + "signature": "0x17bfdfbc" }, { "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", + "inputs": [], + "name": "totalSupply", "outputs": [ { "name": "", @@ -14712,36 +17655,40 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x70a08231" + "signature": "0x18160ddd" }, { "constant": true, "inputs": [], - "name": "symbol", + "name": "exchangeRateStored", "outputs": [ { "name": "", - "type": "string" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95d89b41" + "signature": "0x182df0f5" }, { "constant": false, "inputs": [ { - "name": "_to", + "name": "src", "type": "address" }, { - "name": "_value", + "name": "dst", + "type": "address" + }, + { + "name": "amount", "type": "uint256" } ], - "name": "transfer", + "name": "transferFrom", "outputs": [ { "name": "", @@ -14751,195 +17698,156 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa9059cbb" + "signature": "0x23b872dd" }, { "constant": false, "inputs": [ { - "name": "_spender", + "name": "borrower", "type": "address" }, { - "name": "_addedValue", + "name": "repayAmount", "type": "uint256" } ], - "name": "increaseApproval", + "name": "repayBorrowBehalf", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xd73dd623" + "signature": "0x2608f818" }, { "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - }, - { - "name": "_spender", - "type": "address" - } - ], - "name": "allowance", + "inputs": [], + "name": "pendingAdmin", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xdd62ed3e" + "signature": "0x26782247" }, { - "inputs": [ + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ { - "name": "_initialAmount", + "name": "", "type": "uint256" - }, - { - "name": "_tokenName", - "type": "string" - }, - { - "name": "_decimalUnits", - "type": "uint8" - }, - { - "name": "_tokenSymbol", - "type": "string" } ], "payable": false, - "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": true, "name": "owner", "type": "address" - }, - { - "indexed": true, - "name": "spender", - "type": "address" - }, + } + ], + "name": "balanceOfUnderlying", + "outputs": [ { - "indexed": false, - "name": "value", + "name": "", "type": "uint256" } ], - "name": "Approval", - "type": "event", - "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x3af9e669" }, { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" - }, + "constant": true, + "inputs": [], + "name": "getCash", + "outputs": [ { - "indexed": false, - "name": "value", + "name": "", "type": "uint256" } ], - "name": "Transfer", - "type": "event", - "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x3b1d21a2" }, { "constant": false, "inputs": [ { - "name": "_owner", + "name": "newComptroller", "type": "address" - }, + } + ], + "name": "_setComptroller", + "outputs": [ { - "name": "value", + "name": "", "type": "uint256" } ], - "name": "allocateTo", - "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x08bca566" - } - ], - "cWBTC": [ + "signature": "0x4576b5db" + }, { "constant": true, "inputs": [], - "name": "name", + "name": "totalBorrows", "outputs": [ { "name": "", - "type": "string" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x06fdde03" + "signature": "0x47bd3718" }, { - "constant": false, - "inputs": [ - { - "name": "spender", - "type": "address" - }, - { - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", + "constant": true, + "inputs": [], + "name": "comptroller", "outputs": [ { "name": "", - "type": "bool" + "type": "address" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0x095ea7b3" + "signature": "0x5fe3b567" }, { "constant": false, "inputs": [ { - "name": "repayAmount", + "name": "reduceAmount", "type": "uint256" } ], - "name": "repayBorrow", + "name": "_reduceReserves", "outputs": [ { "name": "", @@ -14949,12 +17857,12 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x0e752702" + "signature": "0x601a0bf1" }, { "constant": true, "inputs": [], - "name": "reserveFactorMantissa", + "name": "initialExchangeRateMantissa", "outputs": [ { "name": "", @@ -14964,17 +17872,12 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x173b9904" + "signature": "0x675d972c" }, { - "constant": false, - "inputs": [ - { - "name": "account", - "type": "address" - } - ], - "name": "borrowBalanceCurrent", + "constant": true, + "inputs": [], + "name": "accrualBlockNumber", "outputs": [ { "name": "", @@ -14982,14 +17885,34 @@ } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0x17bfdfbc" + "signature": "0x6c540baf" }, { "constant": true, "inputs": [], - "name": "totalSupply", + "name": "underlying", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6f307dc3" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", "outputs": [ { "name": "", @@ -14999,12 +17922,12 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x18160ddd" + "signature": "0x70a08231" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "exchangeRateStored", + "name": "totalBorrowsCurrent", "outputs": [ { "name": "", @@ -15012,51 +17935,34 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x182df0f5" + "signature": "0x73acee98" }, { "constant": false, "inputs": [ { - "name": "src", - "type": "address" - }, - { - "name": "dst", - "type": "address" - }, - { - "name": "amount", + "name": "redeemAmount", "type": "uint256" } ], - "name": "transferFrom", + "name": "redeemUnderlying", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x23b872dd" + "signature": "0x852a12e3" }, { - "constant": false, - "inputs": [ - { - "name": "borrower", - "type": "address" - }, - { - "name": "repayAmount", - "type": "uint256" - } - ], - "name": "repayBorrowBehalf", + "constant": true, + "inputs": [], + "name": "totalReserves", "outputs": [ { "name": "", @@ -15064,29 +17970,34 @@ } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0x2608f818" + "signature": "0x8f840ddd" }, { "constant": true, "inputs": [], - "name": "pendingAdmin", + "name": "symbol", "outputs": [ { "name": "", - "type": "address" + "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x26782247" + "signature": "0x95d89b41" }, { "constant": true, - "inputs": [], - "name": "decimals", + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", "outputs": [ { "name": "", @@ -15096,17 +18007,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x313ce567" + "signature": "0x95dd9193" }, { "constant": false, "inputs": [ { - "name": "owner", - "type": "address" + "name": "mintAmount", + "type": "uint256" } ], - "name": "balanceOfUnderlying", + "name": "mint", "outputs": [ { "name": "", @@ -15116,12 +18027,12 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x3af9e669" + "signature": "0xa0712d68" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "getCash", + "name": "accrueInterest", "outputs": [ { "name": "", @@ -15129,34 +18040,38 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x3b1d21a2" + "signature": "0xa6afed95" }, { "constant": false, "inputs": [ { - "name": "newComptroller", + "name": "dst", "type": "address" + }, + { + "name": "amount", + "type": "uint256" } ], - "name": "_setComptroller", + "name": "transfer", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x4576b5db" + "signature": "0xa9059cbb" }, { "constant": true, "inputs": [], - "name": "totalBorrows", + "name": "borrowIndex", "outputs": [ { "name": "", @@ -15166,32 +18081,40 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x47bd3718" + "signature": "0xaa5af0fd" }, { "constant": true, "inputs": [], - "name": "comptroller", + "name": "supplyRatePerBlock", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x5fe3b567" + "signature": "0xae9d70b0" }, { "constant": false, "inputs": [ { - "name": "reduceAmount", + "name": "liquidator", + "type": "address" + }, + { + "name": "borrower", + "type": "address" + }, + { + "name": "seizeTokens", "type": "uint256" } ], - "name": "_reduceReserves", + "name": "seize", "outputs": [ { "name": "", @@ -15201,27 +18124,17 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x601a0bf1" + "signature": "0xb2a02ff1" }, { - "constant": true, - "inputs": [], - "name": "initialExchangeRateMantissa", - "outputs": [ + "constant": false, + "inputs": [ { - "name": "", - "type": "uint256" + "name": "newPendingAdmin", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x675d972c" - }, - { - "constant": true, - "inputs": [], - "name": "accrualBlockNumber", + "name": "_setPendingAdmin", "outputs": [ { "name": "", @@ -15229,35 +18142,47 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x6c540baf" + "signature": "0xb71d1a0c" }, { - "constant": true, + "constant": false, "inputs": [], - "name": "underlying", + "name": "exchangeRateCurrent", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x6f307dc3" + "signature": "0xbd6d894d" }, { "constant": true, "inputs": [ { - "name": "owner", + "name": "account", "type": "address" } ], - "name": "balanceOf", + "name": "getAccountSnapshot", "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, { "name": "", "type": "uint256" @@ -15266,12 +18191,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x70a08231" + "signature": "0xc37f68e2" }, { "constant": false, - "inputs": [], - "name": "totalBorrowsCurrent", + "inputs": [ + { + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", "outputs": [ { "name": "", @@ -15281,17 +18211,17 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x73acee98" + "signature": "0xc5ebeaec" }, { "constant": false, "inputs": [ { - "name": "redeemAmount", + "name": "redeemTokens", "type": "uint256" } ], - "name": "redeemUnderlying", + "name": "redeem", "outputs": [ { "name": "", @@ -15301,47 +18231,36 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0x852a12e3" + "signature": "0xdb006a75" }, { "constant": true, - "inputs": [], - "name": "totalReserves", - "outputs": [ + "inputs": [ { - "name": "", - "type": "uint256" + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0x8f840ddd" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", + "name": "allowance", "outputs": [ { "name": "", - "type": "string" + "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0x95d89b41" + "signature": "0xdd62ed3e" }, { - "constant": true, - "inputs": [ - { - "name": "account", - "type": "address" - } - ], - "name": "borrowBalanceStored", + "constant": false, + "inputs": [], + "name": "_acceptAdmin", "outputs": [ { "name": "", @@ -15349,19 +18268,19 @@ } ], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function", - "signature": "0x95dd9193" + "signature": "0xe9c714f2" }, { "constant": false, "inputs": [ { - "name": "mintAmount", - "type": "uint256" + "name": "newInterestRateModel", + "type": "address" } ], - "name": "mint", + "name": "_setInterestRateModel", "outputs": [ { "name": "", @@ -15371,66 +18290,70 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa0712d68" + "signature": "0xf2b3abbd" }, { - "constant": false, + "constant": true, "inputs": [], - "name": "accrueInterest", + "name": "interestRateModel", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xa6afed95" + "signature": "0xf3fdb15a" }, { "constant": false, "inputs": [ { - "name": "dst", + "name": "borrower", "type": "address" }, { - "name": "amount", - "type": "uint256" + "name": "repayAmount", + "type": "uint256" + }, + { + "name": "cTokenCollateral", + "type": "address" } ], - "name": "transfer", + "name": "liquidateBorrow", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xa9059cbb" + "signature": "0xf5e3c462" }, { "constant": true, "inputs": [], - "name": "borrowIndex", + "name": "admin", "outputs": [ { "name": "", - "type": "uint256" + "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xaa5af0fd" + "signature": "0xf851a440" }, { "constant": true, "inputs": [], - "name": "supplyRatePerBlock", + "name": "borrowRatePerBlock", "outputs": [ { "name": "", @@ -15440,45 +18363,17 @@ "payable": false, "stateMutability": "view", "type": "function", - "signature": "0xae9d70b0" + "signature": "0xf8f9da28" }, { "constant": false, "inputs": [ { - "name": "liquidator", - "type": "address" - }, - { - "name": "borrower", - "type": "address" - }, - { - "name": "seizeTokens", - "type": "uint256" - } - ], - "name": "seize", - "outputs": [ - { - "name": "", + "name": "newReserveFactorMantissa", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xb2a02ff1" - }, - { - "constant": false, - "inputs": [ - { - "name": "newPendingAdmin", - "type": "address" - } - ], - "name": "_setPendingAdmin", + "name": "_setReserveFactor", "outputs": [ { "name": "", @@ -15488,596 +18383,680 @@ "payable": false, "stateMutability": "nonpayable", "type": "function", - "signature": "0xb71d1a0c" + "signature": "0xfca7820b" }, { - "constant": false, + "constant": true, "inputs": [], - "name": "exchangeRateCurrent", + "name": "isCToken", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function", - "signature": "0xbd6d894d" + "signature": "0xfe9c44ae" }, { - "constant": true, "inputs": [ { - "name": "account", + "name": "underlying_", "type": "address" - } - ], - "name": "getAccountSnapshot", - "outputs": [ + }, { - "name": "", - "type": "uint256" + "name": "comptroller_", + "type": "address" }, { - "name": "", - "type": "uint256" + "name": "interestRateModel_", + "type": "address" }, { - "name": "", + "name": "initialExchangeRateMantissa_", "type": "uint256" }, { - "name": "", + "name": "name_", + "type": "string" + }, + { + "name": "symbol_", + "type": "string" + }, + { + "name": "decimals_", "type": "uint256" } ], "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xc37f68e2" + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "borrowAmount", + "indexed": false, + "name": "interestAccumulated", "type": "uint256" - } - ], - "name": "borrow", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "borrowIndex", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xc5ebeaec" + "name": "AccrueInterest", + "type": "event", + "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "redeemTokens", + "indexed": false, + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "name": "mintAmount", "type": "uint256" - } - ], - "name": "redeem", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "mintTokens", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xdb006a75" + "name": "Mint", + "type": "event", + "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" }, { - "constant": true, + "anonymous": false, "inputs": [ { - "name": "owner", + "indexed": false, + "name": "redeemer", "type": "address" }, { - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "name": "", + "indexed": false, + "name": "redeemAmount", "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xdd62ed3e" - }, - { - "constant": false, - "inputs": [], - "name": "_acceptAdmin", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "redeemTokens", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xe9c714f2" + "name": "Redeem", + "type": "event", + "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "newInterestRateModel", + "indexed": false, + "name": "borrower", "type": "address" - } - ], - "name": "_setInterestRateModel", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "borrowAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xf2b3abbd" + "name": "Borrow", + "type": "event", + "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" }, { - "constant": true, - "inputs": [], - "name": "interestRateModel", - "outputs": [ + "anonymous": false, + "inputs": [ { - "name": "", + "indexed": false, + "name": "payer", + "type": "address" + }, + { + "indexed": false, + "name": "borrower", "type": "address" + }, + { + "indexed": false, + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "name": "totalBorrows", + "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xf3fdb15a" + "name": "RepayBorrow", + "type": "event", + "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" }, { - "constant": false, + "anonymous": false, "inputs": [ { + "indexed": false, + "name": "liquidator", + "type": "address" + }, + { + "indexed": false, "name": "borrower", "type": "address" }, { + "indexed": false, "name": "repayAmount", "type": "uint256" }, { + "indexed": false, "name": "cTokenCollateral", "type": "address" - } - ], - "name": "liquidateBorrow", - "outputs": [ + }, { - "name": "", + "indexed": false, + "name": "seizeTokens", "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xf5e3c462" + "name": "LiquidateBorrow", + "type": "event", + "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" }, { - "constant": true, - "inputs": [], - "name": "admin", - "outputs": [ + "anonymous": false, + "inputs": [ { - "name": "", + "indexed": false, + "name": "oldPendingAdmin", "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xf851a440" - }, - { - "constant": true, - "inputs": [], - "name": "borrowRatePerBlock", - "outputs": [ + }, { - "name": "", - "type": "uint256" + "indexed": false, + "name": "newPendingAdmin", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xf8f9da28" + "name": "NewPendingAdmin", + "type": "event", + "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" }, { - "constant": false, + "anonymous": false, "inputs": [ { - "name": "newReserveFactorMantissa", - "type": "uint256" - } - ], - "name": "_setReserveFactor", - "outputs": [ + "indexed": false, + "name": "oldAdmin", + "type": "address" + }, { - "name": "", - "type": "uint256" + "indexed": false, + "name": "newAdmin", + "type": "address" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function", - "signature": "0xfca7820b" + "name": "NewAdmin", + "type": "event", + "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" }, { - "constant": true, - "inputs": [], - "name": "isCToken", - "outputs": [ + "anonymous": false, + "inputs": [ { - "name": "", - "type": "bool" + "indexed": false, + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": false, + "name": "newComptroller", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function", - "signature": "0xfe9c44ae" + "name": "NewComptroller", + "type": "event", + "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" }, { + "anonymous": false, "inputs": [ { - "name": "underlying_", - "type": "address" - }, - { - "name": "comptroller_", + "indexed": false, + "name": "oldInterestRateModel", "type": "address" }, { - "name": "interestRateModel_", + "indexed": false, + "name": "newInterestRateModel", "type": "address" - }, - { - "name": "initialExchangeRateMantissa_", - "type": "uint256" - }, - { - "name": "name_", - "type": "string" - }, - { - "name": "symbol_", - "type": "string" - }, - { - "name": "decimals_", - "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor", - "signature": "constructor" + "name": "NewMarketInterestRateModel", + "type": "event", + "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "interestAccumulated", - "type": "uint256" - }, - { - "indexed": false, - "name": "borrowIndex", + "name": "oldReserveFactorMantissa", "type": "uint256" }, { "indexed": false, - "name": "totalBorrows", + "name": "newReserveFactorMantissa", "type": "uint256" } ], - "name": "AccrueInterest", + "name": "NewReserveFactor", "type": "event", - "signature": "0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9" + "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "minter", + "name": "admin", "type": "address" }, { "indexed": false, - "name": "mintAmount", + "name": "reduceAmount", "type": "uint256" }, { "indexed": false, - "name": "mintTokens", + "name": "newTotalReserves", "type": "uint256" } ], - "name": "Mint", + "name": "ReservesReduced", "type": "event", - "signature": "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f" + "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" }, { "anonymous": false, "inputs": [ { "indexed": false, - "name": "redeemer", - "type": "address" + "name": "error", + "type": "uint256" }, { "indexed": false, - "name": "redeemAmount", + "name": "info", "type": "uint256" }, { "indexed": false, - "name": "redeemTokens", + "name": "detail", "type": "uint256" } ], - "name": "Redeem", + "name": "Failure", "type": "event", - "signature": "0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929" + "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" }, { "anonymous": false, "inputs": [ { - "indexed": false, - "name": "borrower", + "indexed": true, + "name": "from", "type": "address" }, { - "indexed": false, - "name": "borrowAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "accountBorrows", - "type": "uint256" + "indexed": true, + "name": "to", + "type": "address" }, { "indexed": false, - "name": "totalBorrows", + "name": "amount", "type": "uint256" } ], - "name": "Borrow", + "name": "Transfer", "type": "event", - "signature": "0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80" + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" }, { "anonymous": false, "inputs": [ { - "indexed": false, - "name": "payer", + "indexed": true, + "name": "owner", "type": "address" }, { - "indexed": false, - "name": "borrower", + "indexed": true, + "name": "spender", "type": "address" }, { "indexed": false, - "name": "repayAmount", + "name": "amount", "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + } + ], + "USDC": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x06fdde03" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" }, { - "indexed": false, - "name": "accountBorrows", + "name": "_value", "type": "uint256" - }, + } + ], + "name": "approve", + "outputs": [ { - "indexed": false, - "name": "totalBorrows", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", "type": "uint256" } ], - "name": "RepayBorrow", - "type": "event", - "signature": "0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x18160ddd" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": false, - "name": "liquidator", + "name": "_from", "type": "address" }, { - "indexed": false, - "name": "borrower", + "name": "_to", "type": "address" }, { - "indexed": false, - "name": "repayAmount", + "name": "_value", "type": "uint256" - }, + } + ], + "name": "transferFrom", + "outputs": [ { - "indexed": false, - "name": "cTokenCollateral", - "type": "address" - }, + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x23b872dd" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ { - "indexed": false, - "name": "seizeTokens", - "type": "uint256" + "name": "", + "type": "uint8" } ], - "name": "LiquidateBorrow", - "type": "event", - "signature": "0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": false, - "name": "oldPendingAdmin", + "name": "_spender", "type": "address" }, { - "indexed": false, - "name": "newPendingAdmin", - "type": "address" + "name": "_subtractedValue", + "type": "uint256" } ], - "name": "NewPendingAdmin", - "type": "event", - "signature": "0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9" + "name": "decreaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x66188463" }, { - "anonymous": false, + "constant": true, "inputs": [ { - "indexed": false, - "name": "oldAdmin", + "name": "_owner", "type": "address" - }, + } + ], + "name": "balanceOf", + "outputs": [ { - "indexed": false, - "name": "newAdmin", - "type": "address" + "name": "", + "type": "uint256" } ], - "name": "NewAdmin", - "type": "event", - "signature": "0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x70a08231" }, { - "anonymous": false, + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95d89b41" + }, + { + "constant": false, "inputs": [ { - "indexed": false, - "name": "oldComptroller", + "name": "_to", "type": "address" }, { - "indexed": false, - "name": "newComptroller", - "type": "address" + "name": "_value", + "type": "uint256" } ], - "name": "NewComptroller", - "type": "event", - "signature": "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d" + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa9059cbb" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": false, - "name": "oldInterestRateModel", + "name": "_spender", "type": "address" }, { - "indexed": false, - "name": "newInterestRateModel", - "type": "address" + "name": "_addedValue", + "type": "uint256" } ], - "name": "NewMarketInterestRateModel", - "type": "event", - "signature": "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926" + "name": "increaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xd73dd623" }, { - "anonymous": false, + "constant": true, "inputs": [ { - "indexed": false, - "name": "oldReserveFactorMantissa", - "type": "uint256" + "name": "_owner", + "type": "address" }, { - "indexed": false, - "name": "newReserveFactorMantissa", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", "type": "uint256" } ], - "name": "NewReserveFactor", - "type": "event", - "signature": "0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460" + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdd62ed3e" }, { - "anonymous": false, "inputs": [ { - "indexed": false, - "name": "admin", - "type": "address" + "name": "_initialAmount", + "type": "uint256" }, { - "indexed": false, - "name": "reduceAmount", - "type": "uint256" + "name": "_tokenName", + "type": "string" }, { - "indexed": false, - "name": "newTotalReserves", - "type": "uint256" + "name": "_decimalUnits", + "type": "uint8" + }, + { + "name": "_tokenSymbol", + "type": "string" } ], - "name": "ReservesReduced", - "type": "event", - "signature": "0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e" + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" }, { "anonymous": false, "inputs": [ { - "indexed": false, - "name": "error", - "type": "uint256" + "indexed": true, + "name": "owner", + "type": "address" }, { - "indexed": false, - "name": "info", - "type": "uint256" + "indexed": true, + "name": "spender", + "type": "address" }, { "indexed": false, - "name": "detail", + "name": "value", "type": "uint256" } ], - "name": "Failure", + "name": "Approval", "type": "event", - "signature": "0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0" + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" }, { "anonymous": false, @@ -16094,7 +19073,7 @@ }, { "indexed": false, - "name": "amount", + "name": "value", "type": "uint256" } ], @@ -16103,27 +19082,23 @@ "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": true, - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "name": "spender", + "name": "_owner", "type": "address" }, { - "indexed": false, - "name": "amount", + "name": "value", "type": "uint256" } ], - "name": "Approval", - "type": "event", - "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + "name": "allocateTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x08bca566" } ] } \ No newline at end of file diff --git a/networks/rinkeby.json b/networks/rinkeby.json index c8349eae5..abd180637 100644 --- a/networks/rinkeby.json +++ b/networks/rinkeby.json @@ -16,8 +16,10 @@ "BAT": "0xbF7A7169562078c96f0eC1A8aFD6aE50f12e5A99", "cETH": "0xd6801a1DfFCd0a410336Ef88DeF4320D6DF1883e", "Base500bps_Slope1200bps": "0xE12630c8Fdd7d0096c9Cd72Cd228598AEBe58795", + "Timelock": "0xe4cD9a61431FbD268ea842D68533561262C17513", "Base200bps_Slope3000bps": "0x6330D442A2D7eE4DC66C0adb9969e8702aEfc9fE", "cREP": "0xEBe09eB3411D18F4FF8D859e096C533CAC5c6B60", + "ComptrollerG2": "0xb1983eE0064Fdb2A581966715DC9bA4D8B289A6A", "WBTC": "0x577D296678535e4903D59A4C929B718e1D575e0A", "REP": "0x6e894660985207feb7cf89Faf048998c71E8EE89", "cZRX": "0x52201ff1720134bBbBB2f6BC97Bf3715490EC19B", @@ -36,8 +38,10 @@ "Base0bps_Slope2000bps": 4319840, "cETH": 4319846, "Base500bps_Slope1200bps": 4319839, + "Timelock": 5245966, "Base200bps_Slope3000bps": 4319841, "cREP": 4319845, + "ComptrollerG2": 5245967, "WBTC": 4717533, "cZRX": 4319842, "cWBTC": 4717535 @@ -67,8 +71,18 @@ "address": "0x16e175eE9f555E43FD01f3aFa359A37b10e5139b", "contract": "Comptroller", "description": "Standard Comptroller Impl" + }, + "ComptrollerG2": { + "address": "0xb1983eE0064Fdb2A581966715DC9bA4D8B289A6A", + "contract": "Comptroller", + "description": "Standard Comptroller Impl" } }, + "Timelock": { + "address": "0xe4cD9a61431FbD268ea842D68533561262C17513", + "contract": "Timelock", + "description": "Test Timelock" + }, "Constructors": { "ZRX": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000002307800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a52580000000000000000000000000000000000000000000000000000000000", "cUSDC": "0x0000000000000000000000004dbcdf9b62e891a7cec5a2568c3f4faf9e8abe2b0000000000000000000000002eaa9d77ae4d8f9cdd9faacd44016e746485bddb0000000000000000000000001a43bfd39b15dcf444e17ab408c4b5be32deb7f50000000000000000000000000000000000000000000000000000b5e620f4800000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000016436f6d706f756e642055534420436f696e20f09f93880000000000000000000000000000000000000000000000000000000000000000000000000000000000056355534443000000000000000000000000000000000000000000000000000000", @@ -84,8 +98,10 @@ "BAT": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000015426173696320417474656e74696f6e20546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000034241540000000000000000000000000000000000000000000000000000000000", "cETH": "0x0000000000000000000000002eaa9d77ae4d8f9cdd9faacd44016e746485bddb0000000000000000000000001a43bfd39b15dcf444e17ab408c4b5be32deb7f5000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000013436f6d706f756e6420457468657220f09f93880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046345544800000000000000000000000000000000000000000000000000000000", "Base500bps_Slope1200bps": "0x00000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000001aa535d3d0c0000", + "Timelock": "0x0000000000000000000000009c1856636d78c051dead6cab9c5699e4e25549e900000000000000000000000000000000000000000000000000000000000000b4", "Base200bps_Slope3000bps": "0x00000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000429d069189e0000", "cREP": "0x0000000000000000000000006e894660985207feb7cf89faf048998c71e8ee890000000000000000000000002eaa9d77ae4d8f9cdd9faacd44016e746485bddb0000000000000000000000006330d442a2d7ee4dc66c0adb9969e8702aefc9fe000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000013436f6d706f756e6420417567757220f09f93880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046352455000000000000000000000000000000000000000000000000000000000", + "ComptrollerG2": "0x", "WBTC": "0x", "REP": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000005417567757200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035245500000000000000000000000000000000000000000000000000000000000", "cZRX": "0x000000000000000000000000ddea378a6ddc8afec82c36e9b0078826bf9e68b60000000000000000000000002eaa9d77ae4d8f9cdd9faacd44016e746485bddb0000000000000000000000006330d442a2d7ee4dc66c0adb9969e8702aefc9fe000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010436f6d706f756e6420307820f09f9388000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004635a525800000000000000000000000000000000000000000000000000000000", diff --git a/package.json b/package.json index 8f161e436..5f4881fe7 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,13 @@ "description": "The Compound Protocol", "main": "index.js", "scripts": { + "chain": "./script/ganache", + "compile": "truffle compile", "console": "./script/console", "coverage": "./script/coverage", "deploy": "./scenario/script/repl -s ./script/scen/deploy.scen", "lint": "./script/lint", + "ganache": "./script/ganache", "repl": "./scenario/script/repl", "scenario": "./script/scenario", "test": "./script/test" diff --git a/scenario/SCENARIO.md b/scenario/SCENARIO.md index 3ee226eaf..3ca472798 100644 --- a/scenario/SCENARIO.md +++ b/scenario/SCENARIO.md @@ -61,7 +61,7 @@ ## cToken Events -* `CToken Deploy name: underlying: comptroller: interestRateModel: initialExchangeRate: decimals:` - Generates a new comptroller and sets to world global +* `CToken Deploy name: underlying: comptroller: interestRateModel: initialExchangeRate: decimals: admin:
` - Generates a new comptroller and sets to world global * E.g. `CToken Deploy cZRX (Erc20 ZRX Address) (Comptroller Address) (InterestRateModel Address) 1.0 18` * `CToken AccrueInterest` - Accrues interest for given token * E.g. `CToken cZRX AccrueInterest` diff --git a/scenario/src/Builder/CTokenBuilder.ts b/scenario/src/Builder/CTokenBuilder.ts index d90bf2253..7f3ef23ff 100644 --- a/scenario/src/Builder/CTokenBuilder.ts +++ b/scenario/src/Builder/CTokenBuilder.ts @@ -32,14 +32,15 @@ export interface TokenData { address?: string contract: string initial_exchange_rate_mantissa: string + admin: string } export async function buildCToken(world: World, from: string, params: Event): Promise<{world: World, cToken: CToken, tokenData: TokenData}> { const fetchers = [ - new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` + new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV, admin: AddressV}, TokenData>(` #### Scenario - * "Scenario symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: " - A CToken Scenario for local testing + * "Scenario symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: admin:
" - A CToken Scenario for local testing * E.g. "CToken Deploy Scenario cZRX (Erc20 ZRX Address) (Comptroller Address) (InterestRateModel Address) 1.0 8" `, "Scenario", @@ -50,24 +51,26 @@ export async function buildCToken(world: World, from: string, params: Event): Pr new Arg("comptroller", getAddressV), new Arg("interestRateModel", getAddressV), new Arg("initialExchangeRate", getExpNumberV), - new Arg("decimals", getNumberV) + new Arg("decimals", getNumberV), + new Arg("admin", getAddressV) ], - async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals}) => { + async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals, admin}) => { return { - invokation: await CErc20ScenarioContract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val]), + invokation: await CErc20ScenarioContract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val, admin.val]), name: name.val, symbol: symbol.val, decimals: decimals.toNumber(), underlying: underlying.val, contract: 'CErc20Scenario', - initial_exchange_rate_mantissa: initialExchangeRate.encode().toString() + initial_exchange_rate_mantissa: initialExchangeRate.encode().toString(), + admin: admin.val }; } ), - new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` + new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, admin: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` #### CEtherScenario - * "CEtherScenario symbol: name: comptroller:
interestRateModel:
initialExchangeRate: decimals: " - A CToken Scenario for local testing + * "CEtherScenario symbol: name: comptroller:
interestRateModel:
initialExchangeRate: decimals: admin:
" - A CToken Scenario for local testing * E.g. "CToken Deploy CEtherScenario (Erc20 ZRX Address) (Comptroller Address) (InterestRateModel Address) 1.0 8" `, "CEtherScenario", @@ -77,24 +80,26 @@ export async function buildCToken(world: World, from: string, params: Event): Pr new Arg("comptroller", getAddressV), new Arg("interestRateModel", getAddressV), new Arg("initialExchangeRate", getExpNumberV), - new Arg("decimals", getNumberV) + new Arg("decimals", getNumberV), + new Arg("admin", getAddressV) ], - async (world, {symbol, name, comptroller, interestRateModel, initialExchangeRate, decimals}) => { + async (world, {symbol, name, comptroller, interestRateModel, initialExchangeRate, decimals, admin}) => { return { - invokation: await CEtherScenarioContract.deploy(world, from, [name.val, symbol.val, decimals.val, comptroller.val, interestRateModel.val, initialExchangeRate.val]), + invokation: await CEtherScenarioContract.deploy(world, from, [name.val, symbol.val, decimals.val, admin.val, comptroller.val, interestRateModel.val, initialExchangeRate.val]), name: name.val, symbol: symbol.val, decimals: decimals.toNumber(), underlying: "", contract: 'CEtherScenario', - initial_exchange_rate_mantissa: initialExchangeRate.encode().toString() + initial_exchange_rate_mantissa: initialExchangeRate.encode().toString(), + admin: admin.val }; } ), - new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` + new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, admin: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` #### CEther - * "CEther symbol: name: comptroller:
interestRateModel:
initialExchangeRate: decimals: " - A CToken Scenario for local testing + * "CEther symbol: name: comptroller:
interestRateModel:
initialExchangeRate: decimals: admin:
" - A CToken Scenario for local testing * E.g. "CToken Deploy CEther cETH (Comptroller Address) (InterestRateModel Address) 1.0 8" `, "CEther", @@ -104,24 +109,26 @@ export async function buildCToken(world: World, from: string, params: Event): Pr new Arg("comptroller", getAddressV), new Arg("interestRateModel", getAddressV), new Arg("initialExchangeRate", getExpNumberV), - new Arg("decimals", getNumberV) + new Arg("decimals", getNumberV), + new Arg("admin", getAddressV) ], - async (world, {symbol, name, comptroller, interestRateModel, initialExchangeRate, decimals}) => { + async (world, {symbol, name, comptroller, interestRateModel, initialExchangeRate, decimals, admin}) => { return { - invokation: await CEtherContract.deploy(world, from, [comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val]), + invokation: await CEtherContract.deploy(world, from, [comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val, admin.val]), name: name.val, symbol: symbol.val, decimals: decimals.toNumber(), underlying: "", contract: 'CEther', - initial_exchange_rate_mantissa: initialExchangeRate.encode().toString() + initial_exchange_rate_mantissa: initialExchangeRate.encode().toString(), + admin: admin.val }; } ), - new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` + new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, admin: AddressV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` #### CErc20 - * "CErc20 symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: " - A official CToken contract + * "CErc20 symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: admin:
" - A official CToken contract * E.g. "CToken Deploy CErc20 cZRX (Erc20 ZRX Address) (Comptroller Address) (InterestRateModel Address) 1.0 8" `, "CErc20", @@ -132,24 +139,26 @@ export async function buildCToken(world: World, from: string, params: Event): Pr new Arg("comptroller", getAddressV), new Arg("interestRateModel", getAddressV), new Arg("initialExchangeRate", getExpNumberV), - new Arg("decimals", getNumberV) + new Arg("decimals", getNumberV), + new Arg("admin", getAddressV) ], - async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals}) => { + async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals, admin}) => { return { - invokation: await CErc20Contract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val]), + invokation: await CErc20Contract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val, admin.val]), name: name.val, symbol: symbol.val, decimals: decimals.toNumber(), underlying: underlying.val, contract: 'CErc20', - initial_exchange_rate_mantissa: initialExchangeRate.encode().toString() + initial_exchange_rate_mantissa: initialExchangeRate.encode().toString(), + admin: admin.val }; } ), - new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` + new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, admin: AddressV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` #### CEvil - * "CEvil symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: " - A malicious CToken contract + * "CEvil symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: admin:
" - A malicious CToken contract * E.g. "CToken Deploy CEvil cEVL (Erc20 ZRX Address) (Comptroller Address) (InterestRateModel Address) 1.0 8" `, "CEvil", @@ -160,24 +169,26 @@ export async function buildCToken(world: World, from: string, params: Event): Pr new Arg("comptroller", getAddressV), new Arg("interestRateModel", getAddressV), new Arg("initialExchangeRate", getExpNumberV), - new Arg("decimals", getNumberV) + new Arg("decimals", getNumberV), + new Arg("admin", getAddressV) ], - async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals}) => { + async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals, admin}) => { return { - invokation: await CEvilContract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val]), + invokation: await CEvilContract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val, admin.val]), name: name.val, symbol: symbol.val, decimals: decimals.toNumber(), underlying: underlying.val, contract: 'CEvil', - initial_exchange_rate_mantissa: initialExchangeRate.encode().toString() + initial_exchange_rate_mantissa: initialExchangeRate.encode().toString(), + admin: admin.val }; } ), - new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` + new Fetcher<{symbol: StringV, name: StringV, decimals: NumberV, admin: AddressV, underlying: AddressV, comptroller: AddressV, interestRateModel: AddressV, initialExchangeRate: NumberV}, TokenData>(` #### Standard - * "symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: " - A official CToken contract + * "symbol: name: underlying:
comptroller:
interestRateModel:
initialExchangeRate: decimals: admin:
" - A official CToken contract * E.g. "CToken Deploy cZRX (Erc20 ZRX Address) (Comptroller Address) (InterestRateModel Address) 1.0 8" `, "Standard", @@ -188,29 +199,32 @@ export async function buildCToken(world: World, from: string, params: Event): Pr new Arg("comptroller", getAddressV), new Arg("interestRateModel", getAddressV), new Arg("initialExchangeRate", getExpNumberV), - new Arg("decimals", getNumberV) + new Arg("decimals", getNumberV), + new Arg("admin", getAddressV) ], - async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals}) => { + async (world, {symbol, name, underlying, comptroller, interestRateModel, initialExchangeRate, decimals, admin}) => { // Note: we're going to use the scenario contract as the standard deployment on local networks if (world.isLocalNetwork()) { return { - invokation: await CErc20ScenarioContract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val]), + invokation: await CErc20ScenarioContract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val, admin.val]), name: name.val, symbol: symbol.val, decimals: decimals.toNumber(), underlying: underlying.val, contract: 'CErc20Scenario', - initial_exchange_rate_mantissa: initialExchangeRate.encode().toString() + initial_exchange_rate_mantissa: initialExchangeRate.encode().toString(), + admin: admin.val }; } else { return { - invokation: await CErc20Contract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val]), + invokation: await CErc20Contract.deploy(world, from, [underlying.val, comptroller.val, interestRateModel.val, initialExchangeRate.val, name.val, symbol.val, decimals.val, admin.val]), name: name.val, symbol: symbol.val, decimals: decimals.toNumber(), underlying: underlying.val, contract: 'CErc20', - initial_exchange_rate_mantissa: initialExchangeRate.encode().toString() + initial_exchange_rate_mantissa: initialExchangeRate.encode().toString(), + admin: admin.val }; } }, @@ -241,4 +255,4 @@ export async function buildCToken(world: World, from: string, params: Event): Pr ); return {world, cToken, tokenData}; -} +} \ No newline at end of file diff --git a/scenario/src/Builder/ComptrollerImplBuilder.ts b/scenario/src/Builder/ComptrollerImplBuilder.ts index 33e04c6a5..1799aafeb 100644 --- a/scenario/src/Builder/ComptrollerImplBuilder.ts +++ b/scenario/src/Builder/ComptrollerImplBuilder.ts @@ -1,151 +1,154 @@ -import {Event} from '../Event'; -import {addAction, World} from '../World'; -import {ComptrollerImpl} from '../Contract/ComptrollerImpl'; -import {Invokation, invoke} from '../Invokation'; -import { - getAddressV, - getExpNumberV, - getNumberV, - getStringV -} from '../CoreValue'; -import { - AddressV, - NothingV, - NumberV, - StringV -} from '../Value'; -import {Arg, Fetcher, getFetcherValue} from '../Command'; -import {storeAndSaveContract} from '../Networks'; -import {getContract, getTestContract} from '../Contract'; +import { Event } from '../Event'; +import { addAction, World } from '../World'; +import { ComptrollerImpl } from '../Contract/ComptrollerImpl'; +import { Invokation, invoke } from '../Invokation'; +import { getAddressV, getExpNumberV, getNumberV, getStringV } from '../CoreValue'; +import { AddressV, NumberV, StringV } from '../Value'; +import { Arg, Fetcher, getFetcherValue } from '../Command'; +import { storeAndSaveContract } from '../Networks'; +import { getContract, getTestContract } from '../Contract'; -const ComptrollerContract = getContract("Comptroller"); +const ComptrollerContract = getContract('Comptroller'); +const ComptrollerG1Contract = getContract('ComptrollerG1'); +const ComptrollerScenarioG1Contract = getTestContract('ComptrollerScenarioG1'); const ComptrollerScenarioContract = getTestContract('ComptrollerScenario'); const ComptrollerBorkedContract = getTestContract('ComptrollerBorked'); -const ComptrollerBoolContract = getTestContract('BoolComptroller'); export interface ComptrollerImplData { - invokation: Invokation - name: string - contract: string - description: string + invokation: Invokation; + name: string; + contract: string; + description: string; } -export async function buildComptrollerImpl(world: World, from: string, event: Event): Promise<{world: World, comptrollerImpl: ComptrollerImpl, comptrollerImplData: ComptrollerImplData}> { +export async function buildComptrollerImpl( + world: World, + from: string, + event: Event +): Promise<{ world: World; comptrollerImpl: ComptrollerImpl; comptrollerImplData: ComptrollerImplData }> { const fetchers = [ - new Fetcher<{name: StringV | NothingV}, ComptrollerImplData>(` + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` + #### ScenarioG1 + + * "ScenarioG1 name:" - The Comptroller Scenario for local testing (G1) + * E.g. "ComptrollerImpl Deploy ScenarioG1 MyScen" + `, + 'ScenarioG1', + [new Arg('name', getStringV)], + async (world, { name }) => ({ + invokation: await ComptrollerScenarioG1Contract.deploy(world, from, []), + name: name.val, + contract: 'ComptrollerScenarioG1', + description: 'ScenarioG1 Comptroller Impl' + }) + ), + + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` #### Scenario * "Scenario name:" - The Comptroller Scenario for local testing * E.g. "ComptrollerImpl Deploy Scenario MyScen" `, - "Scenario", - [ - new Arg("name", getStringV, {nullable: true}), - ], - async (world, {name}) => ({ + 'Scenario', + [new Arg('name', getStringV)], + async (world, { name }) => ({ invokation: await ComptrollerScenarioContract.deploy(world, from, []), - name: name instanceof StringV ? name.val : "Comptroller", - contract: "ComptrollerScenario", - description: "Scenario Comptroller Impl" + name: name.val, + contract: 'ComptrollerScenario', + description: 'Scenario Comptroller Impl' }) ), - new Fetcher<{name: StringV | NothingV}, ComptrollerImplData>(` - #### Standard + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` + #### StandardG1 - * "Standard name:" - The standard Comptroller contract - * E.g. "Comptroller Deploy Standard MyStandard" + * "StandardG1 name:" - The standard generation 1 Comptroller contract + * E.g. "Comptroller Deploy StandardG1 MyStandard" `, - "Standard", - [ - new Arg("name", getStringV, {nullable: true}) - ], - async (world, {name}) => { - let invokation; - let contract; - + 'StandardG1', + [new Arg('name', getStringV)], + async (world, { name }) => { return { - invokation: await ComptrollerContract.deploy(world, from, []), - name: name instanceof StringV ? name.val : "Comptroller", - contract: "Comptroller", - description: "Standard Comptroller Impl" + invokation: await ComptrollerG1Contract.deploy(world, from, []), + name: name.val, + contract: 'ComptrollerG1', + description: 'StandardG1 Comptroller Impl' }; - }, + } ), - new Fetcher<{name: StringV | NothingV}, ComptrollerImplData>(` - #### YesNo + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` + #### Standard - * "YesNo name:" - The bool Comptroller contract - * E.g. "Comptroller Deploy YesNo MyBool" + * "Standard name:" - The standard Comptroller contract + * E.g. "Comptroller Deploy Standard MyStandard" `, - "YesNo", - [ - new Arg("name", getStringV, {nullable: true}) - ], - async (world, {name}) => { - let invokation; - let contract; - + 'Standard', + [new Arg('name', getStringV)], + async (world, { name }) => { return { - invokation: await ComptrollerBoolContract.deploy(world, from, []), - name: name instanceof StringV ? name.val : "Comptroller", - contract: "Comptroller", - description: "YesNo Comptroller" + invokation: await ComptrollerContract.deploy(world, from, []), + name: name.val, + contract: 'Comptroller', + description: 'Standard Comptroller Impl' }; - }, + } ), - new Fetcher<{name: StringV | NothingV}, ComptrollerImplData>(` + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` #### Borked * "Borked name:" - A Borked Comptroller for testing * E.g. "ComptrollerImpl Deploy Borked MyBork" `, - "Borked", - [ - new Arg("name", getStringV, {nullable: true}) - ], - async (world, {name}) => ({ + 'Borked', + [new Arg('name', getStringV)], + async (world, { name }) => ({ invokation: await ComptrollerBorkedContract.deploy(world, from, []), - name: name instanceof StringV ? name.val : "Comptroller", - contract: "ComptrollerBorked", - description: "Borked Comptroller Impl" + name: name.val, + contract: 'ComptrollerBorked', + description: 'Borked Comptroller Impl' }) ), - new Fetcher<{name: StringV | NothingV}, ComptrollerImplData>(` + new Fetcher<{ name: StringV }, ComptrollerImplData>( + ` #### Default * "name:" - The standard Comptroller contract * E.g. "ComptrollerImpl Deploy MyDefault" `, - "Default", - [ - new Arg("name", getStringV, {nullable: true}) - ], - async (world, {name}) => { - let invokation; - let contract; - + 'Default', + [new Arg('name', getStringV)], + async (world, { name }) => { if (world.isLocalNetwork()) { // Note: we're going to use the scenario contract as the standard deployment on local networks return { invokation: await ComptrollerScenarioContract.deploy(world, from, []), - name: name instanceof StringV ? name.val : "Comptroller", - contract: "ComptrollerScenario", - description: "Scenario Comptroller Impl" + name: name.val, + contract: 'ComptrollerScenario', + description: 'Scenario Comptroller Impl' }; } else { return { invokation: await ComptrollerContract.deploy(world, from, []), - name: name instanceof StringV ? name.val : "Comptroller", - contract: "Comptroller", - description: "Standard Comptroller Impl" + name: name.val, + contract: 'Comptroller', + description: 'Standard Comptroller Impl' }; } }, - {catchall: true} + { catchall: true } ) ]; - let comptrollerImplData = await getFetcherValue("DeployComptrollerImpl", fetchers, world, event); + let comptrollerImplData = await getFetcherValue( + 'DeployComptrollerImpl', + fetchers, + world, + event + ); let invokation = comptrollerImplData.invokation; delete comptrollerImplData.invokation; @@ -154,22 +157,16 @@ export async function buildComptrollerImpl(world: World, from: string, event: Ev } const comptrollerImpl = invokation.value!; - world = await storeAndSaveContract( - world, - comptrollerImpl, - comptrollerImplData.name, - invokation, - [ - { - index: ['Comptroller', comptrollerImplData.name], - data: { - address: comptrollerImpl._address, - contract: comptrollerImplData.contract, - description: comptrollerImplData.description - } + world = await storeAndSaveContract(world, comptrollerImpl, comptrollerImplData.name, invokation, [ + { + index: ['Comptroller', comptrollerImplData.name], + data: { + address: comptrollerImpl._address, + contract: comptrollerImplData.contract, + description: comptrollerImplData.description } - ] - ); + } + ]); - return {world, comptrollerImpl, comptrollerImplData}; + return { world, comptrollerImpl, comptrollerImplData }; } diff --git a/scenario/src/Builder/TimelockBuilder.ts b/scenario/src/Builder/TimelockBuilder.ts new file mode 100644 index 000000000..2da6a11fd --- /dev/null +++ b/scenario/src/Builder/TimelockBuilder.ts @@ -0,0 +1,136 @@ +import { Event } from '../Event'; +import { World } from '../World'; +import { Timelock } from '../Contract/Timelock'; +import { Invokation } from '../Invokation'; +import { getAddressV, getNumberV } from '../CoreValue'; +import { AddressV, NumberV } from '../Value'; +import { Arg, Fetcher, getFetcherValue } from '../Command'; +import { storeAndSaveContract } from '../Networks'; +import { getContract, getTestContract } from '../Contract'; + +const TimelockContract = getContract('Timelock'); +const TimelockScenarioContract = getTestContract('TimelockHarness'); +const TimelockTestContract = getTestContract('TimelockTest'); + +export interface TimelockData { + invokation: Invokation; + contract: string; + description: string; + address?: string; + admin: string; + delay: string | number; +} + +export async function buildTimelock( + world: World, + from: string, + event: Event +): Promise<{ world: World; timelock: Timelock; timelockData: TimelockData }> { + const fetchers = [ + new Fetcher<{ admin: AddressV; delay: NumberV }, TimelockData>( + ` + #### Scenario + + * "Scenario admin:
delay:" - The Timelock Scenario for local testing + * E.g. "Timelock Deploy Scenario Geoff 604800" + `, + 'Scenario', + [new Arg('admin', getAddressV), new Arg('delay', getNumberV)], + async (world, { admin, delay }) => ({ + invokation: await TimelockScenarioContract.deploy(world, from, [admin.val, delay.val]), + contract: 'TimelockScenario', + description: 'Scenario Timelock', + admin: admin.val, + delay: delay.val + }) + ), + new Fetcher<{ admin: AddressV; delay: NumberV }, TimelockData>( + ` + #### Standard + + * "Standard admin:
delay:" - The standard Timelock contract + * E.g. "Timelock Deploy Standard Geoff 604800" + `, + 'Standard', + [new Arg('admin', getAddressV), new Arg('delay', getNumberV)], + async (world, { admin, delay }) => ({ + invokation: await TimelockContract.deploy(world, from, [admin.val, delay.val]), + contract: 'Timelock', + description: 'Standard Timelock', + admin: admin.val, + delay: delay.val + }) + ), + new Fetcher<{ admin: AddressV; delay: NumberV }, TimelockData>( + ` + #### Test + + * "Test admin:
delay:" - The a standard Timelock contract with a lower minimum delay for testing + * E.g. "Timelock Deploy Test Geoff 120" + `, + 'Test', + [new Arg('admin', getAddressV), new Arg('delay', getNumberV)], + async (world, { admin, delay }) => ({ + invokation: await TimelockTestContract.deploy(world, from, [admin.val, delay.val]), + contract: 'Timelock', + description: 'Test Timelock', + admin: admin.val, + delay: delay.val + }) + ), + new Fetcher<{ admin: AddressV; delay: NumberV }, TimelockData>( + ` + #### Default + + * "name:" - The standard Timelock contract + * E.g. "Timelock Deploy Geoff 604800" + `, + 'Default', + [new Arg('admin', getAddressV), new Arg('delay', getNumberV)], + async (world, { admin, delay }) => { + if (world.isLocalNetwork()) { + // Note: we're going to use the scenario contract as the standard deployment on local networks + return { + invokation: await TimelockScenarioContract.deploy(world, from, [admin.val, delay.val]), + contract: 'TimelockScenario', + description: 'Scenario Timelock', + admin: admin.val, + delay: delay.val + }; + } else { + return { + invokation: await TimelockContract.deploy(world, from, [admin.val, delay.val]), + contract: 'Timelock', + description: 'Standard Timelock', + admin: admin.val, + delay: delay.val + }; + } + }, + { catchall: true } + ) + ]; + + const timelockData = await getFetcherValue('DeployTimelock', fetchers, world, event); + const invokation = timelockData.invokation; + delete timelockData.invokation; + + if (invokation.error) { + throw invokation.error; + } + const timelock = invokation.value!; + timelockData.address = timelock._address; + + world = await storeAndSaveContract(world, timelock, 'Timelock', invokation, [ + { + index: ['Timelock'], + data: { + address: timelock._address, + contract: timelockData.contract, + description: timelockData.description + } + } + ]); + + return { world, timelock, timelockData }; +} diff --git a/scenario/src/Contract/Comptroller.ts b/scenario/src/Contract/Comptroller.ts index 8b650dcdd..a77ee36f4 100644 --- a/scenario/src/Contract/Comptroller.ts +++ b/scenario/src/Contract/Comptroller.ts @@ -33,6 +33,16 @@ interface ComptrollerMethods { pendingAdmin(): Callable _setPendingAdmin(string): Sendable _acceptAdmin(): Sendable + _setPauseGuardian(string): Sendable + pauseGuardian(): Callable + _setMintPaused(string): Sendable + _setBorrowPaused(string): Sendable + _setTransferPaused(string): Sendable + _setSeizePaused(string): Sendable + mintGuardianPaused(): Callable + borrowGuardianPaused(): Callable + transferGuardianPaused(): Callable + seizeGuardianPaused(): Callable } export interface Comptroller extends Contract { diff --git a/scenario/src/Contract/ComptrollerImpl.ts b/scenario/src/Contract/ComptrollerImpl.ts index 75570535e..728d2fc5d 100644 --- a/scenario/src/Contract/ComptrollerImpl.ts +++ b/scenario/src/Contract/ComptrollerImpl.ts @@ -1,11 +1,17 @@ -import {Contract} from '../Contract'; -import {Callable, Sendable} from '../Invokation'; -import {encodedNumber} from '../Encoding'; +import { Contract } from '../Contract'; +import { Callable, Sendable } from '../Invokation'; +import { encodedNumber } from '../Encoding'; interface ComptrollerImplMethods { - _become(comptroller: string, priceOracle: string, maxAssets: encodedNumber, closeFactor: encodedNumber, reinitializing: boolean): Sendable + _become( + comptroller: string, + priceOracle?: string, + maxAssets?: encodedNumber, + closeFactor?: encodedNumber, + reinitializing?: boolean + ): Sendable; } export interface ComptrollerImpl extends Contract { - methods: ComptrollerImplMethods + methods: ComptrollerImplMethods; } diff --git a/scenario/src/Contract/Timelock.ts b/scenario/src/Contract/Timelock.ts new file mode 100644 index 000000000..081fc050f --- /dev/null +++ b/scenario/src/Contract/Timelock.ts @@ -0,0 +1,42 @@ +import { Contract } from '../Contract'; +import { Callable, Sendable } from '../Invokation'; +import { encodedNumber } from '../Encoding'; + +interface TimelockMethods { + admin(): Callable; + pendingAdmin(): Callable; + delay(): Callable; + queuedTransactions(txHash: string): Callable; + setDelay(delay: encodedNumber): Sendable; + acceptAdmin(): Sendable; + setPendingAdmin(admin: string): Sendable; + queueTransaction( + target: string, + value: encodedNumber, + signature: string, + data: string, + eta: encodedNumber + ): Sendable; + cancelTransaction( + target: string, + value: encodedNumber, + signature: string, + data: string, + eta: encodedNumber + ): Sendable; + executeTransaction( + target: string, + value: encodedNumber, + signature: string, + data: string, + eta: encodedNumber + ): Sendable; + + blockTimestamp(): Callable; + harnessFastForward(seconds: encodedNumber): Sendable; + harnessSetBlockTimestamp(seconds: encodedNumber): Sendable; +} + +export interface Timelock extends Contract { + methods: TimelockMethods; +} diff --git a/scenario/src/Contract/Unitroller.ts b/scenario/src/Contract/Unitroller.ts index 89704d6d2..484f11b89 100644 --- a/scenario/src/Contract/Unitroller.ts +++ b/scenario/src/Contract/Unitroller.ts @@ -1,12 +1,16 @@ -import {Contract} from '../Contract'; -import {Callable, Sendable} from '../Invokation'; +import { Contract } from '../Contract'; +import { Callable, Sendable } from '../Invokation'; interface UnitrollerMethods { - admin(): Callable - _setPendingImplementation(string): Sendable - comptrollerImplementation(): Callable + admin(): Callable; + pendingAdmin(): Callable; + _acceptAdmin(): Sendable; + _setPendingAdmin(pendingAdmin: string): Sendable; + _setPendingImplementation(pendingImpl: string): Sendable; + comptrollerImplementation(): Callable; + pendingComptrollerImplementation(): Callable; } export interface Unitroller extends Contract { - methods: UnitrollerMethods + methods: UnitrollerMethods; } diff --git a/scenario/src/ContractLookup.ts b/scenario/src/ContractLookup.ts index 9682e5e11..8a006629c 100644 --- a/scenario/src/ContractLookup.ts +++ b/scenario/src/ContractLookup.ts @@ -1,18 +1,20 @@ -import {Event} from './Event'; -import {World} from './World'; -import {accountMap} from './Accounts'; -import {Contract} from './Contract'; -import {mustString} from './Utils'; - -import {Comptroller} from './Contract/Comptroller'; -import {ComptrollerImpl} from './Contract/ComptrollerImpl'; -import {CToken} from './Contract/CToken'; -import {Erc20} from './Contract/Erc20'; -import {InterestRateModel} from './Contract/InterestRateModel'; -import {PriceOracle} from './Contract/PriceOracle'; -import {Map} from 'immutable'; - -type ContractDataEl = string | Map | null; +import { Map } from 'immutable'; + +import { Event } from './Event'; +import { World } from './World'; +import { accountMap } from './Accounts'; +import { Contract } from './Contract'; +import { mustString } from './Utils'; + +import { Comptroller } from './Contract/Comptroller'; +import { ComptrollerImpl } from './Contract/ComptrollerImpl'; +import { CToken } from './Contract/CToken'; +import { Erc20 } from './Contract/Erc20'; +import { InterestRateModel } from './Contract/InterestRateModel'; +import { PriceOracle } from './Contract/PriceOracle'; +import { Timelock } from './Contract/Timelock'; + +type ContractDataEl = string | Map | undefined; function getContractData(world: World, indices: string[][]): ContractDataEl { return indices.reduce((value: ContractDataEl, index) => { @@ -23,24 +25,26 @@ function getContractData(world: World, indices: string[][]): ContractDataEl { let lowerEl = el.toLowerCase(); if (!data) { - return null; - } else if (typeof(data) === "string") { + return; + } else if (typeof data === 'string') { return data; } else { - return ( - Map(data).find( - (_v, key) => key.toLowerCase().trim() === lowerEl.trim())); + return (data as Map).find((_v, key) => key.toLowerCase().trim() === lowerEl.trim()); } }, world.contractData); } - }, null); + }, undefined); } function getContractDataString(world: World, indices: string[][]): string { const value: ContractDataEl = getContractData(world, indices); - if (!value || typeof(value) !== "string") { - throw new Error(`Failed to find string value by index (got ${value}): ${JSON.stringify(indices)}, index contains: ${JSON.stringify(world.contractData.toJSON())}`); + if (!value || typeof value !== 'string') { + throw new Error( + `Failed to find string value by index (got ${value}): ${JSON.stringify( + indices + )}, index contains: ${JSON.stringify(world.contractData.toJSON())}` + ); } return value; @@ -56,10 +60,18 @@ export function getWorldContractByAddress(world: World, address: string): T { const contract = world.contractIndex[address.toLowerCase()]; if (!contract) { - throw new Error(`Failed to find world contract by address: ${address}, index contains: ${JSON.stringify(Object.keys(world.contractIndex))}`); + throw new Error( + `Failed to find world contract by address: ${address}, index contains: ${JSON.stringify( + Object.keys(world.contractIndex) + )}` + ); } - return contract; + return (contract); +} + +export async function getTimelock(world: World): Promise { + return getWorldContract(world, [['Contracts', 'Timelock']]); } export async function getUnitroller(world: World): Promise { @@ -94,53 +106,70 @@ export async function getPriceOracle(world: World): Promise { return getWorldContract(world, [['Contracts', 'PriceOracle']]); } -export async function getInterestRateModel(world: World, interestRateModelArg: Event): Promise { +export async function getInterestRateModel( + world: World, + interestRateModelArg: Event +): Promise { return getWorldContract(world, [['InterestRateModel', mustString(interestRateModelArg), 'address']]); } -export async function getInterestRateModelData(world: World, interestRateModelArg: string): Promise<[InterestRateModel, string, Map]> { - let contract = await getInterestRateModel(world, interestRateModelArg); +export async function getInterestRateModelData( + world: World, + interestRateModelArg: string +): Promise<[InterestRateModel, string, Map]> { + let contract = await getInterestRateModel(world, (interestRateModelArg)); let data = getContractData(world, [['InterestRateModel', interestRateModelArg]]); - return [contract, interestRateModelArg, >data]; + return [contract, interestRateModelArg, >(data)]; } -export async function getErc20Data(world: World, erc20Arg: string): Promise<[Erc20, string, Map]> { +export async function getErc20Data( + world: World, + erc20Arg: string +): Promise<[Erc20, string, Map]> { let contract = getWorldContract(world, [['Tokens', erc20Arg, 'address']]); let data = getContractData(world, [['Tokens', erc20Arg]]); - return [contract, erc20Arg, >data]; + return [contract, erc20Arg, >(data)]; } -export async function getCTokenData(world: World, cTokenArg: string): Promise<[CToken, string, Map]> { +export async function getCTokenData( + world: World, + cTokenArg: string +): Promise<[CToken, string, Map]> { let contract = getWorldContract(world, [['cTokens', cTokenArg, 'address']]); let data = getContractData(world, [['CTokens', cTokenArg]]); - return [contract, cTokenArg, >data]; + return [contract, cTokenArg, >(data)]; } -export async function getComptrollerImplData(world: World, comptrollerImplArg: string): Promise<[ComptrollerImpl, string, Map]> { - let contract = await getComptrollerImpl(world, comptrollerImplArg); +export async function getComptrollerImplData( + world: World, + comptrollerImplArg: string +): Promise<[ComptrollerImpl, string, Map]> { + let contract = await getComptrollerImpl(world, (comptrollerImplArg)); let data = getContractData(world, [['Comptroller', comptrollerImplArg]]); - return [contract, comptrollerImplArg, >data]; + return [contract, comptrollerImplArg, >(data)]; } export function getAddress(world: World, addressArg: string): string { - if (addressArg.toLowerCase() === "zero") { - return "0x0000000000000000000000000000000000000000"; + if (addressArg.toLowerCase() === 'zero') { + return '0x0000000000000000000000000000000000000000'; } - if (addressArg.startsWith("0x")) { + if (addressArg.startsWith('0x')) { return addressArg; } - let alias = Object.entries(world.settings.aliases).find(([alias, addr]) => alias.toLowerCase() === addressArg.toLowerCase()); + let alias = Object.entries(world.settings.aliases).find( + ([alias, addr]) => alias.toLowerCase() === addressArg.toLowerCase() + ); if (alias) { return alias[1]; } - let account = world.accounts.find((account) => account.name.toLowerCase() === addressArg.toLowerCase()); + let account = world.accounts.find(account => account.name.toLowerCase() === addressArg.toLowerCase()); if (account) { return account.address; } diff --git a/scenario/src/CoreEvent.ts b/scenario/src/CoreEvent.ts index cc524130a..18114da88 100644 --- a/scenario/src/CoreEvent.ts +++ b/scenario/src/CoreEvent.ts @@ -8,46 +8,35 @@ import { setEvent, World } from './World'; -import {Event} from './Event'; -import { - getAddressV, - getEventV, - getNumberV, - getStringV -} from './CoreValue'; -import { - AddressV, - EventV, - NothingV, - NumberV, - StringV, - Value -} from './Value'; -import {Arg, Command, processCommandEvent, View} from './Command'; -import {assertionCommands, processAssertionEvent} from './Event/AssertionEvent'; -import {comptrollerCommands, processComptrollerEvent} from './Event/ComptrollerEvent'; -import {processUnitrollerEvent, unitrollerCommands} from './Event/UnitrollerEvent'; -import {comptrollerImplCommands, processComptrollerImplEvent} from './Event/ComptrollerImplEvent'; -import {cTokenCommands, processCTokenEvent} from './Event/CTokenEvent'; -import {erc20Commands, processErc20Event} from './Event/Erc20Event'; -import {interestRateModelCommands, processInterestRateModelEvent} from './Event/InterestRateModelEvent'; -import {priceOracleCommands, processPriceOracleEvent} from './Event/PriceOracleEvent'; -import {priceOracleProxyCommands, processPriceOracleProxyEvent} from './Event/PriceOracleProxyEvent'; -import {maximillionCommands, processMaximillionEvent} from './Event/MaximillionEvent'; -import {invariantCommands, processInvariantEvent} from './Event/InvariantEvent'; -import {expectationCommands, processExpectationEvent} from './Event/ExpectationEvent'; -import {processTrxEvent, trxCommands} from './Event/TrxEvent'; -import {fetchers, getCoreValue} from './CoreValue'; -import {formatEvent} from './Formatter'; -import {fallback} from './Invokation'; -import {sleep} from './Utils'; -import {Map} from 'immutable'; -import {encodedNumber} from './Encoding'; -import {printHelp} from './Help'; +import { Event } from './Event'; +import { getAddressV, getEventV, getNumberV, getStringV } from './CoreValue'; +import { AddressV, EventV, NothingV, NumberV, StringV, Value } from './Value'; +import { Arg, Command, processCommandEvent, View } from './Command'; +import { assertionCommands, processAssertionEvent } from './Event/AssertionEvent'; +import { comptrollerCommands, processComptrollerEvent } from './Event/ComptrollerEvent'; +import { processUnitrollerEvent, unitrollerCommands } from './Event/UnitrollerEvent'; +import { comptrollerImplCommands, processComptrollerImplEvent } from './Event/ComptrollerImplEvent'; +import { cTokenCommands, processCTokenEvent } from './Event/CTokenEvent'; +import { erc20Commands, processErc20Event } from './Event/Erc20Event'; +import { interestRateModelCommands, processInterestRateModelEvent } from './Event/InterestRateModelEvent'; +import { priceOracleCommands, processPriceOracleEvent } from './Event/PriceOracleEvent'; +import { priceOracleProxyCommands, processPriceOracleProxyEvent } from './Event/PriceOracleProxyEvent'; +import { maximillionCommands, processMaximillionEvent } from './Event/MaximillionEvent'; +import { invariantCommands, processInvariantEvent } from './Event/InvariantEvent'; +import { expectationCommands, processExpectationEvent } from './Event/ExpectationEvent'; +import { timelockCommands, processTimelockEvent } from './Event/TimelockEvent'; +import { processTrxEvent, trxCommands } from './Event/TrxEvent'; +import { fetchers, getCoreValue } from './CoreValue'; +import { formatEvent } from './Formatter'; +import { fallback } from './Invokation'; +import { sleep } from './Utils'; +import { Map } from 'immutable'; +import { encodedNumber } from './Encoding'; +import { printHelp } from './Help'; export class EventProcessingError extends Error { - error: Error - event: Event + error: Error; + event: Event; constructor(error: Error, event: Event) { super(error.message); @@ -69,7 +58,7 @@ export async function processEvents(originalWorld: World, events: Event[]): Prom console.error(err); } throw new EventProcessingError(err, event); - }; + } // Next, check any unchecked invariants world = await checkInvariants(world); @@ -84,7 +73,9 @@ export async function processEvents(originalWorld: World, events: Event[]): Prom if (!world) { throw new Error(`Encountered null world result when processing event ${event[0]}: ${world}`); } else if (!(world instanceof World)) { - throw new Error(`Encountered world result which was not isWorld when processing event ${event[0]}: ${world}`); + throw new Error( + `Encountered world result which was not isWorld when processing event ${event[0]}: ${world}` + ); } return world; @@ -99,9 +90,9 @@ async function print(world: World, message: string): Promise { async function inspect(world: World, string: string | null): Promise { if (string !== null) { - console.log(["Inspect", string, world.toJS()]); + console.log(['Inspect', string, world.toJS()]); } else { - console.log(["Inspect", world.toJS()]); + console.log(['Inspect', world.toJS()]); } return world; @@ -110,141 +101,130 @@ async function inspect(world: World, string: string | null): Promise { async function sendEther(world: World, from: string, to: string, amount: encodedNumber): Promise { let invokation = await fallback(world, from, to, amount); - world = addAction( - world, - `Send ${amount} from ${from} to ${to}`, - invokation - ); + world = addAction(world, `Send ${amount} from ${from} to ${to}`, invokation); return world; } export const commands = [ - new View<{n: NumberV}>(` + new View<{ n: NumberV }>( + ` #### History * "History n:=5" - Prints history of actions * E.g. "History" * E.g. "History 10" `, - "History", - [ - new Arg("n", getNumberV, {default: new NumberV(5)}) - ], - async (world, {n}) => { - world.actions.slice(0, Number(n.val)).forEach((action) => { + 'History', + [new Arg('n', getNumberV, { default: new NumberV(5) })], + async (world, { n }) => { + world.actions.slice(0, Number(n.val)).forEach(action => { world.printer.printLine(action.toString()); }); return world; } ), - new View<{ms: NumberV}>(` + new View<{ ms: NumberV }>( + ` #### Sleep * "Sleep ms:" - Sleeps for given amount of time. * E.g. "Sleep 1000" - Sleeps for one second `, - "Sleep", - [ - new Arg("ms", getNumberV) - ], - async (world, {ms}) => { + 'Sleep', + [new Arg('ms', getNumberV)], + async (world, { ms }) => { await sleep(ms.toNumber()); return world; } ), - new View<{errMsg: StringV}>(` + new View<{ errMsg: StringV }>( + ` #### Throw * "Throw errMsg:" - Throws given error * E.g. "Throw \"my error message\"" `, - "Throw", - [ - new Arg("errMsg", getStringV) - ], - async (world, {errMsg}) => { + 'Throw', + [new Arg('errMsg', getStringV)], + async (world, { errMsg }) => { throw new Error(errMsg.val); return world; } ), - new View<{res: Value}>(` + new View<{ res: Value }>( + ` #### Read * "Read ..." - Reads given value and prints result * E.g. "Read CToken cBAT ExchangeRateStored" - Returns exchange rate of cBAT `, - "Read", - [ - new Arg("res", getCoreValue, {variadic: true}) - ], - async (world, {res}) => { + 'Read', + [new Arg('res', getCoreValue, { variadic: true })], + async (world, { res }) => { world.printer.printValue(res); return world; }, { subExpressions: fetchers } ), - new View<{message: StringV}>(` + new View<{ message: StringV }>( + ` #### Print * "Print ..." - Prints given string * E.g. "Print \"Hello there\"" `, - "Print", - [ - new Arg("message", getStringV) - ], - async (world, {message}) => print(world, message.val) + 'Print', + [new Arg('message', getStringV)], + async (world, { message }) => print(world, message.val) ), - new View<{address: AddressV}>(` + new View<{ address: AddressV }>( + ` #### MyAddress * "MyAddress address:" - Sets default from address (same as "Alias Me ") * E.g. "MyAddress \"0x9C1856636d78C051deAd6CAB9c5699e4E25549e9\"" `, - "MyAddress", - [ - new Arg("address", getAddressV) - ], - async (world, {address}) => { - return await world.updateSettings(async (settings) => { - settings.aliases["Me"] = address.val; + 'MyAddress', + [new Arg('address', getAddressV)], + async (world, { address }) => { + return await world.updateSettings(async settings => { + settings.aliases['Me'] = address.val; return settings; }); } ), - new View<{name: StringV, address: AddressV}>(` + new View<{ name: StringV; address: AddressV }>( + ` #### Alias * "Alias name: address:" - Stores an alias between name and address * E.g. "Alias Me \"0x9C1856636d78C051deAd6CAB9c5699e4E25549e9\"" `, - "Alias", - [ - new Arg("name", getStringV), - new Arg("address", getAddressV) - ], - async (world, {name, address}) => { - return await world.updateSettings(async (settings) => { + 'Alias', + [new Arg('name', getStringV), new Arg('address', getAddressV)], + async (world, { name, address }) => { + return await world.updateSettings(async settings => { settings.aliases[name.val] = address.val; return settings; }); } ), - new View<{name: StringV, address: AddressV}>(` + new View<{ name: StringV; address: AddressV }>( + ` #### Aliases * "Aliases - Prints all aliases `, - "Aliases", + 'Aliases', [], - async (world, {name, address}) => { - world.printer.printLine("Aliases:"); + async (world, { name, address }) => { + world.printer.printLine('Aliases:'); Object.entries(world.settings.aliases).forEach(([name, address]) => { world.printer.printLine(`\t${name}: ${address}`); }); @@ -252,79 +232,75 @@ export const commands = [ return world; } ), - new View<{}>(` + new View<{}>( + ` #### Inspect * "Inspect" - Prints debugging information about the world `, - "Inspect", + 'Inspect', [], async (world, {}) => inspect(world, null) ), - new View<{message: StringV}>(` + new View<{ message: StringV }>( + ` #### Debug * "Debug message:" - Same as inspect but prepends with a string `, - "Debug", - [ - new Arg("message", getStringV) - ], - async (world, {message}) => inspect(world, message.val) + 'Debug', + [new Arg('message', getStringV)], + async (world, { message }) => inspect(world, message.val) ), - new View<{account: AddressV, event: EventV}>(` + new View<{ account: AddressV; event: EventV }>( + ` #### From * "From " - Runs event as the given user * E.g. "From Geoff (CToken cZRX Mint 5e18)" `, - "From", - [ - new Arg("account", getAddressV), - new Arg("event", getEventV) - ], - async (world, {account, event}) => processCoreEvent(world, event.val, account.val) + 'From', + [new Arg('account', getAddressV), new Arg('event', getEventV)], + async (world, { account, event }) => processCoreEvent(world, event.val, account.val) ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Trx * "Trx ...trxEvent" - Handles event to set details of next transaction * E.g. "Trx Value 1.0e18 (CToken cEth Mint 1.0e18)" `, - "Trx", - [ - new Arg("event", getEventV, {variadic: true}) - ], - async (world, from, {event}) => processTrxEvent(world, event.val, from), + 'Trx', + [new Arg('event', getEventV, { variadic: true })], + async (world, from, { event }) => processTrxEvent(world, event.val, from), { subExpressions: trxCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Invariant * "Invariant ...invariant" - Adds a new invariant to the world which is checked after each transaction * E.g. "Invariant Static (CToken cZRX TotalSupply)" `, - "Invariant", - [ - new Arg("event", getEventV, {variadic: true}) - ], - async (world, from, {event}) => processInvariantEvent(world, event.val, from), + 'Invariant', + [new Arg('event', getEventV, { variadic: true })], + async (world, from, { event }) => processInvariantEvent(world, event.val, from), { subExpressions: invariantCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Expect * "Expect ...expectation" - Adds an expectation to hold after the next transaction * E.g. "Expect Changes (CToken cZRX TotalSupply) +10.0e18" `, - "Expect", - [ - new Arg("event", getEventV, {variadic: true}) - ], - async (world, from, {event}) => processExpectationEvent(world, event.val, from), + 'Expect', + [new Arg('event', getEventV, { variadic: true })], + async (world, from, { event }) => processExpectationEvent(world, event.val, from), { subExpressions: expectationCommands() } ), - new View<{type: StringV}>(` + new View<{ type: StringV }>( + ` #### HoldInvariants * "HoldInvariants type:" - Skips checking invariants on next command. @@ -334,13 +310,12 @@ export const commands = [ * E.g. "HoldInvariants Remains" - Skips "remains" invariants * E.g. "HoldInvariants Static" - Skips "static" invariants `, - "HoldInvariants", - [ - new Arg("type", getStringV, {default: new StringV("All")}) - ], - async (world, {type}) => holdInvariants(world, type.val) + 'HoldInvariants', + [new Arg('type', getStringV, { default: new StringV('All') })], + async (world, { type }) => holdInvariants(world, type.val) ), - new View<{type: StringV}>(` + new View<{ type: StringV }>( + ` #### ClearInvariants * "ClearInvariants type:" - Removes all invariants. @@ -350,37 +325,32 @@ export const commands = [ * E.g. "ClearInvariants Remains" - Removes "remains" invariants * E.g. "ClearInvariants Static" - Removes "static" invariants `, - "ClearInvariants", - [ - new Arg("type", getStringV, {default: new StringV("All")}) - ], - async (world, {type}) => clearInvariants(world, type.val) + 'ClearInvariants', + [new Arg('type', getStringV, { default: new StringV('All') })], + async (world, { type }) => clearInvariants(world, type.val) ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Assert * "Assert ...event" - Validates given assertion, raising an exception if assertion fails * E.g. "Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 5.0)" `, - "Assert", - [ - new Arg("event", getEventV, {variadic: true}) - ], - async (world, from, {event}) => processAssertionEvent(world, event.val, from), + 'Assert', + [new Arg('event', getEventV, { variadic: true })], + async (world, from, { event }) => processAssertionEvent(world, event.val, from), { subExpressions: assertionCommands() } ), - new Command<{gate: Value, event: EventV}>(` + new Command<{ gate: Value; event: EventV }>( + ` #### Gate * "Gate value event" - Runs event only if value is falsey. Thus, gate can be used to build idempotency. * E.g. "Gate (Erc20 ZRX Address) (Erc20 Deploy BAT)" `, - "Gate", - [ - new Arg("gate", getCoreValue, {rescue: new NothingV()}), - new Arg("event", getEventV) - ], - async (world, from, {gate, event}) => { + 'Gate', + [new Arg('gate', getCoreValue, { rescue: new NothingV() }), new Arg('event', getEventV)], + async (world, from, { gate, event }) => { if (gate.truthy()) { return world; } else { @@ -388,18 +358,16 @@ export const commands = [ } } ), - new Command<{given: Value, event: EventV}>(` + new Command<{ given: Value; event: EventV }>( + ` #### Given * "Given value event" - Runs event only if value is truthy. Thus, given can be used to build existence checks. * E.g. "Given ($var) (PriceOracle SetPrice cBAT $var)" `, - "Given", - [ - new Arg("given", getCoreValue, {rescue: new NothingV()}), - new Arg("event", getEventV) - ], - async (world, from, {given, event}) => { + 'Given', + [new Arg('given', getCoreValue, { rescue: new NothingV() }), new Arg('event', getEventV)], + async (world, from, { given, event }) => { if (given.truthy()) { return processCoreEvent(world, event.val, from); } else { @@ -407,152 +375,154 @@ export const commands = [ } } ), - new Command<{address: AddressV, amount: NumberV}>(` + new Command<{ address: AddressV; amount: NumberV }>( + ` #### Send * "Send
" - Sends a given amount of eth to given address * E.g. "Send cETH 0.5e18" `, - "Send", - [ - new Arg("address", getAddressV), - new Arg("amount", getNumberV) - ], - (world, from, {address, amount}) => sendEther(world, from, address.val, amount.encode()) + 'Send', + [new Arg('address', getAddressV), new Arg('amount', getNumberV)], + (world, from, { address, amount }) => sendEther(world, from, address.val, amount.encode()) ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Unitroller * "Unitroller ...event" - Runs given Unitroller event * E.g. "Unitroller SetPendingImpl MyComptrollerImpl" `, - "Unitroller", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => processUnitrollerEvent(world, event.val, from), + 'Unitroller', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => processUnitrollerEvent(world, event.val, from), { subExpressions: unitrollerCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Comptroller * "Comptroller ...event" - Runs given Comptroller event * E.g. "Comptroller _setReserveFactor 0.5" `, - "Comptroller", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => processComptrollerEvent(world, event.val, from), + 'Comptroller', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => processComptrollerEvent(world, event.val, from), { subExpressions: comptrollerCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### ComptrollerImpl * "ComptrollerImpl ...event" - Runs given ComptrollerImpl event * E.g. "ComptrollerImpl MyImpl Become" `, - "ComptrollerImpl", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => processComptrollerImplEvent(world, event.val, from), + 'ComptrollerImpl', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => processComptrollerImplEvent(world, event.val, from), { subExpressions: comptrollerImplCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### CToken * "CToken ...event" - Runs given CToken event * E.g. "CToken cZRX Mint 5e18" `, - "CToken", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => processCTokenEvent(world, event.val, from), + 'CToken', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => processCTokenEvent(world, event.val, from), { subExpressions: cTokenCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Erc20 * "Erc20 ...event" - Runs given Erc20 event * E.g. "Erc20 ZRX Facuet Geoff 5e18" `, - "Erc20", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => processErc20Event(world, event.val, from), + 'Erc20', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => processErc20Event(world, event.val, from), { subExpressions: erc20Commands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### InterestRateModel * "InterestRateModel ...event" - Runs given interest rate model event * E.g. "InterestRateModel Deploy Fixed StdRate 0.5" `, - "InterestRateModel", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => processInterestRateModelEvent(world, event.val, from), + 'InterestRateModel', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => processInterestRateModelEvent(world, event.val, from), { subExpressions: interestRateModelCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### PriceOracle * "PriceOracle ...event" - Runs given Price Oracle event * E.g. "PriceOracle SetPrice cZRX 1.5" `, - "PriceOracle", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => processPriceOracleEvent(world, event.val, from), + 'PriceOracle', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => processPriceOracleEvent(world, event.val, from), { subExpressions: priceOracleCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### PriceOracleProxy * "PriceOracleProxy ...event" - Runs given Price Oracle event * E.g. "PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (CToken cETH Address)" `, - "PriceOracleProxy", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => { - return processPriceOracleProxyEvent(world, event.val, from) + 'PriceOracleProxy', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => { + return processPriceOracleProxyEvent(world, event.val, from); }, { subExpressions: priceOracleProxyCommands() } ), - new Command<{event: EventV}>(` + new Command<{ event: EventV }>( + ` #### Maximillion * "Maximillion ...event" - Runs given Maximillion event * E.g. "Maximillion Deploy (CToken cETH Address)" `, - "Maximillion", - [ - new Arg("event", getEventV, {variadic: true}) - ], - (world, from, {event}) => { - return processMaximillionEvent(world, event.val, from) + 'Maximillion', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => { + return processMaximillionEvent(world, event.val, from); }, { subExpressions: maximillionCommands() } ), - new View<{event: EventV}>(` + new Command<{ event: EventV }>( + ` + #### Timelock + + * "Timelock ...event" - Runs given Timelock event + * E.g. "Timelock Deploy Geoff 604800" + `, + 'Timelock', + [new Arg('event', getEventV, { variadic: true })], + (world, from, { event }) => { + return processTimelockEvent(world, event.val, from); + }, + { subExpressions: timelockCommands() } + ), + new View<{ event: EventV }>( + ` #### Help * "Help ...event" - Prints help for given command * E.g. "Help From" `, - "Help", - [ - new Arg("event", getEventV, {variadic: true}) - ], - async (world, {event}) => { - world.printer.printLine(""); + 'Help', + [new Arg('event', getEventV, { variadic: true })], + async (world, { event }) => { + world.printer.printLine(''); printHelp(world.printer, event.val, commands); return world; @@ -561,5 +531,5 @@ export const commands = [ ]; export async function processCoreEvent(world: World, event: Event, from: string | null): Promise { - return await processCommandEvent("Core", commands, world, event, from); + return await processCommandEvent('Core', commands, world, event, from); } diff --git a/scenario/src/CoreValue.ts b/scenario/src/CoreValue.ts index 825fff3d4..6047d52d4 100644 --- a/scenario/src/CoreValue.ts +++ b/scenario/src/CoreValue.ts @@ -1,5 +1,5 @@ -import {Event} from './Event'; -import {World} from './World'; +import { Event } from './Event'; +import { World } from './World'; import { AddressV, AnythingV, @@ -15,21 +15,22 @@ import { StringV, Value } from './Value'; -import {Arg, Fetcher, getFetcherValue} from './Command'; -import {getUserValue, userFetchers} from './Value/UserValue'; -import {comptrollerFetchers, getComptrollerValue} from './Value/ComptrollerValue'; -import {comptrollerImplFetchers, getComptrollerImplValue} from './Value/ComptrollerImplValue'; -import {getUnitrollerValue, unitrollerFetchers} from './Value/UnitrollerValue'; -import {cTokenFetchers, getCTokenValue} from './Value/CTokenValue'; -import {erc20Fetchers, getErc20Value} from './Value/Erc20Value'; -import {getInterestRateModelValue, interestRateModelFetchers} from './Value/InterestRateModelValue'; -import {getPriceOracleValue, priceOracleFetchers} from './Value/PriceOracleValue'; -import {getPriceOracleProxyValue, priceOracleProxyFetchers} from './Value/PriceOracleProxyValue'; -import {getMaximillionValue, maximillionFetchers} from './Value/MaximillionValue'; -import {getAddress} from './ContractLookup'; -import {mustArray} from './Utils'; -import {toEncodableNum} from './Encoding'; -import {BigNumber} from 'bignumber.js'; +import { Arg, Fetcher, getFetcherValue } from './Command'; +import { getUserValue, userFetchers } from './Value/UserValue'; +import { comptrollerFetchers, getComptrollerValue } from './Value/ComptrollerValue'; +import { comptrollerImplFetchers, getComptrollerImplValue } from './Value/ComptrollerImplValue'; +import { getUnitrollerValue, unitrollerFetchers } from './Value/UnitrollerValue'; +import { cTokenFetchers, getCTokenValue } from './Value/CTokenValue'; +import { erc20Fetchers, getErc20Value } from './Value/Erc20Value'; +import { getInterestRateModelValue, interestRateModelFetchers } from './Value/InterestRateModelValue'; +import { getPriceOracleValue, priceOracleFetchers } from './Value/PriceOracleValue'; +import { getPriceOracleProxyValue, priceOracleProxyFetchers } from './Value/PriceOracleProxyValue'; +import { getTimelockValue, timelockFetchers, getTimelockAddress } from './Value/TimelockValue'; +import { getMaximillionValue, maximillionFetchers } from './Value/MaximillionValue'; +import { getAddress } from './ContractLookup'; +import { mustArray } from './Utils'; +import { toEncodableNum } from './Encoding'; +import { BigNumber } from 'bignumber.js'; const expMantissa = new BigNumber('1000000000000000000'); @@ -49,11 +50,17 @@ export async function getEventV(world: World, event: Event): Promise { // TODO: We may want to handle simple values -> complex values at the parser level // This is currently trying to parse simple values as simple or complex values, // and this is because items like `Some` could work either way. -export async function mapValue(world: World, event: Event, simple: (string) => T, complex: (World, Event) => Promise, type: any): Promise { +export async function mapValue( + world: World, + event: Event, + simple: (string) => T, + complex: (World, Event) => Promise, + type: any +): Promise { let simpleErr; let val; - if (typeof(event) === "string") { + if (typeof event === 'string') { try { return simple(event); } catch (err) { @@ -78,14 +85,14 @@ export async function mapValue(world: World, event: Event, simple: (string) = } // We just did a typecheck above... - return val; + return (val); } export async function getBoolV(world: World, event: Event): Promise { return mapValue( world, event, - (str) => { + str => { const lower = str.trim().toLowerCase(); if (lower == 'true' || lower == 't' || lower == '1') { @@ -103,7 +110,7 @@ export async function getAddressV(world: World, event: Event): Promise return mapValue( world, event, - (str) => new AddressV(getAddress(world, str)), + str => new AddressV(getAddress(world, str)), async (currWorld, val) => { const coreVal = await getCoreValue(currWorld, val); @@ -119,7 +126,7 @@ export async function getAddressV(world: World, event: Event): Promise function strToNumberV(str: string): NumberV { if (isNaN(Number(str))) { - throw "not a number"; + throw 'not a number'; } return new NumberV(str); @@ -132,23 +139,11 @@ function strToExpNumberV(str: string): NumberV { } export async function getNumberV(world: World, event: Event): Promise { - return mapValue( - world, - event, - strToNumberV, - getCoreValue, - NumberV - ); + return mapValue(world, event, strToNumberV, getCoreValue, NumberV); } export async function getExpNumberV(world: World, event: Event): Promise { - let res = await mapValue( - world, - event, - strToNumberV, - getCoreValue, - NumberV - ); + let res = await mapValue(world, event, strToNumberV, getCoreValue, NumberV); const r = new BigNumber(res.val); @@ -165,33 +160,29 @@ export async function getPercentV(world: World, event: Event): Promise export async function getMapV(world: World, event: Event): Promise { const res: object = {}; - await Promise.all(mustArray(event).map(async (e) => { - if (Array.isArray(e) && e.length === 2 && typeof e[0] === "string") { - const [key, valueEvent] = e; - let value; - if (typeof valueEvent === "string") { - value = new StringV(valueEvent); + await Promise.all( + mustArray(event).map(async e => { + if (Array.isArray(e) && e.length === 2 && typeof e[0] === 'string') { + const [key, valueEvent] = e; + let value; + if (typeof valueEvent === 'string') { + value = new StringV(valueEvent); + } else { + value = await getCoreValue(world, valueEvent); + } + + res[key] = value; } else { - value = await getCoreValue(world, valueEvent); + throw new Error(`Expected all string pairs for MapV from ${event.toString()}, got: ${e.toString()}`); } - - res[key] = value; - } else { - throw new Error(`Expected all string pairs for MapV from ${event.toString()}, got: ${e.toString()}`); - } - })); + }) + ); return new MapV(res); } export async function getStringV(world: World, event: Event): Promise { - return mapValue( - world, - event, - (str) => new StringV(str), - getCoreValue, - StringV - ); + return mapValue(world, event, str => new StringV(str), getCoreValue, StringV); } async function getEtherBalance(world: World, address: string): Promise { @@ -201,156 +192,341 @@ async function getEtherBalance(world: World, address: string): Promise } export const fetchers = [ - new Fetcher<{}, BoolV>(` + new Fetcher<{}, BoolV>( + ` #### True * "True" - Returns true `, - "True", + 'True', [], async (world, {}) => new BoolV(true) ), - new Fetcher<{}, BoolV>(` + new Fetcher<{}, BoolV>( + ` #### False * "False" - Returns false `, - "False", + 'False', [], async (world, {}) => new BoolV(false) ), - new Fetcher<{}, NumberV>(` + new Fetcher<{}, NumberV>( + ` #### Zero * "Zero" - Returns 0 `, - "Zero", + 'Zero', [], - async (world, {}) => strToNumberV("0") + async (world, {}) => strToNumberV('0') ), - new Fetcher<{}, NumberV>(` + new Fetcher<{}, NumberV>( + ` #### Max * "Max" - Returns 2^256 - 1 `, - "Max", + 'Max', [], - async (world, {}) => new NumberV("115792089237316195423570985008687907853269984665640564039457584007913129639935") + async (world, {}) => + new NumberV('115792089237316195423570985008687907853269984665640564039457584007913129639935') ), - new Fetcher<{}, NumberV>(` + new Fetcher<{}, NumberV>( + ` #### Some * "Some" - Returns 100e18 `, - "Some", + 'Some', [], - async (world, {}) => strToNumberV("100e18") + async (world, {}) => strToNumberV('100e18') ), - new Fetcher<{}, NumberV>(` + new Fetcher<{}, NumberV>( + ` #### Little * "Little" - Returns 100e10 `, - "Little", + 'Little', [], - async (world, {}) => strToNumberV("100e10") + async (world, {}) => strToNumberV('100e10') ), - new Fetcher<{amt: EventV}, NumberV>(` + new Fetcher<{ amt: EventV }, NumberV>( + ` #### Exactly * "Exactly " - Returns a strict numerical value * E.g. "Exactly 5.0" `, - "Exactly", - [new Arg("amt", getEventV)], - async (world, {amt}) => getNumberV(world, amt.val) + 'Exactly', + [new Arg('amt', getEventV)], + async (world, { amt }) => getNumberV(world, amt.val) ), - new Fetcher<{hexVal: EventV}, StringV>(` + new Fetcher<{ hexVal: EventV }, StringV>( + ` #### Hex * "Hex " - Returns a byte string with given hex value * E.g. "Hex \"0xffff\"" `, - "Hex", - [new Arg("hexVal", getEventV)], - async (world, {hexVal}) => getStringV(world, hexVal.val) + 'Hex', + [new Arg('hexVal', getEventV)], + async (world, { hexVal }) => getStringV(world, hexVal.val) ), - new Fetcher<{str: EventV}, StringV>(` + new Fetcher<{ str: EventV }, StringV>( + ` #### String * "String " - Returns a string literal * E.g. "String MyString" `, - "String", - [new Arg("str", getEventV)], - async (world, {str}) => getStringV(world, str.val) + 'String', + [new Arg('str', getEventV)], + async (world, { str }) => getStringV(world, str.val) ), - new Fetcher<{amt: EventV}, NumberV>(` + new Fetcher<{ amt: EventV }, NumberV>( + ` #### Exp * "Exp " - Returns the mantissa for a given exp * E.g. "Exp 5.5" `, - "Exp", - [new Arg("amt", getEventV)], - async (world, {amt}) => getExpNumberV(world, amt.val) + 'Exp', + [new Arg('amt', getEventV)], + async (world, { amt }) => getExpNumberV(world, amt.val) ), - new Fetcher<{amt: StringV}, PreciseV>(` + new Fetcher<{ amt: StringV }, PreciseV>( + ` #### Precisely * "Precisely " - Matches a number to given number of significant figures * E.g. "Exactly 5.1000" - Matches to 5 sig figs `, - "Precisely", - [ - new Arg("amt", getStringV) - ], - async (world, {amt}) => new PreciseV(toEncodableNum(amt.val), getSigFigs(amt.val)) + 'Precisely', + [new Arg('amt', getStringV)], + async (world, { amt }) => new PreciseV(toEncodableNum(amt.val), getSigFigs(amt.val)) ), - new Fetcher<{}, AnythingV>(` + new Fetcher<{}, AnythingV>( + ` #### Anything * "Anything" - Matches any value for assertions `, - "Anything", + 'Anything', [], async (world, {}) => new AnythingV() ), - new Fetcher<{}, NothingV>(` + new Fetcher<{}, NothingV>( + ` #### Nothing * "Nothing" - Matches no values and is nothing. `, - "Nothing", + 'Nothing', [], async (world, {}) => new NothingV() ), - new Fetcher<{addr: AddressV}, AddressV>(` + new Fetcher<{ addr: AddressV }, AddressV>( + ` #### Address * "Address arg:
" - Returns an address `, - "Address", + 'Address', + [new Arg('addr', getAddressV)], + async (world, { addr }) => addr + ), + new Fetcher< + { addr: AddressV; slot: NumberV; start: NumberV; valType: StringV }, + BoolV | AddressV | ExpNumberV | undefined + >( + ` + #### StorageAt + + * "StorageAt addr:
slot: start:, valType:" - Returns bytes at storage slot + `, + 'StorageAt', [ - new Arg("addr", getAddressV) + new Arg('addr', getAddressV), + new Arg('slot', getNumberV), + new Arg('start', getNumberV), + new Arg('valType', getStringV) ], - async (world, {addr}) => addr + async (world, { addr, slot, start, valType }) => { + let stored = await world.web3.eth.getStorageAt(addr.val, slot.val); + const startVal = start.val; + let reverse = s => + s + .split('') + .reverse() + .join(''); + let val; + stored = stored + .slice(2) //drop leading 0x and reverse since items are packed from the back of the slot + .split('') + .reverse() + .join(''); + + //dont forget to re-reverse + switch (valType.val) { + case 'bool': + val = '0x' + reverse(stored.slice(startVal, (startVal as number) + 2)); + return new BoolV(val != '0x' && val != '0x0'); + case 'address': + val = '0x' + reverse(stored.slice(startVal, (startVal as number) + 40)); + return new AddressV(val); + case 'number': + let parsed = world.web3.utils.toBN('0x' + reverse(stored)); + + // if the numbers are big, they are big... + if (parsed.gt(world.web3.utils.toBN(1000))) { + return new ExpNumberV(parsed, 1e18); + } else { + return new ExpNumberV(parsed, 1); + } + } + } ), - new Fetcher<{}, AddressV>(` + + new Fetcher< + { addr: AddressV; slot: NumberV; key: AddressV; nestedKey: AddressV; valType: StringV }, + ListV | undefined + >( + ` + #### StorageAtNestedMapping + + * "StorageAtNestedMapping addr:
slot:, key:
, nestedKey:
, valType:" - Returns bytes at storage slot + `, + 'StorageAtNestedMapping', + [ + new Arg('addr', getAddressV), + new Arg('slot', getNumberV), + new Arg('key', getAddressV), + new Arg('nestedKey', getAddressV), + new Arg('valType', getStringV) + ], + async (world, { addr, slot, key, nestedKey, valType }) => { + let paddedSlot = world.web3.utils.padLeft(slot.val, 64); + let paddedKey = world.web3.utils.padLeft(key.val, 64); + let newKey = world.web3.utils.sha3(paddedKey + paddedSlot, { encoding: 'hex' }); + + let val = await world.web3.eth.getStorageAt(addr.val, newKey); + + switch (valType.val) { + case 'marketStruct': + let isListed = val == '0x01'; + let collateralFactorKey = + '0x' + + world.web3.utils + .toBN(newKey) + .add(world.web3.utils.toBN(1)) + .toString(16); + + let collateralFactor = await world.web3.eth.getStorageAt(addr.val, collateralFactorKey); + collateralFactor = world.web3.utils.toBN(collateralFactor); + + let userMarketBaseKey = world.web3.utils + .toBN(newKey) + .add(world.web3.utils.toBN(2)) + .toString(16); + userMarketBaseKey = world.web3.utils.padLeft(userMarketBaseKey, 64); + + let paddedSlot = world.web3.utils.padLeft(userMarketBaseKey, 64); + let paddedKey = world.web3.utils.padLeft(nestedKey.val, 64); + let newKeyToo = world.web3.utils.sha3(paddedKey + paddedSlot, { encoding: 'hex' }); + + let userInMarket = await world.web3.eth.getStorageAt(addr.val, newKeyToo); + + return new ListV([ + new BoolV(isListed), + new ExpNumberV(collateralFactor, 1e18), + new BoolV(userInMarket == '0x01') + ]); + } + } + ), + + new Fetcher< + { addr: AddressV; slot: NumberV; key: AddressV; valType: StringV }, + AddressV | BoolV | ExpNumberV | ListV | undefined + >( + ` + #### StorageAtMapping + + * "StorageAtMapping addr:
slot:, key:
, valType:" - Returns bytes at storage slot + `, + 'StorageAtMapping', + [ + new Arg('addr', getAddressV), + new Arg('slot', getNumberV), + new Arg('key', getAddressV), + new Arg('valType', getStringV) + ], + async (world, { addr, slot, key, valType }) => { + let paddedSlot = world.web3.utils.padLeft(slot.val, 64); + let paddedKey = world.web3.utils.padLeft(key.val, 64); + let newKey = world.web3.utils.sha3(paddedKey + paddedSlot, { encoding: 'hex' }); + + let val = await world.web3.eth.getStorageAt(addr.val, newKey); + + switch (valType.val) { + case 'list(address)': + let num = world.web3.utils.toBN(val); + + let p = new Array(num, 'n').map(async (_v, index) => { + let itemKey; + itemKey = world.web3.utils.sha3(newKey, { encoding: 'hex' }); + itemKey = + '0x' + + world.web3.utils + .toBN(itemKey) + .add(world.web3.utils.toBN(index)) + .toString(16); + + let x = await world.web3.eth.getStorageAt(addr.val, itemKey); + + return new AddressV(x); + }); + + let all = await Promise.all(p); + return new ListV(all); + + case 'bool': + return new BoolV(val != '0x' && val != '0x0'); + case 'address': + return new AddressV(val); + case 'number': + let parsed = world.web3.utils.toBN(val); + + // if the numbers are big, they are big... + if (parsed.gt(world.web3.utils.toBN(1000))) { + return new ExpNumberV(parsed, 1e18); + } else { + return new ExpNumberV(parsed, 1); + } + } + } + ), + + new Fetcher<{}, AddressV>( + ` #### LastContract * "LastContract" - The address of last constructed contract `, - "LastContract", + 'LastContract', [], async (world, {}) => new AddressV(world.get('lastContract')) ), - new Fetcher<{}, NumberV>(` + new Fetcher<{}, NumberV>( + ` #### LastGas * "LastGas" - The gas consumed by the last transaction `, - "LastGas", + 'LastGas', [], async (world, {}) => { let invokation = world.get('lastInvokation'); @@ -365,28 +541,25 @@ export const fetchers = [ return new NumberV(invokation.receipt.gasUsed); } ), - new Fetcher<{els: Value[]}, AnythingV>(` + new Fetcher<{ els: Value[] }, AnythingV>( + ` #### List * "List ..." - Returns a list of given elements `, - "List", - [ - new Arg("els", getCoreValue, {variadic: true, mapped: true}) - ], - async (world, {els}) => new ListV(els) + 'List', + [new Arg('els', getCoreValue, { variadic: true, mapped: true })], + async (world, { els }) => new ListV(els) ), - new Fetcher<{val: Value, def: EventV}, Value>(` + new Fetcher<{ val: Value; def: EventV }, Value>( + ` #### Default * "Default val: def:" - Returns value if truthy, otherwise default. Note: this **does** short circuit. `, - "Default", - [ - new Arg("val", getCoreValue), - new Arg("def", getEventV) - ], - async (world, {val, def}) => { + 'Default', + [new Arg('val', getCoreValue), new Arg('def', getEventV)], + async (world, { val, def }) => { if (val.truthy()) { return val; } else { @@ -394,30 +567,118 @@ export const fetchers = [ } } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ minutes: NumberV }, NumberV>( + ` + #### Minutes + + * "Minutes minutes:" - Returns number of minutes in seconds + `, + 'Minutes', + [new Arg('minutes', getNumberV)], + async (world, { minutes }) => { + const minutesBn = new BigNumber(minutes.val); + return new NumberV(minutesBn.times(60).toFixed(0)); + } + ), + new Fetcher<{ hours: NumberV }, NumberV>( + ` + #### Hours + + * "Hours hours:" - Returns number of hours in seconds + `, + 'Hours', + [new Arg('hours', getNumberV)], + async (world, { hours }) => { + const hoursBn = new BigNumber(hours.val); + return new NumberV(hoursBn.times(3600).toFixed(0)); + } + ), + new Fetcher<{ days: NumberV }, NumberV>( + ` + #### Days + + * "Days days:" - Returns number of days in seconds + `, + 'Days', + [new Arg('days', getNumberV)], + async (world, { days }) => { + const daysBn = new BigNumber(days.val); + return new NumberV(daysBn.times(86400).toFixed(0)); + } + ), + new Fetcher<{ weeks: NumberV }, NumberV>( + ` + #### Weeks + + * "Weeks weeks:" - Returns number of weeks in seconds + `, + 'Weeks', + [new Arg('weeks', getNumberV)], + async (world, { weeks }) => { + const weeksBn = new BigNumber(weeks.val); + return new NumberV(weeksBn.times(604800).toFixed(0)); + } + ), + new Fetcher<{ years: NumberV }, NumberV>( + ` + #### Years + + * "Years years:" - Returns number of years in seconds + `, + 'Years', + [new Arg('years', getNumberV)], + async (world, { years }) => { + const yearsBn = new BigNumber(years.val); + return new NumberV(yearsBn.times(31536000).toFixed(0)); + } + ), + new Fetcher<{ seconds: NumberV }, NumberV>( + ` + #### FromNow + + * "FromNow seconds:" - Returns future timestamp of given seconds from now + `, + 'FromNow', + [new Arg('seconds', getNumberV)], + async (world, { seconds }) => { + const secondsBn = new BigNumber(seconds.val); + const now = Math.floor(Date.now() / 1000); + return new NumberV(secondsBn.plus(now).toFixed(0)); + } + ), + new Fetcher<{}, StringV>( + ` + #### Network + + * "Network" - Returns the current Network + `, + 'Network', + [], + async world => new StringV(world.network) + ), + new Fetcher<{ res: Value }, Value>( + ` #### User * "User ...userArgs" - Returns user value `, - "User", - [ - new Arg("res", getUserValue, {variadic: true}) - ], - async (world, {res}) => res, + 'User', + [new Arg('res', getUserValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: userFetchers() } ), - new Fetcher<{address: AddressV}, Value>(` + new Fetcher<{ address: AddressV }, Value>( + ` #### EtherBalance * "EtherBalance
" - Returns given address' ether balance. `, - "EtherBalance", - [ - new Arg("address", getAddressV) - ], - (world, {address}) => getEtherBalance(world, address.val) + 'EtherBalance', + [new Arg('address', getAddressV)], + (world, { address }) => getEtherBalance(world, address.val) ), - new Fetcher<{given: Value, expected: Value}, BoolV>(` + new Fetcher<{ given: Value; expected: Value }, BoolV>( + ` #### Equal * "Equal given: expected:" - Returns true if given values are equal @@ -425,123 +686,122 @@ export const fetchers = [ * E.g. "Equal (CToken cZRX TotalSupply) (Exactly 55)" * E.g. "Equal (CToken cZRX Comptroller) (Comptroller Address)" `, - "Equal", - [ - new Arg("given", getCoreValue), - new Arg("expected", getCoreValue) - ], - async (world, {given, expected}) => new BoolV(expected.compareTo(world, given)) + 'Equal', + [new Arg('given', getCoreValue), new Arg('expected', getCoreValue)], + async (world, { given, expected }) => new BoolV(expected.compareTo(world, given)) ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### Unitroller * "Unitroller ...unitrollerArgs" - Returns unitroller value `, - "Unitroller", - [ - new Arg("res", getUnitrollerValue, {variadic: true}) - ], - async (world, {res}) => res, + 'Unitroller', + [new Arg('res', getUnitrollerValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: unitrollerFetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### Comptroller * "Comptroller ...comptrollerArgs" - Returns comptroller value `, - "Comptroller", - [ - new Arg("res", getComptrollerValue, {variadic: true}) - ], - async (world, {res}) => res, + 'Comptroller', + [new Arg('res', getComptrollerValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: comptrollerFetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### ComptrollerImpl * "ComptrollerImpl ...comptrollerImplArgs" - Returns comptroller implementation value `, - "ComptrollerImpl", - [ - new Arg("res", getComptrollerImplValue, {variadic: true}) - ], - async (world, {res}) => res, + 'ComptrollerImpl', + [new Arg('res', getComptrollerImplValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: comptrollerImplFetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### CToken * "CToken ...cTokenArgs" - Returns cToken value `, - "CToken", - [ - new Arg("res", getCTokenValue, {variadic: true}) - ], - async (world, {res}) => res, + 'CToken', + [new Arg('res', getCTokenValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: cTokenFetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### Erc20 * "Erc20 ...erc20Args" - Returns Erc20 value `, - "Erc20", - [ - new Arg("res", getErc20Value, {variadic: true}) - ], - async (world, {res}) => res, + 'Erc20', + [new Arg('res', getErc20Value, { variadic: true })], + async (world, { res }) => res, { subExpressions: erc20Fetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### InterestRateModel * "InterestRateModel ...interestRateModelArgs" - Returns InterestRateModel value `, - "InterestRateModel", - [ - new Arg("res", getInterestRateModelValue, {variadic: true}) - ], - async (world, {res}) => res, + 'InterestRateModel', + [new Arg('res', getInterestRateModelValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: interestRateModelFetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### PriceOracle * "PriceOracle ...priceOracleArgs" - Returns PriceOracle value `, - "PriceOracle", - [ - new Arg("res", getPriceOracleValue, {variadic: true}) - ], - async (world, {res}) => res, + 'PriceOracle', + [new Arg('res', getPriceOracleValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: priceOracleFetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` #### PriceOracleProxy * "PriceOracleProxy ...priceOracleProxyArgs" - Returns PriceOracleProxy value `, - "PriceOracleProxy", - [ - new Arg("res", getPriceOracleProxyValue, {variadic: true}) - ], - async (world, {res}) => res, + 'PriceOracleProxy', + [new Arg('res', getPriceOracleProxyValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: priceOracleProxyFetchers() } ), - new Fetcher<{res: Value}, Value>(` + new Fetcher<{ res: Value }, Value>( + ` + #### Timelock + + * "Timelock ...timeLockArgs" - Returns Timelock value + `, + 'Timelock', + [new Arg('res', getTimelockValue, { variadic: true })], + async (world, { res }) => res, + { subExpressions: timelockFetchers() } + ), + new Fetcher<{ res: Value }, Value>( + ` #### Maximillion * "Maximillion ...maximillionArgs" - Returns Maximillion value `, - "Maximillion", - [ - new Arg("res", getMaximillionValue, {variadic: true}) - ], - async (world, {res}) => res, + 'Maximillion', + [new Arg('res', getMaximillionValue, { variadic: true })], + async (world, { res }) => res, { subExpressions: maximillionFetchers() } ) ]; export async function getCoreValue(world: World, event: Event): Promise { - return await getFetcherValue("Core", fetchers, world, event); + return await getFetcherValue('Core', fetchers, world, event); } diff --git a/scenario/src/Event/ComptrollerEvent.ts b/scenario/src/Event/ComptrollerEvent.ts index 438bc7be6..56a25006c 100644 --- a/scenario/src/Event/ComptrollerEvent.ts +++ b/scenario/src/Event/ComptrollerEvent.ts @@ -232,6 +232,45 @@ async function acceptAdmin(world: World, from: string, comptroller: Comptroller) return world; } +async function setPauseGuardian(world: World, from: string, comptroller: Comptroller, newPauseGuardian: string): Promise { + let invokation = await invoke(world, comptroller.methods._setPauseGuardian(newPauseGuardian), from, ComptrollerErrorReporter); + + world = addAction( + world, + `Comptroller: ${describeUser(world, from)} sets pause guardian to ${newPauseGuardian}`, + invokation + ); + + return world; +} + +async function setGuardianPaused(world: World, from: string, comptroller: Comptroller, action: string, state: boolean): Promise { + let fun; + switch(action){ + case "Mint": + fun = comptroller.methods._setMintPaused + break; + case "Borrow": + fun = comptroller.methods._setBorrowPaused + break; + case "Transfer": + fun = comptroller.methods._setTransferPaused + break; + case "Seize": + fun = comptroller.methods._setSeizePaused + break; + } + let invokation = await invoke(world, fun(state), from, ComptrollerErrorReporter); + + world = addAction( + world, + `Comptroller: ${describeUser(world, from)} sets ${action} paused`, + invokation + ); + + return world; +} + export function comptrollerCommands() { return [ new Command<{comptrollerParams: EventV}>(` @@ -401,6 +440,33 @@ export function comptrollerCommands() { ], (world, from, {comptroller}) => acceptAdmin(world, from, comptroller) ), + new Command<{comptroller: Comptroller, newPauseGuardian: AddressV}>(` + #### SetPauseGuardian + + * "Comptroller SetPauseGuardian newPauseGuardian:
" - Sets the PauseGuardian for the Comptroller + * E.g. "Comptroller SetPauseGuardian Geoff" + `, + "SetPauseGuardian", + [ + new Arg("comptroller", getComptroller, {implicit: true}), + new Arg("newPauseGuardian", getAddressV) + ], + (world, from, {comptroller, newPauseGuardian}) => setPauseGuardian(world, from, comptroller, newPauseGuardian.val) + ), + new Command<{comptroller: Comptroller, action: StringV, isPaused: BoolV}>(` + #### SetGuardianPaused + + * "Comptroller SetGuardianPaused " - Pauses or unpaused given cToken function + * E.g. "Comptroller SetGuardianPaused "Mint" True" + `, + "SetGuardianPaused", + [ + new Arg("comptroller", getComptroller, {implicit: true}), + new Arg("action", getStringV), + new Arg("isPaused", getBoolV) + ], + (world, from, {comptroller, action, isPaused}) => setGuardianPaused(world, from, comptroller, action.val, isPaused.val) + ), new Command<{comptroller: Comptroller, blocks: NumberV, _keyword: StringV}>(` #### FastForward diff --git a/scenario/src/Event/ComptrollerImplEvent.ts b/scenario/src/Event/ComptrollerImplEvent.ts index c7222fd24..a0fc2d6b2 100644 --- a/scenario/src/Event/ComptrollerImplEvent.ts +++ b/scenario/src/Event/ComptrollerImplEvent.ts @@ -1,31 +1,24 @@ -import {Event} from '../Event'; -import {addAction, describeUser, World} from '../World'; -import {ComptrollerImpl} from '../Contract/ComptrollerImpl'; -import {Unitroller} from '../Contract/Unitroller'; -import {invoke} from '../Invokation'; -import { - getAddressV, - getEventV, - getExpNumberV, - getNumberV, - getStringV -} from '../CoreValue'; -import { - AddressV, - EventV, - NumberV, - StringV -} from '../Value'; -import {Arg, Command, View, processCommandEvent} from '../Command'; -import {buildComptrollerImpl} from '../Builder/ComptrollerImplBuilder'; -import {ComptrollerErrorReporter} from '../ErrorReporter'; -import {getComptrollerImpl, getComptrollerImplData, getUnitroller} from '../ContractLookup'; -import {verify} from '../Verify'; -import {mergeContractABI} from '../Networks'; -import {encodedNumber} from '../Encoding'; +import { Event } from '../Event'; +import { addAction, describeUser, World } from '../World'; +import { ComptrollerImpl } from '../Contract/ComptrollerImpl'; +import { Unitroller } from '../Contract/Unitroller'; +import { invoke } from '../Invokation'; +import { getAddressV, getEventV, getExpNumberV, getNumberV, getStringV } from '../CoreValue'; +import { AddressV, EventV, NumberV, StringV } from '../Value'; +import { Arg, Command, View, processCommandEvent } from '../Command'; +import { buildComptrollerImpl } from '../Builder/ComptrollerImplBuilder'; +import { ComptrollerErrorReporter } from '../ErrorReporter'; +import { getComptrollerImpl, getComptrollerImplData, getUnitroller } from '../ContractLookup'; +import { verify } from '../Verify'; +import { mergeContractABI } from '../Networks'; +import { encodedNumber } from '../Encoding'; async function genComptrollerImpl(world: World, from: string, params: Event): Promise { - let {world: nextWorld, comptrollerImpl, comptrollerImplData} = await buildComptrollerImpl(world, from, params); + let { world: nextWorld, comptrollerImpl, comptrollerImplData } = await buildComptrollerImpl( + world, + from, + params + ); world = nextWorld; world = addAction( @@ -35,10 +28,60 @@ async function genComptrollerImpl(world: World, from: string, params: Event): Pr ); return world; -}; +} -async function become(world: World, from: string, comptrollerImpl: ComptrollerImpl, unitroller: Unitroller, priceOracleAddr: string, closeFactor: encodedNumber, maxAssets: encodedNumber): Promise { - let invokation = await invoke(world, comptrollerImpl.methods._become(unitroller._address, priceOracleAddr, closeFactor, maxAssets, false), from, ComptrollerErrorReporter); +async function become( + world: World, + from: string, + comptrollerImpl: ComptrollerImpl, + unitroller: Unitroller +): Promise { + let invokation = await invoke( + world, + comptrollerImpl.methods._become(unitroller._address), + from, + ComptrollerErrorReporter + ); + + if (!world.dryRun) { + // Skip this specifically on dry runs since it's likely to crash due to a number of reasons + world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); + } + + world = addAction(world, `Become ${unitroller._address}'s Comptroller Impl`, invokation); + + return world; +} + +async function mergeABI( + world: World, + from: string, + comptrollerImpl: ComptrollerImpl, + unitroller: Unitroller +): Promise { + if (!world.dryRun) { + // Skip this specifically on dry runs since it's likely to crash due to a number of reasons + world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); + } + + return world; +} + +async function becomeG1( + world: World, + from: string, + comptrollerImpl: ComptrollerImpl, + unitroller: Unitroller, + priceOracleAddr: string, + closeFactor: encodedNumber, + maxAssets: encodedNumber +): Promise { + let invokation = await invoke( + world, + comptrollerImpl.methods._become(unitroller._address, priceOracleAddr, closeFactor, maxAssets, false), + from, + ComptrollerErrorReporter + ); if (!world.dryRun) { // Skip this specifically on dry runs since it's likely to crash due to a number of reasons @@ -53,22 +96,40 @@ async function become(world: World, from: string, comptrollerImpl: ComptrollerIm return world; } - -async function recome(world: World, from: string, comptrollerImpl: ComptrollerImpl, unitroller: Unitroller): Promise { - let invokation = await invoke(world, comptrollerImpl.methods._become(unitroller._address, "0x0000000000000000000000000000000000000000", 0, 0, true), from, ComptrollerErrorReporter); +// Recome calls `become` on the G1 Comptroller, but passes a flag to not modify any of the initialization variables. +async function recome( + world: World, + from: string, + comptrollerImpl: ComptrollerImpl, + unitroller: Unitroller +): Promise { + let invokation = await invoke( + world, + comptrollerImpl.methods._become( + unitroller._address, + '0x0000000000000000000000000000000000000000', + 0, + 0, + true + ), + from, + ComptrollerErrorReporter + ); world = await mergeContractABI(world, 'Comptroller', unitroller, unitroller.name, comptrollerImpl.name); - world = addAction( - world, - `Recome ${unitroller._address}'s Comptroller Impl`, - invokation - ); + world = addAction(world, `Recome ${unitroller._address}'s Comptroller Impl`, invokation); return world; } -async function verifyComptrollerImpl(world: World, comptrollerImpl: ComptrollerImpl, name: string, contract: string, apiKey: string): Promise { +async function verifyComptrollerImpl( + world: World, + comptrollerImpl: ComptrollerImpl, + name: string, + contract: string, + apiKey: string +): Promise { if (world.isLocalNetwork()) { world.printer.printLine(`Politely declining to verify on local network: ${world.network}.`); } else { @@ -80,68 +141,124 @@ async function verifyComptrollerImpl(world: World, comptrollerImpl: ComptrollerI export function comptrollerImplCommands() { return [ - new Command<{comptrollerImplParams: EventV}>(` + new Command<{ comptrollerImplParams: EventV }>( + ` #### Deploy * "ComptrollerImpl Deploy ...comptrollerImplParams" - Generates a new Comptroller Implementation * E.g. "ComptrollerImpl Deploy MyScen Scenario" `, - "Deploy", - [new Arg("comptrollerImplParams", getEventV, {variadic: true})], - (world, from, {comptrollerImplParams}) => genComptrollerImpl(world, from, comptrollerImplParams.val) + 'Deploy', + [new Arg('comptrollerImplParams', getEventV, { variadic: true })], + (world, from, { comptrollerImplParams }) => genComptrollerImpl(world, from, comptrollerImplParams.val) ), - new View<{comptrollerImplArg: StringV, apiKey: StringV}>(` + new View<{ comptrollerImplArg: StringV; apiKey: StringV }>( + ` #### Verify * "ComptrollerImpl Verify apiKey:" - Verifies Comptroller Implemetation in Etherscan * E.g. "ComptrollerImpl Verify "myApiKey" `, - "Verify", - [ - new Arg("comptrollerImplArg", getStringV), - new Arg("apiKey", getStringV) - ], - async (world, {comptrollerImplArg, apiKey}) => { + 'Verify', + [new Arg('comptrollerImplArg', getStringV), new Arg('apiKey', getStringV)], + async (world, { comptrollerImplArg, apiKey }) => { let [comptrollerImpl, name, data] = await getComptrollerImplData(world, comptrollerImplArg.val); return await verifyComptrollerImpl(world, comptrollerImpl, name, data.get('contract')!, apiKey.val); }, - {namePos: 1} + { namePos: 1 } + ), + new Command<{ + unitroller: Unitroller; + comptrollerImpl: ComptrollerImpl; + priceOracle: AddressV; + closeFactor: NumberV; + maxAssets: NumberV; + }>( + ` + #### BecomeG1 + + * "ComptrollerImpl BecomeG1 priceOracle: closeFactor: maxAssets:" - Become the comptroller, if possible. + * E.g. "ComptrollerImpl MyImpl BecomeG1 + `, + 'BecomeG1', + [ + new Arg('unitroller', getUnitroller, { implicit: true }), + new Arg('comptrollerImpl', getComptrollerImpl), + new Arg('priceOracle', getAddressV), + new Arg('closeFactor', getExpNumberV), + new Arg('maxAssets', getNumberV) + ], + (world, from, { unitroller, comptrollerImpl, priceOracle, closeFactor, maxAssets }) => + becomeG1( + world, + from, + comptrollerImpl, + unitroller, + priceOracle.val, + closeFactor.encode(), + maxAssets.encode() + ), + { namePos: 1 } ), - new Command<{unitroller: Unitroller, comptrollerImpl: ComptrollerImpl, priceOracle: AddressV, closeFactor: NumberV, maxAssets: NumberV}>(` + new Command<{ + unitroller: Unitroller; + comptrollerImpl: ComptrollerImpl; + }>( + ` #### Become - * "ComptrollerImpl Become priceOracle: closeFactor: maxAssets:" - Become the comptroller, if possible. + * "ComptrollerImpl Become" - Become the comptroller, if possible. * E.g. "ComptrollerImpl MyImpl Become `, - "Become", + 'Become', + [ + new Arg('unitroller', getUnitroller, { implicit: true }), + new Arg('comptrollerImpl', getComptrollerImpl) + ], + (world, from, { unitroller, comptrollerImpl }) => become(world, from, comptrollerImpl, unitroller), + { namePos: 1 } + ), + new Command<{ + unitroller: Unitroller; + comptrollerImpl: ComptrollerImpl; + }>( + ` + #### MergeABI + + * "ComptrollerImpl MergeABI" - Merges the ABI, as if it was a become. + * E.g. "ComptrollerImpl MyImpl MergeABI + `, + 'MergeABI', [ - new Arg("unitroller", getUnitroller, {implicit: true}), - new Arg("comptrollerImpl", getComptrollerImpl), - new Arg("priceOracle", getAddressV), - new Arg("closeFactor", getExpNumberV), - new Arg("maxAssets", getNumberV) + new Arg('unitroller', getUnitroller, { implicit: true }), + new Arg('comptrollerImpl', getComptrollerImpl) ], - (world, from, {unitroller, comptrollerImpl, priceOracle, closeFactor, maxAssets}) => become(world, from, comptrollerImpl, unitroller, priceOracle.val, closeFactor.encode(), maxAssets.encode()), - {namePos: 1} + (world, from, { unitroller, comptrollerImpl }) => mergeABI(world, from, comptrollerImpl, unitroller), + { namePos: 1 } ), - new Command<{unitroller: Unitroller, comptrollerImpl: ComptrollerImpl}>(` + new Command<{ unitroller: Unitroller; comptrollerImpl: ComptrollerImpl }>( + ` #### Recome * "ComptrollerImpl Recome" - Recome the comptroller * E.g. "ComptrollerImpl MyImpl Recome `, - "Recome", + 'Recome', [ - new Arg("unitroller", getUnitroller, {implicit: true}), - new Arg("comptrollerImpl", getComptrollerImpl) + new Arg('unitroller', getUnitroller, { implicit: true }), + new Arg('comptrollerImpl', getComptrollerImpl) ], - (world, from, {unitroller, comptrollerImpl}) => recome(world, from, comptrollerImpl, unitroller), - {namePos: 1} + (world, from, { unitroller, comptrollerImpl }) => recome(world, from, comptrollerImpl, unitroller), + { namePos: 1 } ) ]; } -export async function processComptrollerImplEvent(world: World, event: Event, from: string | null): Promise { - return await processCommandEvent("ComptrollerImpl", comptrollerImplCommands(), world, event, from); +export async function processComptrollerImplEvent( + world: World, + event: Event, + from: string | null +): Promise { + return await processCommandEvent('ComptrollerImpl', comptrollerImplCommands(), world, event, from); } diff --git a/scenario/src/Event/TimelockEvent.ts b/scenario/src/Event/TimelockEvent.ts new file mode 100644 index 000000000..1ca5b02f7 --- /dev/null +++ b/scenario/src/Event/TimelockEvent.ts @@ -0,0 +1,341 @@ +import { Event } from '../Event'; +import { addAction, World } from '../World'; +import { Timelock } from '../Contract/Timelock'; +import { buildTimelock, TimelockData } from '../Builder/TimelockBuilder'; +import { invoke } from '../Invokation'; +import { getAddressV, getEventV, getNumberV, getStringV, getCoreValue } from '../CoreValue'; +import { AddressV, EventV, NumberV, StringV } from '../Value'; +import { Arg, Command, processCommandEvent, View } from '../Command'; +import { getTimelock } from '../ContractLookup'; +import { verify } from '../Verify'; +import { decodeParameters, encodeParameters } from '../Utils'; + +async function genTimelock(world: World, from: string, params: Event): Promise { + let { world: nextWorld, timelock, timelockData } = await buildTimelock(world, from, params); + world = nextWorld; + + world = addAction(world, `Deployed Timelock to address ${timelock._address}`, timelockData.invokation); + + return world; +} + +async function acceptAdmin(world: World, from: string, timeLock: Timelock): Promise { + return addAction( + world, + `Set Timelock admin to ${from}`, + await invoke(world, timeLock.methods.acceptAdmin(), from) + ); +} + +async function setPendingAdmin( + world: World, + from: string, + timeLock: Timelock, + admin: string +): Promise { + return addAction( + world, + `Set Timelock admin to ${admin}`, + await invoke(world, timeLock.methods.setPendingAdmin(admin), from) + ); +} + +async function setDelay(world: World, from: string, timeLock: Timelock, delay: NumberV): Promise { + return addAction( + world, + `Set Timelock delay to ${delay.show()}`, + await invoke(world, timeLock.methods.setDelay(delay.encode()), from) + ); +} + +async function harnessFastForward( + world: World, + from: string, + timeLock: Timelock, + seconds: NumberV +): Promise { + return addAction( + world, + `Set Timelock blockTimestamp forward by ${seconds.show()}`, + await invoke(world, timeLock.methods.harnessFastForward(seconds.encode()), from) + ); +} + +async function harnessSetBlockTimestamp( + world: World, + from: string, + timeLock: Timelock, + seconds: NumberV +): Promise { + return addAction( + world, + `Set Timelock blockTimestamp to ${seconds.show()}`, + await invoke(world, timeLock.methods.harnessSetBlockTimestamp(seconds.encode()), from) + ); +} + +async function queueTransaction( + world: World, + from: string, + timeLock: Timelock, + target: string, + value: NumberV, + signature: string, + data: string, + eta: NumberV +): Promise { + const dataArgs = decodeParameters(world, signature, data); + const etaString = eta.show(); + const dateFromEta = new Date(Number(etaString) * 1000); + + return addAction( + world, + `Queue transaction on Timelock with target: ${target}\nvalue: ${value.show()}\nsignature: ${signature}\ndata: ${data} (args: ${dataArgs.join( + ', ' + )})\neta: ${etaString} (${dateFromEta.toString()})`, + await invoke( + world, + timeLock.methods.queueTransaction(target, value.encode(), signature, data, eta.encode()), + from + ) + ); +} + +async function cancelTransaction( + world: World, + from: string, + timeLock: Timelock, + target: string, + value: NumberV, + signature: string, + data: string, + eta: NumberV +): Promise { + return addAction( + world, + `Cancel transaction on Timelock with target: ${target} value: ${value.show()} signature: ${signature} data: ${data} eta: ${eta.show()}`, + await invoke( + world, + timeLock.methods.cancelTransaction(target, value.encode(), signature, data, eta.encode()), + from + ) + ); +} + +async function executeTransaction( + world: World, + from: string, + timeLock: Timelock, + target: string, + value: NumberV, + signature: string, + data: string, + eta: NumberV +): Promise { + const dataArgs = decodeParameters(world, signature, data); + const etaString = eta.show(); + const dateFromEta = new Date(Number(etaString) * 1000); + + return addAction( + world, + `Execute transaction on Timelock with target: ${target}\nvalue: ${value.show()}\nsignature: ${signature}\ndata: ${data} (args: ${dataArgs.join( + ', ' + )})\neta: ${etaString} (${dateFromEta.toString()})`, + await invoke( + world, + timeLock.methods.executeTransaction(target, value.encode(), signature, data, eta.encode()), + from + ) + ); +} + +async function verifyTimelock( + world: World, + timelock: Timelock, + apiKey: string, + contractName: string +): Promise { + if (world.isLocalNetwork()) { + world.printer.printLine(`Politely declining to verify on local network: ${world.network}.`); + } else { + await verify(world, apiKey, 'Timelock', contractName, timelock._address); + } + + return world; +} + +export function timelockCommands() { + return [ + new Command<{ params: EventV }>( + ` + #### Deploy + + * "Deploy ...params" - Generates a new price oracle proxy + * E.g. "Timelock Deploy Geoff 604800" + `, + 'Deploy', + [new Arg('params', getEventV, { variadic: true })], + (world, from, { params }) => genTimelock(world, from, params.val) + ), + new Command<{ timelock: Timelock; seconds: NumberV }>( + ` + #### FastForward + + * "FastForward " - Sets the blockTimestamp of the TimelockHarness forward + * E.g. "Timelock FastForward 604800" + `, + 'FastForward', + [new Arg('timelock', getTimelock, { implicit: true }), new Arg('seconds', getNumberV)], + (world, from, { timelock, seconds }) => harnessFastForward(world, from, timelock, seconds) + ), + new Command<{ timelock: Timelock; seconds: NumberV }>( + ` + #### SetBlockTimestamp + + * "SetBlockTimestamp " - Sets the blockTimestamp of the TimelockHarness + * E.g. "Timelock SetBlockTimestamp 1569973599" + `, + 'SetBlockTimestamp', + [new Arg('timelock', getTimelock, { implicit: true }), new Arg('seconds', getNumberV)], + (world, from, { timelock, seconds }) => harnessSetBlockTimestamp(world, from, timelock, seconds) + ), + new Command<{ timelock: Timelock; delay: NumberV }>( + ` + #### SetDelay + + * "SetDelay " - Sets the delay for the Timelock + * E.g. "Timelock SetDelay 604800" + `, + 'SetDelay', + [new Arg('timelock', getTimelock, { implicit: true }), new Arg('delay', getNumberV)], + (world, from, { timelock, delay }) => setDelay(world, from, timelock, delay) + ), + new Command<{ timelock: Timelock }>( + ` + #### AcceptAdmin + + * "AcceptAdmin" - Accept the admin for the Timelock + * E.g. "Timelock AcceptAdmin" + `, + 'AcceptAdmin', + [new Arg('timelock', getTimelock, { implicit: true })], + (world, from, { timelock }) => acceptAdmin(world, from, timelock) + ), + new Command<{ timelock: Timelock; admin: AddressV }>( + ` + #### SetPendingAdmin + + * "SetPendingAdmin
" - Sets the pending admin for the Timelock + * E.g. "Timelock SetPendingAdmin \"0x0000000000000000000000000000000000000000\"" + `, + 'SetPendingAdmin', + [new Arg('timelock', getTimelock, { implicit: true }), new Arg('admin', getAddressV)], + (world, from, { timelock, admin }) => setPendingAdmin(world, from, timelock, admin.val) + ), + new Command<{ + timelock: Timelock; + target: AddressV; + value: NumberV; + eta: NumberV; + signature: StringV; + data: StringV[]; + }>( + ` + #### QueueTransaction + + * "QueueTransaction target:
value: eta: signature: ...funArgs:" - Queues a transaction for the Timelock + * E.g. "Timelock QueueTransaction \"0x0000000000000000000000000000000000000000\" 0 1569286014 \"setDelay(uint256)\" 60680" + * + `, + 'QueueTransaction', + [ + new Arg('timelock', getTimelock, { implicit: true }), + new Arg('target', getAddressV), + new Arg('value', getNumberV), + new Arg('eta', getNumberV), + new Arg('signature', getStringV), + new Arg('data', getCoreValue, { variadic: true, mapped: true }) + ], + (world, from, { timelock, target, value, signature, data, eta }) => { + const encodedData = encodeParameters(world, signature.val, data.map(a => a.val)); + return queueTransaction(world, from, timelock, target.val, value, signature.val, encodedData, eta); + } + ), + new Command<{ + timelock: Timelock; + target: AddressV; + value: NumberV; + eta: NumberV; + signature: StringV; + data: StringV[]; + }>( + ` + #### CancelTransaction + + * "CancelTransaction target:
value: eta: signature: ...funArgs:" - Cancels a transaction from the Timelock + * E.g. "Timelock CancelTransaction \"0x0000000000000000000000000000000000000000\" 0 1569286014 \"setDelay(uint256)\" 60680" + `, + 'CancelTransaction', + [ + new Arg('timelock', getTimelock, { implicit: true }), + new Arg('target', getAddressV), + new Arg('value', getNumberV), + new Arg('eta', getNumberV), + new Arg('signature', getStringV), + new Arg('data', getCoreValue, { variadic: true, mapped: true }) + ], + (world, from, { timelock, target, value, signature, data, eta }) => { + const encodedData = encodeParameters(world, signature.val, data.map(a => a.val)); + return cancelTransaction(world, from, timelock, target.val, value, signature.val, encodedData, eta); + } + ), + new Command<{ + timelock: Timelock; + target: AddressV; + value: NumberV; + eta: NumberV; + signature: StringV; + data: StringV[]; + }>( + ` + #### ExecuteTransaction + + * "ExecuteTransaction target:
value: eta: signature: ...funArgs:" - Executes a transaction from the Timelock + * E.g. "Timelock ExecuteTransaction \"0x0000000000000000000000000000000000000000\" 0 1569286014 \"setDelay(uint256)\" 60680" + `, + 'ExecuteTransaction', + [ + new Arg('timelock', getTimelock, { implicit: true }), + new Arg('target', getAddressV), + new Arg('value', getNumberV), + new Arg('eta', getNumberV), + new Arg('signature', getStringV), + new Arg('data', getCoreValue, { variadic: true, mapped: true }) + ], + (world, from, { timelock, target, value, signature, data, eta }) => { + const encodedData = encodeParameters(world, signature.val, data.map(a => a.val)); + return executeTransaction(world, from, timelock, target.val, value, signature.val, encodedData, eta); + } + ), + new View<{ timelock: Timelock; apiKey: StringV; contractName: StringV }>( + ` + #### Verify + + * "Verify apiKey: contractName:=Timelock" - Verifies Timelock in Etherscan + * E.g. "Timelock Verify "myApiKey" + `, + 'Verify', + [ + new Arg('timelock', getTimelock, { implicit: true }), + new Arg('apiKey', getStringV), + new Arg('contractName', getStringV, { default: new StringV('Timelock') }) + ], + (world, { timelock, apiKey, contractName }) => + verifyTimelock(world, timelock, apiKey.val, contractName.val) + ) + ]; +} + +export async function processTimelockEvent(world: World, event: Event, from: string | null): Promise { + return await processCommandEvent('Timelock', timelockCommands(), world, event, from); +} diff --git a/scenario/src/Event/UnitrollerEvent.ts b/scenario/src/Event/UnitrollerEvent.ts index ed963832f..6adc46133 100644 --- a/scenario/src/Event/UnitrollerEvent.ts +++ b/scenario/src/Event/UnitrollerEvent.ts @@ -1,24 +1,18 @@ -import {Event} from '../Event'; -import {addAction, describeUser, World} from '../World'; -import {Unitroller} from '../Contract/Unitroller'; -import {ComptrollerImpl} from '../Contract/ComptrollerImpl'; -import {invoke} from '../Invokation'; -import { - getEventV, - getStringV -} from '../CoreValue'; -import { - EventV, - StringV -} from '../Value'; -import {Arg, Command, View, processCommandEvent} from '../Command'; -import {ComptrollerErrorReporter} from '../ErrorReporter'; -import {buildUnitroller} from '../Builder/UnitrollerBuilder'; -import {getComptrollerImpl, getUnitroller} from '../ContractLookup'; -import {verify} from '../Verify'; +import { Event } from '../Event'; +import { addAction, describeUser, World } from '../World'; +import { Unitroller } from '../Contract/Unitroller'; +import { ComptrollerImpl } from '../Contract/ComptrollerImpl'; +import { invoke } from '../Invokation'; +import { getEventV, getStringV, getAddressV } from '../CoreValue'; +import { EventV, StringV, AddressV } from '../Value'; +import { Arg, Command, View, processCommandEvent } from '../Command'; +import { ComptrollerErrorReporter } from '../ErrorReporter'; +import { buildUnitroller } from '../Builder/UnitrollerBuilder'; +import { getComptrollerImpl, getUnitroller } from '../ContractLookup'; +import { verify } from '../Verify'; async function genUnitroller(world: World, from: string, params: Event): Promise { - let {world: nextWorld, unitroller, unitrollerData} = await buildUnitroller(world, from, params); + let { world: nextWorld, unitroller, unitrollerData } = await buildUnitroller(world, from, params); world = nextWorld; world = addAction( @@ -34,65 +28,125 @@ async function verifyUnitroller(world: World, unitroller: Unitroller, apiKey: st if (world.isLocalNetwork()) { world.printer.printLine(`Politely declining to verify on local network: ${world.network}.`); } else { - await verify(world, apiKey, "Unitroller", "Unitroller", unitroller._address); + await verify(world, apiKey, 'Unitroller', 'Unitroller', unitroller._address); } return world; } -async function setPendingImpl(world: World, from: string, unitroller: Unitroller, comptrollerImpl: ComptrollerImpl): Promise { - let invokation = await invoke(world, unitroller.methods._setPendingImplementation(comptrollerImpl._address), from, ComptrollerErrorReporter); +async function acceptAdmin(world: World, from: string, unitroller: Unitroller): Promise { + let invokation = await invoke(world, unitroller.methods._acceptAdmin(), from, ComptrollerErrorReporter); - world = addAction( + world = addAction(world, `Accept admin as ${from}`, invokation); + + return world; +} + +async function setPendingAdmin( + world: World, + from: string, + unitroller: Unitroller, + pendingAdmin: string +): Promise { + let invokation = await invoke( world, - `Set pending comptroller impl to ${comptrollerImpl.name}`, - invokation + unitroller.methods._setPendingAdmin(pendingAdmin), + from, + ComptrollerErrorReporter ); + world = addAction(world, `Set pending admin to ${pendingAdmin}`, invokation); + + return world; +} + +async function setPendingImpl( + world: World, + from: string, + unitroller: Unitroller, + comptrollerImpl: ComptrollerImpl +): Promise { + let invokation = await invoke( + world, + unitroller.methods._setPendingImplementation(comptrollerImpl._address), + from, + ComptrollerErrorReporter + ); + + world = addAction(world, `Set pending comptroller impl to ${comptrollerImpl.name}`, invokation); + return world; } export function unitrollerCommands() { return [ - new Command<{unitrollerParams: EventV}>(` + new Command<{ unitrollerParams: EventV }>( + ` #### Deploy * "Unitroller Deploy ...unitrollerParams" - Generates a new Unitroller * E.g. "Unitroller Deploy" `, - "Deploy", - [new Arg("unitrollerParams", getEventV, {variadic: true})], - (world, from, {unitrollerParams}) => genUnitroller(world, from, unitrollerParams.val) + 'Deploy', + [new Arg('unitrollerParams', getEventV, { variadic: true })], + (world, from, { unitrollerParams }) => genUnitroller(world, from, unitrollerParams.val) ), - new View<{unitroller: Unitroller, apiKey: StringV}>(` + new View<{ unitroller: Unitroller; apiKey: StringV }>( + ` #### Verify * "Unitroller Verify apiKey:" - Verifies Unitroller in Etherscan * E.g. "Unitroller Verify "myApiKey" `, - "Verify", - [ - new Arg("unitroller", getUnitroller, {implicit: true}), - new Arg("apiKey", getStringV) - ], - (world, {unitroller, apiKey}) => verifyUnitroller(world, unitroller, apiKey.val) + 'Verify', + [new Arg('unitroller', getUnitroller, { implicit: true }), new Arg('apiKey', getStringV)], + (world, { unitroller, apiKey }) => verifyUnitroller(world, unitroller, apiKey.val) + ), + new Command<{ unitroller: Unitroller; pendingAdmin: AddressV }>( + ` + #### AcceptAdmin + + * "AcceptAdmin" - Accept admin for this unitroller + * E.g. "Unitroller AcceptAdmin" + `, + 'AcceptAdmin', + [new Arg('unitroller', getUnitroller, { implicit: true })], + (world, from, { unitroller }) => acceptAdmin(world, from, unitroller) + ), + new Command<{ unitroller: Unitroller; pendingAdmin: AddressV }>( + ` + #### SetPendingAdmin + + * "SetPendingAdmin admin:" - Sets the pending admin for this unitroller + * E.g. "Unitroller SetPendingAdmin Jared" + `, + 'SetPendingAdmin', + [new Arg('unitroller', getUnitroller, { implicit: true }), new Arg('pendingAdmin', getAddressV)], + (world, from, { unitroller, pendingAdmin }) => + setPendingAdmin(world, from, unitroller, pendingAdmin.val) ), - new Command<{unitroller: Unitroller, comptrollerImpl: ComptrollerImpl}>(` + new Command<{ unitroller: Unitroller; comptrollerImpl: ComptrollerImpl }>( + ` #### SetPendingImpl * "SetPendingImpl impl:" - Sets the pending comptroller implementation for this unitroller * E.g. "Unitroller SetPendingImpl MyScenImpl" - Sets the current comptroller implementation to MyScenImpl `, - "SetPendingImpl", + 'SetPendingImpl', [ - new Arg("unitroller", getUnitroller, {implicit: true}), - new Arg("comptrollerImpl", getComptrollerImpl) + new Arg('unitroller', getUnitroller, { implicit: true }), + new Arg('comptrollerImpl', getComptrollerImpl) ], - (world, from, {unitroller, comptrollerImpl}) => setPendingImpl(world, from, unitroller, comptrollerImpl) + (world, from, { unitroller, comptrollerImpl }) => + setPendingImpl(world, from, unitroller, comptrollerImpl) ) ]; } -export async function processUnitrollerEvent(world: World, event: Event, from: string | null): Promise { - return await processCommandEvent("Unitroller", unitrollerCommands(), world, event, from); +export async function processUnitrollerEvent( + world: World, + event: Event, + from: string | null +): Promise { + return await processCommandEvent('Unitroller', unitrollerCommands(), world, event, from); } diff --git a/scenario/src/Utils.ts b/scenario/src/Utils.ts index fa8c62051..5c3019117 100644 --- a/scenario/src/Utils.ts +++ b/scenario/src/Utils.ts @@ -1,5 +1,5 @@ -import {Event} from './Event'; -import {World} from './World'; +import { Event } from './Event'; +import { World } from './World'; // Wraps the element in an array, if it was not already an array // If array is null or undefined, return the empty array @@ -30,7 +30,7 @@ export function mustLen(arg: any[] | any, len: number, maxLen?: number): any[] { } export function mustString(arg: Event): string { - if (typeof(arg) === "string") { + if (typeof arg === 'string') { return arg; } @@ -45,14 +45,37 @@ export function encodeABI(world: World, fnABI: string, fnParams: string[]): stri if (!res) { throw new Error(`Expected ABI signature, got: ${fnABI}`); } - const [_, fnName, fnInputs] = <[string, string, string]>res; + const [_, fnName, fnInputs] = <[string, string, string]>(res); const jsonInterface = { name: fnName, - inputs: fnInputs.split(',').map((i) => ({name: '', type: i})) + inputs: fnInputs.split(',').map(i => ({ name: '', type: i })) }; return world.web3.eth.abi.encodeFunctionCall(jsonInterface, fnParams); } +export function encodeParameters(world: World, fnABI: string, fnParams: string[]): string { + const regex = /(\w+)\(([\w,]+)\)/; + const res = regex.exec(fnABI); + if (!res) { + return '0x0'; + } + const [_, __, fnInputs] = <[string, string, string]>(res); + return world.web3.eth.abi.encodeParameters(fnInputs.split(','), fnParams); +} + +export function decodeParameters(world: World, fnABI: string, data: string): string[] { + const regex = /(\w+)\(([\w,]+)\)/; + const res = regex.exec(fnABI); + if (!res) { + return []; + } + const [_, __, fnInputs] = <[string, string, string]>(res); + const inputTypes = fnInputs.split(','); + const parameters = world.web3.eth.abi.decodeParameters(inputTypes, data); + + return inputTypes.map((_, index) => parameters[index]); +} + export function sleep(timeout: number): Promise { return new Promise((resolve, reject) => { setTimeout(() => { diff --git a/scenario/src/Value.ts b/scenario/src/Value.ts index be87535c2..938c1b2a7 100644 --- a/scenario/src/Value.ts +++ b/scenario/src/Value.ts @@ -147,8 +147,8 @@ export class StringV implements Value { compareTo(world: World, given: Value): boolean { if (given instanceof StringV) { return this.val === given.val; - } else if (given instanceof AddressV) { - return this.val === given.val; + } else if ( given instanceof AddressV) { + return world.web3.utils.toChecksumAddress(this.val) === world.web3.utils.toChecksumAddress(given.val); } else { throw new Error(`Cannot compare ${typeof this} to ${typeof given} (${this.toString()}, ${given.toString()})`); } @@ -203,10 +203,8 @@ export class AddressV implements Value { } compareTo(world: World, given: Value): boolean { - if (given instanceof AddressV) { - return this.val === given.val; - } else if (given instanceof StringV) { - return this.val === given.val; + if (given instanceof AddressV || given instanceof StringV) { + return world.web3.utils.toChecksumAddress(this.val) === world.web3.utils.toChecksumAddress(given.val); } else { throw new Error(`Cannot compare ${typeof this} to ${typeof given} (${this.toString()}, ${given.toString()})`); } diff --git a/scenario/src/Value/ComptrollerValue.ts b/scenario/src/Value/ComptrollerValue.ts index 7b001f527..0068d6795 100644 --- a/scenario/src/Value/ComptrollerValue.ts +++ b/scenario/src/Value/ComptrollerValue.ts @@ -304,6 +304,58 @@ export function comptrollerFetchers() { new Arg("cToken", getCTokenV) ], (world, {comptroller, cToken}) => checkListed(world, comptroller, cToken) + ), + new Fetcher<{comptroller: Comptroller}, AddressV>(` + #### PauseGuardian + + * "PauseGuardian" - Returns the Comptrollers's PauseGuardian + * E.g. "Comptroller PauseGuardian" + `, + "PauseGuardian", + [ + new Arg("comptroller", getComptroller, {implicit: true}) + ], + async (world, {comptroller}) => new AddressV(await comptroller.methods.pauseGuardian().call()) + ), + new Fetcher<{comptroller: Comptroller}, BoolV>(` + #### MintGuardianPaused + + * "MintGuardianPaused" - Returns the Comptrollers's Mint paused status + * E.g. "Comptroller MintGuardianPaused" + `, + "MintGuardianPaused", + [new Arg("comptroller", getComptroller, {implicit: true})], + async (world, {comptroller}) => new BoolV(await comptroller.methods.mintGuardianPaused().call()) + ), + new Fetcher<{comptroller: Comptroller}, BoolV>(` + #### BorrowGuardianPaused + + * "BorrowGuardianPaused" - Returns the Comptrollers's Borrow paused status + * E.g. "Comptroller BorrowGuardianPaused" + `, + "BorrowGuardianPaused", + [new Arg("comptroller", getComptroller, {implicit: true})], + async (world, {comptroller}) => new BoolV(await comptroller.methods.borrowGuardianPaused().call()) + ), + new Fetcher<{comptroller: Comptroller}, BoolV>(` + #### TransferGuardianPaused + + * "TransferGuardianPaused" - Returns the Comptrollers's Transfer paused status + * E.g. "Comptroller TransferGuardianPaused" + `, + "TransferGuardianPaused", + [new Arg("comptroller", getComptroller, {implicit: true})], + async (world, {comptroller}) => new BoolV(await comptroller.methods.transferGuardianPaused().call()) + ), + new Fetcher<{comptroller: Comptroller}, BoolV>(` + #### SeizeGuardianPaused + + * "SeizeGuardianPaused" - Returns the Comptrollers's Seize paused status + * E.g. "Comptroller SeizeGuardianPaused" + `, + "SeizeGuardianPaused", + [new Arg("comptroller", getComptroller, {implicit: true})], + async (world, {comptroller}) => new BoolV(await comptroller.methods.seizeGuardianPaused().call()) ) ]; } diff --git a/scenario/src/Value/TimelockValue.ts b/scenario/src/Value/TimelockValue.ts new file mode 100644 index 000000000..c80bc6f67 --- /dev/null +++ b/scenario/src/Value/TimelockValue.ts @@ -0,0 +1,135 @@ +import { Event } from '../Event'; +import { World } from '../World'; +import { Timelock } from '../Contract/Timelock'; +import { getAddressV, getCoreValue, getNumberV, getStringV } from '../CoreValue'; +import { AddressV, BoolV, NumberV, StringV, Value } from '../Value'; +import { Arg, Fetcher, getFetcherValue } from '../Command'; +import { getTimelock } from '../ContractLookup'; +import { encodeParameters } from '../Utils'; + +export async function getTimelockAddress(world: World, timelock: Timelock): Promise { + return new AddressV(timelock._address); +} + +async function getAdmin(world: World, timelock: Timelock): Promise { + return new AddressV(await timelock.methods.admin().call()); +} + +async function getPendingAdmin(world: World, timelock: Timelock): Promise { + return new AddressV(await timelock.methods.pendingAdmin().call()); +} + +async function getBlockTimestamp(world: World, timelock: Timelock): Promise { + return new NumberV(await timelock.methods.blockTimestamp().call()); +} + +async function getDelay(world: World, timelock: Timelock): Promise { + return new NumberV(await timelock.methods.delay().call()); +} + +async function queuedTransaction(world: World, timelock: Timelock, txHash: string): Promise { + return new BoolV(await timelock.methods.queuedTransactions(txHash).call()); +} + +export function timelockFetchers() { + return [ + new Fetcher<{ timelock: Timelock }, AddressV>( + ` + #### Address + + * "Address" - Gets the address of the Timelock + `, + 'Address', + [new Arg('timelock', getTimelock, { implicit: true })], + (world, { timelock }) => getTimelockAddress(world, timelock) + ), + new Fetcher<{ timelock: Timelock }, AddressV>( + ` + #### Admin + + * "Admin" - Gets the address of the Timelock admin + `, + 'Admin', + [new Arg('timelock', getTimelock, { implicit: true })], + (world, { timelock }) => getAdmin(world, timelock) + ), + new Fetcher<{ timelock: Timelock }, AddressV>( + ` + #### PendingAdmin + + * "PendingAdmin" - Gets the address of the Timelock pendingAdmin + `, + 'PendingAdmin', + [new Arg('timelock', getTimelock, { implicit: true })], + (world, { timelock }) => getPendingAdmin(world, timelock) + ), + new Fetcher<{ timelock: Timelock }, NumberV>( + ` + #### BlockTimestamp + + * "BlockTimestamp" - Gets the blockTimestamp of the Timelock + `, + 'BlockTimestamp', + [new Arg('timelock', getTimelock, { implicit: true })], + (world, { timelock }) => getBlockTimestamp(world, timelock) + ), + new Fetcher<{ timelock: Timelock }, NumberV>( + ` + #### Delay + + * "Delay" - Gets the delay of the Timelock + `, + 'Delay', + [new Arg('timelock', getTimelock, { implicit: true })], + (world, { timelock }) => getDelay(world, timelock) + ), + new Fetcher< + { + target: AddressV; + value: NumberV; + eta: NumberV; + signature: StringV; + data: StringV[]; + }, + StringV + >( + ` + #### TxHash + + * "TxHash target:
value: eta: signature: ...funArgs:" - Returns a hash of a transactions values + * E.g. "Timelock TxHash \"0x0000000000000000000000000000000000000000\" 0 1569286014 \"setDelay(uint256)\" 60680" + `, + 'TxHash', + [ + new Arg('target', getAddressV), + new Arg('value', getNumberV), + new Arg('eta', getNumberV), + new Arg('signature', getStringV), + new Arg('data', getCoreValue, { variadic: true, mapped: true }) + ], + (world, { target, value, signature, data, eta }) => { + const encodedData = encodeParameters(world, signature.val, data.map(a => a.val)); + const encodedTransaction = world.web3.eth.abi.encodeParameters( + ['address', 'uint256', 'string', 'bytes', 'uint256'], + [target.val, value.val, signature.val, encodedData, eta.val] + ); + + return Promise.resolve(new StringV(world.web3.utils.keccak256(encodedTransaction))); + } + ), + new Fetcher<{ timelock: Timelock; txHash: StringV }, BoolV>( + ` + #### QueuedTransaction + + * "QueuedTransaction txHash:" - Gets the boolean value of the given txHash in the queuedTransactions mapping + `, + 'QueuedTransaction', + [new Arg('timelock', getTimelock, { implicit: true }), new Arg('txHash', getStringV)], + (world, { timelock, txHash }) => queuedTransaction(world, timelock, txHash.val) + ) + ]; +} + +export async function getTimelockValue(world: World, event: Event): Promise { + return await getFetcherValue('Timelock', timelockFetchers(), world, event); +} diff --git a/scenario/src/Value/UnitrollerValue.ts b/scenario/src/Value/UnitrollerValue.ts index 0dcea38bc..e57c3887e 100644 --- a/scenario/src/Value/UnitrollerValue.ts +++ b/scenario/src/Value/UnitrollerValue.ts @@ -1,34 +1,89 @@ -import {Event} from '../Event'; -import {World} from '../World'; -import {Unitroller} from '../Contract/Unitroller'; -import { - getAddressV -} from '../CoreValue'; -import { - AddressV, - Value -} from '../Value'; -import {Arg, Fetcher, getFetcherValue} from '../Command'; -import {getUnitroller} from '../ContractLookup'; +import { Event } from '../Event'; +import { World } from '../World'; +import { Unitroller } from '../Contract/Unitroller'; +import { AddressV, Value } from '../Value'; +import { Arg, Fetcher, getFetcherValue } from '../Command'; +import { getUnitroller } from '../ContractLookup'; export async function getUnitrollerAddress(world: World, unitroller: Unitroller): Promise { return new AddressV(unitroller._address); } +async function getUnitrollerAdmin(world: World, unitroller: Unitroller): Promise { + return new AddressV(await unitroller.methods.admin().call()); +} + +async function getUnitrollerPendingAdmin(world: World, unitroller: Unitroller): Promise { + return new AddressV(await unitroller.methods.pendingAdmin().call()); +} + +async function getComptrollerImplementation(world: World, unitroller: Unitroller): Promise { + return new AddressV(await unitroller.methods.comptrollerImplementation().call()); +} + +async function getPendingComptrollerImplementation(world: World, unitroller: Unitroller): Promise { + return new AddressV(await unitroller.methods.pendingComptrollerImplementation().call()); +} + export function unitrollerFetchers() { return [ - new Fetcher<{unitroller: Unitroller}, AddressV>(` + new Fetcher<{ unitroller: Unitroller }, AddressV>( + ` #### Address * "Unitroller Address" - Returns address of unitroller `, - "Address", - [new Arg("unitroller", getUnitroller, {implicit: true})], - (world, {unitroller}) => getUnitrollerAddress(world, unitroller) + 'Address', + [new Arg('unitroller', getUnitroller, { implicit: true })], + (world, { unitroller }) => getUnitrollerAddress(world, unitroller) + ), + new Fetcher<{ unitroller: Unitroller }, AddressV>( + ` + #### Admin + + * "Unitroller Admin" - Returns the admin of Unitroller contract + * E.g. "Unitroller Admin" - Returns address of admin + `, + 'Admin', + [new Arg('unitroller', getUnitroller, { implicit: true })], + (world, { unitroller }) => getUnitrollerAdmin(world, unitroller) + ), + new Fetcher<{ unitroller: Unitroller }, AddressV>( + ` + #### PendingAdmin + + * "Unitroller PendingAdmin" - Returns the pending admin of Unitroller contract + * E.g. "Unitroller PendingAdmin" - Returns address of pendingAdmin + `, + 'PendingAdmin', + [new Arg('unitroller', getUnitroller, { implicit: true })], + (world, { unitroller }) => getUnitrollerPendingAdmin(world, unitroller) + ), + new Fetcher<{ unitroller: Unitroller }, AddressV>( + ` + #### Implementation + + * "Unitroller Implementation" - Returns the Implementation of Unitroller contract + * E.g. "Unitroller Implementation" - Returns address of comptrollerImplentation + `, + 'Implementation', + [new Arg('unitroller', getUnitroller, { implicit: true })], + (world, { unitroller }) => getComptrollerImplementation(world, unitroller) + ), + new Fetcher<{ unitroller: Unitroller }, AddressV>( + ` + #### PendingImplementation + + * "Unitroller PendingImplementation" - Returns the pending implementation of Unitroller contract + * E.g. "Unitroller PendingImplementation" - Returns address of pendingComptrollerImplementation + `, + 'PendingImplementation', + [new Arg('unitroller', getUnitroller, { implicit: true })], + (world, { unitroller }) => getPendingComptrollerImplementation(world, unitroller) ) ]; } export async function getUnitrollerValue(world: World, event: Event): Promise { - return await getFetcherValue("Unitroller", unitrollerFetchers(), world, event); + return await getFetcherValue('Unitroller', unitrollerFetchers(), world, event); } diff --git a/scenario/src/Web.ts b/scenario/src/Web.ts index f68e4bbaf..7cf437a9d 100644 --- a/scenario/src/Web.ts +++ b/scenario/src/Web.ts @@ -1,29 +1,76 @@ -import {parse} from './Parser'; -import {IWeb3, World, initWorld} from './World'; -import {throwAssert} from './Assert'; -import {CallbackPrinter} from './Printer'; -import {runCommand} from './Runner'; -import {loadContractData, parseNetworkFile} from './Networks'; - -export async function webWorld(web3: IWeb3, networksData: string, networksABIData: string, printerCallback: (message: any) => void): Promise { - let printer = new CallbackPrinter(printerCallback); - let accounts = [web3.currentProvider.address]; - let network = 'rinkeby'; // TODO: Get from web3 - - let world = await initWorld(throwAssert, printer, web3, null, network, accounts, null); - - let networks = parseNetworkFile(networksData); - let networksABI = parseNetworkFile(networksABIData); - - [world,] = await loadContractData(world, networks, networksABI); - // world = loadInvokationOpts(world); - // world = loadVerbose(world); - // world = loadDryRun(world); - // world = await loadSettings(world); - - return world; +import { parse } from './Parser'; +import { IWeb3, World, initWorld } from './World'; +import { throwAssert } from './Assert'; +import { CallbackPrinter } from './Printer'; +import { runCommand } from './Runner'; +import { loadContractData, parseNetworkFile } from './Networks'; + +function networkFromId(id: number) { + switch (id) { + case 0: + return 'olympic'; + + case 1: + return 'mainnet'; + + case 2: + return 'morden'; + + case 3: + return 'ropsten'; + + case 4: + return 'rinkeby'; + + case 5: + return 'goerli'; + + case 8: + return 'ubiq'; + + case 42: + return 'kovan'; + + case 77: + return 'sokol'; + + case 99: + return 'core'; + + case 999: + return 'development'; + + default: + return ''; + } +} + +export async function webWorld( + web3: IWeb3, + networksData: string, + networksABIData: string, + printerCallback: (message: any) => void +): Promise { + let printer = new CallbackPrinter(printerCallback); + let accounts = [web3.currentProvider.address]; + + const networkId = await (web3 as any).net.getId(); + const network: string = networkFromId(networkId); + + let world = await initWorld(throwAssert, printer, web3, null, network, accounts, null); + + let networks = parseNetworkFile(networksData); + let networksABI = parseNetworkFile(networksABIData); + + [world] = await loadContractData(world, networks, networksABI); + // world = loadInvokationOpts(world); + // world = loadVerbose(world); + // world = loadDryRun(world); + // world = await loadSettings(world); + + return world; } export async function webParse(world: World, line: string): Promise { - return runCommand(world, line, {}); + return runCommand(world, line, {}); } diff --git a/script/test b/script/test index be995d2c1..8e0f6cee1 100755 --- a/script/test +++ b/script/test @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -eo pipefail + dir=`dirname $0` proj_root="$dir/.." test_root="$dir/../test" diff --git a/spec/scenario/Borrow.scen b/spec/scenario/Borrow.scen index 3790a73cf..a2811ecb5 100644 --- a/spec/scenario/Borrow.scen +++ b/spec/scenario/Borrow.scen @@ -1,18 +1,39 @@ --- Waiting on Comptroller actually checking market entered -Test "Borrow some BAT fails when BAT not entered" +Test "Borrow some BAT and enters BAT if BAT not entered" NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken BAT cBAT + Give cBAT 10e18 BAT -- Faucet some bat to borrow Support cZRX collateralFactor:0.5 Support cBAT collateralFactor:0.5 Prep Geoff Some ZRX cZRX Mint Geoff 100e18 cZRX EnterMarkets Geoff cZRX - Invariant Static (CToken cZRX ExchangeRateStored) + Borrow Geoff 1e18 cBAT + Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 1e18) + Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 1e18) + Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 9e18) + Assert Equal (Comptroller MembershipLength Geoff) (Exactly 2) + Assert True (Comptroller CheckMembership Geoff cZRX) + Assert True (Comptroller CheckMembership Geoff cBAT) + +Test "Borrow some BAT fails, but user still entered" + NewComptroller price:1.0 + NewCToken ZRX cZRX + NewCToken BAT cBAT + Support cZRX collateralFactor:0.5 + Support cBAT collateralFactor:0.5 + Prep Geoff Some ZRX cZRX + Mint Geoff 100e18 cZRX + EnterMarkets Geoff cZRX + Invariant Static (Erc20 BAT TokenBalance Geoff) + Invariant Static (Erc20 BAT TokenBalance cBAT) AllowFailures Borrow Geoff 1e18 cBAT - Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION MARKET_NOT_ENTERED + Assert Failure TOKEN_INSUFFICIENT_CASH BORROW_CASH_NOT_AVAILABLE + Assert Equal (Comptroller MembershipLength Geoff) (Exactly 2) + Assert True (Comptroller CheckMembership Geoff cZRX) + Assert True (Comptroller CheckMembership Geoff cBAT) Test "Borrow some BAT fails when no BAT available" NewComptroller price:1.0 @@ -28,6 +49,33 @@ Test "Borrow some BAT fails when no BAT available" Borrow Geoff 1e18 cBAT Assert Failure TOKEN_INSUFFICIENT_CASH BORROW_CASH_NOT_AVAILABLE +Test "Borrow some BAT fails when already entered max assets" + NewComptroller price:1.0 maxAssets:1 + NewCToken ZRX cZRX + NewCToken BAT cBAT + Give cBAT 10e18 BAT -- Faucet some bat to borrow + Support cZRX collateralFactor:0.5 + Support cBAT collateralFactor:0.5 + Prep Geoff Some ZRX cZRX + Mint Geoff 100e18 cZRX + EnterMarkets Geoff cZRX + AllowFailures + Borrow Geoff 1e18 cBAT + Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION TOO_MANY_ASSETS + +Test "Borrow fails if market not listed" + NewComptroller price:1.0 + NewCToken ZRX cZRX + NewCToken BAT cBAT + Give cBAT 10e18 BAT -- Faucet some bat to borrow + Support cZRX collateralFactor:0.5 + Prep Geoff Some ZRX cZRX + Mint Geoff 100e18 cZRX + EnterMarkets Geoff cZRX + AllowFailures + Borrow Geoff 1e18 cBAT + Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION MARKET_NOT_LISTED + Test "Borrow some BAT from Excess Cash" Invariant Success NewComptroller price:1.0 @@ -40,7 +88,30 @@ Test "Borrow some BAT from Excess Cash" Mint Geoff 100e18 cZRX EnterMarkets Geoff cZRX cBAT Borrow Geoff 1e18 cBAT + Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 1e18) + Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 1e18) + Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 9e18) + +Test "Borrow some BAT reverts if borrow is paused" + NewComptroller price:1.0 + Comptroller SetPauseGuardian Coburn + NewCToken ZRX cZRX + NewCToken BAT cBAT + Give cBAT 10e18 BAT -- Faucet some bat to borrow + Support cZRX collateralFactor:0.5 + Support cBAT collateralFactor:0.5 + Prep Geoff Some ZRX cZRX + Mint Geoff 100e18 cZRX EnterMarkets Geoff cZRX cBAT + From Coburn (Comptroller SetGuardianPaused "Borrow" True) + AllowFailures + Borrow Geoff 1e18 cBAT + Assert Revert "revert borrow is paused" + Assert Equal (cToken cBAT BorrowBalance Geoff) 0 + Assert Equal (Erc20 BAT TokenBalance Geoff) 0 + Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 10e18) + Comptroller SetGuardianPaused "Borrow" False + Borrow Geoff 1e18 cBAT Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 9e18) diff --git a/spec/scenario/BorrowBalance.scen b/spec/scenario/BorrowBalance.scen index 312f8a748..4ba070485 100644 --- a/spec/scenario/BorrowBalance.scen +++ b/spec/scenario/BorrowBalance.scen @@ -15,65 +15,65 @@ Macro SimpleBorrow user borrowAmount EnterMarkets user cZRX cBAT Borrow user borrowAmount cBAT -Test "Borrow Balance after 3000 blocks" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 +Test "Borrow Balance after 300000 blocks" + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 1e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 9e18) -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 2.5e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 2.5e18) -Test "Borrow Balance after 3000 blocks and then 6000 blocks" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 +Test "Borrow Balance after 300000 blocks and then 600000 blocks" + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 1e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 9e18) -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) AccrueInterest cBAT Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 2.5e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 2.5e18) - FastForward 6000 Blocks -- 1e18 * (1 + 3000 * 0.0005) * (1 + 6000 * 0.0005) + FastForward 600000 Blocks -- 1e18 * (1 + 300000 * 0.000005) * (1 + 600000 * 0.000005) Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 10e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 10e18) Test "Borrow Balance after accrual then changed interest rate" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 1e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 1e18) Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 9e18) -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- Current: 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- Current: 1e18 * (1 + 300000 * 0.000005) -- Note: this should accrue interest - InterestRateModel Deploy Fixed Std 0.0004 + InterestRateModel Deploy Fixed Std 0.000004 CToken cBAT SetInterestRateModel (InterestRateModel Std Address) -- Check borrow balance still based on old figure (with previous interest accrual) Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 2.5e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 2.5e18) -- Now accrue with new rate - FastForward 8000 Blocks -- 1e18 * (1 + 3000 * 0.0005) * (1 + 8000 * 0.0004) + FastForward 800000 Blocks -- 1e18 * (1 + 300000 * 0.000005) * (1 + 800000 * 0.000004) Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 10.5e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 10.5e18) Test "Total Borrow Balance with Two Borrowers" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 user:Geoff - FastForward 3000 Blocks - InterestRateModel Deploy Fixed Std 0.0004 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 user:Geoff + FastForward 300000 Blocks + InterestRateModel Deploy Fixed Std 0.000004 CToken cBAT SetInterestRateModel (InterestRateModel Std Address) -- Check borrow balance still based on old figure (with previous interest accrual) Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 2.5e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 2.5e18) SimpleBorrow user:Torrey borrowAmount:5e18 -- Now accrue with new rate - FastForward 8000 Blocks - -- Geoff: 1e18 * (1 + 3000 * 0.0005) * (1 + 8000 * 0.0004) + FastForward 800000 Blocks + -- Geoff: 1e18 * (1 + 300000 * 0.000005) * (1 + 800000 * 0.000004) Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 10.5e18) - -- Torrey: 5e18 * (1 + 8000 * 0.0004) + -- Torrey: 5e18 * (1 + 800000 * 0.000004) Assert Equal (cToken cBAT BorrowBalance Torrey) (Exactly 21e18) Assert Equal (cToken cBAT TotalBorrowsCurrent) (Exactly 31.5e18) -- And test some repayment diff --git a/spec/scenario/BorrowEth.scen b/spec/scenario/BorrowEth.scen index 83ba4d5b5..5b95182f7 100644 --- a/spec/scenario/BorrowEth.scen +++ b/spec/scenario/BorrowEth.scen @@ -1,20 +1,20 @@ -Test "Borrow some Eth fails when Eth not entered" +Test "Borrow some Eth enters Eth and succeeds when Eth not entered" NewComptroller price:1.0 ListedCToken ZRX cZRX ListedEtherToken cETH initialExchangeRate:0.005e9 SetCollateralFactor cZRX collateralFactor:0.5 SetCollateralFactor cETH collateralFactor:0.5 + Donate cETH 0.003e18 Prep Geoff Some ZRX cZRX - Mint Geoff 100e18 cZRX + Mint Geoff 1e18 cZRX EnterMarkets Geoff cZRX - AllowFailures - Invariant Static (CToken cZRX ExchangeRateStored) - Invariant Static (CToken cETH ExchangeRateStored) - Invariant Static (Comptroller Liquidity Geoff) - Invariant Static (EtherBalance Geoff) - BorrowEth Geoff 1e18 cETH - Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION MARKET_NOT_ENTERED + Expect Changes (EtherBalance Geoff) +0.001e18 + BorrowEth Geoff 0.001e18 cETH + Assert Equal (EtherBalance cETH) 0.002e18 + Assert Equal (Comptroller Liquidity Geoff) 4.99e17 + Assert Equal (Comptroller MembershipLength Geoff) (Exactly 2) + Assert True (Comptroller CheckMembership Geoff cETH) Test "Borrow some ETH fails when no ETH available" NewComptroller price:1.0 diff --git a/spec/scenario/BorrowWBTC.scen b/spec/scenario/BorrowWBTC.scen index 5607efd97..ae7d79df4 100644 --- a/spec/scenario/BorrowWBTC.scen +++ b/spec/scenario/BorrowWBTC.scen @@ -1,18 +1,19 @@ --- Waiting on Comptroller actually checking market entered -Test "Borrow some WBTC fails when WBTC not entered" +Test "Borrow some WBTC enters WBTC and succeeds when not entered" + Invariant Success NewComptroller price:1.0 NewCToken ZRX cZRX NewCToken WBTC cWBTC tokenType:WBTC + Give cWBTC 10e8 WBTC -- Faucet some WBTC to borrow Support cZRX collateralFactor:0.5 Support cWBTC collateralFactor:0.5 Prep Geoff Some ZRX cZRX Mint Geoff 100e18 cZRX EnterMarkets Geoff cZRX - Invariant Static (CToken cZRX ExchangeRateStored) - AllowFailures Borrow Geoff 1e8 cWBTC - Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION MARKET_NOT_ENTERED + Assert Equal (cToken cWBTC BorrowBalance Geoff) (Exactly 1e8) + Assert Equal (Erc20 WBTC TokenBalance Geoff) (Exactly 1e8) + Assert Equal (Erc20 WBTC TokenBalance cWBTC) (Exactly 9e8) Test "Borrow some WBTC fails when no WBTC available" NewComptroller price:1.0 diff --git a/spec/scenario/BreakLiquidate.scen b/spec/scenario/BreakLiquidate.scen index 0990ef2ae..1dc0e9c8c 100644 --- a/spec/scenario/BreakLiquidate.scen +++ b/spec/scenario/BreakLiquidate.scen @@ -1,5 +1,5 @@ -Macro NewBorrow borrowAmount mintAmount borrowRate=0.0005 user=Geoff collateralPrice=1.0 borrowPrice=1.0 liquidationIncentive=1.1 +Macro NewBorrow borrowAmount mintAmount borrowRate=0.000005 user=Geoff collateralPrice=1.0 borrowPrice=1.0 liquidationIncentive=1.1 PricedComptroller closeFactor:0.9 -- Set the close factor high to reduce number of steps to demonstrate Comptroller LiquidationIncentive liquidationIncentive NewCToken ZRX cZRX diff --git a/spec/scenario/CTokenAdmin.scen b/spec/scenario/CTokenAdmin.scen index dfd317f82..e658ae1dd 100644 --- a/spec/scenario/CTokenAdmin.scen +++ b/spec/scenario/CTokenAdmin.scen @@ -11,6 +11,19 @@ Test "Set admin" Assert Equal (CToken cZRX Admin) (Address Geoff) Assert Equal (CToken cZRX PendingAdmin) (Address Zero) +Test "Set admin to contructor argument" + NewComptroller + NewCToken ZRX cZRX admin:Torrey + Assert Equal (CToken cZRX Admin) (Address Torrey) + Assert Equal (CToken cZRX PendingAdmin) (Address Zero) + From Torrey (CToken cZRX SetPendingAdmin Geoff) + Assert Equal (CToken cZRX Admin) (Address Torrey) + Assert Equal (CToken cZRX PendingAdmin) (Address Geoff) + From Geoff (CToken cZRX AcceptAdmin) + Assert Equal (CToken cZRX Admin) (Address Geoff) + Assert Equal (CToken cZRX PendingAdmin) (Address Zero) + + Test "Fail to set pending admin" NewComptroller NewCToken ZRX cZRX diff --git a/spec/scenario/CoreMacros b/spec/scenario/CoreMacros index 784972468..e150a7ee8 100644 --- a/spec/scenario/CoreMacros +++ b/spec/scenario/CoreMacros @@ -9,40 +9,46 @@ Macro AllowFailures Macro PricedComptroller closeFactor=0.1 maxAssets=20 Unitroller Deploy PriceOracle Deploy Simple + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address Zero) (Address Zero) (Address Zero) -- Final argument is a Junk address, if listing cEther use ListedEtherToken to replace proxy + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracleProxy Address) closeFactor maxAssets ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address Zero) (Address Zero) (Address Zero) -- Final argument is a Junk address, if listing cEther use ListedEtherToken to replace proxy - ComptrollerImpl ScenComptroller Become (PriceOracleProxy Address) closeFactor maxAssets + ComptrollerImpl ScenComptroller Become Macro NewComptroller price=1.0 closeFactor=0.1 maxAssets=20 Unitroller Deploy PriceOracle Deploy Fixed price + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address Zero) (Address Zero) (Address Zero) -- Junk address, if listing cEther use ListedEtherToken to replace proxy + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracleProxy Address) closeFactor maxAssets ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address Zero) (Address Zero) (Address Zero) -- Junk address, if listing cEther use ListedEtherToken to replace proxy - ComptrollerImpl ScenComptroller Become (PriceOracleProxy Address) closeFactor maxAssets + ComptrollerImpl ScenComptroller Become -Macro NewCToken erc20 cToken borrowRate=0.0005 initialExchangeRate=2e9 decimals=8 tokenType=Standard cTokenType=Scenario +Macro NewCToken erc20 cToken borrowRate=0.000005 initialExchangeRate=2e9 decimals=8 tokenType=Standard cTokenType=Scenario admin=Admin Erc20 Deploy tokenType erc20 erc20 InterestRateModel Deploy Fixed StdInterest borrowRate -- Note: interest rate model probably shouldn't be global - CToken Deploy cTokenType cToken cToken (Erc20 erc20 Address) (Comptroller Address) (InterestRateModel StdInterest Address) initialExchangeRate decimals + CToken Deploy cTokenType cToken cToken (Erc20 erc20 Address) (Comptroller Address) (InterestRateModel StdInterest Address) initialExchangeRate decimals admin -Macro NewEtherToken cToken borrowRate=0.0005 initialExchangeRate=2e9 decimals=8 +Macro NewEtherToken cToken borrowRate=0.000005 initialExchangeRate=2e9 decimals=8 admin=Admin InterestRateModel Deploy Fixed StdInterest borrowRate -- Note: interest rate model probably shouldn't be global - CToken Deploy CEtherScenario cToken cToken (Comptroller Address) (InterestRateModel StdInterest Address) initialExchangeRate decimals + CToken Deploy CEtherScenario cToken cToken (Comptroller Address) (InterestRateModel StdInterest Address) initialExchangeRate decimals admin -Macro ListedCToken erc20 cToken borrowRate=0.0005 initialExchangeRate=2e9 decimals=8 tokenType=Standard cTokenType=Scenario - NewCToken erc20 cToken borrowRate initialExchangeRate decimals tokenType cTokenType +Macro ListedCToken erc20 cToken borrowRate=0.000005 initialExchangeRate=2e9 decimals=8 tokenType=Standard cTokenType=Scenario admin=Admin + NewCToken erc20 cToken borrowRate initialExchangeRate decimals tokenType cTokenType admin Comptroller SupportMarket cToken -Macro ListedEtherToken cToken borrowRate=0.0005 initialExchangeRate=2e9 decimals=8 - NewEtherToken cToken borrowRate initialExchangeRate decimals +Macro ListedEtherToken cToken borrowRate=0.000005 initialExchangeRate=2e9 decimals=8 admin=Admin + NewEtherToken cToken borrowRate initialExchangeRate decimals admin Comptroller SupportMarket cToken PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address cETH) (Address Zero) (Address Zero) Comptroller SetPriceOracle (PriceOracleProxy Address) -Macro ListedEtherTokenMinted cToken borrowRate=0.0005 initialExchangeRate=2e9 decimals=8 - NewEtherToken cToken borrowRate initialExchangeRate decimals +Macro ListedEtherTokenMinted cToken borrowRate=0.000005 initialExchangeRate=2e9 decimals=8 admin=Admin + NewEtherToken cToken borrowRate initialExchangeRate decimals admin Comptroller SupportMarket cToken CallMintEth Root 1e18 cToken @@ -183,3 +189,26 @@ Macro BorrowAndRepayWithInterest erc20 cToken amount interestAmount interestRate Give Torrey interestAmount erc20 RepayBorrow Torrey interestAmount cToken Assert Equal (CToken cToken BorrowBalance Torrey) Zero + +-- Macro for performing a borrow with the sole +-- purpose of adding interest to the ether market +Macro BorrowAndRepayEthWithInterest cEther amount interestAmount interestRate blocks reserveRate=0 + -- TODO: Make invariant success for these? + -- TODO: Named args as macros + InterestRateModel Deploy Fixed Std interestRate + CToken cEther SetInterestRateModel (InterestRateModel Std Address) + CToken cEther SetReserveFactor reserveRate + ListedCToken COLLAT cCOLLAT + Comptroller SetCollateralFactor cCOLLAT 0.9 + Prep Torrey 1e30 COLLAT cCOLLAT + Mint Torrey 1e30 cCOLLAT + EnterMarkets Torrey cCOLLAT cEther + Assert True (Comptroller CheckMembership Torrey cCOLLAT) + Assert True (Comptroller CheckMembership Torrey cEther) + Borrow Torrey amount cEther + -- Cool, we've borrowed, now let's accrue interest then repay all + FastForward blocks Blocks + -- RepayBorrow Torrey (CToken BorrowBalance Torrey) cEther + RepayBorrowEth Torrey amount cEther + RepayBorrowEth Torrey interestAmount cEther + Assert Equal (CToken cEther BorrowBalance Torrey) Zero diff --git a/spec/scenario/EnterExitMarkets.scen b/spec/scenario/EnterExitMarkets.scen index aefc43955..c258e176e 100644 --- a/spec/scenario/EnterExitMarkets.scen +++ b/spec/scenario/EnterExitMarkets.scen @@ -113,12 +113,6 @@ Test "Realistic Market Scenario" -- Enter ZRX and check liquidity EnterMarkets Geoff cZRX Assert Equal (Comptroller Liquidity Geoff) 1.0e18 - -- Try to borrow BAT but fail - HoldInvariants - Borrow Geoff 1.0e27 cBAT - Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION MARKET_NOT_ENTERED - -- Enter BAT for borrowing - EnterMarkets Geoff cBAT -- Fail to borrow BAT due to liquidity Give cBAT 1000e18 BAT HoldInvariants diff --git a/spec/scenario/Liquidate.scen b/spec/scenario/Liquidate.scen index e47f60f7d..4b3148d9e 100644 --- a/spec/scenario/Liquidate.scen +++ b/spec/scenario/Liquidate.scen @@ -2,7 +2,7 @@ Macro NewBorrow borrowAmount borrowRate user=Geoff collateralPrice=1.0 borrowPrice=1.0 mintAmount=100e18 collateralTokenType=Standard borrowTokenType=Standard PricedComptroller Comptroller LiquidationIncentive 1.1 - NewCToken ZRX cZRX 0.0005 2e9 8 collateralTokenType + NewCToken ZRX cZRX 0.000005 2e9 8 collateralTokenType NewCToken BAT cBAT borrowRate 2e9 8 borrowTokenType -- note: cannot use macros with named args right now Give cBAT 10e18 BAT -- Faucet some bat to borrow PriceOracle SetPrice cZRX collateralPrice @@ -18,17 +18,16 @@ Macro SimpleBorrow user borrowAmount mintAmount Borrow user borrowAmount cBAT Test "Insufficient shortfall" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 -- Prices is 1:1 and collateral factor is 0.5 - -- thus supplies 100e18 cZRX which gives the user 50e18 - -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be - -- underwater. - FastForward 98000 Blocks -- 1e18 * (1 + 98000 * 0.0005) + -- thus suppluing 100e18 cZRX gives the user 50e18 + -- capacity of BAT. After 9800000 at a 0.005% interest rate + -- the user will have 50e18 oustanding BAT, just barely enough collateral to avoid liquidation + FastForward 9800000 Blocks -- 1e18 * (1 + 9800000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 50e18 @@ -42,17 +41,17 @@ Test "Insufficient shortfall" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION INSUFFICIENT_SHORTFALL Test "Cannot self-liquidate" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 -- Prices is 1:1 and collateral factor is 0.5 - -- thus supplies 100e18 cZRX which gives the user 50e18 + -- thus supplying 100e18 cZRX gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -66,7 +65,7 @@ Test "Cannot self-liquidate" Assert Failure INVALID_ACCOUNT_PAIR LIQUIDATE_LIQUIDATOR_IS_BORROWER Test "Liqidate beyond max close" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -74,9 +73,9 @@ Test "Liqidate beyond max close" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -90,17 +89,17 @@ Test "Liqidate beyond max close" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION TOO_MUCH_REPAY Test "Proper liquidation with 1:1 price ratio" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 -- Prices are 1:1 and collateral factor is 0.5 - -- thus supplies 100e18 cZRX which gives the user 50e18 + -- thus supplying 100e18 cZRX gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a lot of blocks at a 0.05% interest rate, he'll be + -- a lot of blocks at a 0.005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -141,17 +140,17 @@ Test "Proper liquidation with 1:1 price ratio" Assert Equal (Comptroller Liquidity Geoff) -0.1e18 Test "Proper liquidation with 2:1 price ratio" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 borrowPrice:2.0 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 borrowPrice:2.0 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 - -- Prices are 2:1 and collateral factor is 0.5 - -- thus supplies 100e18 cZRX which gives the user 50e18 + -- Prices are 2:1 and collateral factor is 0.5, + -- thus supplying 100e18 cZRX gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a lot blocks at a 0.05% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -193,7 +192,7 @@ Test "Proper liquidation with 2:1 price ratio" Assert Equal (Comptroller Liquidity Geoff) -50.2e18 Test "Liquidate exactly zero" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -201,9 +200,9 @@ Test "Liquidate exactly zero" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 100000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -217,7 +216,7 @@ Test "Liquidate exactly zero" Assert Failure INVALID_CLOSE_AMOUNT_REQUESTED LIQUIDATE_CLOSE_AMOUNT_IS_ZERO Test "When price oracle for collateral token is zero" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -225,9 +224,9 @@ Test "When price oracle for collateral token is zero" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -242,7 +241,7 @@ Test "When price oracle for collateral token is zero" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION PRICE_ERROR Test "When price oracle for collateral token is whack" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -250,9 +249,9 @@ Test "When price oracle for collateral token is whack" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -267,7 +266,7 @@ Test "When price oracle for collateral token is whack" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION MATH_ERROR Test "When price oracle for borrow token is zero" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -275,9 +274,9 @@ Test "When price oracle for borrow token is zero" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -292,7 +291,7 @@ Test "When price oracle for borrow token is zero" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION PRICE_ERROR Test "When price oracle for borrow token is whack" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -300,9 +299,9 @@ Test "When price oracle for borrow token is whack" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -317,7 +316,7 @@ Test "When price oracle for borrow token is whack" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION MATH_ERROR Test "When repay borrow fails" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -325,9 +324,9 @@ Test "When repay borrow fails" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -342,7 +341,7 @@ Test "When repay borrow fails" Test "Proper liquidation of paused WBTC as collateral" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 collateralTokenType:WBTC + NewBorrow borrowAmount:1e18 borrowRate:0.000005 collateralTokenType:WBTC Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -350,9 +349,9 @@ Test "Proper liquidation of paused WBTC as collateral" -- Prices are 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a lot of blocks at a 0.05% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -396,7 +395,7 @@ Test "Proper liquidation of paused WBTC as collateral" Test "When WBTC token as borrowed is paused, cannot liquidate" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 borrowTokenType:WBTC + NewBorrow borrowAmount:1e18 borrowRate:0.000005 borrowTokenType:WBTC Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -404,9 +403,9 @@ Test "When WBTC token as borrowed is paused, cannot liquidate" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -422,7 +421,7 @@ Test "When WBTC token as borrowed is paused, cannot liquidate" Test "When seize not allowed due to unlisted collateral" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -430,9 +429,9 @@ Test "When seize not allowed due to unlisted collateral" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -445,7 +444,7 @@ Test "When seize not allowed due to unlisted collateral" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION MARKET_NOT_LISTED Test "When seize not allowed due to unlisted borrow" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -453,9 +452,9 @@ Test "When seize not allowed due to unlisted borrow" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -468,7 +467,7 @@ Test "When seize not allowed due to unlisted borrow" Assert Failure COMPTROLLER_REJECTION LIQUIDATE_COMPTROLLER_REJECTION MARKET_NOT_LISTED Test "When seize not allowed due to mismatched comptrollers" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -476,9 +475,9 @@ Test "When seize not allowed due to mismatched comptrollers" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -488,14 +487,14 @@ Test "When seize not allowed due to mismatched comptrollers" -- Change the comptroller of cZRX NewComptroller From Root (CToken cZRX SetComptroller (Unitroller Address)) - FastForward 100000 Blocks -- To match other comptroller + FastForward 10000000 Blocks -- To match other comptroller -- AllowFailures Liquidate Torrey "->" Geoff 2e18 cBAT "Seizing" cZRX Assert Revert "revert token seizure failed" Test "When there's insufficient collateral" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 borrowPrice:0.001 mintAmount:1e18 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 borrowPrice:0.001 mintAmount:1e18 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrows) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 @@ -503,9 +502,9 @@ Test "When there's insufficient collateral" -- Prices is 1:1 and collateral factor is 0.5 -- thus supplies 100e18 cZRX which gives the user 50e18 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 1e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Invariant Remains (CToken cBAT BorrowBalanceStored Geoff) 51e18 @@ -518,3 +517,35 @@ Test "When there's insufficient collateral" AllowFailures Liquidate Torrey "->" Geoff 2e18 cBAT "Seizing" cZRX Assert Failure TOKEN_INSUFFICIENT_BALANCE LIQUIDATE_SEIZE_TOO_MUCH + +Test "when seize is paused" + NewBorrow borrowAmount:1e18 borrowRate:0.000005 borrowPrice:2.0 + Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 + Assert Equal (CToken cBAT TotalBorrows) 1e18 + Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 + Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 + FastForward 10000000 Blocks -- 1e18 * (1 + 10000000 * 0.000005) + AccrueInterest cBAT -- Note: we have to accrue interest + -- since it's not automatic for liquidity + Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 51e18 + Assert Equal (CToken cBAT TotalBorrows) 51e18 + -- Check user liquidity and verify < 0 + Assert Equal (Comptroller Liquidity Geoff) -52e18 -- ( ( 1.0 * 100e18 * 0.5 ) - ( 2.0 * 51e18 ) ) / 1e18 + -- Okay, so we should be able to liquidate, so let's do that. + Prep Torrey 2e18 BAT cBAT + Comptroller SetPauseGuardian Coburn + From Coburn (Comptroller SetGuardianPaused "Seize" True) + AllowFailures + Liquidate Torrey "->" Geoff 2e18 cBAT "Seizing" cZRX + Assert Revert "revert seize is paused" + -- unpause and check correct values + Invariant Success + Comptroller SetGuardianPaused "Seize" False + Liquidate Torrey "->" Geoff 2e18 cBAT "Seizing" cZRX + Assert Equal (Erc20 cZRX TokenBalance Geoff) 47.8e9 + Assert Equal (Erc20 cZRX TokenBalance Torrey) 2.2e9 + Assert Equal (Erc20 BAT TokenBalance Torrey) 0e18 + Assert Equal (Erc20 BAT TokenBalance cBAT) 11e18 + Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 49e18 + Assert Equal (CToken cBAT TotalBorrows) 49e18 + Assert Equal (Comptroller Liquidity Geoff) -50.2e18 diff --git a/spec/scenario/LiquidateEthBorrow.scen b/spec/scenario/LiquidateEthBorrow.scen index dd4657dbe..88b498316 100644 --- a/spec/scenario/LiquidateEthBorrow.scen +++ b/spec/scenario/LiquidateEthBorrow.scen @@ -18,15 +18,15 @@ Macro SimpleBorrow user borrowAmount BorrowEth user borrowAmount cETH Test "Insufficient shortfall" - NewBorrow borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrow borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrows) 0.001e18 -- Prices is 1:1000 and collateral factor is 0.5 -- thus supplies 100.0e18 cBAT which gives the user (0.5 * 100.0e18 * 1.0e-3)=0.05e18 -- capacity of Eth. User only borrowed 0.001e18, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 98000 Blocks -- 0.001e18 * (1 + 98000 * 0.0005) + FastForward 9800000 Blocks -- 0.001e18 * (1 + 9800000 * 0.000005) AccrueInterest cETH -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cETH BorrowBalanceStored Geoff) 0.05e18 @@ -41,15 +41,15 @@ Test "Insufficient shortfall" Assert RevertFailure COMPTROLLER_REJECTION "revert liquidateBorrow failed" Test "Cannot self-liquidate" - NewBorrow borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrow borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrows) 0.001e18 -- Prices is 1:1000 and collateral factor is 0.5 -- thus supplies 100.0e18 cBAT which gives the user (0.5 * 100.0e18 * 1.0e-3)=0.05e18 -- capacity of Eth. User only borrowed 0.001e18, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 0.001e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 0.001e18 * (1 + 10000000 * 0.000005) AccrueInterest cETH -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cETH BorrowBalanceStored Geoff) 0.051e18 @@ -65,15 +65,15 @@ Test "Cannot self-liquidate" Assert RevertFailure INVALID_ACCOUNT_PAIR "revert liquidateBorrow failed" Test "Liqidate beyond max close" - NewBorrow borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrow borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrows) 0.001e18 -- Prices is 1:1000 and collateral factor is 0.5 -- thus supplies 100.0e18 cBAT which gives the user (0.5 * 100.0e18 * 1.0e-3)=0.05e18 -- capacity of Eth. User only borrowed 0.001e18, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 0.001e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 0.001e18 * (1 + 10000000 * 0.000005) AccrueInterest cETH -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cETH BorrowBalanceStored Geoff) 0.051e18 @@ -90,15 +90,15 @@ Test "Liqidate beyond max close" Assert RevertFailure COMPTROLLER_REJECTION "revert liquidateBorrow failed" Test "Proper liquidation" - NewBorrow borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrow borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrows) 0.001e18 -- Prices is 1:1000 and collateral factor is 0.5 -- thus supplies 100.0e18 cBAT which gives the user (0.5 * 100.0e18 * 1.0e-3)=0.05e18 -- capacity of Eth. User only borrowed 0.001e18, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 0.001e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 0.001e18 * (1 + 10000000 * 0.000005) AccrueInterest cETH -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cETH BorrowBalanceStored Geoff) 0.051e18 diff --git a/spec/scenario/LiquidateEthCollateral.scen b/spec/scenario/LiquidateEthCollateral.scen index cad8400fb..b2e33275a 100644 --- a/spec/scenario/LiquidateEthCollateral.scen +++ b/spec/scenario/LiquidateEthCollateral.scen @@ -1,7 +1,7 @@ Macro NewBorrow borrowAmount user=Geoff PricedComptroller 0.1 20 Comptroller LiquidationIncentive 1.1 - ListedCToken BAT cBAT borrowRate:0.0005 + ListedCToken BAT cBAT borrowRate:0.000005 ListedEtherToken cETH initialExchangeRate:0.05e9 PriceOracle SetPrice cBAT 0.001 Give cBAT 10e18 BAT @@ -22,9 +22,9 @@ Test "Insufficient shortfall" -- Prices is 1:1.0e-3 and collateral factor is 0.5 -- thus supplies 1e15 cETH which gives the user (0.5 x 1.0e15 ÷ 1.0e-3)=0.5 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 98000 Blocks -- 0.01e18 * (1 + 98000 * 0.0005) + FastForward 9800000 Blocks -- 0.01e18 * (1 + 9800000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 0.5e18 @@ -46,9 +46,9 @@ Test "Cannot self-liquidate" -- Prices is 1:1.0e-3 and collateral factor is 0.5 -- thus supplies 1e15 cETH which gives the user (0.5 x 1.0e15 ÷ 1.0e-3)=0.5 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 0.01e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 0.01e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 0.51e18 @@ -71,9 +71,9 @@ Test "Liqidate beyond max close" -- Prices is 1:1.0e-3 and collateral factor is 0.5 -- thus supplies 1e15 cETH which gives the user (0.5 x 1.0e15 ÷ 1.0e-3)=0.5 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 0.01e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 0.01e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 0.51e18 @@ -96,9 +96,9 @@ Test "Proper liquidation" -- Prices is 1:1.0e-3 and collateral factor is 0.5 -- thus supplies 1e15 cETH which gives the user (0.5 x 1.0e15 ÷ 1.0e-3)=0.5 -- capacity of BAT. User only borrowed 1BAT, but after - -- a few blocks at a 50% interest rate, he'll be + -- a lot of blocks at a 0.0005% interest rate, he'll be -- underwater. - FastForward 100000 Blocks -- 0.01e18 * (1 + 100000 * 0.0005) + FastForward 10000000 Blocks -- 0.01e18 * (1 + 10000000 * 0.000005) AccrueInterest cBAT -- Note: we have to accrue interest -- since it's not automatic for liquidity Assert Equal (CToken cBAT BorrowBalanceStored Geoff) 0.51e18 diff --git a/spec/scenario/Mint.scen b/spec/scenario/Mint.scen index e00130923..5975a9c16 100644 --- a/spec/scenario/Mint.scen +++ b/spec/scenario/Mint.scen @@ -88,3 +88,18 @@ Test "Denied by comptroller because unlisted" AllowFailures Mint Geoff 2e18 cZRX Assert Failure COMPTROLLER_REJECTION MINT_COMPTROLLER_REJECTION MARKET_NOT_LISTED + +Test "mint reverts if mint is paused" + NewComptroller + ListedCToken ZRX cZRX + Prep Geoff Some ZRX cZRX + Prep Torrey Some ZRX cZRX + Invariant Static (Erc20 cZRX TokenBalance Geoff) + Invariant Static (Erc20 cZRX TotalSupply) + Invariant Static (Erc20 ZRX TotalSupply) + Comptroller SetPauseGuardian Coburn + From Coburn (Comptroller SetGuardianPaused "Mint" True) + AllowFailures + Mint Geoff 2e18 cZRX + Assert Revert "revert mint is paused" + Comptroller SetGuardianPaused "Mint" False diff --git a/spec/scenario/PriceOracleProxy.scen b/spec/scenario/PriceOracleProxy.scen index 0f956be13..16677575a 100644 --- a/spec/scenario/PriceOracleProxy.scen +++ b/spec/scenario/PriceOracleProxy.scen @@ -1,10 +1,15 @@ Macro SetupPriceOracleProxy Unitroller Deploy PriceOracle Deploy Simple + -- Update to G1 + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address Zero) (Address Zero) (Address Zero) + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracleProxy Address) 0.1 20 + -- Update to G* ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address Zero) (Address Zero) (Address Zero) - ComptrollerImpl ScenComptroller Become (PriceOracleProxy Address) 0.1 20 + ComptrollerImpl ScenComptroller Become NewEtherToken cETH NewCToken USDC cUSDC NewCToken DAI cDAI diff --git a/spec/scenario/ReEntry.scen b/spec/scenario/ReEntry.scen index 1152a1174..a25c116ad 100644 --- a/spec/scenario/ReEntry.scen +++ b/spec/scenario/ReEntry.scen @@ -3,8 +3,8 @@ Test "ReEntry Mint" NewComptroller Erc20 Deploy ReEntrant PHREAK PHREAK "transferFrom" "mint(uint256)" "0" - InterestRateModel Deploy Fixed Std 0.0001 - CToken Deploy cPHREAK cPHREAK (Erc20 PHREAK Address) (Comptroller Address) (InterestRateModel Std Address) 1e9 8 + InterestRateModel Deploy Fixed Std 0.000001 + CToken Deploy cPHREAK cPHREAK (Erc20 PHREAK Address) (Comptroller Address) (InterestRateModel Std Address) 1e9 8 Admin Comptroller SupportMarket cPHREAK Prep Geoff Some PHREAK cPHREAK AllowFailures diff --git a/spec/scenario/Redeem.scen b/spec/scenario/Redeem.scen index 2dd24ec25..aa86005dd 100644 --- a/spec/scenario/Redeem.scen +++ b/spec/scenario/Redeem.scen @@ -90,10 +90,10 @@ Test "Mint then redeem with interest - no reserves" Assert Equal (CToken cZRX UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cZRX ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e18 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cZRX TokenBalance Geoff) 500e8 Assert Equal (CToken cZRX UnderlyingBalance Geoff) 55e18 @@ -116,10 +116,10 @@ Test "Mint then redeem part with interest - no reserves" Assert Equal (CToken cZRX UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cZRX ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e18 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cZRX TokenBalance Geoff) 500e8 Assert Equal (CToken cZRX UnderlyingBalance Geoff) 55e18 @@ -139,8 +139,8 @@ Test "Mint then redeem with reserves and interest" Assert Equal (CToken cZRX UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cZRX ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e18 of interest for the protocol -- The reserves should get 20% of this, or 1e18, and the rest -- is due pro-rata to all holders. We just have one, so diff --git a/spec/scenario/RedeemUnderlying.scen b/spec/scenario/RedeemUnderlying.scen index 79143fbb4..afea3436e 100644 --- a/spec/scenario/RedeemUnderlying.scen +++ b/spec/scenario/RedeemUnderlying.scen @@ -73,10 +73,10 @@ Test "Mint then redeem with interest - no reserves" Assert Equal (CToken cZRX UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cZRX ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e18 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cZRX TokenBalance Geoff) 500e8 Assert Equal (CToken cZRX UnderlyingBalance Geoff) 55e18 @@ -101,10 +101,10 @@ Test "Mint then redeem part with interest - no reserves" Assert Equal (CToken cZRX UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cZRX ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e18 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cZRX TokenBalance Geoff) 500e8 Assert Equal (CToken cZRX UnderlyingBalance Geoff) 55e18 @@ -126,11 +126,11 @@ Test "Mint then redeem with reserves and interest" Assert Equal (CToken cZRX UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cZRX ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e18 of interest for the protocol -- The reserves should get 20% of this, or 1e18, and the rest - -- is due pro-rata to all holders. We just have one, so + -- is due pro-rata to all suppliers. We just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cZRX TokenBalance Geoff) 500e8 Assert Equal (CToken cZRX Reserves) 1e18 diff --git a/spec/scenario/RedeemUnderlyingEth.scen b/spec/scenario/RedeemUnderlyingEth.scen index 0c9ed5ef9..c03ab5a8b 100644 --- a/spec/scenario/RedeemUnderlyingEth.scen +++ b/spec/scenario/RedeemUnderlyingEth.scen @@ -58,18 +58,17 @@ Test "Mint then Redeem Zero" FastForward 2 Blocks RedeemUnderlyingEth Geoff 0e18 cETH -Pending "Mint then redeem with interest - no reserves" +Test "Mint then redeem with interest - no reserves" Invariant Success NewComptroller - ListedCToken ZRX cETH initialExchangeRate:1e9 + ListedEtherToken cETH initialExchangeRate:1e9 Invariant Remains (CToken cETH Reserves) Zero - Prep Geoff 50e18 ZRX cETH - Mint Geoff 50e18 cETH + CallMintEth Geoff 50e18 cETH Assert Equal (Erc20 cETH TokenBalance Geoff) 500e8 Assert Equal (CToken cETH UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cETH ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cETH 10e18 5e18 interestRate:0.0001 blocks:5000 + BorrowAndRepayEthWithInterest cETH 10e18 5e18 interestRate:0.000001 blocks:500000 -- We've accrued 10% interest for 5 blocks, or 50% of the amount, -- thus, we should have accrued 5e18 of interest for the protocol -- This is due pro-rata to all holders, but we just have one, so @@ -78,9 +77,9 @@ Pending "Mint then redeem with interest - no reserves" Assert Equal (CToken cETH UnderlyingBalance Geoff) 55e18 Assert Equal (CToken cETH ExchangeRate) 1.1e9 -- Now redeem all with interest - Redeem Geoff 500e8 cETH - Assert Equal (Erc20 ZRX TokenBalance Geoff) 55e18 - Assert Equal (Erc20 ZRX TokenBalance cETH) 0e18 + Expect Changes (EtherBalance Geoff) 55e18 + RedeemUnderlyingEth Geoff 55e18 cETH + Assert Equal (EtherBalance cETH) 0e18 Assert Equal (Erc20 cETH TokenBalance Geoff) 0e8 Assert Equal (CToken cETH UnderlyingBalance Geoff) 0e18 Assert Equal (CToken cETH ExchangeRate) 1e9 @@ -96,7 +95,7 @@ Pending "Mint then redeem part with interest - no reserves" Assert Equal (CToken cETH UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cETH ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cETH 10e18 5e18 interestRate:0.0001 blocks:5000 + BorrowAndRepayWithInterest ZRX cETH 10e18 5e18 interestRate:0.000001 blocks:500000 -- We've accrued 10% interest for 5 blocks, or 50% of the amount, -- thus, we should have accrued 5e18 of interest for the protocol -- This is due pro-rata to all holders, but we just have one, so @@ -120,7 +119,7 @@ Pending "Mint then redeem with reserves and interest" Assert Equal (CToken cETH UnderlyingBalance Geoff) 50e18 Assert Equal (CToken cETH ExchangeRate) 1e9 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cETH 10e18 5e18 interestRate:0.0001 blocks:5000 reserveRate:0.2 + BorrowAndRepayWithInterest ZRX cETH 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 -- We've accrued 10% interest for 5 blocks, or 50% of the amount, -- thus, we should have accrued 5e18 of interest for the protocol -- The reserves should get 20% of this, or 1e18, and the rest diff --git a/spec/scenario/RedeemUnderlyingWBTC.scen b/spec/scenario/RedeemUnderlyingWBTC.scen index 02b7c4652..d3c058a5e 100644 --- a/spec/scenario/RedeemUnderlyingWBTC.scen +++ b/spec/scenario/RedeemUnderlyingWBTC.scen @@ -73,10 +73,10 @@ Test "Mint then redeem with interest - no reserves" Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 50e8 Assert Equal (CToken cWBTC ExchangeRate) 0.1 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e8 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cWBTC TokenBalance Geoff) 500e8 Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 55e8 @@ -101,10 +101,10 @@ Test "Mint then redeem part with interest - no reserves" Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 50e8 Assert Equal (CToken cWBTC ExchangeRate) 0.1 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e8 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cWBTC TokenBalance Geoff) 500e8 Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 55e8 @@ -126,11 +126,11 @@ Test "Mint then redeem with reserves and interest" Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 50e8 Assert Equal (CToken cWBTC ExchangeRate) 0.1 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e8 of interest for the protocol -- The reserves should get 20% of this, or 1e8, and the rest - -- is due pro-rata to all holders. We just have one, so + -- is due pro-rata to all suppliers. We just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cWBTC TokenBalance Geoff) 500e8 Assert Equal (CToken cWBTC Reserves) 1e8 diff --git a/spec/scenario/RedeemWBTC.scen b/spec/scenario/RedeemWBTC.scen index e6c415b90..b95ea0da2 100644 --- a/spec/scenario/RedeemWBTC.scen +++ b/spec/scenario/RedeemWBTC.scen @@ -90,10 +90,10 @@ Test "Mint WBTC then redeem with interest - no reserves" Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 50e8 Assert Equal (CToken cWBTC ExchangeRate) 0.1 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e8 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cWBTC TokenBalance Geoff) 500e8 Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 55e8 @@ -116,10 +116,10 @@ Test "Mint WBTC then redeem part with interest - no reserves" Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 50e8 Assert Equal (CToken cWBTC ExchangeRate) 0.1 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.0001 blocks:5000 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.000001 blocks:500000 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e8 of interest for the protocol - -- This is due pro-rata to all holders, but we just have one, so + -- This is due pro-rata to all suppliers, but we just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cWBTC TokenBalance Geoff) 500e8 Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 55e8 @@ -139,11 +139,11 @@ Test "Mint WBTC then redeem with reserves and interest" Assert Equal (CToken cWBTC UnderlyingBalance Geoff) 50e8 Assert Equal (CToken cWBTC ExchangeRate) 0.1 -- Get some brah to borrow then repay - BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest WBTC cWBTC 10e8 5e8 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .001% interest for 500000 blocks, or 50% of the principal, -- thus, we should have accrued 5e8 of interest for the protocol -- The reserves should get 20% of this, or 1e8, and the rest - -- is due pro-rata to all holders. We just have one, so + -- is due pro-rata to all suppliers. We just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 cWBTC TokenBalance Geoff) 500e8 Assert Equal (CToken cWBTC Reserves) 1e8 diff --git a/spec/scenario/ReduceReserves.scen b/spec/scenario/ReduceReserves.scen index 670cdba6d..1d290e2db 100644 --- a/spec/scenario/ReduceReserves.scen +++ b/spec/scenario/ReduceReserves.scen @@ -4,15 +4,15 @@ Test "Reduce all reserves and verify effects" ListedCToken ZRX cZRX initialExchangeRate:1e9 Prep Geoff 50e18 ZRX cZRX Mint Geoff 50e18 cZRX - Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 500e8) + Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 500e8) -- 50e18 / 1e9 Assert Equal (CToken cZRX UnderlyingBalance Geoff) (Exactly 50e18) Assert Equal (CToken cZRX ExchangeRate) (Exactly 1e9) -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .0001% interest for 500000 blocks, or 50% of the principal (10e18), -- thus, we should have accrued 5e18 of interest for the protocol -- The reserves should get 20% of this, or 1e18, and the rest - -- is due pro-rata to all holders. We just have one, so + -- is due pro-rata to all suppliers. We just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 55e18) Assert Equal (Erc20 ZRX TokenBalance Root) (Exactly 0e18) @@ -40,11 +40,11 @@ Test "Reduce partial reserves and verify effects" Assert Equal (CToken cZRX UnderlyingBalance Geoff) (Exactly 50e18) Assert Equal (CToken cZRX ExchangeRate) (Exactly 1e9) -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .0001% interest for 500000 blocks, or 50% of the principal (10e18), -- thus, we should have accrued 5e18 of interest for the protocol -- The reserves should get 20% of this, or 1e18, and the rest - -- is due pro-rata to all holders. We just have one, so + -- is due pro-rata to all suppliers. We just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 55e18) Assert Equal (Erc20 ZRX TokenBalance Root) (Exactly 0e18) @@ -72,11 +72,11 @@ Test "Redeem all and then reduce all reserves" Assert Equal (CToken cZRX UnderlyingBalance Geoff) (Exactly 50e18) Assert Equal (CToken cZRX ExchangeRate) (Exactly 1e9) -- Get some brah to borrow then repay - BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .0001% interest for 500000 blocks, or 50% of the principal (10e18), -- thus, we should have accrued 5e18 of interest for the protocol -- The reserves should get 20% of this, or 1e18, and the rest - -- is due pro-rata to all holders. We just have one, so + -- is due pro-rata to all suppliers. We just have one, so -- let's check that account is given correct new balance. Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 55e18) Assert Equal (Erc20 ZRX TokenBalance Root) (Exactly 0e18) @@ -116,11 +116,11 @@ Test "Reduce reserves WBTC when paused" Assert Equal (CToken cWBTC UnderlyingBalance Geoff) (Exactly 50e18) Assert Equal (CToken cWBTC ExchangeRate) (Exactly 1e9) -- Get some brah to borrow then repay - BorrowAndRepayWithInterest WBTC cWBTC 10e18 5e18 interestRate:0.0001 blocks:5000 reserveRate:0.2 - -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + BorrowAndRepayWithInterest WBTC cWBTC 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued .0001% interest for 500000 blocks, or 50% of the principal (10e18), -- thus, we should have accrued 5e18 of interest for the protocol -- The reserves should get 20% of this, or 1e18, and the rest - -- is due pro-rata to all holders. We just have one, so + -- is due pro-rata to all suppliers. We just have one, so -- let's check that account is given correct new balance. Invariant Remains (Erc20 WBTC TokenBalance cWBTC) (Exactly 55e18) Invariant Remains (Erc20 WBTC TokenBalance Root) (Exactly 0e18) diff --git a/spec/scenario/RepayBorrow.scen b/spec/scenario/RepayBorrow.scen index c90ecb35b..56b8355d2 100644 --- a/spec/scenario/RepayBorrow.scen +++ b/spec/scenario/RepayBorrow.scen @@ -13,13 +13,13 @@ Macro NewBorrow borrowAmount borrowRate Borrow Geoff borrowAmount cBAT Test "Borrow, hold a few blocks, and repay part" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 2.5e18 -- Now let's repay one full token From Geoff (Erc20 BAT Approve cBAT 1.0e18) @@ -29,18 +29,18 @@ Test "Borrow, hold a few blocks, and repay part" Assert Equal (Erc20 BAT TokenBalance Geoff) Zero Assert Equal (Erc20 BAT TokenBalance cBAT) 10e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1.5e18 - FastForward 2000 Blocks -- 1.5e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 1.5e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 3e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 3e18 Test "Borrow, hold a few blocks, and repay full" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 2.5e18 -- Now let's repay fully Give Geoff 1.5e18 BAT -- Geoff had the 1.0e18 borrowed BAT @@ -51,16 +51,16 @@ Test "Borrow, hold a few blocks, and repay full" Assert Equal (Erc20 BAT TokenBalance Geoff) 0e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 11.5e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 0e18 - FastForward 2000 Blocks -- 0e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 0e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 0e18 Test "Borrow, hold a few blocks, and repay too much" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 2.5e18 -- Now let's repay fully AllowFailures @@ -75,13 +75,13 @@ Test "Borrow, hold a few blocks, and repay too much" Test "Borrow, and get a negative total cash situation" Invariant Success - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 -- Let's zoom way forward into the future - FastForward 98000 Blocks -- 1e18 * (1 + 98000 * 0.0005) + FastForward 9800000 Blocks -- 1e18 * (1 + 9800000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 50e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 50e18 -- Now let's repay one full token @@ -94,13 +94,13 @@ Test "Borrow, and get a negative total cash situation" Test "Borrow, hold a few blocks, and repay behalf part" Invariant Success - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 9e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 2.5e18 -- Now let's repay one full token from another user Prep Torrey 1.0e18 BAT cBAT @@ -112,17 +112,17 @@ Test "Borrow, hold a few blocks, and repay behalf part" Assert Equal (Erc20 BAT TokenBalance Geoff) 1e18 Assert Equal (Erc20 BAT TokenBalance cBAT) 10e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1.5e18 - FastForward 2000 Blocks -- 1.5e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 1.5e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Torrey) Zero Assert Equal (CToken cBAT BorrowBalance Geoff) 3e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 3e18 Test "Prohibit repay by comptroller rejection due to mock unlist" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 2.5e18 -- Now let's repay fully AllowFailures @@ -137,11 +137,11 @@ Test "Prohibit repay by comptroller rejection due to mock unlist" Assert Equal (CToken cBAT TotalBorrowsCurrent) 2.5e18 Test "Repay fails with insufficient allowance" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 2.5e18 -- Now let's repay fully AllowFailures @@ -155,11 +155,11 @@ Test "Repay fails with insufficient allowance" Assert Equal (CToken cBAT TotalBorrowsCurrent) 2.5e18 Test "Repay fails with insufficient balance" - NewBorrow borrowAmount:1e18 borrowRate:0.0005 + NewBorrow borrowAmount:1e18 borrowRate:0.000005 Assert Equal (CToken cBAT BorrowBalance Geoff) 1e18 Assert Equal (CToken cBAT TotalBorrowsCurrent) 1e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cBAT BorrowBalance Geoff) 2.5e18 -- Now let's repay fully AllowFailures diff --git a/spec/scenario/RepayBorrowEth.scen b/spec/scenario/RepayBorrowEth.scen index 767466af2..695fd88be 100644 --- a/spec/scenario/RepayBorrowEth.scen +++ b/spec/scenario/RepayBorrowEth.scen @@ -16,11 +16,11 @@ Macro NewBorrowEth borrowAmount borrowRate Borrow Geoff borrowAmount cETH Test "Borrow, hold a few blocks, and repay part" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay one full token Expect Changes (EtherBalance Geoff) -0.001e18 @@ -29,17 +29,17 @@ Test "Borrow, hold a few blocks, and repay part" ---- Let's check the overall numbers Assert Equal (CToken cETH BorrowBalance Geoff) 0.0015e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.0015e18 - FastForward 2000 Blocks -- 0.0015e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0.0015e18 * (1 + 2000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.003e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.003e18 Test "Borrow, hold a few blocks, and repay part via maximillion" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Maximillion Deploy cETH Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay one full token Expect Changes (EtherBalance Geoff) -0.001e18 @@ -48,16 +48,16 @@ Test "Borrow, hold a few blocks, and repay part via maximillion" ---- Let's check the overall numbers Assert Equal (CToken cETH BorrowBalance Geoff) 0.0015e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.0015e18 - FastForward 2000 Blocks -- 0.0015e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0.0015e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.003e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.003e18 Test "Don't borrow and then do repay" - SetupBorrow borrowRate:0.0005 + SetupBorrow borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) Zero -- Now let's repay one full token Expect Changes (EtherBalance Geoff) Zero @@ -68,12 +68,12 @@ Test "Don't borrow and then do repay" Assert Equal (CToken cETH TotalBorrowsCurrent) Zero Test "Don't borrow and repay part via maximillion" - SetupBorrow borrowRate:0.0005 + SetupBorrow borrowRate:0.000005 Maximillion Deploy cETH Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) Zero -- Now let's repay one full token Expect Changes (EtherBalance Geoff) Zero @@ -84,11 +84,11 @@ Test "Don't borrow and repay part via maximillion" Assert Equal (CToken cETH TotalBorrowsCurrent) Zero Test "Borrow, hold a few blocks, and repay full" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay fully Expect Changes (EtherBalance Geoff) -0.0025e18 @@ -97,17 +97,17 @@ Test "Borrow, hold a few blocks, and repay full" -- Let's check the overall numbers Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero - FastForward 2000 Blocks -- 0e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero Test "Borrow, hold a few blocks, and repay full via maximillion" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Maximillion Deploy cETH Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay fully Expect Changes (EtherBalance Geoff) -0.0025e18 @@ -116,16 +116,16 @@ Test "Borrow, hold a few blocks, and repay full via maximillion" -- Let's check the overall numbers Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero - FastForward 2000 Blocks -- 0e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero Test "Borrow, hold a few blocks, and repay too much" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay fully Expect Changes (EtherBalance Geoff) Zero @@ -139,12 +139,12 @@ Test "Borrow, hold a few blocks, and repay too much" Assert Equal (CToken cETH TotalBorrowsCurrent) 0.0025e18 Test "Borrow, hold a few blocks, and repay too much via maximillion" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Maximillion Deploy cETH Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay fully Expect Changes (EtherBalance Geoff) -0.0025e18 @@ -153,16 +153,16 @@ Test "Borrow, hold a few blocks, and repay too much via maximillion" -- Let's check the overall numbers Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero - FastForward 2000 Blocks -- 0e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) Zero Assert Equal (CToken cETH TotalBorrowsCurrent) Zero Test "Borrow, and get a negative total cash situation" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Let's zoom way forward into the future - FastForward 98000 Blocks -- 0.001e18 * (1 + 98000 * 0.0005) + FastForward 9800000 Blocks -- 0.001e18 * (1 + 9800000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.05e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.05e18 -- Now let's repay one bit @@ -171,28 +171,28 @@ Test "Borrow, and get a negative total cash situation" Assert Equal (CToken cETH TotalBorrowsCurrent) 0.049e18 Test "Borrow, hold a few blocks, and repay behalf part" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay one full token from another user RepayBorrowEthBehalf Torrey Geoff 0.001e18 cETH Assert Equal (CToken cETH BorrowBalance Torrey) Zero Assert Equal (CToken cETH BorrowBalance Geoff) 0.0015e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.0015e18 - FastForward 2000 Blocks -- 0.0015e18 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0.0015e18 * (1 + 200000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Torrey) Zero Assert Equal (CToken cETH BorrowBalance Geoff) 0.003e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.003e18 Test "Prohibit repay by comptroller hook" - NewBorrowEth borrowAmount:0.001e18 borrowRate:0.0005 + NewBorrowEth borrowAmount:0.001e18 borrowRate:0.000005 Assert Equal (CToken cETH BorrowBalance Geoff) 0.001e18 Assert Equal (CToken cETH TotalBorrowsCurrent) 0.001e18 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cETH BorrowBalance Geoff) 0.0025e18 -- Now let's repay one full token Expect Changes (EtherBalance Geoff) Zero diff --git a/spec/scenario/RepayBorrowWBTC.scen b/spec/scenario/RepayBorrowWBTC.scen index b9381c12d..9b9b47689 100644 --- a/spec/scenario/RepayBorrowWBTC.scen +++ b/spec/scenario/RepayBorrowWBTC.scen @@ -13,13 +13,13 @@ Macro NewBorrow borrowAmount borrowRate Borrow Geoff borrowAmount cWBTC Test "Borrow WBTC, hold a few blocks, and repay part" - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 Assert Equal (Erc20 WBTC TokenBalance Geoff) 1e8 Assert Equal (Erc20 WBTC TokenBalance cWBTC) 9e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay one full token From Geoff (Erc20 WBTC Approve cWBTC 1.0e8) @@ -29,18 +29,18 @@ Test "Borrow WBTC, hold a few blocks, and repay part" Assert Equal (Erc20 WBTC TokenBalance Geoff) Zero Assert Equal (Erc20 WBTC TokenBalance cWBTC) 10e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1.5e8 - FastForward 2000 Blocks -- 1.5e8 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 1.5e8 * (1 + 200000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 3e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 3e8 Test "Borrow, hold a few blocks, and repay full" - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 Assert Equal (Erc20 WBTC TokenBalance Geoff) 1e8 Assert Equal (Erc20 WBTC TokenBalance cWBTC) 9e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e8 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e8 * (1 + 300000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay fully Give Geoff 1.5e8 WBTC -- Geoff had the 1.0e8 borrowed WBTC @@ -51,16 +51,16 @@ Test "Borrow, hold a few blocks, and repay full" Assert Equal (Erc20 WBTC TokenBalance Geoff) 0e8 Assert Equal (Erc20 WBTC TokenBalance cWBTC) 11.5e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 0e8 - FastForward 2000 Blocks -- 0e8 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 0e8 * (1 + 200000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 0e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 0e8 Test "Borrow, hold a few blocks, and repay too much" - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e8 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e8 * (1 + 300000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay fully AllowFailures @@ -75,13 +75,13 @@ Test "Borrow, hold a few blocks, and repay too much" Test "Borrow, and get a negative total cash situation" Invariant Success - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 Assert Equal (Erc20 WBTC TokenBalance Geoff) 1e8 Assert Equal (Erc20 WBTC TokenBalance cWBTC) 9e8 -- Let's zoom way forward into the future - FastForward 98000 Blocks -- 1e8 * (1 + 98000 * 0.0005) + FastForward 9800000 Blocks -- 1e8 * (1 + 9800000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 50e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 50e8 -- Now let's repay one full token @@ -94,13 +94,13 @@ Test "Borrow, and get a negative total cash situation" Test "Borrow, hold a few blocks, and repay behalf part" Invariant Success - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 Assert Equal (Erc20 WBTC TokenBalance Geoff) 1e8 Assert Equal (Erc20 WBTC TokenBalance cWBTC) 9e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e8 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e8 * (1 + 300000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay one full token from another user Prep Torrey 1.0e8 WBTC cWBTC @@ -112,17 +112,17 @@ Test "Borrow, hold a few blocks, and repay behalf part" Assert Equal (Erc20 WBTC TokenBalance Geoff) 1e8 Assert Equal (Erc20 WBTC TokenBalance cWBTC) 10e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1.5e8 - FastForward 2000 Blocks -- 1.5e8 * (1 + 2000 * 0.0005) + FastForward 200000 Blocks -- 1.5e8 * (1 + 200000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Torrey) Zero Assert Equal (CToken cWBTC BorrowBalance Geoff) 3e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 3e8 Test "Prohibit repay by comptroller rejection due to mock unlist" - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e8 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 0.001e8 * (1 + 300000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay fully AllowFailures @@ -137,13 +137,13 @@ Test "Prohibit repay by comptroller rejection due to mock unlist" Assert Equal (CToken cWBTC TotalBorrowsCurrent) 2.5e8 Test "Borrow WBTC, can't repay when paused" - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 Assert Equal (Erc20 WBTC TokenBalance Geoff) 1e8 Assert Equal (Erc20 WBTC TokenBalance cWBTC) 9e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 1e18 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e18 * (1 + 300000 * 0.000005) Invariant Remains (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay one full token From Geoff (Erc20 WBTC Approve cWBTC 1.0e8) @@ -153,11 +153,11 @@ Test "Borrow WBTC, can't repay when paused" Assert Revert Test "Repay fails with insufficient allowance" - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e8 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e8 * (1 + 300000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay fully AllowFailures @@ -171,11 +171,11 @@ Test "Repay fails with insufficient allowance" Assert Equal (CToken cWBTC TotalBorrowsCurrent) 2.5e8 Test "Repay fails with insufficient balance" - NewBorrow borrowAmount:1e8 borrowRate:0.0005 + NewBorrow borrowAmount:1e8 borrowRate:0.000005 Assert Equal (CToken cWBTC BorrowBalance Geoff) 1e8 Assert Equal (CToken cWBTC TotalBorrowsCurrent) 1e8 -- Now let's add some blocks and see what happs - FastForward 3000 Blocks -- 0.001e8 * (1 + 3000 * 0.0005) + FastForward 300000 Blocks -- 1e8 * (1 + 300000 * 0.000005) Assert Equal (CToken cWBTC BorrowBalance Geoff) 2.5e8 -- Now let's repay fully AllowFailures diff --git a/spec/scenario/Timelock.scen b/spec/scenario/Timelock.scen new file mode 100644 index 000000000..9db1ffd19 --- /dev/null +++ b/spec/scenario/Timelock.scen @@ -0,0 +1,208 @@ +-- Timelock Tests +Test "Reverts if calling setDelay directly" + Timelock Deploy Geoff 604800 + AllowFailures + Timelock SetDelay 604 + Assert Revert "revert Timelock::setDelay: Call must come from Timelock." + +Test "Reverts if calling setPendingAdmin directly" + Timelock Deploy Geoff 604800 + AllowFailures + Timelock SetPendingAdmin Jared + Assert Revert "revert Timelock::setPendingAdmin: Call must come from Timelock." + +Test "Reverts if calling acceptAdmin while not being pendingAdmin" + Timelock Deploy Geoff 604800 + AllowFailures + From Jared (Timelock AcceptAdmin) + Assert Revert "revert Timelock::acceptAdmin: Call must come from pendingAdmin." + +Test "Queuing and execute a transaction for setDelay" + -- The default blockTimestamp is 100 + -- Admin:Geoff Delay:1 week + Timelock Deploy Geoff 604800 + Assert Equal (Timelock Delay) 604800 + -- eta = 1 week (604800) + blockTimestamp (100) = 604900 + Assert False (Timelock QueuedTransaction (Timelock TxHash (Timelock Address) 0 604900 "setDelay(uint256)" "1209600")) + From Geoff (Timelock QueueTransaction (Timelock Address) 0 604900 "setDelay(uint256)" "1209600") + Assert True (Timelock QueuedTransaction (Timelock TxHash (Timelock Address) 0 604900 "setDelay(uint256)" "1209600")) + -- Now execute after delay time + Timelock FastForward 604801 + From Geoff (Timelock ExecuteTransaction (Timelock Address) 0 604900 "setDelay(uint256)" "1209600") + Assert False (Timelock QueuedTransaction (Timelock TxHash (Timelock Address) 0 604900 "setDelay(uint256)" "1209600")) + Assert Equal (Timelock Delay) 1209600 + +Test "Queuing and execute a transaction for setPendingAdmin" + -- The default blockTimestamp is 100 + -- Admin:Geoff Delay:1 week + Timelock Deploy Geoff 604800 + Assert Equal (Timelock Admin) (User Geoff Address) + Assert Equal (Timelock PendingAdmin) (Address Zero) + -- eta = 1 week (604800) + blockTimestamp (100) = 604900 + Assert False (Timelock QueuedTransaction (Timelock TxHash (Timelock Address) 0 604900 "setPendingAdmin(address)" (User Jared Address))) + From Geoff (Timelock QueueTransaction (Timelock Address) 0 604900 "setPendingAdmin(address)" (User Jared Address)) + Assert True (Timelock QueuedTransaction (Timelock TxHash (Timelock Address) 0 604900 "setPendingAdmin(address)" (User Jared Address))) + -- Now execute after delay time + Timelock FastForward 604801 + From Geoff (Timelock ExecuteTransaction (Timelock Address) 0 604900 "setPendingAdmin(address)" (User Jared Address)) + Assert False (Timelock QueuedTransaction (Timelock TxHash (Timelock Address) 0 604900 "setPendingAdmin(address)" (User Jared Address))) + Assert Equal (Timelock PendingAdmin) (User Jared Address) + From Jared (Timelock AcceptAdmin) + Assert Equal (Timelock Admin) (User Jared Address) + Assert Equal (Timelock PendingAdmin) (Address Zero) + +Test "Accept cToken admin from Timelock" + -- The default blockTimestamp is 100 + -- Admin:Geoff Delay:1 week + Timelock Deploy Geoff 604800 + Assert Equal (Timelock Admin) (User Geoff Address) + NewComptroller + NewCToken ZRX cZRX + Assert Equal (CToken cZRX Admin) (Address Root) + Assert Equal (CToken cZRX PendingAdmin) (Address Zero) + From Root (CToken cZRX SetPendingAdmin (Timelock Address)) + Assert Equal (CToken cZRX PendingAdmin) (Timelock Address) + -- eta = 1 week (604800) + blockTimestamp (100) = 604900 + Assert False (Timelock QueuedTransaction (Timelock TxHash (CToken cZRX Address) 0 604900 "_acceptAdmin()" "")) + From Geoff (Timelock QueueTransaction (CToken cZRX Address) 0 604900 "_acceptAdmin()" "") + Assert True (Timelock QueuedTransaction (Timelock TxHash (CToken cZRX Address) 0 604900 "_acceptAdmin()" "")) + -- Now execute after delay time + Timelock FastForward 604801 + From Geoff (Timelock ExecuteTransaction (CToken cZRX Address) 0 604900 "_acceptAdmin()" "") + Assert False (Timelock QueuedTransaction (Timelock TxHash (CToken cZRX Address) 0 604900 "_acceptAdmin()" "")) + Assert Equal (CToken cZRX Admin) (Timelock Address) + Assert Equal (CToken cZRX PendingAdmin) (Address Zero) + +Test "Accept unitroller admin from Timelock" + -- The default blockTimestamp is 100 + -- Admin:Geoff Delay:1 week + Timelock Deploy Geoff 604800 + Assert Equal (Timelock Admin) (User Geoff Address) + NewComptroller + Assert Equal (Unitroller Admin) (Address Root) + Assert Equal (Unitroller PendingAdmin) (Address Zero) + From Root (Unitroller SetPendingAdmin (Timelock Address)) + Assert Equal (Unitroller PendingAdmin) (Timelock Address) + -- eta = 1 week (604800) + blockTimestamp (100) = 604900 + Assert False (Timelock QueuedTransaction (Timelock TxHash (Unitroller Address) 0 604900 "_acceptAdmin()" "")) + From Geoff (Timelock QueueTransaction (Unitroller Address) 0 604900 "_acceptAdmin()" "") + Assert True (Timelock QueuedTransaction (Timelock TxHash (Unitroller Address) 0 604900 "_acceptAdmin()" "")) + -- Now execute after delay time + Timelock FastForward 604801 + From Geoff (Timelock ExecuteTransaction (Unitroller Address) 0 604900 "_acceptAdmin()" "") + Assert False (Timelock QueuedTransaction (Timelock TxHash (Unitroller Address) 0 604900 "_acceptAdmin()" "")) + Assert Equal (Unitroller Admin) (Timelock Address) + Assert Equal (Unitroller PendingAdmin) (Address Zero) + + +Test "Reduce reserves for CErc20 from Timelock and send reserves to external address" + NewComptroller + ListedCToken ZRX cZRX initialExchangeRate:1e9 + Prep Geoff 50e18 ZRX cZRX + Mint Geoff 50e18 cZRX + Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 500e8) + Assert Equal (CToken cZRX UnderlyingBalance Geoff) (Exactly 50e18) + Assert Equal (CToken cZRX ExchangeRate) (Exactly 1e9) + BorrowAndRepayWithInterest ZRX cZRX 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + -- thus, we should have accrued 5e18 of interest for the protocol + -- The reserves should get 20% of this, or 1e18, and the rest + -- is due pro-rata to all holders. We just have one, so + -- let's check that account is given correct new balance. + Timelock Deploy Jared 604800 + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 55e18) + Assert Equal (Erc20 ZRX TokenBalance (Timelock Address)) (Exactly 0e18) + Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 500e8) + Assert Equal (CToken cZRX Reserves) (Exactly 1e18) + Assert Equal (CToken cZRX UnderlyingBalance Geoff) (Exactly 54e18) + -- (55.0e18+0.0e18-1.0e18)/500.0e8 + Assert Equal (CToken cZRX ExchangeRate) (Exactly 1.08e9) + -- Set Timelock as admin + From Root (CToken cZRX SetPendingAdmin (Timelock Address)) + -- Queue Transactions + From Jared (Timelock QueueTransaction (CToken cZRX Address) 0 604900 "_acceptAdmin()" "") + From Jared (Timelock QueueTransaction (CToken cZRX Address) 0 604900 "_reduceReserves(uint256)" "1000000000000000000") + From Jared (Timelock QueueTransaction (Erc20 ZRX Address) 0 604900 "transfer(address,uint256)" "0x0000000000000000000000000000000000000001" "1000000000000000000") + Timelock FastForward 604802 + From Jared (Timelock ExecuteTransaction (CToken cZRX Address) 0 604900 "_acceptAdmin()" "") + -- Now, let's pull out all of our reserves (1e18) + From Jared (Timelock ExecuteTransaction (CToken cZRX Address) 0 604900 "_reduceReserves(uint256)" "1000000000000000000") + Assert Equal (Erc20 ZRX TokenBalance cZRX) (Exactly 54e18) + Assert Equal (Erc20 ZRX TokenBalance (Timelock Address)) (Exactly 1e18) + Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 500e8) + Assert Equal (CToken cZRX Reserves) (Exactly 0e18) + Assert Equal (CToken cZRX UnderlyingBalance Geoff) (Exactly 54e18) + -- (54.0e18+0.0e18-0.0e18)/500.0e8 + Assert Equal (CToken cZRX ExchangeRate) (Exactly 1.08e9) + Assert Equal (Erc20 ZRX TokenBalance "0x0000000000000000000000000000000000000001") (Exactly 0e18) + From Jared (Timelock ExecuteTransaction (Erc20 ZRX Address) 0 604900 "transfer(address,uint256)" "0x0000000000000000000000000000000000000001" "1000000000000000000") + Assert Equal (Erc20 ZRX TokenBalance (Timelock Address)) (Exactly 0e18) + Assert Equal (Erc20 ZRX TokenBalance "0x0000000000000000000000000000000000000001") (Exactly 1e18) + +Test "Reduce reserves for CEther from Timelock and send reserves to external address" + NewComptroller + ListedEtherToken cETH initialExchangeRate:1e9 + CallMintEth Geoff 50e18 cETH + Assert Equal (Erc20 cETH TokenBalance Geoff) (Exactly 500e8) + Assert Equal (CToken cETH UnderlyingBalance Geoff) (Exactly 50e18) + Assert Equal (CToken cETH ExchangeRate) (Exactly 1e9) + BorrowAndRepayEthWithInterest cETH 10e18 5e18 interestRate:0.000001 blocks:500000 reserveRate:0.2 + -- We've accrued 10% interest for 5 blocks, or 50% of the amount, + -- thus, we should have accrued 5e18 of interest for the protocol + -- The reserves should get 20% of this, or 1e18, and the rest + -- is due pro-rata to all holders. We just have one, so + -- let's check that account is given correct new balance. + Timelock Deploy Jared 604800 + Assert Equal (EtherBalance cETH) (Exactly 55e18) + Assert Equal (EtherBalance (Timelock Address)) (Exactly 0e18) + Assert Equal (Erc20 cETH TokenBalance Geoff) (Exactly 500e8) + Assert Equal (CToken cETH Reserves) (Exactly 1e18) + Assert Equal (CToken cETH UnderlyingBalance Geoff) (Exactly 54e18) + -- (55.0e18+0.0e18-1.0e18)/500.0e8 + Assert Equal (CToken cETH ExchangeRate) (Exactly 1.08e9) + -- Set Timelock as admin + From Root (CToken cETH SetPendingAdmin (Timelock Address)) + -- Queue Transactions + From Jared (Timelock QueueTransaction (CToken cETH Address) 0 604900 "_acceptAdmin()" "") + From Jared (Timelock QueueTransaction (CToken cETH Address) 0 604900 "_reduceReserves(uint256)" "1000000000000000000") + From Jared (Timelock QueueTransaction Jared 1000000000000000000 604900 "" "") + Timelock FastForward 604802 + From Jared (Timelock ExecuteTransaction (CToken cETH Address) 0 604900 "_acceptAdmin()" "") + -- Now, let's pull out all of our reserves (1e18) + From Jared (Timelock ExecuteTransaction (CToken cETH Address) 0 604900 "_reduceReserves(uint256)" "1000000000000000000") + Assert Equal (EtherBalance (Timelock Address)) 1e18 + Assert Equal (EtherBalance cETH) (Exactly 54e18) + Assert Equal (Erc20 cETH TokenBalance Geoff) (Exactly 500e8) + Assert Equal (CToken cETH Reserves) (Exactly 0e18) + Assert Equal (CToken cETH UnderlyingBalance Geoff) (Exactly 54e18) + -- (54.0e18+0.0e18-0.0e18)/500.0e8 + Assert Equal (CToken cETH ExchangeRate) (Exactly 1.08e9) + Expect Changes (EtherBalance Jared) 1e18 + Trx GasPrice 0 (From Jared (Timelock ExecuteTransaction Jared 1000000000000000000 604900 "" "")) + Assert Equal (EtherBalance (Timelock Address)) (Exactly 0e18) + +Test "Set Pending Comptroller implemention on Unitroller from Timelock" + Unitroller Deploy + PriceOracle Deploy Simple + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + Assert Equal (Unitroller PendingImplementation) (ComptrollerImpl ScenComptrollerG1 Address) + PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address Zero) (Address Zero) (Address Zero) -- Final argument is a Junk address, if listing cEther use ListedEtherToken to replace proxy + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracleProxy Address) 0.1 20 + Assert Equal (Unitroller PendingImplementation) (Address Zero) + Assert Equal (Unitroller Implementation) (Address ScenComptrollerG1) + Timelock Deploy Coburn 604800 + ComptrollerImpl Deploy Scenario ScenComptroller + Unitroller SetPendingImpl ScenComptroller + Unitroller SetPendingAdmin (Timelock Address) + Assert Equal (Unitroller PendingAdmin) (Timelock Address) + Assert Equal (Unitroller PendingImplementation) (ComptrollerImpl ScenComptroller Address) + From Coburn (Timelock QueueTransaction (Unitroller Address) 0 604900 "_acceptAdmin()" "") + From Coburn (Timelock QueueTransaction (ComptrollerImpl ScenComptroller Address) 0 604900 "_become(address)" (Unitroller Address)) + Timelock FastForward 604802 + From Coburn (Timelock ExecuteTransaction (Unitroller Address) 0 604900 "_acceptAdmin()" "") + Assert Equal (Unitroller Admin) (Timelock Address) + Assert Equal (Unitroller PendingAdmin) (Address Zero) + From Coburn (Timelock ExecuteTransaction (ComptrollerImpl ScenComptroller Address) 0 604900 "_become(address)" (Unitroller Address)) + Assert Equal (Unitroller Implementation) (Address ScenComptroller) + Assert Equal (Unitroller PendingImplementation) (Address Zero) diff --git a/spec/scenario/TokenTransfer.scen b/spec/scenario/TokenTransfer.scen index 1c5b01d4b..e480747ce 100644 --- a/spec/scenario/TokenTransfer.scen +++ b/spec/scenario/TokenTransfer.scen @@ -84,3 +84,25 @@ Test "cToken Transfer From Not Allowed" Erc20 cZRX TransferFrom Geoff Torrey 10e9 Assert Failure MATH_ERROR TRANSFER_NOT_ALLOWED +Test "cToken Transfer paused" + NewComptroller + ListedCToken ZRX cZRX initialExchangeRate:1e9 + Prep Geoff Some ZRX cZRX + Mint Geoff 50e18 cZRX + -- Just to be sure, check initial balances + Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 50e9) + Assert Equal (Erc20 cZRX TokenBalance Coburn) Zero + -- Pause and attempt transfer + Comptroller SetPauseGuardian Coburn + From Coburn (Comptroller SetGuardianPaused "Transfer" True) + AllowFailures + Transfer Geoff Torrey 10e9 cZRX + Assert Revert "revert transfer is paused" + Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 50e9) + Assert Equal (Erc20 cZRX TokenBalance Coburn) Zero + -- unPause and complete transfer + Invariant Success + Comptroller SetGuardianPaused "Transfer" False + Transfer Geoff Coburn 10e9 cZRX + Assert Equal (Erc20 cZRX TokenBalance Geoff) (Exactly 40e9) + Assert Equal (Erc20 cZRX TokenBalance Coburn) (Exactly 10e9) diff --git a/spec/scenario/Unitroller.scen b/spec/scenario/Unitroller.scen index 941d0299b..7cde06a9c 100644 --- a/spec/scenario/Unitroller.scen +++ b/spec/scenario/Unitroller.scen @@ -2,58 +2,252 @@ Test "Standard Upgrade" Unitroller Deploy PriceOracle Deploy Fixed 1.0 + -- Upgrade to G1 + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracle Address) 0.2 20 + Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (Comptroller MaxAssets) 20 + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1) + -- Upgrade to G* ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become (PriceOracle Address) 0.2 20 + ComptrollerImpl ScenComptroller Become Assert Equal (Comptroller CloseFactor) 0.2 Assert Equal (Comptroller MaxAssets) 20 Assert Equal (Comptroller Implementation) (Address ScenComptroller) -Test "Once become, can become again" +Test "Standard Upgrade, then downgrade then upgrade again" Unitroller Deploy PriceOracle Deploy Fixed 1.0 + -- Upgrade to G1 + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracle Address) 0.2 20 + Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (Comptroller MaxAssets) 20 + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1) + -- Upgrade to G* ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become (PriceOracle Address) 0.2 20 + ComptrollerImpl ScenComptroller Become + Comptroller SetPauseGuardian Coburn + Assert Equal (Comptroller PauseGuardian) (Address Coburn) + Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (Comptroller MaxAssets) 20 + Assert Equal (Comptroller Implementation) (Address ScenComptroller) + -- Downgrade to G1 + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 Recome + Assert ReadRevert (Comptroller PauseGuardian) "revert" + Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (Comptroller MaxAssets) 20 + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1) + -- Upgrade again + Unitroller SetPendingImpl ScenComptroller + ComptrollerImpl ScenComptroller Become + Assert Equal (Comptroller PauseGuardian) (Address Coburn) Assert Equal (Comptroller CloseFactor) 0.2 Assert Equal (Comptroller MaxAssets) 20 Assert Equal (Comptroller Implementation) (Address ScenComptroller) - ComptrollerImpl Deploy Scenario ScenComptroller2 - Unitroller SetPendingImpl ScenComptroller2 - ComptrollerImpl ScenComptroller2 Become (PriceOracle Address) 0.4 40 + +Test "Once become, can become again" + Unitroller Deploy + PriceOracle Deploy Fixed 1.0 + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracle Address) 0.2 20 + Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (Comptroller MaxAssets) 20 + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1) + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1_2 + Unitroller SetPendingImpl ScenComptrollerG1_2 + ComptrollerImpl ScenComptrollerG1_2 BecomeG1 (PriceOracle Address) 0.4 40 Assert Equal (Comptroller CloseFactor) 0.4 Assert Equal (Comptroller MaxAssets) 40 - Assert Equal (Comptroller Implementation) (Address ScenComptroller2) + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1_2) Test "Recome has default values" Unitroller Deploy PriceOracle Deploy Fixed 1.0 - ComptrollerImpl Deploy Scenario ScenComptroller - Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Recome + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 Recome Assert Equal (Comptroller CloseFactor) 0.0 Assert Equal (Comptroller MaxAssets) 0 - Assert Equal (Comptroller Implementation) (Address ScenComptroller) + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1) Test "Bork and unbork" Unitroller Deploy PriceOracle Deploy Fixed 1.0 - ComptrollerImpl Deploy Scenario ScenComptroller + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 -- Set a normal impl - Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Become (PriceOracle Address) 0.2 20 + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracle Address) 0.2 20 Assert Equal (Comptroller CloseFactor) 0.2 Assert Equal (Comptroller MaxAssets) 20 - Assert Equal (Comptroller Implementation) (Address ScenComptroller) + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1) -- Now move to a borked one that's bad ComptrollerImpl Deploy Borked MyBork Unitroller SetPendingImpl MyBork - ComptrollerImpl MyBork Become (PriceOracle Address) 0.4 40 + ComptrollerImpl MyBork BecomeG1 (PriceOracle Address) 0.4 40 Assert ReadRevert (Comptroller CloseFactor) "revert" Assert Equal (Comptroller Implementation) (Address MyBork) -- Now change back to the first good one + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 Recome + Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (Comptroller MaxAssets) 20 + Assert Equal (Comptroller Implementation) (Address ScenComptrollerG1) + +Test "Keeps all storage" + Unitroller Deploy + PriceOracle Deploy Fixed 1.0 + ComptrollerImpl Deploy ScenarioG1 ScenComptrollerG1 + -- + Unitroller SetPendingImpl ScenComptrollerG1 + ComptrollerImpl ScenComptrollerG1 BecomeG1 (PriceOracle Address) 0.2 20 + NewCToken ZRX cZRX + NewCToken BAT cBAT + Give cBAT 10e18 BAT -- Faucet some bat to borrow + Support cZRX collateralFactor:0.5 + Support cBAT collateralFactor:0.4 + Prep Geoff Some ZRX cZRX + Mint Geoff 100e18 cZRX + EnterMarkets Geoff cZRX cBAT + Borrow Geoff 1e18 cBAT + -- + -- Unitroller Storage + -- + -- Admin; 0 + Assert Equal (Comptroller Admin) (Address Root) + Assert Equal (StorageAt Comptroller 0 0 "address") (Address Root) + -- PendingAdmin; 1 + Comptroller SetPendingAdmin Coburn + Assert Equal (Comptroller PendingAdmin) (Address Coburn) + Assert Equal (StorageAt Comptroller 1 0 "address") (Address Coburn) + -- ComptrollerImplementation; 2 + Assert Equal (Unitroller Implementation) (Address ScenComptrollerG1) + Assert Equal (StorageAt Comptroller 2 0 "address") (Address ScenComptrollerG1) + -- PendingComptrollerImplementation; 3 + ComptrollerImpl Deploy Scenario ScenComptroller Unitroller SetPendingImpl ScenComptroller - ComptrollerImpl ScenComptroller Recome + Assert Equal (Unitroller PendingImplementation) (Address ScenComptroller) + Assert Equal (StorageAt Comptroller 3 0 "address") (Address ScenComptroller) + -- + -- V1 Storage + -- + -- Oracle; 4 + Assert Equal (Comptroller PriceOracle) (Address PriceOracle) + Assert Equal (StorageAt Comptroller 4 0 "address") (Address PriceOracle) + -- CloseFactorMantissa; 5 Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (StorageAt Comptroller 5 0 "number") 0.2 + -- LiquidationIncentiveMantissa; 6 + Assert Equal (Comptroller LiquidationIncentive) 1 + Assert Equal (StorageAt Comptroller 6 0 "number") 1 + -- MaxAssets; 7 Assert Equal (Comptroller MaxAssets) 20 - Assert Equal (Comptroller Implementation) (Address ScenComptroller) + Assert Equal (StorageAt Comptroller 7 0 "number") 20 + -- + -- AccountAssets; 8 * + Assert Equal (StorageAt Comptroller 8 0 "number") 0 + Assert Equal (StorageAtMapping Comptroller 8 (Address Geoff) "number") 2 + Assert Equal (StorageAtMapping Comptroller 8 (Address Geoff) "list(address)") [(Address cZRX) (Address cBAT)] + Assert Equal (Comptroller AssetsIn Geoff) [(Address cZRX) (Address cBAT)] + -- Markets; 9 * + Assert Equal (StorageAt Comptroller 9 0 "number") 0 + Assert Equal (StorageAtNestedMapping Comptroller 9 (Address cZRX) Geoff "marketStruct") [True 0.5 True] + Assert Equal (StorageAtNestedMapping Comptroller 9 (Address cBAT) Geoff "marketStruct") [True 0.4 True] + Assert Equal (StorageAtNestedMapping Comptroller 9 (Address cBAT) Coburn "marketStruct") [True 0.4 False] + Assert Equal (Comptroller CollateralFactor cZRX) 0.5 + Assert Equal (Comptroller CollateralFactor cBAT) 0.4 + Assert Equal (Comptroller CheckMembership Geoff cZRX) True + Assert Equal (Comptroller CheckMembership Geoff cBAT) True + -- + ComptrollerImpl ScenComptroller Become + -- + -- Recheck all unitroller and v1 storage + -- + -- Unitroller Storage again + -- + -- Admin; 0 + Assert Equal (StorageAt Comptroller 0 0 "address") (Address Root) + -- PendingAdmin; 1 + Assert Equal (StorageAt Comptroller 1 0 "address") (Address Coburn) + -- ComptrollerImplementation; 2 + Assert Equal (Unitroller Implementation) (Address ScenComptroller) + Assert Equal (StorageAt Comptroller 2 0 "address") (Address ScenComptroller) + -- PendingComptrollerImplementation; 3 + -- check as number since casting address 0 is not defined + Assert Equal (StorageAt Comptroller 3 0 "number") 0 + -- + -- V1 Storage again + -- + -- Oracle; 4 + Assert Equal (Comptroller PriceOracle) (Address PriceOracle) + Assert Equal (StorageAt Comptroller 4 0 "address") (Address PriceOracle) + -- + -- CloseFactorMantissa; 5 + Assert Equal (Comptroller CloseFactor) 0.2 + Assert Equal (StorageAt Comptroller 5 0 "number") 0.2 + -- + -- LiquidationIncentiveMantissa; 6 + Assert Equal (Comptroller LiquidationIncentive) 1 + Assert Equal (StorageAt Comptroller 6 0 "number") 1 + -- + -- MaxAssets; 7 + Assert Equal (Comptroller MaxAssets) 20 + Assert Equal (StorageAt Comptroller 7 0 "number") 20 + -- + -- AccountAssets; 8 * + Assert Equal (StorageAt Comptroller 8 0 "number") 0 + Assert Equal (StorageAtMapping Comptroller 8 (Address Geoff) "number") 2 + Assert Equal (StorageAtMapping Comptroller 8 (Address Geoff) "list(address)") [(Address cZRX) (Address cBAT)] + Assert Equal (Comptroller AssetsIn Geoff) [(Address cZRX) (Address cBAT)] + -- Markets; 9 * + Assert Equal (Comptroller CollateralFactor cZRX) 0.5 + Assert Equal (Comptroller CollateralFactor cBAT) 0.4 + Assert Equal (Comptroller CheckMembership Geoff cZRX) True + Assert Equal (Comptroller CheckMembership Geoff cBAT) True + Assert Equal (Comptroller CheckMembership Coburn cBAT) False + Assert Equal (StorageAt Comptroller 9 0 "number") 0 + Assert Equal (StorageAtNestedMapping Comptroller 9 (Address cZRX) Geoff "marketStruct") [True 0.5 True] + Assert Equal (StorageAtNestedMapping Comptroller 9 (Address cBAT) Geoff "marketStruct") [True 0.4 True] + Assert Equal (StorageAtNestedMapping Comptroller 9 (Address cBAT) Coburn "marketStruct") [True 0.4 False] + Assert Equal (Comptroller CollateralFactor cZRX) 0.5 + Assert Equal (Comptroller CollateralFactor cBAT) 0.4 + Assert Equal (Comptroller CheckMembership Geoff cZRX) True + Assert Equal (Comptroller CheckMembership Geoff cBAT) True + -- + -- V2 Storage + -- + -- PauseGuardian; 10 + Comptroller SetPauseGuardian Coburn + Assert Equal (Comptroller PauseGuardian) (Address Coburn) + Assert Equal (StorageAt Comptroller 10 0 "address") (Address Coburn) + -- + -- MintGuardianPaused; 11 + Assert Equal (StorageAt Comptroller 10 40 "bool") False + Assert Equal (Comptroller MintGuardianPaused) False + From Coburn (Comptroller SetGuardianPaused "Mint" True) + Assert Equal (Comptroller MintGuardianPaused) True + Assert Equal (StorageAt Comptroller 10 40 "bool") True + -- + -- BorrowGuardianPaused; 12 + Assert Equal (StorageAt Comptroller 10 42 "bool") False + From Coburn (Comptroller SetGuardianPaused "Borrow" True) + Assert Equal (Comptroller BorrowGuardianPaused) True + Assert Equal (StorageAt Comptroller 10 42 "bool") True + -- + -- TransferGuardianPaused; 13 + Assert Equal (StorageAt Comptroller 10 44 "bool") False + From Coburn (Comptroller SetGuardianPaused "Transfer" True) + Assert Equal (Comptroller TransferGuardianPaused) True + Assert Equal (StorageAt Comptroller 10 44 "bool") True + -- + -- SeizeGuardianPaused; 14 + Assert Equal (StorageAt Comptroller 10 46 "bool") False + From Coburn (Comptroller SetGuardianPaused "Seize" True) + Assert Equal (Comptroller SeizeGuardianPaused) True + Assert Equal (StorageAt Comptroller 10 46 "bool") True diff --git a/test/Comptroller/accountLiquidityTest.js b/test/Comptroller/accountLiquidityTest.js index 3390d9472..1e9f1f5d5 100644 --- a/test/Comptroller/accountLiquidityTest.js +++ b/test/Comptroller/accountLiquidityTest.js @@ -114,4 +114,4 @@ contract('Comptroller', ([root, ...accounts]) => { assert.equal(shortfall, 0, "shortfall should be zero"); }); }); -}); \ No newline at end of file +}); diff --git a/test/Comptroller/assetsListTest.js b/test/Comptroller/assetsListTest.js index 7e5c0c92f..1b66f387a 100644 --- a/test/Comptroller/assetsListTest.js +++ b/test/Comptroller/assetsListTest.js @@ -183,4 +183,36 @@ contract('assetListTest', function([root, customer, ...accounts]) { await exitAndCheckMarkets(ZRX, [OMG, BAT], 'NO_ERROR'); }); }); + + describe('entering from borrowAllowed', async () => { + it("enters when called by a ctoken", async () => { + await send(BAT, 'harnessCallBorrowAllowed', [1], {from: customer}); + + const assetsIn = await call(comptroller, 'getAssetsIn', [customer]); + + assert.deepEqual([BAT._address], assetsIn); + + await checkMarkets([BAT]); + }); + + it("reverts when called by not a ctoken", async () => { + await assert.revert(send(comptroller, 'borrowAllowed', [BAT._address, customer, 1], {from: customer}), 'revert sender must be cToken'); + + const assetsIn = await call(comptroller, 'getAssetsIn', [customer]); + + assert.deepEqual([], assetsIn); + + await checkMarkets([]); + }); + + it("adds to the asset list only once", async () => { + await send(BAT, 'harnessCallBorrowAllowed', [1], {from: customer}); + + await enterAndCheckMarkets([BAT], [BAT]); + + await send(BAT, 'harnessCallBorrowAllowed', [1], {from: customer}); + const assetsIn = await call(comptroller, 'getAssetsIn', [customer]); + assert.deepEqual([BAT._address], assetsIn); + }); + }); }); diff --git a/test/Comptroller/comptrollerTest.js b/test/Comptroller/comptrollerTest.js index b183b58ba..bee390575 100644 --- a/test/Comptroller/comptrollerTest.js +++ b/test/Comptroller/comptrollerTest.js @@ -26,10 +26,6 @@ contract('Comptroller', ([root, ...accounts]) => { assert.equal(10, await call(comptroller, 'maxAssets')); }); - it("reverts on invalid closeFactor", async () => { - await assert.revert(makeComptroller({closeFactor: 1}), 'revert set close factor error'); - }); - it("allows small and large maxAssets", async () => { const comptroller = await makeComptroller({maxAssets: 0}); assert.equal(0, await call(comptroller, 'maxAssets')); @@ -59,7 +55,7 @@ contract('Comptroller', ([root, ...accounts]) => { 'UNAUTHORIZED', 'SET_LIQUIDATION_INCENTIVE_OWNER_CHECK' ); - assert.equal(await call(comptroller, 'liquidationIncentiveMantissa'), initialIncentive); + assert.numEqual(await call(comptroller, 'liquidationIncentiveMantissa'), initialIncentive); }); it("fails if incentive is less than min", async () => { @@ -70,7 +66,7 @@ contract('Comptroller', ([root, ...accounts]) => { 'INVALID_LIQUIDATION_INCENTIVE', 'SET_LIQUIDATION_INCENTIVE_VALIDATION' ); - assert.equal(await call(comptroller, 'liquidationIncentiveMantissa'), initialIncentive); + assert.numEqual(await call(comptroller, 'liquidationIncentiveMantissa'), initialIncentive); }); it("fails if incentive is greater than max", async () => { @@ -81,7 +77,7 @@ contract('Comptroller', ([root, ...accounts]) => { 'INVALID_LIQUIDATION_INCENTIVE', 'SET_LIQUIDATION_INCENTIVE_VALIDATION' ); - assert.equal(await call(comptroller, 'liquidationIncentiveMantissa'), initialIncentive); + assert.numEqual(await call(comptroller, 'liquidationIncentiveMantissa'), initialIncentive); }); it("accepts a valid incentive and emits a NewLiquidationIncentive event", async () => { @@ -91,7 +87,7 @@ contract('Comptroller', ([root, ...accounts]) => { oldLiquidationIncentiveMantissa: initialIncentive.toString(), newLiquidationIncentiveMantissa: validIncentive.toString() }); - assert.equal(await call(comptroller, 'liquidationIncentiveMantissa'), validIncentive); + assert.numEqual(await call(comptroller, 'liquidationIncentiveMantissa'), validIncentive); }); }); @@ -134,6 +130,35 @@ contract('Comptroller', ([root, ...accounts]) => { }); }); + describe('_setCloseFactor', async () => { + it("fails if not called by admin", async () => { + const cToken = await makeCToken(); + assert.hasTrollFailure( + await send(cToken.comptroller, '_setCloseFactor', [1], {from: accounts[0]}), + 'UNAUTHORIZED', + 'SET_CLOSE_FACTOR_OWNER_CHECK' + ); + }); + + it("fails if close factor too low", async () => { + const cToken = await makeCToken(); + assert.hasTrollFailure( + await send(cToken.comptroller, '_setCloseFactor', [1]), + 'INVALID_CLOSE_FACTOR', + 'SET_CLOSE_FACTOR_VALIDATION' + ); + }); + + it("fails if close factor too low", async () => { + const cToken = await makeCToken(); + assert.hasTrollFailure( + await send(cToken.comptroller, '_setCloseFactor', [etherMantissa(1e18)]), + 'INVALID_CLOSE_FACTOR', + 'SET_CLOSE_FACTOR_VALIDATION' + ); + }); + }); + describe('_setCollateralFactor', async () => { const half = etherMantissa(0.5), one = etherMantissa(1); diff --git a/test/Comptroller/pauseGuardianTest.js b/test/Comptroller/pauseGuardianTest.js new file mode 100644 index 000000000..366c3d6d7 --- /dev/null +++ b/test/Comptroller/pauseGuardianTest.js @@ -0,0 +1,119 @@ +const { address, both, call, etherMantissa, send } = require('../Utils/MochaTruffle'); + +const { makeComptroller, makePriceOracle, makeCToken, makeToken } = require('../Utils/Compound'); + +contract('Comptroller', ([root, ...accounts]) => { + let comptroller; + + describe("_setPauseGuardian", async () => { + before(async () => { + comptroller = await makeComptroller(); + }); + + describe("failing", async () => { + it("emits a failure log if not sent by admin", async () => { + let result = await send(comptroller, '_setPauseGuardian', [root], {from: accounts[1]}); + assert.hasTrollFailure( + result, + 'UNAUTHORIZED', + 'SET_PAUSE_GUARDIAN_OWNER_CHECK' + ); + }); + + it("does not change the pause guardian", async () => { + let pauseGuardian = await call(comptroller, 'pauseGuardian'); + assert.equal(pauseGuardian, address(0)); + await send(comptroller, '_setPauseGuardian', [root], {from: accounts[1]}); + + pauseGuardian = await call(comptroller, 'pauseGuardian'); + assert.equal(pauseGuardian, address(0)); + }); + }); + + + describe('succesfully changing pause guardian', async () => { + let result; + + beforeEach(async () => { + comptroller = await makeComptroller(); + + result = await send(comptroller, '_setPauseGuardian', [accounts[1]]); + }); + + it('emits new pause guardian event', async () => { + assert.hasLog(result, 'NewPauseGuardian', {newPauseGuardian: accounts[1], oldPauseGuardian: address(0)}); + }); + + it('changes pending pause guardian', async () => { + let pauseGuardian = await call(comptroller, 'pauseGuardian'); + assert.equal(pauseGuardian, accounts[1]); + }); + }); + }); + + describe('setting paused', async () => { + before(async () => { + comptroller = await makeComptroller(); + }); + + let methods = ["Borrow", "Mint", "Transfer", "Seize"]; + describe('succeeding', async() => { + let pauseGuardian; + before(async () => { + pauseGuardian = accounts[1]; + await send(comptroller, '_setPauseGuardian', [accounts[1]], {from: root}); + }); + + methods.forEach(async (method) => { + it(`only pause guardian or admin can pause ${method}`, async () => { + await assert.revert(send(comptroller, `_set${method}Paused`, [true], {from: accounts[2]}), "revert only pause guardian and admin can pause"); + await assert.revert(send(comptroller, `_set${method}Paused`, [false], {from: accounts[2]}), "revert only pause guardian and admin can pause"); + }); + + it(`PauseGuardian can pause of ${method}GuardianPaused`, async () => { + result = await send(comptroller, `_set${method}Paused`, [true], {from: pauseGuardian}); + assert.hasLog(result, `ActionPaused`, {action: method, pauseState: true}); + + let camelCase = method.charAt(0).toLowerCase() + method.substring(1); + + state = await call(comptroller, `${camelCase}GuardianPaused`); + assert.equal(state, true); + + await assert.revert(send(comptroller, `_set${method}Paused`, [false], {from: pauseGuardian}), "revert only admin can unpause"); + result = await send(comptroller, `_set${method}Paused`, [false]); + + assert.hasLog(result, `ActionPaused`, {action: method, pauseState: false}); + + state = await call(comptroller, `${camelCase}GuardianPaused`); + assert.equal(state, false); + }); + + it(`pauses ${method}`, async() => { + await send(comptroller, `_set${method}Paused`, [true], {from: pauseGuardian}); + + let camelCase = method.charAt(0).toLowerCase() + method.substring(1); + switch (method) { + case "Mint": + await assert.revert(send(comptroller, `${camelCase}Allowed`, [address(1), address(2), 1]), `revert ${method.toLowerCase()} is paused`); + break; + + case "Borrow": + await assert.revert(send(comptroller, `${camelCase}Allowed`, [address(1), address(2), 1]), `revert ${method.toLowerCase()} is paused`); + break; + + case "Transfer": + await assert.revert(send(comptroller, `${camelCase}Allowed`, [address(1), address(2), address(3), 1]), `revert ${method.toLowerCase()} is paused`); + break; + + case "Seize": + await assert.revert(send(comptroller, `${camelCase}Allowed`, [address(1), address(2), address(3), address(4), 1]), `revert ${method.toLowerCase()} is paused`); + break; + + default: + break; + } + }); + }); + }); + }); +}); diff --git a/test/Comptroller/proxiedComptrollerV1Test.js b/test/Comptroller/proxiedComptrollerV1Test.js index 74b6a4d6e..08b606641 100644 --- a/test/Comptroller/proxiedComptrollerV1Test.js +++ b/test/Comptroller/proxiedComptrollerV1Test.js @@ -1,20 +1,9 @@ -const { - address, - etherMantissa, - getContract, - getTestContract, - call, - send -} = require('../Utils/MochaTruffle'); - -const { - makeComptroller, - makeCToken, - makePriceOracle -} = require('../Utils/Compound'); +const { address, etherMantissa, getContract, getTestContract, call, send } = require('../Utils/MochaTruffle'); + +const { makeComptroller, makeCToken, makePriceOracle } = require('../Utils/Compound'); const Unitroller = getContract('Unitroller'); -const Comptroller = getContract('Comptroller'); +const ComptrollerG1 = getContract('ComptrollerG1'); contract('ComptrollerV1', function([root, ...accounts]) { let unitroller; @@ -22,95 +11,108 @@ contract('ComptrollerV1', function([root, ...accounts]) { let oracle; before(async () => { - oracle = await makePriceOracle() - brains = await Comptroller.deploy().send({from: root}); + oracle = await makePriceOracle(); + brains = await ComptrollerG1.deploy().send({ from: root }); }); beforeEach(async () => { - unitroller = await Unitroller.deploy().send({from: root}); + unitroller = await Unitroller.deploy().send({ from: root }); }); let initializeBrains = async (priceOracle, closeFactor, maxAssets) => { await send(unitroller, '_setPendingImplementation', [brains._address]); await send(brains, '_become', [unitroller._address, priceOracle._address, closeFactor, maxAssets, false]); - return Comptroller.at(unitroller._address); + return ComptrollerG1.at(unitroller._address); }; let reinitializeBrains = async () => { await send(unitroller, '_setPendingImplementation', [brains._address]); await send(brains, '_become', [unitroller._address, address(0), 0, 0, true]); - return Comptroller.at(unitroller._address); + return ComptrollerG1.at(unitroller._address); }; - describe("delegating to comptroller v1", async () => { - const closeFactor = etherMantissa(.051), maxAssets = 10; + describe('delegating to comptroller v1', async () => { + const closeFactor = etherMantissa(0.051), + maxAssets = 10; let unitrollerAsComptroller, cToken; - beforeEach( async () => { - unitrollerAsComptroller = await initializeBrains(oracle, etherMantissa(.06), 30); - cToken = await makeCToken({comptroller: unitrollerAsComptroller}); + beforeEach(async () => { + unitrollerAsComptroller = await initializeBrains(oracle, etherMantissa(0.06), 30); + cToken = await makeCToken({ comptroller: unitrollerAsComptroller }); }); - describe("becoming brains sets initial state", async () => { - it("reverts if this is not the pending implementation", async () => { - await assert.revert(send(brains, '_become', [unitroller._address, oracle._address, 0, 10, false]), "revert change not authorized"); + describe('becoming brains sets initial state', async () => { + it('reverts if this is not the pending implementation', async () => { + await assert.revert( + send(brains, '_become', [unitroller._address, oracle._address, 0, 10, false]), + 'revert change not authorized' + ); }); - it("on success it sets admin to caller of constructor", async () => { + it('on success it sets admin to caller of constructor', async () => { assert.equal(await call(unitrollerAsComptroller, 'admin'), root); - assert.addressZero(await call(unitrollerAsComptroller, 'pendingAdmin'), "pendingAdmin should be zero for a new contract"); + assert.addressZero( + await call(unitrollerAsComptroller, 'pendingAdmin'), + 'pendingAdmin should be zero for a new contract' + ); }); - it("on success it sets closeFactor and maxAssets as specified", async () => { + it('on success it sets closeFactor and maxAssets as specified', async () => { const comptroller = await initializeBrains(oracle, closeFactor, maxAssets); - assert.equal(await call(comptroller, 'closeFactorMantissa'), closeFactor, "closeFactor"); - assert.equal(await call(comptroller, 'maxAssets'), maxAssets, "maxAssets"); + assert.equal(await call(comptroller, 'closeFactorMantissa'), closeFactor, 'closeFactor'); + assert.equal(await call(comptroller, 'maxAssets'), maxAssets, 'maxAssets'); }); it("on reinitialization success, it doesn't set closeFactor or maxAssets", async () => { let comptroller = await initializeBrains(oracle, closeFactor, maxAssets); assert.equal(await call(unitroller, 'comptrollerImplementation'), brains._address); - assert.equal(await call(comptroller, 'closeFactorMantissa',), closeFactor, "closeFactor"); - assert.equal(await call(comptroller, 'maxAssets'), maxAssets, "maxAssets"); + assert.equal(await call(comptroller, 'closeFactorMantissa'), closeFactor, 'closeFactor'); + assert.equal(await call(comptroller, 'maxAssets'), maxAssets, 'maxAssets'); // Create new brains - brains = await Comptroller.deploy().send({from: root}); + brains = await ComptrollerG1.deploy().send({ from: root }); comptroller = await reinitializeBrains(); assert.equal(await call(unitroller, 'comptrollerImplementation'), brains._address); - assert.equal(await call(comptroller, 'closeFactorMantissa'), closeFactor, "closeFactor"); - assert.equal(await call(comptroller, 'maxAssets'), maxAssets, "maxAssets"); + assert.equal(await call(comptroller, 'closeFactorMantissa'), closeFactor, 'closeFactor'); + assert.equal(await call(comptroller, 'maxAssets'), maxAssets, 'maxAssets'); }); - it("reverts on invalid closeFactor", async () => { + it('reverts on invalid closeFactor', async () => { await send(unitroller, '_setPendingImplementation', [brains._address]); - await assert.revert(send(brains, '_become', [unitroller._address, oracle._address, 0, maxAssets, false]), "revert set close factor error"); + await assert.revert( + send(brains, '_become', [unitroller._address, oracle._address, 0, maxAssets, false]), + 'revert set close factor error' + ); }); - it("allows 0 maxAssets", async () => { + it('allows 0 maxAssets', async () => { const comptroller = await initializeBrains(oracle, closeFactor, 0); - assert.equal(await call(comptroller, 'maxAssets'), 0, "maxAssets"); + assert.equal(await call(comptroller, 'maxAssets'), 0, 'maxAssets'); }); - it("allows 5000 maxAssets", async () => { + it('allows 5000 maxAssets', async () => { // 5000 is an arbitrary number larger than what we expect to ever actually use const comptroller = await initializeBrains(oracle, closeFactor, 5000); - assert.equal(await call(comptroller, 'maxAssets'), 5000, "maxAssets"); + assert.equal(await call(comptroller, 'maxAssets'), 5000, 'maxAssets'); }); }); - describe("_setCollateralFactor", async () => { - const half = etherMantissa(.5), one = etherMantissa(1); + describe('_setCollateralFactor', async () => { + const half = etherMantissa(0.5), + one = etherMantissa(1); - it("fails if not called by admin", async () => { + it('fails if not called by admin', async () => { assert.hasTrollFailure( - await send(unitrollerAsComptroller, '_setCollateralFactor', [cToken._address, half], {from: accounts[1]}), + await send(unitrollerAsComptroller, '_setCollateralFactor', [cToken._address, half], { + from: accounts[1] + }), 'UNAUTHORIZED', 'SET_COLLATERAL_FACTOR_OWNER_CHECK' ); }); - it("fails if asset is not listed", async () => { + it('fails if asset is not listed', async () => { assert.hasTrollFailure( await send(unitrollerAsComptroller, '_setCollateralFactor', [cToken._address, half]), 'MARKET_NOT_LISTED', @@ -118,8 +120,8 @@ contract('ComptrollerV1', function([root, ...accounts]) { ); }); - it("fails if factor is too high", async () => { - const cToken = await makeCToken({supportMarket: true, comptroller: unitrollerAsComptroller}); + it('fails if factor is too high', async () => { + const cToken = await makeCToken({ supportMarket: true, comptroller: unitrollerAsComptroller }); assert.hasTrollFailure( await send(unitrollerAsComptroller, '_setCollateralFactor', [cToken._address, one]), 'INVALID_COLLATERAL_FACTOR', @@ -127,8 +129,8 @@ contract('ComptrollerV1', function([root, ...accounts]) { ); }); - it("fails if factor is set without an underlying price", async () => { - const cToken = await makeCToken({supportMarket: true, comptroller: unitrollerAsComptroller}); + it('fails if factor is set without an underlying price', async () => { + const cToken = await makeCToken({ supportMarket: true, comptroller: unitrollerAsComptroller }); assert.hasTrollFailure( await send(unitrollerAsComptroller, '_setCollateralFactor', [cToken._address, half]), 'PRICE_ERROR', @@ -136,56 +138,54 @@ contract('ComptrollerV1', function([root, ...accounts]) { ); }); - it("succeeds and sets market", async () => { - const cToken = await makeCToken({supportMarket: true, comptroller: unitrollerAsComptroller}); + it('succeeds and sets market', async () => { + const cToken = await makeCToken({ supportMarket: true, comptroller: unitrollerAsComptroller }); await send(oracle, 'setUnderlyingPrice', [cToken._address, 1]); assert.hasLog( await send(unitrollerAsComptroller, '_setCollateralFactor', [cToken._address, half]), - 'NewCollateralFactor', { + 'NewCollateralFactor', + { cToken: cToken._address, - oldCollateralFactorMantissa: "0", + oldCollateralFactorMantissa: '0', newCollateralFactorMantissa: half.toString() - }); + } + ); }); }); - describe("_supportMarket", async () => { - it("fails if not called by admin", async () => { + describe('_supportMarket', async () => { + it('fails if not called by admin', async () => { assert.hasTrollFailure( - await send(unitrollerAsComptroller, '_supportMarket', [cToken._address], {from: accounts[1]}), + await send(unitrollerAsComptroller, '_supportMarket', [cToken._address], { from: accounts[1] }), 'UNAUTHORIZED', 'SUPPORT_MARKET_OWNER_CHECK' ); }); - it("fails if asset is not a CToken", async () => { + it('fails if asset is not a CToken', async () => { const notACToken = await makePriceOracle(); await assert.revert(send(unitrollerAsComptroller, '_supportMarket', [notACToken._address])); }); - it("succeeds and sets market", async () => { + it('succeeds and sets market', async () => { const result = await send(unitrollerAsComptroller, '_supportMarket', [cToken._address]); - assert.hasLog(result, 'MarketListed', {cToken: cToken._address}); + assert.hasLog(result, 'MarketListed', { cToken: cToken._address }); }); - it("cannot list a market a second time", async () => { + it('cannot list a market a second time', async () => { const result1 = await send(unitrollerAsComptroller, '_supportMarket', [cToken._address]); const result2 = await send(unitrollerAsComptroller, '_supportMarket', [cToken._address]); - assert.hasLog(result1, 'MarketListed', {cToken: cToken._address}); - assert.hasTrollFailure( - result2, - 'MARKET_ALREADY_LISTED', - 'SUPPORT_MARKET_EXISTS' - ); + assert.hasLog(result1, 'MarketListed', { cToken: cToken._address }); + assert.hasTrollFailure(result2, 'MARKET_ALREADY_LISTED', 'SUPPORT_MARKET_EXISTS'); }); - it("can list two different markets", async () => { - const cToken1 = await makeCToken({comptroller: unitroller}); - const cToken2 = await makeCToken({comptroller: unitroller}); + it('can list two different markets', async () => { + const cToken1 = await makeCToken({ comptroller: unitroller }); + const cToken2 = await makeCToken({ comptroller: unitroller }); const result1 = await send(unitrollerAsComptroller, '_supportMarket', [cToken1._address]); const result2 = await send(unitrollerAsComptroller, '_supportMarket', [cToken2._address]); - assert.hasLog(result1, 'MarketListed', {cToken: cToken1._address}); - assert.hasLog(result2, 'MarketListed', {cToken: cToken2._address}); + assert.hasLog(result1, 'MarketListed', { cToken: cToken1._address }); + assert.hasLog(result2, 'MarketListed', { cToken: cToken2._address }); }); }); }); diff --git a/test/Comptroller/unitrollerTest.js b/test/Comptroller/unitrollerTest.js index 0eea6fa41..7c8b5e8a4 100644 --- a/test/Comptroller/unitrollerTest.js +++ b/test/Comptroller/unitrollerTest.js @@ -13,7 +13,7 @@ const { } = require('../Utils/Compound'); const Unitroller = getContract('Unitroller'); -const Comptroller = getContract('Comptroller'); +const ComptrollerG1 = getContract('ComptrollerG1'); const EchoTypesComptroller = getTestContract('EchoTypesComptroller'); contract('Unitroller', function([root, ...accounts]) { @@ -21,8 +21,8 @@ contract('Unitroller', function([root, ...accounts]) { let brains; let oracle; before(async () => { - oracle = await makePriceOracle() - brains = await Comptroller.deploy().send({from: root}); + oracle = await makePriceOracle(); + brains = await ComptrollerG1.deploy().send({ from: root }); }); beforeEach(async () => { diff --git a/test/TimelockTest.js b/test/TimelockTest.js new file mode 100644 index 000000000..e9423db29 --- /dev/null +++ b/test/TimelockTest.js @@ -0,0 +1,393 @@ +const { + call, + encodeParameters, + etherMantissa, + bigNumberify, + getTestContract, + keccak256, + send +} = require('./Utils/MochaTruffle'); + +const Timelock = getTestContract('TimelockHarness'); +const oneWeekInSeconds = bigNumberify(7 * 24 * 60 * 60); +const zero = bigNumberify(0); +const gracePeriod = oneWeekInSeconds.mul(2); + +contract('Timelock', function([root, notAdmin, newAdmin]) { + let blockTimestamp; + let timelock; + let delay = oneWeekInSeconds; + let newDelay = delay.mul(2); + let target; + let value = zero; + let signature = 'setDelay(uint256)'; + let data = encodeParameters(['uint256'], [newDelay]); + let revertData = encodeParameters(['uint256'], [bigNumberify(60 * 60)]); + let eta; + let queuedTxHash; + + before(async () => { + timelock = await Timelock.deploy({ + arguments: [root, delay] + }).send({ from: root }); + + blockTimestamp = bigNumberify(await call(timelock, 'blockTimestamp')); + target = timelock.options.address; + eta = blockTimestamp.add(delay); + + queuedTxHash = keccak256( + encodeParameters( + ['address', 'uint256', 'string', 'bytes', 'uint256'], + [target, value, signature, data, eta] + ) + ); + }); + + describe('constructor', async () => { + it('sets address of admin', async () => { + let configuredAdmin = await call(timelock, 'admin'); + assert.equal(configuredAdmin, root); + }); + + it('sets delay', async () => { + let configuredDelay = await call(timelock, 'delay'); + assert.equal(configuredDelay, delay.toString()); + }); + }); + + describe('setDelay', async () => { + it('requires msg.sender to be Timelock', async () => { + await assert.revert( + send(timelock, 'setDelay', [delay], { from: root }), + 'revert Timelock::setDelay: Call must come from Timelock.' + ); + }); + }); + + describe('setPendingAdmin', async () => { + it('requires msg.sender to be Timelock', async () => { + await assert.revert( + send(timelock, 'setPendingAdmin', [newAdmin], { from: root }), + 'revert Timelock::setPendingAdmin: Call must come from Timelock.' + ); + }); + }); + + describe('acceptAdmin', async () => { + after(async () => { + await send(timelock, 'harnessSetAdmin', [root], { from: root }); + }); + + it('requires msg.sender to be pendingAdmin', async () => { + await assert.revert( + send(timelock, 'acceptAdmin', [], { from: notAdmin }), + 'revert Timelock::acceptAdmin: Call must come from pendingAdmin.' + ); + }); + + it('sets pendingAdmin to address 0 and changes admin', async () => { + await send(timelock, 'harnessSetPendingAdmin', [newAdmin], { from: root }); + const pendingAdminBefore = await call(timelock, 'pendingAdmin'); + assert.equal(pendingAdminBefore, newAdmin); + + const result = await send(timelock, 'acceptAdmin', [], { from: newAdmin }); + const pendingAdminAfter = await call(timelock, 'pendingAdmin'); + assert.equal(pendingAdminAfter, '0x0000000000000000000000000000000000000000'); + + const timelockAdmin = await call(timelock, 'admin'); + assert.equal(timelockAdmin, newAdmin); + + assert.hasLog(result, 'NewAdmin', { + newAdmin + }); + }); + }); + + describe('queueTransaction', async () => { + it('requires admin to be msg.sender', async () => { + await assert.revert( + send(timelock, 'queueTransaction', [target, value, signature, data, eta], { from: notAdmin }), + 'revert Timelock::queueTransaction: Call must come from admin.' + ); + }); + + it('requires eta to exceed delay', async () => { + const etaLessThanDelay = blockTimestamp.add(delay).sub(1); + + await assert.revert( + send(timelock, 'queueTransaction', [target, value, signature, data, etaLessThanDelay], { + from: root + }), + 'revert Timelock::queueTransaction: Estimated execution block must satisfy delay.' + ); + }); + + it('sets hash as true in queuedTransactions mapping', async () => { + const queueTransactionsHashValueBefore = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueBefore, false); + + await send(timelock, 'queueTransaction', [target, value, signature, data, eta], { from: root }); + + const queueTransactionsHashValueAfter = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueAfter, true); + }); + + it('should emit QueueTransaction event', async () => { + const result = await send(timelock, 'queueTransaction', [target, value, signature, data, eta], { + from: root + }); + + assert.hasLog(result, 'QueueTransaction', { + data, + signature, + target, + eta: eta.toString(), + txHash: queuedTxHash, + value: value.toString() + }); + }); + }); + + describe('cancelTransaction', async () => { + before(async () => { + await send(timelock, 'queueTransaction', [target, value, signature, data, eta], { from: root }); + }); + + it('requires admin to be msg.sender', async () => { + await assert.revert( + send(timelock, 'cancelTransaction', [target, value, signature, data, eta], { from: notAdmin }), + 'revert Timelock::cancelTransaction: Call must come from admin.' + ); + }); + + it('sets hash from true to false in queuedTransactions mapping', async () => { + const queueTransactionsHashValueBefore = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueBefore, true); + + await send(timelock, 'cancelTransaction', [target, value, signature, data, eta], { from: root }); + + const queueTransactionsHashValueAfter = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueAfter, false); + }); + + it('should emit CancelTransaction event', async () => { + const result = await send(timelock, 'cancelTransaction', [target, value, signature, data, eta], { + from: root + }); + + assert.hasLog(result, 'CancelTransaction', { + data, + signature, + target, + eta: eta.toString(), + txHash: queuedTxHash, + value: value.toString() + }); + }); + }); + + describe('queue and cancel empty', async () => { + it('can queue and cancel an empty signature and data', async () => { + const txHash = keccak256( + encodeParameters( + ['address', 'uint256', 'string', 'bytes', 'uint256'], + [target, value, '', '0x', eta] + ) + ); + assert.notOk(await call(timelock, 'queuedTransactions', [txHash])); + await send(timelock, 'queueTransaction', [target, value, '', '0x', eta], {from: root}); + assert.ok(await call(timelock, 'queuedTransactions', [txHash])); + await send(timelock, 'cancelTransaction', [target, value, '', '0x', eta], {from: root}); + assert.notOk(await call(timelock, 'queuedTransactions', [txHash])); + }); + }); + + describe('executeTransaction (setDelay)', async () => { + before(async () => { + // Queue transaction that will succeed + await send(timelock, 'queueTransaction', [target, value, signature, data, eta], { + from: root + }); + + // Queue transaction that will revert when executed + await send(timelock, 'queueTransaction', [target, value, signature, revertData, eta], { + from: root + }); + }); + + it('requires admin to be msg.sender', async () => { + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, eta], { from: notAdmin }), + 'revert Timelock::executeTransaction: Call must come from admin.' + ); + }); + + it('requires transaction to be queued', async () => { + const differentEta = eta.add(1); + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, differentEta], { from: root }), + "revert Timelock::executeTransaction: Transaction hasn't been queued." + ); + }); + + it('requires timestamp to be greater than or equal to eta', async () => { + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, eta], { + from: root + }), + "revert Timelock::executeTransaction: Transaction hasn't surpassed time lock." + ); + }); + + it('requires timestamp to be less than eta plus gracePeriod', async () => { + const blockFastForward = delay.add(gracePeriod).add(1); + await send(timelock, 'harnessFastForward', [blockFastForward]); + + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, eta], { + from: root + }), + 'revert Timelock::executeTransaction: Transaction is stale.' + ); + }); + + it('requires target.call transaction to succeed', async () => { + const newBlockTimestamp = blockTimestamp.add(delay).add(1); + await send(timelock, 'harnessSetBlockTimestamp', [newBlockTimestamp]); + + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, revertData, eta], { + from: root + }), + 'revert Timelock::executeTransaction: Transaction execution reverted.' + ); + }); + + it('sets hash from true to false in queuedTransactions mapping, updates delay, and emits ExecuteTransaction event', async () => { + const configuredDelayBefore = await call(timelock, 'delay'); + assert.equal(configuredDelayBefore, delay.toString()); + + const queueTransactionsHashValueBefore = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueBefore, true); + + const newBlockTimestamp = blockTimestamp.add(delay).add(1); + await send(timelock, 'harnessSetBlockTimestamp', [newBlockTimestamp]); + + const result = await send(timelock, 'executeTransaction', [target, value, signature, data, eta], { + from: root + }); + + const queueTransactionsHashValueAfter = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueAfter, false); + + const configuredDelayAfter = await call(timelock, 'delay'); + assert.equal(configuredDelayAfter, newDelay.toString()); + + assert.hasLog(result, 'ExecuteTransaction', { + data, + signature, + target, + eta: eta.toString(), + txHash: queuedTxHash, + value: value.toString() + }); + + assert.hasLog(result, 'NewDelay', { + newDelay: newDelay.toString() + }); + }); + }); + + describe('executeTransaction (setPendingAdmin)', async () => { + before(async () => { + const configuredDelay = await call(timelock, 'delay'); + + delay = bigNumberify(configuredDelay); + signature = 'setPendingAdmin(address)'; + data = encodeParameters(['address'], [newAdmin]); + eta = blockTimestamp.add(delay); + + queuedTxHash = keccak256( + encodeParameters( + ['address', 'uint256', 'string', 'bytes', 'uint256'], + [target, value, signature, data, eta] + ) + ); + + await send(timelock, 'harnessSetBlockTimestamp', [blockTimestamp]); + await send(timelock, 'queueTransaction', [target, value, signature, data, eta], { + from: root + }); + }); + + it('requires admin to be msg.sender', async () => { + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, eta], { from: notAdmin }), + 'revert Timelock::executeTransaction: Call must come from admin.' + ); + }); + + it('requires transaction to be queued', async () => { + const differentEta = eta.add(1); + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, differentEta], { from: root }), + "revert Timelock::executeTransaction: Transaction hasn't been queued." + ); + }); + + it('requires timestamp to be greater than or equal to eta', async () => { + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, eta], { + from: root + }), + "revert Timelock::executeTransaction: Transaction hasn't surpassed time lock." + ); + }); + + it('requires timestamp to be less than eta plus gracePeriod', async () => { + const blockFastForward = delay.add(gracePeriod).add(1); + await send(timelock, 'harnessFastForward', [blockFastForward]); + + await assert.revert( + send(timelock, 'executeTransaction', [target, value, signature, data, eta], { + from: root + }), + 'revert Timelock::executeTransaction: Transaction is stale.' + ); + }); + + it('sets hash from true to false in queuedTransactions mapping, updates admin, and emits ExecuteTransaction event', async () => { + const configuredPendingAdminBefore = await call(timelock, 'pendingAdmin'); + assert.equal(configuredPendingAdminBefore, '0x0000000000000000000000000000000000000000'); + + const queueTransactionsHashValueBefore = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueBefore, true); + + const newBlockTimestamp = blockTimestamp.add(delay).add(1); + await send(timelock, 'harnessSetBlockTimestamp', [newBlockTimestamp]); + + const result = await send(timelock, 'executeTransaction', [target, value, signature, data, eta], { + from: root + }); + + const queueTransactionsHashValueAfter = await call(timelock, 'queuedTransactions', [queuedTxHash]); + assert.equal(queueTransactionsHashValueAfter, false); + + const configuredPendingAdminAfter = await call(timelock, 'pendingAdmin'); + assert.equal(configuredPendingAdminAfter, newAdmin); + + assert.hasLog(result, 'ExecuteTransaction', { + data, + signature, + target, + eta: eta.toString(), + txHash: queuedTxHash, + value: value.toString() + }); + + assert.hasLog(result, 'NewPendingAdmin', { + newPendingAdmin: newAdmin + }); + }); + }); +}); diff --git a/test/Tokens/accrueInterestTest.js b/test/Tokens/accrueInterestTest.js index cba5624b0..61e8a3f8c 100644 --- a/test/Tokens/accrueInterestTest.js +++ b/test/Tokens/accrueInterestTest.js @@ -1,4 +1,9 @@ -const {etherUnsigned, call, send} = require('../Utils/MochaTruffle'); +const { + etherMantissa, + etherUnsigned, + call, + send +} = require('../Utils/MochaTruffle'); const { makeCToken, setBorrowRate @@ -6,7 +11,7 @@ const { const blockNumber = 2e7; const borrowIndex = 1e18; -const borrowRate = .0001; +const borrowRate = .000001; async function pretendBlock(cToken, accrualBlock = blockNumber, deltaBlocks = 1) { await send(cToken, 'harnessSetAccrualBlockNumber', [etherUnsigned(blockNumber)]); @@ -33,7 +38,8 @@ contract('CToken', function ([root, ...accounts]) { describe('accrueInterest', async () => { it('reverts if the interest rate is absurdly high', async () => { - await setBorrowRate(cToken, .001); + assert.numEqual(await call(cToken, 'getBorrowRateMaxMantissa'), etherMantissa(0.000005)); // 0.0005% per block + await setBorrowRate(cToken, 0.001e-2); // 0.0010% per block await assert.revert(send(cToken, 'accrueInterest'), "revert borrow rate is absurdly high"); }); @@ -96,7 +102,7 @@ contract('CToken', function ([root, ...accounts]) { }); it('fails if interest accumulated for reserves calculation fails', async () => { - await setBorrowRate(cToken, .0001); + await setBorrowRate(cToken, .000001); await send(cToken, 'harnessExchangeRateDetails', [0, etherUnsigned(1e30), -1]); await send(cToken, 'harnessSetReserveFactorFresh', [etherUnsigned(1e10)]); await pretendBlock(cToken, blockNumber, 5e20) @@ -120,14 +126,18 @@ contract('CToken', function ([root, ...accounts]) { }); it('succeeds and saves updated values in storage on success', async () => { - await send(cToken, 'harnessExchangeRateDetails', [0, etherUnsigned(1e22), etherUnsigned(1e20)]); - await send(cToken, 'harnessSetReserveFactorFresh', [etherUnsigned(1e17)]); + const startingTotalBorrows = 1e22; + const startingTotalReserves = 1e20; + const reserveFactor = 1e17; + + await send(cToken, 'harnessExchangeRateDetails', [0, etherUnsigned(startingTotalBorrows), etherUnsigned(startingTotalReserves)]); + await send(cToken, 'harnessSetReserveFactorFresh', [etherUnsigned(reserveFactor)]); await pretendBlock(cToken) const expectedAccrualBlockNumber = blockNumber + 1; - const expectedBorrowIndex = borrowIndex + borrowRate * 1e18; - const expectedTotalBorrows = 1e22 + 1e18; - const expectedTotalReserves = 1e20 + 1e17; + const expectedBorrowIndex = borrowIndex + borrowIndex * borrowRate; + const expectedTotalBorrows = startingTotalBorrows + startingTotalBorrows * borrowRate; + const expectedTotalReserves = startingTotalReserves + startingTotalBorrows * borrowRate * reserveFactor / 1e18; assert.success(await send(cToken, 'accrueInterest')); assert.equal(await call(cToken, 'accrualBlockNumber'), expectedAccrualBlockNumber); diff --git a/test/Tokens/cTokenTest.js b/test/Tokens/cTokenTest.js index 6f4aee6e4..ed5442d10 100644 --- a/test/Tokens/cTokenTest.js +++ b/test/Tokens/cTokenTest.js @@ -11,7 +11,7 @@ const { pretendBorrow } = require('../Utils/Compound'); -contract('CToken', function ([root, ...accounts]) { +contract('CToken', function ([root, admin, ...accounts]) { describe('constructor', async () => { it("fails when non erc-20 underlying", async () => { await assert.revert(makeCToken({underlying: {_address: root}})); @@ -26,6 +26,11 @@ contract('CToken', function ([root, ...accounts]) { assert.equal(await call(cToken, 'underlying'), cToken.underlying._address); assert.equal(await call(cToken, 'admin',), root); }); + + it("succeeds when setting admin to contructor argument", async () => { + const cToken = await makeCToken({admin: admin}); + assert.equal(await call(cToken, 'admin',), admin); + }); }); describe('name, symbol, decimals', async () => { diff --git a/test/Utils/Compound.js b/test/Utils/Compound.js index 27049f0d3..820f9b0bf 100644 --- a/test/Utils/Compound.js +++ b/test/Utils/Compound.js @@ -38,10 +38,8 @@ async function makeComptroller(opts = {}) { const priceOracle = opts.priceOracle || await makePriceOracle(opts.priceOracleOpts); const closeFactor = etherMantissa(dfn(opts.closeFactor, .051)); const maxAssets = etherUnsigned(dfn(opts.maxAssets, 10)); - const liquidationIncentive = etherUnsigned(1); const comptroller = await Comptroller.deploy().send({from: root}); - await comptroller.methods._setLiquidationIncentive(liquidationIncentive).send({from: root}); await comptroller.methods._setCloseFactor(closeFactor).send({from: root}); await comptroller.methods._setMaxAssets(maxAssets).send({from: root}); await comptroller.methods._setPriceOracle(priceOracle._address).send({from: root}); @@ -56,11 +54,16 @@ async function makeComptroller(opts = {}) { const priceOracle = opts.priceOracle || await makePriceOracle(opts.priceOracleOpts); const closeFactor = etherMantissa(dfn(opts.closeFactor, .051)); const maxAssets = etherUnsigned(dfn(opts.maxAssets, 10)); + const liquidationIncentive = etherMantissa(1); const unitroller = await Unitroller.deploy().send({from: root}); const comptroller = await Comptroller.deploy().send({from: root}); await unitroller.methods._setPendingImplementation(comptroller._address).send({from: root}); - await comptroller.methods._become(unitroller._address, priceOracle._address, closeFactor, maxAssets, false).send({from: root}); + await comptroller.methods._become(unitroller._address).send({from: root}); comptroller.options.address = unitroller._address; + await comptroller.methods._setLiquidationIncentive(liquidationIncentive).send({from: root}); + await comptroller.methods._setCloseFactor(closeFactor).send({from: root}); + await comptroller.methods._setMaxAssets(maxAssets).send({from: root}); + await comptroller.methods._setPriceOracle(priceOracle._address).send({from: root}); return Object.assign(comptroller, {priceOracle}); } } @@ -77,6 +80,7 @@ async function makeCToken(opts = {}) { const decimals = etherUnsigned(dfn(opts.decimals, 8)); const symbol = opts.symbol || 'cOMG'; const name = opts.name || `CToken ${symbol}`; + const admin = opts.admin || root; let cToken, underlying; @@ -90,7 +94,8 @@ async function makeCToken(opts = {}) { exchangeRate, name, symbol, - decimals + decimals, + admin ]}).send({from: root}); break; case 'cerc20': @@ -105,7 +110,8 @@ async function makeCToken(opts = {}) { exchangeRate, name, symbol, - decimals + decimals, + admin ]}).send({from: root}); break; } @@ -372,4 +378,4 @@ module.exports = { setBorrowRate, getBorrowRate, pretendBorrow -}; +}; \ No newline at end of file diff --git a/test/Utils/MochaTruffle.js b/test/Utils/MochaTruffle.js index 75d5f49f1..8c8c27560 100644 --- a/test/Utils/MochaTruffle.js +++ b/test/Utils/MochaTruffle.js @@ -150,7 +150,16 @@ const assert = Object.assign(global.assert || require('assert'), { }); function address(n) { - return `0x${(n).toString(16).padStart(40, '0')}`; + return `0x${n.toString(16).padStart(40, '0')}`; +} + +function bigNumberify(num) { + return ethers.utils.bigNumberify(new BigNum(num).toFixed()); +} + +function encodeParameters(types, values) { + const abi = new ethers.utils.AbiCoder(); + return abi.encode(types, values); } async function etherBalance(addr) { @@ -199,6 +208,10 @@ async function guessRoot() { return accounts[0]; } +function keccak256(values) { + return ethers.utils.keccak256(values); +} + async function minerStart() { return rpc({method: 'miner_start'}); } @@ -246,6 +259,8 @@ module.exports = { asIfTesting, assert, address, + bigNumberify, + encodeParameters, etherBalance, etherGasCost, etherMantissa, @@ -253,6 +268,7 @@ module.exports = { getContract, getTestContract, guessRoot, + keccak256, minerStart, minerStop, diff --git a/test/contracts/CErc20Harness.sol b/test/contracts/CErc20Harness.sol index 2657096ee..f4ecb45aa 100644 --- a/test/contracts/CErc20Harness.sol +++ b/test/contracts/CErc20Harness.sol @@ -19,7 +19,8 @@ contract CErc20Harness is CErc20 { uint initialExchangeRateMantissa, string memory name_, string memory symbol_, - uint decimals_) + uint8 decimals_, + address payable admin_) CErc20( underlying_, comptroller_, @@ -27,7 +28,12 @@ contract CErc20Harness is CErc20 { initialExchangeRateMantissa, name_, symbol_, - decimals_) public {} + decimals_, + admin_) public {} + + function getBorrowRateMaxMantissa() public pure returns (uint) { + return borrowRateMaxMantissa; + } /** * Fresh @@ -184,4 +190,8 @@ contract CErc20Harness is CErc20 { function harnessSetInterestRateModel(address newInterestRateModelAddress) public { interestRateModel = InterestRateModel(newInterestRateModelAddress); } + + function harnessCallBorrowAllowed(uint amount) public returns (uint) { + return comptroller.borrowAllowed(address(this), msg.sender, amount); + } } diff --git a/test/contracts/CErc20Scenario.sol b/test/contracts/CErc20Scenario.sol index 408bf04b3..92e4c487f 100644 --- a/test/contracts/CErc20Scenario.sol +++ b/test/contracts/CErc20Scenario.sol @@ -10,7 +10,8 @@ contract CErc20Scenario is CErc20 { uint initialExchangeRateMantissa, string memory name_, string memory symbol_, - uint decimals_) + uint8 decimals_, + address payable admin_) CErc20( underlying_, comptroller_, @@ -18,7 +19,8 @@ contract CErc20Scenario is CErc20 { initialExchangeRateMantissa, name_, symbol_, - decimals_) public {} + decimals_, + admin_) public {} function setTotalBorrows(uint totalBorrows_) public { totalBorrows = totalBorrows_; diff --git a/test/contracts/CEtherHarness.sol b/test/contracts/CEtherHarness.sol index 6b25cceca..85b2196b9 100644 --- a/test/contracts/CEtherHarness.sol +++ b/test/contracts/CEtherHarness.sol @@ -17,14 +17,16 @@ contract CEtherHarness is CEther { uint initialExchangeRateMantissa, string memory name_, string memory symbol_, - uint decimals_) + uint8 decimals_, + address payable admin_) CEther( comptroller_, interestRateModel_, initialExchangeRateMantissa, name_, symbol_, - decimals_) public {} + decimals_, + admin_) public {} /** * Fresh diff --git a/test/contracts/CEtherScenario.sol b/test/contracts/CEtherScenario.sol index 3e10b6e7a..f7655d813 100644 --- a/test/contracts/CEtherScenario.sol +++ b/test/contracts/CEtherScenario.sol @@ -9,6 +9,7 @@ contract CEtherScenario is CEther { constructor(string memory name_, string memory symbol_, uint8 decimals_, + address payable admin_, ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa) @@ -17,7 +18,8 @@ contract CEtherScenario is CEther { initialExchangeRateMantissa, name_, symbol_, - decimals_) public { + decimals_, + admin_) public { } function setTotalBorrows(uint totalBorrows_) public { diff --git a/test/contracts/CEvil.sol b/test/contracts/CEvil.sol index 0490a4977..0282cf755 100644 --- a/test/contracts/CEvil.sol +++ b/test/contracts/CEvil.sol @@ -9,7 +9,8 @@ contract CEvil is CErc20Scenario { uint initialExchangeRateMantissa, string memory name_, string memory symbol_, - uint decimals_) + uint8 decimals_, + address payable admin_) CErc20Scenario( underlying_, comptroller_, @@ -17,7 +18,8 @@ contract CEvil is CErc20Scenario { initialExchangeRateMantissa, name_, symbol_, - decimals_) public {} + decimals_, + admin_) public {} function evilSeize(CToken treasure, address liquidator, address borrower, uint seizeTokens) public returns (uint) { return treasure.seize(liquidator, borrower, seizeTokens); diff --git a/test/contracts/ComptrollerHarness.sol b/test/contracts/ComptrollerHarness.sol index 182a673a5..db8a7929b 100644 --- a/test/contracts/ComptrollerHarness.sol +++ b/test/contracts/ComptrollerHarness.sol @@ -15,4 +15,8 @@ contract ComptrollerHarness is Comptroller { super.getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); return (uint(err), liquidity, shortfall); } + + function setPauseGuardian(address harnessedPauseGuardian) public { + pauseGuardian = harnessedPauseGuardian; + } } diff --git a/test/contracts/ComptrollerScenario.sol b/test/contracts/ComptrollerScenario.sol index 4e3198e49..db8e1933f 100644 --- a/test/contracts/ComptrollerScenario.sol +++ b/test/contracts/ComptrollerScenario.sol @@ -22,14 +22,8 @@ contract ComptrollerScenario is Comptroller { blockNumber = number; } - function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool reinitializing) public { - super._become(unitroller, _oracle, _closeFactorMantissa, _maxAssets, reinitializing); - - if (!reinitializing) { - ComptrollerScenario freshBrainedComptroller = ComptrollerScenario(address(unitroller)); - - freshBrainedComptroller.setBlockNumber(100000); - } + function _become(Unitroller unitroller) public { + super._become(unitroller); } function getHypotheticalAccountLiquidity( diff --git a/test/contracts/ComptrollerScenarioG1.sol b/test/contracts/ComptrollerScenarioG1.sol new file mode 100644 index 000000000..0ddbd068c --- /dev/null +++ b/test/contracts/ComptrollerScenarioG1.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.5.8; + +import "../ComptrollerG1.sol"; +import "../PriceOracle.sol"; + +contract ComptrollerScenarioG1 is ComptrollerG1 { + uint public blockNumber; + + constructor() ComptrollerG1() public {} + + function membershipLength(CToken cToken) public view returns (uint) { + return accountAssets[address(cToken)].length; + } + + function fastForward(uint blocks) public returns (uint) { + blockNumber += blocks; + + return blockNumber; + } + + function setBlockNumber(uint number) public { + blockNumber = number; + } + + function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool reinitializing) public { + super._become(unitroller, _oracle, _closeFactorMantissa, _maxAssets, reinitializing); + } + + function getHypotheticalAccountLiquidity( + address account, + address cTokenModify, + uint redeemTokens, + uint borrowAmount) public view returns (uint, uint, uint) { + (Error err, uint liquidity, uint shortfall) = + super.getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); + return (uint(err), liquidity, shortfall); + } + + function unlist(CToken cToken) public { + markets[address(cToken)].isListed = false; + } +} diff --git a/test/contracts/TimelockHarness.sol b/test/contracts/TimelockHarness.sol new file mode 100644 index 000000000..485e493b6 --- /dev/null +++ b/test/contracts/TimelockHarness.sol @@ -0,0 +1,37 @@ +pragma solidity ^0.5.8; + +import "../Timelock.sol"; + +contract TimelockHarness is Timelock { + + uint public blockTimestamp; + + constructor(address admin_, uint delay_) + Timelock(admin_, delay_) + public + { + // solium-disable-next-line security/no-block-members + blockTimestamp = 100; + } + + function getBlockTimestamp() internal view returns (uint) { + return blockTimestamp; + } + + function harnessSetBlockTimestamp(uint newBlockTimestamp) public { + blockTimestamp = newBlockTimestamp; + } + + function harnessSetPendingAdmin(address pendingAdmin_) public { + pendingAdmin = pendingAdmin_; + } + + function harnessSetAdmin(address admin_) public { + admin = admin_; + } + + function harnessFastForward(uint seconds_) public { + blockTimestamp += seconds_; + } + +} diff --git a/test/contracts/TimelockTest.sol b/test/contracts/TimelockTest.sol new file mode 100644 index 000000000..32b3d6216 --- /dev/null +++ b/test/contracts/TimelockTest.sol @@ -0,0 +1,11 @@ +pragma solidity ^0.5.8; + +import "../Timelock.sol"; + +contract TimelockTest is Timelock { + + constructor(address admin_, uint delay_) Timelock(admin_, 2 days) public { + delay = delay_; + } + +} \ No newline at end of file