My Name Tag:
Not Available, login to update
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
RewardDistributor
Compiler Version
v0.5.16+commit.9c3226ce
Contract Source Code (Solidity)
/** *Submitted for verification at snowtrace.io on 2021-12-04 */ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.5.16; contract JoetrollerErrorReporter { enum Error { NO_ERROR, UNAUTHORIZED, JOETROLLER_MISMATCH, INSUFFICIENT_SHORTFALL, INSUFFICIENT_LIQUIDITY, INVALID_CLOSE_FACTOR, INVALID_COLLATERAL_FACTOR, INVALID_LIQUIDATION_INCENTIVE, MARKET_NOT_ENTERED, // no longer possible MARKET_NOT_LISTED, MARKET_ALREADY_LISTED, MATH_ERROR, NONZERO_BORROW_BALANCE, PRICE_ERROR, REJECTION, SNAPSHOT_ERROR, TOO_MANY_ASSETS, TOO_MUCH_REPAY } enum FailureInfo { ACCEPT_ADMIN_PENDING_ADMIN_CHECK, ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK, EXIT_MARKET_BALANCE_OWED, EXIT_MARKET_REJECTION, SET_CLOSE_FACTOR_OWNER_CHECK, SET_CLOSE_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_OWNER_CHECK, SET_COLLATERAL_FACTOR_NO_EXISTS, SET_COLLATERAL_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_WITHOUT_PRICE, SET_IMPLEMENTATION_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_VALIDATION, SET_MAX_ASSETS_OWNER_CHECK, SET_PENDING_ADMIN_OWNER_CHECK, SET_PENDING_IMPLEMENTATION_OWNER_CHECK, SET_PRICE_ORACLE_OWNER_CHECK, SUPPORT_MARKET_EXISTS, SUPPORT_MARKET_OWNER_CHECK, SET_PAUSE_GUARDIAN_OWNER_CHECK } /** * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary * contract-specific code that enables us to report opaque error codes from upgradeable contracts. **/ event Failure(uint256 error, uint256 info, uint256 detail); /** * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator */ function fail(Error err, FailureInfo info) internal returns (uint256) { emit Failure(uint256(err), uint256(info), 0); return uint256(err); } /** * @dev use this when reporting an opaque error from an upgradeable collaborator contract */ function failOpaque( Error err, FailureInfo info, uint256 opaqueError ) internal returns (uint256) { emit Failure(uint256(err), uint256(info), opaqueError); return uint256(err); } } contract TokenErrorReporter { enum Error { NO_ERROR, UNAUTHORIZED, BAD_INPUT, JOETROLLER_REJECTION, JOETROLLER_CALCULATION_ERROR, INTEREST_RATE_MODEL_ERROR, INVALID_ACCOUNT_PAIR, INVALID_CLOSE_AMOUNT_REQUESTED, INVALID_COLLATERAL_FACTOR, MATH_ERROR, MARKET_NOT_FRESH, MARKET_NOT_LISTED, TOKEN_INSUFFICIENT_ALLOWANCE, TOKEN_INSUFFICIENT_BALANCE, TOKEN_INSUFFICIENT_CASH, TOKEN_TRANSFER_IN_FAILED, TOKEN_TRANSFER_OUT_FAILED } /* * Note: FailureInfo (but not Error) is kept in alphabetical order * This is because FailureInfo grows significantly faster, and * the order of Error has some meaning, while the order of FailureInfo * is entirely arbitrary. */ enum FailureInfo { ACCEPT_ADMIN_PENDING_ADMIN_CHECK, ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED, BORROW_ACCRUE_INTEREST_FAILED, BORROW_CASH_NOT_AVAILABLE, BORROW_FRESHNESS_CHECK, BORROW_MARKET_NOT_LISTED, BORROW_JOETROLLER_REJECTION, LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED, LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED, LIQUIDATE_COLLATERAL_FRESHNESS_CHECK, LIQUIDATE_JOETROLLER_REJECTION, LIQUIDATE_JOETROLLER_CALCULATE_AMOUNT_SEIZE_FAILED, LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX, LIQUIDATE_CLOSE_AMOUNT_IS_ZERO, LIQUIDATE_FRESHNESS_CHECK, LIQUIDATE_LIQUIDATOR_IS_BORROWER, LIQUIDATE_REPAY_BORROW_FRESH_FAILED, LIQUIDATE_SEIZE_JOETROLLER_REJECTION, LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER, LIQUIDATE_SEIZE_TOO_MUCH, MINT_ACCRUE_INTEREST_FAILED, MINT_JOETROLLER_REJECTION, MINT_FRESHNESS_CHECK, MINT_TRANSFER_IN_FAILED, MINT_TRANSFER_IN_NOT_POSSIBLE, REDEEM_ACCRUE_INTEREST_FAILED, REDEEM_JOETROLLER_REJECTION, REDEEM_FRESHNESS_CHECK, REDEEM_TRANSFER_OUT_NOT_POSSIBLE, REDUCE_RESERVES_ACCRUE_INTEREST_FAILED, REDUCE_RESERVES_ADMIN_CHECK, REDUCE_RESERVES_CASH_NOT_AVAILABLE, REDUCE_RESERVES_FRESH_CHECK, REDUCE_RESERVES_VALIDATION, REPAY_BEHALF_ACCRUE_INTEREST_FAILED, REPAY_BORROW_ACCRUE_INTEREST_FAILED, REPAY_BORROW_JOETROLLER_REJECTION, REPAY_BORROW_FRESHNESS_CHECK, REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE, SET_COLLATERAL_FACTOR_OWNER_CHECK, SET_COLLATERAL_FACTOR_VALIDATION, SET_JOETROLLER_OWNER_CHECK, SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED, SET_INTEREST_RATE_MODEL_FRESH_CHECK, SET_INTEREST_RATE_MODEL_OWNER_CHECK, SET_MAX_ASSETS_OWNER_CHECK, SET_ORACLE_MARKET_NOT_LISTED, SET_PENDING_ADMIN_OWNER_CHECK, SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED, SET_RESERVE_FACTOR_ADMIN_CHECK, SET_RESERVE_FACTOR_FRESH_CHECK, SET_RESERVE_FACTOR_BOUNDS_CHECK, TRANSFER_JOETROLLER_REJECTION, TRANSFER_NOT_ALLOWED, ADD_RESERVES_ACCRUE_INTEREST_FAILED, ADD_RESERVES_FRESH_CHECK, ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE, SET_PROTOCOL_SEIZE_SHARE_ACCRUE_INTEREST_FAILED, SET_PROTOCOL_SEIZE_SHARE_ADMIN_CHECK, SET_PROTOCOL_SEIZE_SHARE_FRESH_CHECK, SET_PROTOCOL_SEIZE_SHARE_BOUNDS_CHECK } /** * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary * contract-specific code that enables us to report opaque error codes from upgradeable contracts. **/ event Failure(uint256 error, uint256 info, uint256 detail); /** * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator */ function fail(Error err, FailureInfo info) internal returns (uint256) { emit Failure(uint256(err), uint256(info), 0); return uint256(err); } /** * @dev use this when reporting an opaque error from an upgradeable collaborator contract */ function failOpaque( Error err, FailureInfo info, uint256 opaqueError ) internal returns (uint256) { emit Failure(uint256(err), uint256(info), opaqueError); return uint256(err); } } /** * @title ERC 20 Token Standard Interface * https://eips.ethereum.org/EIPS/eip-20 */ interface EIP20Interface { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); /** * @notice Get the total number of tokens in circulation * @return The supply of tokens */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return The balance */ function balanceOf(address owner) external view returns (uint256 balance); /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) external returns (bool success); /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom( address src, address dst, uint256 amount ) external returns (bool success); /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); } /** * @title Compound's InterestRateModel Interface * @author Compound */ contract InterestRateModel { /// @notice Indicator that this is an InterestRateModel contract (for inspection) bool public constant isInterestRateModel = true; /** * @notice Calculates the current borrow interest rate per sec * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amnount of reserves the market has * @return The borrow rate per sec (as a percentage, and scaled by 1e18) */ function getBorrowRate( uint256 cash, uint256 borrows, uint256 reserves ) external view returns (uint256); /** * @notice Calculates the current supply interest rate per sec * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amnount of reserves the market has * @param reserveFactorMantissa The current reserve factor the market has * @return The supply rate per sec (as a percentage, and scaled by 1e18) */ function getSupplyRate( uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa ) external view returns (uint256); } interface ERC3156FlashBorrowerInterface { /** * @dev Receive a flash loan. * @param initiator The initiator of the loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @param fee The additional amount of tokens to repay. * @param data Arbitrary data structure, intended to contain user-defined parameters. * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" */ function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); } contract PriceOracle { /** * @notice Get the underlying price of a jToken asset * @param jToken The jToken to get the underlying price of * @return The underlying asset price mantissa (scaled by 1e18). * Zero means the price is unavailable. */ function getUnderlyingPrice(JToken jToken) external view returns (uint256); } /** * @title Careful Math * @author Compound * @notice Derived from OpenZeppelin's SafeMath library * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol */ contract CarefulMath { /** * @dev Possible error codes that we can return */ enum MathError { NO_ERROR, DIVISION_BY_ZERO, INTEGER_OVERFLOW, INTEGER_UNDERFLOW } /** * @dev Multiplies two numbers, returns an error on overflow. */ function mulUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { if (a == 0) { return (MathError.NO_ERROR, 0); } uint256 c = a * b; if (c / a != b) { return (MathError.INTEGER_OVERFLOW, 0); } else { return (MathError.NO_ERROR, c); } } /** * @dev Integer division of two numbers, truncating the quotient. */ function divUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { if (b == 0) { return (MathError.DIVISION_BY_ZERO, 0); } return (MathError.NO_ERROR, a / b); } /** * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). */ function subUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { if (b <= a) { return (MathError.NO_ERROR, a - b); } else { return (MathError.INTEGER_UNDERFLOW, 0); } } /** * @dev Adds two numbers, returns an error on overflow. */ function addUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { uint256 c = a + b; if (c >= a) { return (MathError.NO_ERROR, c); } else { return (MathError.INTEGER_OVERFLOW, 0); } } /** * @dev add a and b and then subtract c */ function addThenSubUInt( uint256 a, uint256 b, uint256 c ) internal pure returns (MathError, uint256) { (MathError err0, uint256 sum) = addUInt(a, b); if (err0 != MathError.NO_ERROR) { return (err0, 0); } return subUInt(sum, c); } } /** * @title Exponential module for storing fixed-precision decimals * @author Compound * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places. * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is: * `Exp({mantissa: 5100000000000000000})`. */ contract Exponential is CarefulMath { uint256 constant expScale = 1e18; uint256 constant doubleScale = 1e36; uint256 constant halfExpScale = expScale / 2; uint256 constant mantissaOne = expScale; struct Exp { uint256 mantissa; } struct Double { uint256 mantissa; } /** * @dev Creates an exponential from numerator and denominator values. * Note: Returns an error if (`num` * 10e18) > MAX_INT, * or if `denom` is zero. */ function getExp(uint256 num, uint256 denom) internal pure returns (MathError, Exp memory) { (MathError err0, uint256 scaledNumerator) = mulUInt(num, expScale); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } (MathError err1, uint256 rational) = divUInt(scaledNumerator, denom); if (err1 != MathError.NO_ERROR) { return (err1, Exp({mantissa: 0})); } return (MathError.NO_ERROR, Exp({mantissa: rational})); } /** * @dev Adds two exponentials, returning a new exponential. */ function addExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { (MathError error, uint256 result) = addUInt(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } /** * @dev Subtracts two exponentials, returning a new exponential. */ function subExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { (MathError error, uint256 result) = subUInt(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } /** * @dev Multiply an Exp by a scalar, returning a new Exp. */ function mulScalar(Exp memory a, uint256 scalar) internal pure returns (MathError, Exp memory) { (MathError err0, uint256 scaledMantissa) = mulUInt(a.mantissa, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa})); } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mulScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (MathError, uint256) { (MathError err, Exp memory product) = mulScalar(a, scalar); if (err != MathError.NO_ERROR) { return (err, 0); } return (MathError.NO_ERROR, truncate(product)); } /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ function mulScalarTruncateAddUInt( Exp memory a, uint256 scalar, uint256 addend ) internal pure returns (MathError, uint256) { (MathError err, Exp memory product) = mulScalar(a, scalar); if (err != MathError.NO_ERROR) { return (err, 0); } return addUInt(truncate(product), addend); } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) { Exp memory product = mul_(a, scalar); return truncate(product); } /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ function mul_ScalarTruncateAddUInt( Exp memory a, uint256 scalar, uint256 addend ) internal pure returns (uint256) { Exp memory product = mul_(a, scalar); return add_(truncate(product), addend); } /** * @dev Divide an Exp by a scalar, returning a new Exp. */ function divScalar(Exp memory a, uint256 scalar) internal pure returns (MathError, Exp memory) { (MathError err0, uint256 descaledMantissa) = divUInt(a.mantissa, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa})); } /** * @dev Divide a scalar by an Exp, returning a new Exp. */ function divScalarByExp(uint256 scalar, Exp memory divisor) internal pure returns (MathError, Exp memory) { /* We are doing this as: getExp(mulUInt(expScale, scalar), divisor.mantissa) How it works: Exp = a / b; Scalar = s; `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` */ (MathError err0, uint256 numerator) = mulUInt(expScale, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } return getExp(numerator, divisor.mantissa); } /** * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. */ function divScalarByExpTruncate(uint256 scalar, Exp memory divisor) internal pure returns (MathError, uint256) { (MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor); if (err != MathError.NO_ERROR) { return (err, 0); } return (MathError.NO_ERROR, truncate(fraction)); } /** * @dev Divide a scalar by an Exp, returning a new Exp. */ function div_ScalarByExp(uint256 scalar, Exp memory divisor) internal pure returns (Exp memory) { /* We are doing this as: getExp(mulUInt(expScale, scalar), divisor.mantissa) How it works: Exp = a / b; Scalar = s; `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` */ uint256 numerator = mul_(expScale, scalar); return Exp({mantissa: div_(numerator, divisor)}); } /** * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. */ function div_ScalarByExpTruncate(uint256 scalar, Exp memory divisor) internal pure returns (uint256) { Exp memory fraction = div_ScalarByExp(scalar, divisor); return truncate(fraction); } /** * @dev Multiplies two exponentials, returning a new exponential. */ function mulExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { (MathError err0, uint256 doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } // We add half the scale before dividing so that we get rounding instead of truncation. // See "Listing 6" and text above it at https://accu.org/index.php/journals/1717 // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18. (MathError err1, uint256 doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct); if (err1 != MathError.NO_ERROR) { return (err1, Exp({mantissa: 0})); } (MathError err2, uint256 product) = divUInt(doubleScaledProductWithHalfScale, expScale); // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero. assert(err2 == MathError.NO_ERROR); return (MathError.NO_ERROR, Exp({mantissa: product})); } /** * @dev Multiplies two exponentials given their mantissas, returning a new exponential. */ function mulExp(uint256 a, uint256 b) internal pure returns (MathError, Exp memory) { return mulExp(Exp({mantissa: a}), Exp({mantissa: b})); } /** * @dev Multiplies three exponentials, returning a new exponential. */ function mulExp3( Exp memory a, Exp memory b, Exp memory c ) internal pure returns (MathError, Exp memory) { (MathError err, Exp memory ab) = mulExp(a, b); if (err != MathError.NO_ERROR) { return (err, ab); } return mulExp(ab, c); } /** * @dev Divides two exponentials, returning a new exponential. * (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b, * which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa) */ function divExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) { return getExp(a.mantissa, b.mantissa); } /** * @dev Truncates the given exp to a whole number value. * For example, truncate(Exp{mantissa: 15 * expScale}) = 15 */ function truncate(Exp memory exp) internal pure returns (uint256) { // Note: We are not using careful math here as we're performing a division that cannot fail return exp.mantissa / expScale; } /** * @dev Checks if first Exp is less than second Exp. */ function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa < right.mantissa; } /** * @dev Checks if left Exp <= right Exp. */ function lessThanOrEqualExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa <= right.mantissa; } /** * @dev returns true if Exp is exactly zero */ function isZeroExp(Exp memory value) internal pure returns (bool) { return value.mantissa == 0; } function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) { require(n < 2**224, errorMessage); return uint224(n); } function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(uint256 a, uint256 b) internal pure returns (uint256) { return add_(a, b, "addition overflow"); } function add_( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, errorMessage); return c; } function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(uint256 a, uint256 b) internal pure returns (uint256) { return sub_(a, b, "subtraction underflow"); } function sub_( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale}); } function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b)}); } function mul_(uint256 a, Exp memory b) internal pure returns (uint256) { return mul_(a, b.mantissa) / expScale; } function mul_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale}); } function mul_(Double memory a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b)}); } function mul_(uint256 a, Double memory b) internal pure returns (uint256) { return mul_(a, b.mantissa) / doubleScale; } function mul_(uint256 a, uint256 b) internal pure returns (uint256) { return mul_(a, b, "multiplication overflow"); } function mul_( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { if (a == 0 || b == 0) { return 0; } uint256 c = a * b; require(c / a == b, errorMessage); return c; } function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)}); } function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) { return Exp({mantissa: div_(a.mantissa, b)}); } function div_(uint256 a, Exp memory b) internal pure returns (uint256) { return div_(mul_(a, expScale), b.mantissa); } function div_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)}); } function div_(Double memory a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: div_(a.mantissa, b)}); } function div_(uint256 a, Double memory b) internal pure returns (uint256) { return div_(mul_(a, doubleScale), b.mantissa); } function div_(uint256 a, uint256 b) internal pure returns (uint256) { return div_(a, b, "divide by zero"); } function div_( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } function fraction(uint256 a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: div_(mul_(a, doubleScale), b)}); } // implementation from https://github.com/Uniswap/uniswap-lib/commit/99f3f28770640ba1bb1ff460ac7c5292fb8291a0 // original implementation: https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687 function sqrt(uint256 x) internal pure returns (uint256) { if (x == 0) return 0; uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return (r < r1 ? r : r1); } } contract UnitrollerAdminStorage { /** * @notice Administrator for this contract */ address public admin; /** * @notice Pending administrator for this contract */ address public pendingAdmin; /** * @notice Active brains of Unitroller */ address public implementation; /** * @notice Pending brains of Unitroller */ address public pendingImplementation; } contract JoetrollerV1Storage is UnitrollerAdminStorage { /** * @notice Oracle which gives the price of any given asset */ PriceOracle public oracle; /** * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow */ uint256 public closeFactorMantissa; /** * @notice Multiplier representing the discount on collateral that a liquidator receives */ uint256 public liquidationIncentiveMantissa; /** * @notice Per-account mapping of "assets you are in" */ mapping(address => JToken[]) public accountAssets; enum Version { VANILLA, COLLATERALCAP, WRAPPEDNATIVE } struct Market { /// @notice Whether or not this market is listed bool isListed; /** * @notice Multiplier representing the most one can borrow against their collateral in this market. * For instance, 0.9 to allow borrowing 90% of collateral value. * Must be between 0 and 1, and stored as a mantissa. */ uint256 collateralFactorMantissa; /// @notice Per-market mapping of "accounts in this asset" mapping(address => bool) accountMembership; /// @notice JToken version Version version; } /** * @notice Official mapping of jTokens -> Market metadata * @dev Used e.g. to determine if a market is supported */ mapping(address => Market) public markets; /** * @notice The Pause Guardian can pause certain actions as a safety mechanism. * Actions which allow users to remove their own assets cannot be paused. * Liquidation / seizing / transfer can only be paused globally, not by market. */ address public pauseGuardian; bool public _mintGuardianPaused; bool public _borrowGuardianPaused; bool public transferGuardianPaused; bool public seizeGuardianPaused; mapping(address => bool) public mintGuardianPaused; mapping(address => bool) public borrowGuardianPaused; /// @notice A list of all markets JToken[] public allMarkets; // @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market. address public borrowCapGuardian; // @notice Borrow caps enforced by borrowAllowed for each jToken address. Defaults to zero which corresponds to unlimited borrowing. mapping(address => uint256) public borrowCaps; // @notice The supplyCapGuardian can set supplyCaps to any number for any market. Lowering the supply cap could disable supplying to the given market. address public supplyCapGuardian; // @notice Supply caps enforced by mintAllowed for each jToken address. Defaults to zero which corresponds to unlimited supplying. mapping(address => uint256) public supplyCaps; // @notice creditLimits allowed specific protocols to borrow and repay without collateral. mapping(address => uint256) public creditLimits; // @notice flashloanGuardianPaused can pause flash loan as a safety mechanism. mapping(address => bool) public flashloanGuardianPaused; // @notice rewardDistributor The module that handles reward distribution. address payable public rewardDistributor; } contract JoetrollerInterface { /// @notice Indicator that this is a Joetroller contract (for inspection) bool public constant isJoetroller = true; /*** Assets You Are In ***/ function enterMarkets(address[] calldata jTokens) external returns (uint256[] memory); function exitMarket(address jToken) external returns (uint256); /*** Policy Hooks ***/ function mintAllowed( address jToken, address minter, uint256 mintAmount ) external returns (uint256); function mintVerify( address jToken, address minter, uint256 mintAmount, uint256 mintTokens ) external; function redeemAllowed( address jToken, address redeemer, uint256 redeemTokens ) external returns (uint256); function redeemVerify( address jToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens ) external; function borrowAllowed( address jToken, address borrower, uint256 borrowAmount ) external returns (uint256); function borrowVerify( address jToken, address borrower, uint256 borrowAmount ) external; function repayBorrowAllowed( address jToken, address payer, address borrower, uint256 repayAmount ) external returns (uint256); function repayBorrowVerify( address jToken, address payer, address borrower, uint256 repayAmount, uint256 borrowerIndex ) external; function liquidateBorrowAllowed( address jTokenBorrowed, address jTokenCollateral, address liquidator, address borrower, uint256 repayAmount ) external returns (uint256); function liquidateBorrowVerify( address jTokenBorrowed, address jTokenCollateral, address liquidator, address borrower, uint256 repayAmount, uint256 seizeTokens ) external; function seizeAllowed( address jTokenCollateral, address jTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external returns (uint256); function seizeVerify( address jTokenCollateral, address jTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external; function transferAllowed( address jToken, address src, address dst, uint256 transferTokens ) external returns (uint256); function transferVerify( address jToken, address src, address dst, uint256 transferTokens ) external; /*** Liquidity/Liquidation Calculations ***/ function liquidateCalculateSeizeTokens( address jTokenBorrowed, address jTokenCollateral, uint256 repayAmount ) external view returns (uint256, uint256); } interface JoetrollerInterfaceExtension { function checkMembership(address account, JToken jToken) external view returns (bool); function updateJTokenVersion(address jToken, JoetrollerV1Storage.Version version) external; function flashloanAllowed( address jToken, address receiver, uint256 amount, bytes calldata params ) external view returns (bool); } contract JTokenStorage { /** * @dev Guard variable for re-entrancy checks */ bool internal _notEntered; /** * @notice EIP-20 token name for this token */ string public name; /** * @notice EIP-20 token symbol for this token */ string public symbol; /** * @notice EIP-20 token decimals for this token */ uint8 public decimals; /** * @notice Maximum borrow rate that can ever be applied (.0005% / sec) */ uint256 internal constant borrowRateMaxMantissa = 0.0005e16; /** * @notice Maximum fraction of interest that can be set aside for reserves */ uint256 internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract */ address payable public admin; /** * @notice Pending administrator for this contract */ address payable public pendingAdmin; /** * @notice Contract which oversees inter-jToken operations */ JoetrollerInterface public joetroller; /** * @notice Model which tells what the current interest rate should be */ InterestRateModel public interestRateModel; /** * @notice Initial exchange rate used when minting the first JTokens (used when totalSupply = 0) */ uint256 internal initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves */ uint256 public reserveFactorMantissa; /** * @notice Block timestamp that interest was last accrued at */ uint256 public accrualBlockTimestamp; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ uint256 public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ uint256 public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ uint256 public totalReserves; /** * @notice Total number of tokens in circulation */ uint256 public totalSupply; /** * @notice Official record of token balances for each account */ mapping(address => uint256) internal accountTokens; /** * @notice Approved token transfer amounts on behalf of others */ mapping(address => mapping(address => uint256)) internal transferAllowances; /** * @notice Container for borrow balance information * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action * @member interestIndex Global borrowIndex as of the most recent balance-changing action */ struct BorrowSnapshot { uint256 principal; uint256 interestIndex; } /** * @notice Mapping of account addresses to outstanding borrow balances */ mapping(address => BorrowSnapshot) internal accountBorrows; } contract JErc20Storage { /** * @notice Underlying asset for this JToken */ address public underlying; /** * @notice Implementation address for this contract */ address public implementation; } contract JSupplyCapStorage { /** * @notice Internal cash counter for this JToken. Should equal underlying.balanceOf(address(this)) for CERC20. */ uint256 public internalCash; } contract JCollateralCapStorage { /** * @notice Total number of tokens used as collateral in circulation. */ uint256 public totalCollateralTokens; /** * @notice Record of token balances which could be treated as collateral for each account. * If collateral cap is not set, the value should be equal to accountTokens. */ mapping(address => uint256) public accountCollateralTokens; /** * @notice Check if accountCollateralTokens have been initialized. */ mapping(address => bool) public isCollateralTokenInit; /** * @notice Collateral cap for this JToken, zero for no cap. */ uint256 public collateralCap; } /*** Interface ***/ contract JTokenInterface is JTokenStorage { /** * @notice Indicator that this is a JToken contract (for inspection) */ bool public constant isJToken = true; /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows); /** * @notice Event emitted when tokens are minted */ event Mint(address minter, uint256 mintAmount, uint256 mintTokens); /** * @notice Event emitted when tokens are redeemed */ event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens); /** * @notice Event emitted when underlying is borrowed */ event Borrow(address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows); /** * @notice Event emitted when a borrow is repaid */ event RepayBorrow( address payer, address borrower, uint256 repayAmount, uint256 accountBorrows, uint256 totalBorrows ); /** * @notice Event emitted when a borrow is liquidated */ event LiquidateBorrow( address liquidator, address borrower, uint256 repayAmount, address jTokenCollateral, uint256 seizeTokens ); /*** Admin Events ***/ /** * @notice Event emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Event emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); /** * @notice Event emitted when joetroller is changed */ event NewJoetroller(JoetrollerInterface oldJoetroller, JoetrollerInterface newJoetroller); /** * @notice Event emitted when interestRateModel is changed */ event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); /** * @notice Event emitted when the reserve factor is changed */ event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa); /** * @notice Event emitted when the reserves are added */ event ReservesAdded(address benefactor, uint256 addAmount, uint256 newTotalReserves); /** * @notice Event emitted when the reserves are reduced */ event ReservesReduced(address admin, uint256 reduceAmount, uint256 newTotalReserves); /** * @notice EIP20 Transfer event */ event Transfer(address indexed from, address indexed to, uint256 amount); /** * @notice EIP20 Approval event */ event Approval(address indexed owner, address indexed spender, uint256 amount); /** * @notice Failure event */ event Failure(uint256 error, uint256 info, uint256 detail); /*** User Interface ***/ function transfer(address dst, uint256 amount) external returns (bool); function transferFrom( address src, address dst, uint256 amount ) external returns (bool); function approve(address spender, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function balanceOfUnderlying(address owner) external returns (uint256); function getAccountSnapshot(address account) external view returns ( uint256, uint256, uint256, uint256 ); function borrowRatePerSecond() external view returns (uint256); function supplyRatePerSecond() external view returns (uint256); function totalBorrowsCurrent() external returns (uint256); function borrowBalanceCurrent(address account) external returns (uint256); function borrowBalanceStored(address account) public view returns (uint256); function exchangeRateCurrent() public returns (uint256); function exchangeRateStored() public view returns (uint256); function getCash() external view returns (uint256); function accrueInterest() public returns (uint256); function seize( address liquidator, address borrower, uint256 seizeTokens ) external returns (uint256); /*** Admin Functions ***/ function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256); function _acceptAdmin() external returns (uint256); function _setJoetroller(JoetrollerInterface newJoetroller) public returns (uint256); function _setReserveFactor(uint256 newReserveFactorMantissa) external returns (uint256); function _reduceReserves(uint256 reduceAmount) external returns (uint256); function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256); } contract JErc20Interface is JErc20Storage { /*** User Interface ***/ function mint(uint256 mintAmount) external returns (uint256); function redeem(uint256 redeemTokens) external returns (uint256); function redeemUnderlying(uint256 redeemAmount) external returns (uint256); function borrow(uint256 borrowAmount) external returns (uint256); function repayBorrow(uint256 repayAmount) external returns (uint256); function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256); function liquidateBorrow( address borrower, uint256 repayAmount, JTokenInterface jTokenCollateral ) external returns (uint256); function _addReserves(uint256 addAmount) external returns (uint256); } contract JWrappedNativeInterface is JErc20Interface { /** * @notice Flash loan fee ratio */ uint256 public constant flashFeeBips = 8; /*** Market Events ***/ /** * @notice Event emitted when a flashloan occured */ event Flashloan(address indexed receiver, uint256 amount, uint256 totalFee, uint256 reservesFee); /*** User Interface ***/ function mintNative() external payable returns (uint256); function redeemNative(uint256 redeemTokens) external returns (uint256); function redeemUnderlyingNative(uint256 redeemAmount) external returns (uint256); function borrowNative(uint256 borrowAmount) external returns (uint256); function repayBorrowNative() external payable returns (uint256); function repayBorrowBehalfNative(address borrower) external payable returns (uint256); function liquidateBorrowNative(address borrower, JTokenInterface jTokenCollateral) external payable returns (uint256); function flashLoan( ERC3156FlashBorrowerInterface receiver, address initiator, uint256 amount, bytes calldata data ) external returns (bool); function _addReservesNative() external payable returns (uint256); } contract JCapableErc20Interface is JErc20Interface, JSupplyCapStorage { /** * @notice Flash loan fee ratio */ uint256 public constant flashFeeBips = 8; /*** Market Events ***/ /** * @notice Event emitted when a flashloan occured */ event Flashloan(address indexed receiver, uint256 amount, uint256 totalFee, uint256 reservesFee); /*** User Interface ***/ function gulp() external; } contract JCollateralCapErc20Interface is JCapableErc20Interface, JCollateralCapStorage { /*** Admin Events ***/ /** * @notice Event emitted when collateral cap is set */ event NewCollateralCap(address token, uint256 newCap); /** * @notice Event emitted when user collateral is changed */ event UserCollateralChanged(address account, uint256 newCollateralTokens); /*** User Interface ***/ function registerCollateral(address account) external returns (uint256); function unregisterCollateral(address account) external; function flashLoan( ERC3156FlashBorrowerInterface receiver, address initiator, uint256 amount, bytes calldata data ) external returns (bool); /*** Admin Functions ***/ function _setCollateralCap(uint256 newCollateralCap) external; } contract JDelegatorInterface { /** * @notice Emitted when implementation is changed */ event NewImplementation(address oldImplementation, address newImplementation); /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ function _setImplementation( address implementation_, bool allowResign, bytes memory becomeImplementationData ) public; } contract JDelegateInterface { /** * @notice Called by the delegator on a delegate to initialize it for duty * @dev Should revert if any issues arise which make it unfit for delegation * @param data The encoded bytes data for any initialization */ function _becomeImplementation(bytes memory data) public; /** * @notice Called by the delegator on a delegate to forfeit its responsibility */ function _resignImplementation() public; } /*** External interface ***/ /** * @title Flash loan receiver interface */ interface IFlashloanReceiver { function executeOperation( address sender, address underlying, uint256 amount, uint256 fee, bytes calldata params ) external; } contract JProtocolSeizeShareStorage { /** * @notice Event emitted when the protocol share of seized collateral is changed */ event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa); /** * @notice Share of seized collateral that is added to reserves */ uint256 public protocolSeizeShareMantissa; /** * @notice Maximum fraction of seized collateral that can be set aside for reserves */ uint256 internal constant protocolSeizeShareMaxMantissa = 1e18; } /** * @title EIP20NonStandardInterface * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ interface EIP20NonStandardInterface { /** * @notice Get the total number of tokens in circulation * @return The supply of tokens */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return The balance */ function balanceOf(address owner) external view returns (uint256 balance); /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer */ function transfer(address dst, uint256 amount) external; /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer */ function transferFrom( address src, address dst, uint256 amount ) external; /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); } /** * @title Compound's JToken Contract * @notice Abstract base for JTokens * @author Compound */ contract JToken is JTokenInterface, Exponential, TokenErrorReporter { /** * @notice Initialize the money market * @param joetroller_ The address of the Joetroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ EIP-20 name of this token * @param symbol_ EIP-20 symbol of this token * @param decimals_ EIP-20 decimal precision of this token */ function initialize( JoetrollerInterface joetroller_, InterestRateModel interestRateModel_, uint256 initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_ ) public { require(msg.sender == admin, "only admin may initialize the market"); require(accrualBlockTimestamp == 0 && borrowIndex == 0, "market may only be initialized once"); // Set initial exchange rate initialExchangeRateMantissa = initialExchangeRateMantissa_; require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero."); // Set the joetroller uint256 err = _setJoetroller(joetroller_); require(err == uint256(Error.NO_ERROR), "setting joetroller failed"); // Initialize block timestamp and borrow index (block timestamp mocks depend on joetroller being set) accrualBlockTimestamp = getBlockTimestamp(); borrowIndex = mantissaOne; // Set the interest rate model (depends on block timestamp / borrow index) err = _setInterestRateModelFresh(interestRateModel_); require(err == uint256(Error.NO_ERROR), "setting interest rate model failed"); name = name_; symbol = symbol_; decimals = decimals_; // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund) _notEntered = true; } /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) external nonReentrant returns (bool) { return transferTokens(msg.sender, msg.sender, dst, amount) == uint256(Error.NO_ERROR); } /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom( address src, address dst, uint256 amount ) external nonReentrant returns (bool) { return transferTokens(msg.sender, src, dst, amount) == uint256(Error.NO_ERROR); } /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool) { address src = msg.sender; transferAllowances[src][spender] = amount; emit Approval(src, spender, amount); return true; } /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view returns (uint256) { return transferAllowances[owner][spender]; } /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ function balanceOf(address owner) external view returns (uint256) { return accountTokens[owner]; } /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ function balanceOfUnderlying(address owner) external returns (uint256) { Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()}); return mul_ScalarTruncate(exchangeRate, accountTokens[owner]); } /** * @notice Get a snapshot of the account's balances, and the cached exchange rate * @dev This is used by joetroller to more efficiently perform liquidity checks. * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ function getAccountSnapshot(address account) external view returns ( uint256, uint256, uint256, uint256 ) { uint256 jTokenBalance = getJTokenBalanceInternal(account); uint256 borrowBalance = borrowBalanceStoredInternal(account); uint256 exchangeRateMantissa = exchangeRateStoredInternal(); return (uint256(Error.NO_ERROR), jTokenBalance, borrowBalance, exchangeRateMantissa); } /** * @dev Function to simply retrieve block timestamp * This exists mainly for inheriting test contracts to stub this result. */ function getBlockTimestamp() internal view returns (uint256) { return block.timestamp; } /** * @notice Returns the current per-sec borrow interest rate for this jToken * @return The borrow interest rate per sec, scaled by 1e18 */ function borrowRatePerSecond() external view returns (uint256) { return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves); } /** * @notice Returns the current per-sec supply interest rate for this jToken * @return The supply interest rate per sec, scaled by 1e18 */ function supplyRatePerSecond() external view returns (uint256) { return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa); } /** * @notice Returns the estimated per-sec borrow interest rate for this jToken after some change * @return The borrow interest rate per sec, scaled by 1e18 */ function estimateBorrowRatePerSecondAfterChange(uint256 change, bool repay) external view returns (uint256) { uint256 cashPriorNew; uint256 totalBorrowsNew; if (repay) { cashPriorNew = add_(getCashPrior(), change); totalBorrowsNew = sub_(totalBorrows, change); } else { cashPriorNew = sub_(getCashPrior(), change); totalBorrowsNew = add_(totalBorrows, change); } return interestRateModel.getBorrowRate(cashPriorNew, totalBorrowsNew, totalReserves); } /** * @notice Returns the estimated per-sec supply interest rate for this jToken after some change * @return The supply interest rate per sec, scaled by 1e18 */ function estimateSupplyRatePerSecondAfterChange(uint256 change, bool repay) external view returns (uint256) { uint256 cashPriorNew; uint256 totalBorrowsNew; if (repay) { cashPriorNew = add_(getCashPrior(), change); totalBorrowsNew = sub_(totalBorrows, change); } else { cashPriorNew = sub_(getCashPrior(), change); totalBorrowsNew = add_(totalBorrows, change); } return interestRateModel.getSupplyRate(cashPriorNew, totalBorrowsNew, totalReserves, reserveFactorMantissa); } /** * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ function totalBorrowsCurrent() external nonReentrant returns (uint256) { require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); return totalBorrows; } /** * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ function borrowBalanceCurrent(address account) external nonReentrant returns (uint256) { require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); return borrowBalanceStored(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return The calculated balance */ function borrowBalanceStored(address account) public view returns (uint256) { return borrowBalanceStoredInternal(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return the calculated balance or 0 if error code is non-zero */ function borrowBalanceStoredInternal(address account) internal view returns (uint256) { /* Get borrowBalance and borrowIndex */ BorrowSnapshot storage borrowSnapshot = accountBorrows[account]; /* If borrowBalance = 0 then borrowIndex is likely also 0. * Rather than failing the calculation with a division by 0, we immediately return 0 in this case. */ if (borrowSnapshot.principal == 0) { return 0; } /* Calculate new borrow balance using the interest index: * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex */ uint256 principalTimesIndex = mul_(borrowSnapshot.principal, borrowIndex); uint256 result = div_(principalTimesIndex, borrowSnapshot.interestIndex); return result; } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() public nonReentrant returns (uint256) { require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed"); return exchangeRateStored(); } /** * @notice Calculates the exchange rate from the underlying to the JToken * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateStored() public view returns (uint256) { return exchangeRateStoredInternal(); } /** * @notice Calculates the exchange rate from the underlying to the JToken * @dev This function does not accrue interest before calculating the exchange rate * @return calculated exchange rate scaled by 1e18 */ function exchangeRateStoredInternal() internal view returns (uint256) { uint256 _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return initialExchangeRateMantissa; } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ uint256 totalCash = getCashPrior(); uint256 cashPlusBorrowsMinusReserves = sub_(add_(totalCash, totalBorrows), totalReserves); uint256 exchangeRate = div_(cashPlusBorrowsMinusReserves, Exp({mantissa: _totalSupply})); return exchangeRate; } } /** * @notice Get cash balance of this jToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ function getCash() external view returns (uint256) { return getCashPrior(); } /** * @notice Applies accrued interest to total borrows and reserves * @dev This calculates interest accrued from the last checkpointed timestamp * up to the current timestamp and writes new checkpoint to storage. */ function accrueInterest() public returns (uint256) { /* Remember the initial block timestamp */ uint256 currentBlockTimestamp = getBlockTimestamp(); uint256 accrualBlockTimestampPrior = accrualBlockTimestamp; /* Short-circuit accumulating 0 interest */ if (accrualBlockTimestampPrior == currentBlockTimestamp) { return uint256(Error.NO_ERROR); } /* Read the previous values out of storage */ uint256 cashPrior = getCashPrior(); uint256 borrowsPrior = totalBorrows; uint256 reservesPrior = totalReserves; uint256 borrowIndexPrior = borrowIndex; /* Calculate the current borrow interest rate */ uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high"); /* Calculate the number of seconds elapsed since the last accrual */ uint256 timestampDelta = sub_(currentBlockTimestamp, accrualBlockTimestampPrior); /* * Calculate the interest accumulated into borrows and reserves and the new index: * simpleInterestFactor = borrowRate * timestampDelta * interestAccumulated = simpleInterestFactor * totalBorrows * totalBorrowsNew = interestAccumulated + totalBorrows * totalReservesNew = interestAccumulated * reserveFactor + totalReserves * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex */ Exp memory simpleInterestFactor = mul_(Exp({mantissa: borrowRateMantissa}), timestampDelta); uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior); uint256 totalBorrowsNew = add_(interestAccumulated, borrowsPrior); uint256 totalReservesNew = mul_ScalarTruncateAddUInt( Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior ); uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ accrualBlockTimestamp = currentBlockTimestamp; borrowIndex = borrowIndexNew; totalBorrows = totalBorrowsNew; totalReserves = totalReservesNew; /* We emit an AccrueInterest event */ emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew); return uint256(Error.NO_ERROR); } /** * @notice Sender supplies assets into the market and receives jTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. */ function mintInternal(uint256 mintAmount, bool isNative) internal nonReentrant returns (uint256, uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0); } // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to return mintFresh(msg.sender, mintAmount, isNative); } /** * @notice Sender redeems jTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of jTokens to redeem into underlying * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemInternal(uint256 redeemTokens, bool isNative) internal nonReentrant returns (uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); } // redeemFresh emits redeem-specific logs on errors, so we don't need to return redeemFresh(msg.sender, redeemTokens, 0, isNative); } /** * @notice Sender redeems jTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to receive from redeeming jTokens * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlyingInternal(uint256 redeemAmount, bool isNative) internal nonReentrant returns (uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); } // redeemFresh emits redeem-specific logs on errors, so we don't need to return redeemFresh(msg.sender, 0, redeemAmount, isNative); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrowInternal(uint256 borrowAmount, bool isNative) internal nonReentrant returns (uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED); } // borrowFresh emits borrow-specific logs on errors, so we don't need to return borrowFresh(msg.sender, borrowAmount, isNative); } struct BorrowLocalVars { MathError mathErr; uint256 accountBorrows; uint256 accountBorrowsNew; uint256 totalBorrowsNew; } /** * @notice Users borrow assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrowFresh( address payable borrower, uint256 borrowAmount, bool isNative ) internal returns (uint256) { /* Fail if borrow not allowed */ uint256 allowed = joetroller.borrowAllowed(address(this), borrower, borrowAmount); if (allowed != 0) { return failOpaque(Error.JOETROLLER_REJECTION, FailureInfo.BORROW_JOETROLLER_REJECTION, allowed); } /* * Return if borrowAmount is zero. * Put behind `borrowAllowed` for accuring potential JOE rewards. */ if (borrowAmount == 0) { accountBorrows[borrower].principal = borrowBalanceStoredInternal(borrower); accountBorrows[borrower].interestIndex = borrowIndex; return uint256(Error.NO_ERROR); } /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK); } /* Fail gracefully if protocol has insufficient underlying cash */ if (getCashPrior() < borrowAmount) { return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.BORROW_CASH_NOT_AVAILABLE); } BorrowLocalVars memory vars; /* * We calculate the new borrower and total borrow balances, failing on overflow: * accountBorrowsNew = accountBorrows + borrowAmount * totalBorrowsNew = totalBorrows + borrowAmount */ vars.accountBorrows = borrowBalanceStoredInternal(borrower); vars.accountBorrowsNew = add_(vars.accountBorrows, borrowAmount); vars.totalBorrowsNew = add_(totalBorrows, borrowAmount); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We invoke doTransferOut for the borrower and the borrowAmount. * Note: The jToken must handle variations between ERC-20 and ETH underlying. * On success, the jToken borrowAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ doTransferOut(borrower, borrowAmount, isNative); /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = vars.accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = vars.totalBorrowsNew; /* We emit a Borrow event */ emit Borrow(borrower, borrowAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); /* We call the defense hook */ // unused function // joetroller.borrowVerify(address(this), borrower, borrowAmount); return uint256(Error.NO_ERROR); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function repayBorrowInternal(uint256 repayAmount, bool isNative) internal nonReentrant returns (uint256, uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0); } // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to return repayBorrowFresh(msg.sender, msg.sender, repayAmount, isNative); } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function repayBorrowBehalfInternal( address borrower, uint256 repayAmount, bool isNative ) internal nonReentrant returns (uint256, uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0); } // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to return repayBorrowFresh(msg.sender, borrower, repayAmount, isNative); } struct RepayBorrowLocalVars { Error err; MathError mathErr; uint256 repayAmount; uint256 borrowerIndex; uint256 accountBorrows; uint256 accountBorrowsNew; uint256 totalBorrowsNew; uint256 actualRepayAmount; } /** * @notice Borrows are repaid by another user (possibly the borrower). * @param payer the account paying off the borrow * @param borrower the account with the debt being payed off * @param repayAmount the amount of undelrying tokens being returned * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function repayBorrowFresh( address payer, address borrower, uint256 repayAmount, bool isNative ) internal returns (uint256, uint256) { /* Fail if repayBorrow not allowed */ uint256 allowed = joetroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); if (allowed != 0) { return (failOpaque(Error.JOETROLLER_REJECTION, FailureInfo.REPAY_BORROW_JOETROLLER_REJECTION, allowed), 0); } /* * Return if repayAmount is zero. * Put behind `repayBorrowAllowed` for accuring potential JOE rewards. */ if (repayAmount == 0) { accountBorrows[borrower].principal = borrowBalanceStoredInternal(borrower); accountBorrows[borrower].interestIndex = borrowIndex; return (uint256(Error.NO_ERROR), 0); } /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.REPAY_BORROW_FRESHNESS_CHECK), 0); } RepayBorrowLocalVars memory vars; /* We remember the original borrowerIndex for verification purposes */ vars.borrowerIndex = accountBorrows[borrower].interestIndex; /* We fetch the amount the borrower owes, with accumulated interest */ vars.accountBorrows = borrowBalanceStoredInternal(borrower); /* If repayAmount == -1, repayAmount = accountBorrows */ if (repayAmount == uint256(-1)) { vars.repayAmount = vars.accountBorrows; } else { vars.repayAmount = repayAmount; } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the payer and the repayAmount * Note: The jToken must handle variations between ERC-20 and ETH underlying. * On success, the jToken holds an additional repayAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount, isNative); /* * We calculate the new borrower and total borrow balances, failing on underflow: * accountBorrowsNew = accountBorrows - actualRepayAmount * totalBorrowsNew = totalBorrows - actualRepayAmount */ vars.accountBorrowsNew = sub_(vars.accountBorrows, vars.actualRepayAmount); vars.totalBorrowsNew = sub_(totalBorrows, vars.actualRepayAmount); /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = vars.accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = vars.totalBorrowsNew; /* We emit a RepayBorrow event */ emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); /* We call the defense hook */ // unused function // joetroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex); return (uint256(Error.NO_ERROR), vars.actualRepayAmount); } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this jToken to be liquidated * @param repayAmount The amount of the underlying borrowed asset to repay * @param jTokenCollateral The market in which to seize collateral from the borrower * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function liquidateBorrowInternal( address borrower, uint256 repayAmount, JTokenInterface jTokenCollateral, bool isNative ) internal nonReentrant returns (uint256, uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED), 0); } error = jTokenCollateral.accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0); } // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to return liquidateBorrowFresh(msg.sender, borrower, repayAmount, jTokenCollateral, isNative); } /** * @notice The liquidator liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this jToken to be liquidated * @param liquidator The address repaying the borrow and seizing collateral * @param jTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function liquidateBorrowFresh( address liquidator, address borrower, uint256 repayAmount, JTokenInterface jTokenCollateral, bool isNative ) internal returns (uint256, uint256) { /* Fail if liquidate not allowed */ uint256 allowed = joetroller.liquidateBorrowAllowed( address(this), address(jTokenCollateral), liquidator, borrower, repayAmount ); if (allowed != 0) { return (failOpaque(Error.JOETROLLER_REJECTION, FailureInfo.LIQUIDATE_JOETROLLER_REJECTION, allowed), 0); } /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_FRESHNESS_CHECK), 0); } /* Verify jTokenCollateral market's block timestamp equals current block timestamp */ if (jTokenCollateral.accrualBlockTimestamp() != getBlockTimestamp()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_COLLATERAL_FRESHNESS_CHECK), 0); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { return (fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_LIQUIDATOR_IS_BORROWER), 0); } /* Fail if repayAmount = 0 */ if (repayAmount == 0) { return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_ZERO), 0); } /* Fail if repayAmount = -1 */ if (repayAmount == uint256(-1)) { return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0); } /* Fail if repayBorrow fails */ (uint256 repayBorrowError, uint256 actualRepayAmount) = repayBorrowFresh( liquidator, borrower, repayAmount, isNative ); if (repayBorrowError != uint256(Error.NO_ERROR)) { return (fail(Error(repayBorrowError), FailureInfo.LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We calculate the number of collateral tokens that will be seized */ (uint256 amountSeizeError, uint256 seizeTokens) = joetroller.liquidateCalculateSeizeTokens( address(this), address(jTokenCollateral), actualRepayAmount ); require(amountSeizeError == uint256(Error.NO_ERROR), "LIQUIDATE_JOETROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"); /* Revert if borrower collateral token balance < seizeTokens */ require(jTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH"); // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call uint256 seizeError; if (address(jTokenCollateral) == address(this)) { seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens); } else { seizeError = jTokenCollateral.seize(liquidator, borrower, seizeTokens); } /* Revert if seize tokens fails (since we cannot be sure of side effects) */ require(seizeError == uint256(Error.NO_ERROR), "token seizure failed"); /* We emit a LiquidateBorrow event */ emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(jTokenCollateral), seizeTokens); /* We call the defense hook */ // unused function // joetroller.liquidateBorrowVerify(address(this), address(jTokenCollateral), liquidator, borrower, actualRepayAmount, seizeTokens); return (uint256(Error.NO_ERROR), actualRepayAmount); } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Will fail unless called by another jToken during the process of liquidation. * Its absolutely critical to use msg.sender as the borrowed jToken and not a parameter. * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of jTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function seize( address liquidator, address borrower, uint256 seizeTokens ) external nonReentrant returns (uint256) { return seizeInternal(msg.sender, liquidator, borrower, seizeTokens); } /*** Admin Functions ***/ /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256) { // Check caller = admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); } // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); return uint256(Error.NO_ERROR); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() external returns (uint256) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin || msg.sender == address(0)) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); } // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return uint256(Error.NO_ERROR); } /** * @notice Sets a new joetroller for the market * @dev Admin function to set a new joetroller * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setJoetroller(JoetrollerInterface newJoetroller) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_JOETROLLER_OWNER_CHECK); } JoetrollerInterface oldJoetroller = joetroller; // Ensure invoke joetroller.isJoetroller() returns true require(newJoetroller.isJoetroller(), "marker method returned false"); // Set market's joetroller to newJoetroller joetroller = newJoetroller; // Emit NewJoetroller(oldJoetroller, newJoetroller) emit NewJoetroller(oldJoetroller, newJoetroller); return uint256(Error.NO_ERROR); } /** * @notice Accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh * @dev Admin function to accrue interest and set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactor(uint256 newReserveFactorMantissa) external nonReentrant returns (uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed. return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED); } // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to. return _setReserveFactorFresh(newReserveFactorMantissa); } /** * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) * @dev Admin function to set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK); } // Verify market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK); } // Check newReserveFactor ≤ maxReserveFactor if (newReserveFactorMantissa > reserveFactorMaxMantissa) { return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK); } uint256 oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); return uint256(Error.NO_ERROR); } /** * @notice Accrues interest and reduces reserves by transferring from msg.sender * @param addAmount Amount of addition to reserves * @param isNative The amount is in native or not * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReservesInternal(uint256 addAmount, bool isNative) internal nonReentrant returns (uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED); } // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to. (error, ) = _addReservesFresh(addAmount, isNative); return error; } /** * @notice Add reserves by transferring from caller * @dev Requires fresh interest accrual * @param addAmount Amount of addition to reserves * @param isNative The amount is in native or not * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees */ function _addReservesFresh(uint256 addAmount, bool isNative) internal returns (uint256, uint256) { // totalReserves + actualAddAmount uint256 totalReservesNew; uint256 actualAddAmount; // We fail gracefully unless market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.ADD_RESERVES_FRESH_CHECK), actualAddAmount); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the caller and the addAmount * Note: The jToken must handle variations between ERC-20 and ETH underlying. * On success, the jToken holds an additional addAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ actualAddAmount = doTransferIn(msg.sender, addAmount, isNative); totalReservesNew = add_(totalReserves, actualAddAmount); // Store reserves[n+1] = reserves[n] + actualAddAmount totalReserves = totalReservesNew; /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */ emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew); /* Return (NO_ERROR, actualAddAmount) */ return (uint256(Error.NO_ERROR), actualAddAmount); } /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReserves(uint256 reduceAmount) external nonReentrant returns (uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. return fail(Error(error), FailureInfo.REDUCE_RESERVES_ACCRUE_INTEREST_FAILED); } // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to. return _reduceReservesFresh(reduceAmount); } /** * @notice Reduces reserves by transferring to admin * @dev Requires fresh interest accrual * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReservesFresh(uint256 reduceAmount) internal returns (uint256) { // totalReserves - reduceAmount uint256 totalReservesNew; // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.REDUCE_RESERVES_ADMIN_CHECK); } // We fail gracefully unless market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK); } // Fail gracefully if protocol has insufficient underlying cash if (getCashPrior() < reduceAmount) { return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_AVAILABLE); } // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) totalReservesNew = sub_(totalReserves, reduceAmount); // Store reserves[n+1] = reserves[n] - reduceAmount totalReserves = totalReservesNew; // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. // Restrict reducing reserves in native token. Implementations except `JWrappedNative` won't use parameter `isNative`. doTransferOut(admin, reduceAmount, true); emit ReservesReduced(admin, reduceAmount, totalReservesNew); return uint256(Error.NO_ERROR); } /** * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh * @dev Admin function to accrue interest and update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint256) { uint256 error = accrueInterest(); if (error != uint256(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted change of interest rate model failed return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED); } // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to. return _setInterestRateModelFresh(newInterestRateModel); } /** * @notice updates the interest rate model (*requires fresh interest accrual) * @dev Admin function to update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint256) { // Used to store old model for use in the event that is emitted on success InterestRateModel oldInterestRateModel; // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK); } // We fail gracefully unless market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK); } // Track the market's current interest rate model oldInterestRateModel = interestRateModel; // Ensure invoke newInterestRateModel.isInterestRateModel() returns true require(newInterestRateModel.isInterestRateModel(), "marker method returned false"); // Set the interest rate model to newInterestRateModel interestRateModel = newInterestRateModel; // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel) emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel); return uint256(Error.NO_ERROR); } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying owned by this contract */ function getCashPrior() internal view returns (uint256); /** * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee. * This may revert due to insufficient balance or insufficient allowance. */ function doTransferIn( address from, uint256 amount, bool isNative ) internal returns (uint256); /** * @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting. * If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract. * If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions. */ function doTransferOut( address payable to, uint256 amount, bool isNative ) internal; /** * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` * @dev Called by both `transfer` and `transferFrom` internally */ function transferTokens( address spender, address src, address dst, uint256 tokens ) internal returns (uint256); /** * @notice Get the account's jToken balances */ function getJTokenBalanceInternal(address account) internal view returns (uint256); /** * @notice User supplies assets into the market and receives jTokens in exchange * @dev Assumes interest has already been accrued up to the current timestamp */ function mintFresh( address minter, uint256 mintAmount, bool isNative ) internal returns (uint256, uint256); /** * @notice User redeems jTokens in exchange for the underlying asset * @dev Assumes interest has already been accrued up to the current timestamp */ function redeemFresh( address payable redeemer, uint256 redeemTokensIn, uint256 redeemAmountIn, bool isNative ) internal returns (uint256); /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another JToken. * Its absolutely critical to use msg.sender as the seizer jToken and not a parameter. */ function seizeInternal( address seizerToken, address liquidator, address borrower, uint256 seizeTokens ) internal returns (uint256); /*** Reentrancy Guard ***/ /** * @dev Prevents a contract from calling itself, directly or indirectly. */ modifier nonReentrant() { require(_notEntered, "re-entered"); _notEntered = false; _; _notEntered = true; // get a gas-refund post-Istanbul } } pragma experimental ABIEncoderV2; contract RewardDistributorStorage { /** * @notice Administrator for this contract */ address public admin; /** * @notice Active brains of Unitroller */ Joetroller public joetroller; struct RewardMarketState { /// @notice The market's last updated joeBorrowIndex or joeSupplyIndex uint224 index; /// @notice The timestamp number the index was last updated at uint32 timestamp; } /// @notice The portion of supply reward rate that each market currently receives mapping(uint8 => mapping(address => uint256)) public rewardSupplySpeeds; /// @notice The portion of borrow reward rate that each market currently receives mapping(uint8 => mapping(address => uint256)) public rewardBorrowSpeeds; /// @notice The JOE/AVAX market supply state for each market mapping(uint8 => mapping(address => RewardMarketState)) public rewardSupplyState; /// @notice The JOE/AVAX market borrow state for each market mapping(uint8 => mapping(address => RewardMarketState)) public rewardBorrowState; /// @notice The JOE/AVAX borrow index for each market for each supplier as of the last time they accrued reward mapping(uint8 => mapping(address => mapping(address => uint256))) public rewardSupplierIndex; /// @notice The JOE/AVAX borrow index for each market for each borrower as of the last time they accrued reward mapping(uint8 => mapping(address => mapping(address => uint256))) public rewardBorrowerIndex; /// @notice The JOE/AVAX accrued but not yet transferred to each user mapping(uint8 => mapping(address => uint256)) public rewardAccrued; /// @notice The initial reward index for a market uint224 public constant rewardInitialIndex = 1e36; /// @notice JOE token contract address address public joeAddress; } contract RewardDistributor is RewardDistributorStorage, Exponential { /// @notice Emitted when a new reward supply speed is calculated for a market event RewardSupplySpeedUpdated(uint8 rewardType, JToken indexed jToken, uint256 newSpeed); /// @notice Emitted when a new reward borrow speed is calculated for a market event RewardBorrowSpeedUpdated(uint8 rewardType, JToken indexed jToken, uint256 newSpeed); /// @notice Emitted when JOE/AVAX is distributed to a supplier event DistributedSupplierReward( uint8 rewardType, JToken indexed jToken, address indexed supplier, uint256 rewardDelta, uint256 rewardSupplyIndex ); /// @notice Emitted when JOE/AVAX is distributed to a borrower event DistributedBorrowerReward( uint8 rewardType, JToken indexed jToken, address indexed borrower, uint256 rewardDelta, uint256 rewardBorrowIndex ); /// @notice Emitted when JOE is granted by admin event RewardGranted(uint8 rewardType, address recipient, uint256 amount); bool private initialized; constructor() public { admin = msg.sender; } function initialize() public { require(!initialized, "RewardDistributor already initialized"); joetroller = Joetroller(msg.sender); initialized = true; } /** * @notice Checks caller is admin, or this contract is becoming the new implementation */ function adminOrInitializing() internal view returns (bool) { return msg.sender == admin || msg.sender == address(joetroller); } /** * @notice Set JOE/AVAX speed for a single market * @param rewardType 0 = QI, 1 = AVAX * @param jToken The market whose reward speed to update * @param rewardSupplySpeed New reward supply speed for market * @param rewardBorrowSpeed New reward borrow speed for market */ function _setRewardSpeed( uint8 rewardType, JToken jToken, uint256 rewardSupplySpeed, uint256 rewardBorrowSpeed ) public { require(rewardType <= 1, "rewardType is invalid"); require(adminOrInitializing(), "only admin can set reward speed"); setRewardSpeedInternal(rewardType, jToken, rewardSupplySpeed, rewardBorrowSpeed); } /** * @notice Set JOE/AVAX speed for a single market * @param rewardType 0: JOE, 1: AVAX * @param jToken The market whose speed to update * @param newSupplySpeed New JOE or AVAX supply speed for market * @param newBorrowSpeed New JOE or AVAX borrow speed for market */ function setRewardSpeedInternal( uint8 rewardType, JToken jToken, uint256 newSupplySpeed, uint256 newBorrowSpeed ) internal { // Handle new supply speeed uint256 currentRewardSupplySpeed = rewardSupplySpeeds[rewardType][address(jToken)]; if (currentRewardSupplySpeed != 0) { // note that JOE speed could be set to 0 to halt liquidity rewards for a market updateRewardSupplyIndex(rewardType, address(jToken)); } else if (newSupplySpeed != 0) { // Add the JOE market require(joetroller.isMarketListed(address(jToken)), "reward market is not listed"); if ( rewardSupplyState[rewardType][address(jToken)].index == 0 && rewardSupplyState[rewardType][address(jToken)].timestamp == 0 ) { rewardSupplyState[rewardType][address(jToken)] = RewardMarketState({ index: rewardInitialIndex, timestamp: safe32(getBlockTimestamp(), "block timestamp exceeds 32 bits") }); } } if (currentRewardSupplySpeed != newSupplySpeed) { rewardSupplySpeeds[rewardType][address(jToken)] = newSupplySpeed; emit RewardSupplySpeedUpdated(rewardType, jToken, newSupplySpeed); } // Handle new borrow speed uint256 currentRewardBorrowSpeed = rewardBorrowSpeeds[rewardType][address(jToken)]; if (currentRewardBorrowSpeed != 0) { // note that JOE speed could be set to 0 to halt liquidity rewards for a market Exp memory borrowIndex = Exp({mantissa: jToken.borrowIndex()}); updateRewardBorrowIndex(rewardType, address(jToken), borrowIndex); } else if (newBorrowSpeed != 0) { // Add the JOE market require(joetroller.isMarketListed(address(jToken)), "reward market is not listed"); if ( rewardBorrowState[rewardType][address(jToken)].index == 0 && rewardBorrowState[rewardType][address(jToken)].timestamp == 0 ) { rewardBorrowState[rewardType][address(jToken)] = RewardMarketState({ index: rewardInitialIndex, timestamp: safe32(getBlockTimestamp(), "block timestamp exceeds 32 bits") }); } } if (currentRewardBorrowSpeed != newBorrowSpeed) { rewardBorrowSpeeds[rewardType][address(jToken)] = newBorrowSpeed; emit RewardBorrowSpeedUpdated(rewardType, jToken, newBorrowSpeed); } } /** * @notice Accrue JOE/AVAX to the market by updating the supply index * @param rewardType 0: JOE, 1: AVAX * @param jToken The market whose supply index to update */ function updateRewardSupplyIndex(uint8 rewardType, address jToken) internal { require(rewardType <= 1, "rewardType is invalid"); RewardMarketState storage supplyState = rewardSupplyState[rewardType][jToken]; uint256 supplySpeed = rewardSupplySpeeds[rewardType][jToken]; uint256 blockTimestamp = getBlockTimestamp(); uint256 deltaTimestamps = sub_(blockTimestamp, uint256(supplyState.timestamp)); if (deltaTimestamps > 0 && supplySpeed > 0) { uint256 supplyTokens = JToken(jToken).totalSupply(); uint256 rewardAccrued = mul_(deltaTimestamps, supplySpeed); Double memory ratio = supplyTokens > 0 ? fraction(rewardAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: supplyState.index}), ratio); rewardSupplyState[rewardType][jToken] = RewardMarketState({ index: safe224(index.mantissa, "new index exceeds 224 bits"), timestamp: safe32(blockTimestamp, "block timestamp exceeds 32 bits") }); } else if (deltaTimestamps > 0) { supplyState.timestamp = safe32(blockTimestamp, "block timestamp exceeds 32 bits"); } } /** * @notice Accrue JOE/AVAX to the market by updating the borrow index * @param rewardType 0: JOE, 1: AVAX * @param jToken The market whose borrow index to update * @param marketBorrowIndex Current index of the borrow market */ function updateRewardBorrowIndex( uint8 rewardType, address jToken, Exp memory marketBorrowIndex ) internal { require(rewardType <= 1, "rewardType is invalid"); RewardMarketState storage borrowState = rewardBorrowState[rewardType][jToken]; uint256 borrowSpeed = rewardBorrowSpeeds[rewardType][jToken]; uint256 blockTimestamp = getBlockTimestamp(); uint256 deltaTimestamps = sub_(blockTimestamp, uint256(borrowState.timestamp)); if (deltaTimestamps > 0 && borrowSpeed > 0) { uint256 borrowAmount = div_(JToken(jToken).totalBorrows(), marketBorrowIndex); uint256 rewardAccrued = mul_(deltaTimestamps, borrowSpeed); Double memory ratio = borrowAmount > 0 ? fraction(rewardAccrued, borrowAmount) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: borrowState.index}), ratio); rewardBorrowState[rewardType][jToken] = RewardMarketState({ index: safe224(index.mantissa, "new index exceeds 224 bits"), timestamp: safe32(blockTimestamp, "block timestamp exceeds 32 bits") }); } else if (deltaTimestamps > 0) { borrowState.timestamp = safe32(blockTimestamp, "block timestamp exceeds 32 bits"); } } /** * @notice Calculate JOE/AVAX accrued by a supplier and possibly transfer it to them * @param rewardType 0: JOE, 1: AVAX * @param jToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute JOE/AVAX to */ function distributeSupplierReward( uint8 rewardType, address jToken, address supplier ) internal { require(rewardType <= 1, "rewardType is invalid"); RewardMarketState storage supplyState = rewardSupplyState[rewardType][jToken]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({mantissa: rewardSupplierIndex[rewardType][jToken][supplier]}); rewardSupplierIndex[rewardType][jToken][supplier] = supplyIndex.mantissa; if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { supplierIndex.mantissa = rewardInitialIndex; } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); uint256 supplierTokens = JToken(jToken).balanceOf(supplier); uint256 supplierDelta = mul_(supplierTokens, deltaIndex); uint256 supplierAccrued = add_(rewardAccrued[rewardType][supplier], supplierDelta); rewardAccrued[rewardType][supplier] = supplierAccrued; emit DistributedSupplierReward(rewardType, JToken(jToken), supplier, supplierDelta, supplyIndex.mantissa); } /** * @notice Calculate JOE/AVAX accrued by a borrower and possibly transfer it to them * @dev Borrowers will not begin to accrue until after the first interaction with the protocol. * @param rewardType 0: JOE, 1: AVAX * @param jToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute JOE/AVAX to * @param marketBorrowIndex Current index of the borrow market */ function distributeBorrowerReward( uint8 rewardType, address jToken, address borrower, Exp memory marketBorrowIndex ) internal { require(rewardType <= 1, "rewardType is invalid"); RewardMarketState storage borrowState = rewardBorrowState[rewardType][jToken]; Double memory borrowIndex = Double({mantissa: borrowState.index}); Double memory borrowerIndex = Double({mantissa: rewardBorrowerIndex[rewardType][jToken][borrower]}); rewardBorrowerIndex[rewardType][jToken][borrower] = borrowIndex.mantissa; if (borrowerIndex.mantissa > 0) { Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); uint256 borrowerAmount = div_(JToken(jToken).borrowBalanceStored(borrower), marketBorrowIndex); uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); uint256 borrowerAccrued = add_(rewardAccrued[rewardType][borrower], borrowerDelta); rewardAccrued[rewardType][borrower] = borrowerAccrued; emit DistributedBorrowerReward(rewardType, JToken(jToken), borrower, borrowerDelta, borrowIndex.mantissa); } } /** * @notice Refactored function to calc and rewards accounts supplier rewards * @param jToken The market to verify the mint against * @param supplier The supplier to be rewarded */ function updateAndDistributeSupplierRewardsForToken(address jToken, address supplier) external { require(adminOrInitializing(), "only admin can update and distribute supplier rewards"); for (uint8 rewardType = 0; rewardType <= 1; rewardType++) { updateRewardSupplyIndex(rewardType, jToken); distributeSupplierReward(rewardType, jToken, supplier); } } /** * @notice Refactored function to calc and rewards accounts supplier rewards * @param jToken The market to verify the mint against * @param borrower Borrower to be rewarded * @param marketBorrowIndex Current index of the borrow market */ function updateAndDistributeBorrowerRewardsForToken( address jToken, address borrower, Exp calldata marketBorrowIndex ) external { require(adminOrInitializing(), "only admin can update and distribute borrower rewards"); for (uint8 rewardType = 0; rewardType <= 1; rewardType++) { updateRewardBorrowIndex(rewardType, jToken, marketBorrowIndex); distributeBorrowerReward(rewardType, jToken, borrower, marketBorrowIndex); } } /*** User functions ***/ /** * @notice Claim all the JOE/AVAX accrued by holder in all markets * @param holder The address to claim JOE/AVAX for */ function claimReward(uint8 rewardType, address payable holder) public { return claimReward(rewardType, holder, joetroller.getAllMarkets()); } /** * @notice Claim all the JOE/AVAX accrued by holder in the specified markets * @param rewardType 0 = JOE, 1 = AVAX * @param holder The address to claim JOE/AVAX for * @param jTokens The list of markets to claim JOE/AVAX in */ function claimReward( uint8 rewardType, address payable holder, JToken[] memory jTokens ) public { address payable[] memory holders = new address payable[](1); holders[0] = holder; claimReward(rewardType, holders, jTokens, true, true); } /** * @notice Claim all JOE/AVAX accrued by the holders * @param rewardType 0 = JOE, 1 = AVAX * @param holders The addresses to claim JOE/AVAX for * @param jTokens The list of markets to claim JOE/AVAX in * @param borrowers Whether or not to claim JOE/AVAX earned by borrowing * @param suppliers Whether or not to claim JOE/AVAX earned by supplying */ function claimReward( uint8 rewardType, address payable[] memory holders, JToken[] memory jTokens, bool borrowers, bool suppliers ) public payable { require(rewardType <= 1, "rewardType is invalid"); for (uint256 i = 0; i < jTokens.length; i++) { JToken jToken = jTokens[i]; require(joetroller.isMarketListed(address(jToken)), "market must be listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: jToken.borrowIndex()}); updateRewardBorrowIndex(rewardType, address(jToken), borrowIndex); for (uint256 j = 0; j < holders.length; j++) { distributeBorrowerReward(rewardType, address(jToken), holders[j], borrowIndex); rewardAccrued[rewardType][holders[j]] = grantRewardInternal( rewardType, holders[j], rewardAccrued[rewardType][holders[j]] ); } } if (suppliers == true) { updateRewardSupplyIndex(rewardType, address(jToken)); for (uint256 j = 0; j < holders.length; j++) { distributeSupplierReward(rewardType, address(jToken), holders[j]); rewardAccrued[rewardType][holders[j]] = grantRewardInternal( rewardType, holders[j], rewardAccrued[rewardType][holders[j]] ); } } } } /** * @notice Transfer JOE/AVAX to the user * @dev Note: If there is not enough JOE/AVAX, we do not perform the transfer all. * @param rewardType 0 = JOE, 1 = AVAX. * @param user The address of the user to transfer JOE/AVAX to * @param amount The amount of JOE/AVAX to (possibly) transfer * @return The amount of JOE/AVAX which was NOT transferred to the user */ function grantRewardInternal( uint8 rewardType, address payable user, uint256 amount ) internal returns (uint256) { if (rewardType == 0) { EIP20Interface joe = EIP20Interface(joeAddress); uint256 joeRemaining = joe.balanceOf(address(this)); if (amount > 0 && amount <= joeRemaining) { joe.transfer(user, amount); return 0; } } else if (rewardType == 1) { uint256 avaxRemaining = address(this).balance; if (amount > 0 && amount <= avaxRemaining) { user.transfer(amount); return 0; } } return amount; } /*** Joe Distribution Admin ***/ /** * @notice Transfer JOE to the recipient * @dev Note: If there is not enough JOE, we do not perform the transfer all. * @param rewardType 0 = JOE, 1 = AVAX * @param recipient The address of the recipient to transfer JOE to * @param amount The amount of JOE to (possibly) transfer */ function _grantReward( uint8 rewardType, address payable recipient, uint256 amount ) public { require(adminOrInitializing(), "only admin can grant joe"); uint256 amountLeft = grantRewardInternal(rewardType, recipient, amount); require(amountLeft == 0, "insufficient joe for grant"); emit RewardGranted(rewardType, recipient, amount); } /** * @notice Set the JOE token address */ function setJoeAddress(address newJoeAddress) public { require(msg.sender == admin, "only admin can set JOE"); joeAddress = newJoeAddress; } /** * @notice Set the Joetroller address */ function setJoetroller(address _joetroller) public { require(msg.sender == admin, "only admin can set Joetroller"); joetroller = Joetroller(_joetroller); } /** * @notice Set the admin */ function setAdmin(address _newAdmin) public { require(msg.sender == admin, "only admin can set admin"); admin = _newAdmin; } /** * @notice payable function needed to receive AVAX */ function() external payable {} function getBlockTimestamp() public view returns (uint256) { return block.timestamp; } } /** * @title JoetrollerCore * @dev Storage for the joetroller is at this address, while execution is delegated to the `implementation`. * JTokens should reference this contract as their joetroller. */ contract Unitroller is UnitrollerAdminStorage, JoetrollerErrorReporter { /** * @notice Emitted when pendingImplementation is changed */ event NewPendingImplementation(address oldPendingImplementation, address newPendingImplementation); /** * @notice Emitted when pendingImplementation is accepted, which means joetroller implementation is updated */ event NewImplementation(address oldImplementation, address newImplementation); /** * @notice Emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); constructor() public { // Set admin to caller admin = msg.sender; } /*** Admin Functions ***/ function _setPendingImplementation(address newPendingImplementation) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_IMPLEMENTATION_OWNER_CHECK); } address oldPendingImplementation = pendingImplementation; pendingImplementation = newPendingImplementation; emit NewPendingImplementation(oldPendingImplementation, pendingImplementation); return uint256(Error.NO_ERROR); } /** * @notice Accepts new implementation of joetroller. msg.sender must be pendingImplementation * @dev Admin function for new implementation to accept it's role as implementation * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptImplementation() public returns (uint256) { // Check caller is pendingImplementation and pendingImplementation ≠ address(0) if (msg.sender != pendingImplementation || pendingImplementation == address(0)) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK); } // Save current values for inclusion in log address oldImplementation = implementation; address oldPendingImplementation = pendingImplementation; implementation = pendingImplementation; pendingImplementation = address(0); emit NewImplementation(oldImplementation, implementation); emit NewPendingImplementation(oldPendingImplementation, pendingImplementation); return uint256(Error.NO_ERROR); } /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address newPendingAdmin) public returns (uint256) { // Check caller = admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); } // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); return uint256(Error.NO_ERROR); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() public returns (uint256) { // Check caller is pendingAdmin if (msg.sender != pendingAdmin) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); } // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return uint256(Error.NO_ERROR); } /** * @dev Delegates execution to an implementation contract. * It returns to the external caller whatever the implementation returns * or forwards reverts. */ function() external payable { // delegate all other functions to current implementation (bool success, ) = implementation.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize) switch success case 0 { revert(free_mem_ptr, returndatasize) } default { return(free_mem_ptr, returndatasize) } } } } /** * @title Compound's Joetroller Contract * @author Compound (modified by Cream) */ contract Joetroller is JoetrollerV1Storage, JoetrollerInterface, JoetrollerErrorReporter, Exponential { /// @notice Emitted when an admin supports a market event MarketListed(JToken jToken); /// @notice Emitted when an admin delists a market event MarketDelisted(JToken jToken); /// @notice Emitted when an account enters a market event MarketEntered(JToken jToken, address account); /// @notice Emitted when an account exits a market event MarketExited(JToken jToken, address account); /// @notice Emitted when close factor is changed by admin event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); /// @notice Emitted when a collateral factor is changed by admin event NewCollateralFactor(JToken jToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); /// @notice Emitted when liquidation incentive is changed by admin event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); /// @notice Emitted when pause guardian is changed event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); /// @notice Emitted when an action is paused globally event ActionPaused(string action, bool pauseState); /// @notice Emitted when an action is paused on a market event ActionPaused(JToken jToken, string action, bool pauseState); /// @notice Emitted when borrow cap for a jToken is changed event NewBorrowCap(JToken indexed jToken, uint256 newBorrowCap); /// @notice Emitted when borrow cap guardian is changed event NewBorrowCapGuardian(address oldBorrowCapGuardian, address newBorrowCapGuardian); /// @notice Emitted when supply cap for a jToken is changed event NewSupplyCap(JToken indexed jToken, uint256 newSupplyCap); /// @notice Emitted when supply cap guardian is changed event NewSupplyCapGuardian(address oldSupplyCapGuardian, address newSupplyCapGuardian); /// @notice Emitted when protocol's credit limit has changed event CreditLimitChanged(address protocol, uint256 creditLimit); /// @notice Emitted when jToken version is changed event NewJTokenVersion(JToken jToken, Version oldVersion, Version newVersion); // No collateralFactorMantissa may exceed this value uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 constructor() public { admin = msg.sender; } /** * @notice Return all of the markets * @dev The automatic getter may be used to access an individual market. * @return The list of market addresses */ function getAllMarkets() public view returns (JToken[] memory) { return allMarkets; } function getBlockTimestamp() public view returns (uint256) { return block.timestamp; } /*** 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 (JToken[] memory) { JToken[] 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 jToken The jToken to check * @return True if the account is in the asset, otherwise false. */ function checkMembership(address account, JToken jToken) external view returns (bool) { return markets[address(jToken)].accountMembership[account]; } /** * @notice Add assets to be included in account liquidity calculation * @param jTokens The list of addresses of the jToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ function enterMarkets(address[] memory jTokens) public returns (uint256[] memory) { uint256 len = jTokens.length; uint256[] memory results = new uint256[](len); for (uint256 i = 0; i < len; i++) { JToken jToken = JToken(jTokens[i]); results[i] = uint256(addToMarketInternal(jToken, msg.sender)); } return results; } /** * @notice Add the market to the borrower's "assets in" for liquidity calculations * @param jToken The market to enter * @param borrower The address of the account to modify * @return Success indicator for whether the market was entered */ function addToMarketInternal(JToken jToken, address borrower) internal returns (Error) { Market storage marketToJoin = markets[address(jToken)]; if (!marketToJoin.isListed) { // market is not listed, cannot join return Error.MARKET_NOT_LISTED; } if (marketToJoin.version == Version.COLLATERALCAP) { // register collateral for the borrower if the token is CollateralCap version. JCollateralCapErc20Interface(address(jToken)).registerCollateral(borrower); } if (marketToJoin.accountMembership[borrower] == true) { // already joined return Error.NO_ERROR; } // survived the gauntlet, add to list // NOTE: we store these somewhat redundantly as a significant optimization // this avoids having to iterate through the list for the most common use cases // that is, only when we need to perform liquidity checks // and not whenever we want to check if an account is in a particular market marketToJoin.accountMembership[borrower] = true; accountAssets[borrower].push(jToken); emit MarketEntered(jToken, borrower); return Error.NO_ERROR; } /** * @notice Removes asset from sender's account liquidity calculation * @dev Sender must not have an outstanding borrow balance in the asset, * or be providing necessary collateral for an outstanding borrow. * @param jTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ function exitMarket(address jTokenAddress) external returns (uint256) { JToken jToken = JToken(jTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the jToken */ (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = jToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ if (amountOwed != 0) { return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED); } /* Fail if the sender is not permitted to redeem all of their tokens */ uint256 allowed = redeemAllowedInternal(jTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } Market storage marketToExit = markets[jTokenAddress]; if (marketToExit.version == Version.COLLATERALCAP) { JCollateralCapErc20Interface(jTokenAddress).unregisterCollateral(msg.sender); } /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { return uint256(Error.NO_ERROR); } /* Set jToken account membership to false */ delete marketToExit.accountMembership[msg.sender]; /* Delete jToken from the account’s list of assets */ // load into memory for faster iteration JToken[] memory userAssetList = accountAssets[msg.sender]; uint256 len = userAssetList.length; uint256 assetIndex = len; for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == jToken) { 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 JToken[] storage storedList = accountAssets[msg.sender]; if (assetIndex != storedList.length - 1) { storedList[assetIndex] = storedList[storedList.length - 1]; } storedList.length--; emit MarketExited(jToken, msg.sender); return uint256(Error.NO_ERROR); } /** * @notice Return whether a specific market is listed or not * @param jTokenAddress The address of the asset to be checked * @return Whether or not the market is listed */ function isMarketListed(address jTokenAddress) public view returns (bool) { return markets[jTokenAddress].isListed; } /*** Policy Hooks ***/ /** * @notice Checks if the account should be allowed to mint tokens in the given market * @param jToken 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 jToken, address minter, uint256 mintAmount ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[jToken], "mint is paused"); require(!isCreditAccount(minter), "credit account cannot mint"); if (!isMarketListed(jToken)) { return uint256(Error.MARKET_NOT_LISTED); } uint256 supplyCap = supplyCaps[jToken]; // Supply cap of 0 corresponds to unlimited supplying if (supplyCap != 0) { uint256 totalCash = JToken(jToken).getCash(); uint256 totalBorrows = JToken(jToken).totalBorrows(); uint256 totalReserves = JToken(jToken).totalReserves(); // totalSupplies = totalCash + totalBorrows - totalReserves (MathError mathErr, uint256 totalSupplies) = addThenSubUInt(totalCash, totalBorrows, totalReserves); require(mathErr == MathError.NO_ERROR, "totalSupplies failed"); uint256 nextTotalSupplies = add_(totalSupplies, mintAmount); require(nextTotalSupplies < supplyCap, "market supply cap reached"); } // Keep the flywheel moving RewardDistributor(rewardDistributor).updateAndDistributeSupplierRewardsForToken(jToken, minter); return uint256(Error.NO_ERROR); } /** * @notice Validates mint and reverts on rejection. May emit logs. * @param jToken Asset being minted * @param minter The address minting the tokens * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ function mintVerify( address jToken, address minter, uint256 actualMintAmount, uint256 mintTokens ) external { // Shh - currently unused jToken; minter; actualMintAmount; mintTokens; // Shh - we don't ever want this hook to be marked pure if (false) { closeFactorMantissa = closeFactorMantissa; } } /** * @notice Checks if the account should be allowed to redeem tokens in the given market * @param jToken The market to verify the redeem against * @param redeemer The account which would redeem the tokens * @param redeemTokens The number of jTokens 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 jToken, address redeemer, uint256 redeemTokens ) external returns (uint256) { uint256 allowed = redeemAllowedInternal(jToken, redeemer, redeemTokens); if (allowed != uint256(Error.NO_ERROR)) { return allowed; } // Keep the flywheel going RewardDistributor(rewardDistributor).updateAndDistributeSupplierRewardsForToken(jToken, redeemer); return uint256(Error.NO_ERROR); } function redeemAllowedInternal( address jToken, address redeemer, uint256 redeemTokens ) internal view returns (uint256) { if (!isMarketListed(jToken)) { return uint256(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[jToken].accountMembership[redeemer]) { return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( redeemer, JToken(jToken), redeemTokens, 0 ); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall > 0) { return uint256(Error.INSUFFICIENT_LIQUIDITY); } return uint256(Error.NO_ERROR); } /** * @notice Validates redeem and reverts on rejection. May emit logs. * @param jToken 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 jToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens ) external { // Shh - currently unused jToken; redeemer; // Require tokens is zero or amount is also zero if (redeemTokens == 0 && redeemAmount > 0) { revert("redeemTokens zero"); } } /** * @notice Checks if the account should be allowed to borrow the underlying asset of the given market * @param jToken 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 jToken, address borrower, uint256 borrowAmount ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[jToken], "borrow is paused"); if (!isMarketListed(jToken)) { return uint256(Error.MARKET_NOT_LISTED); } if (!markets[jToken].accountMembership[borrower]) { // only jTokens may call borrowAllowed if borrower not in market require(msg.sender == jToken, "sender must be jToken"); // attempt to add borrower to the market Error err = addToMarketInternal(JToken(jToken), borrower); if (err != Error.NO_ERROR) { return uint256(err); } // it should be impossible to break the important invariant assert(markets[jToken].accountMembership[borrower]); } if (oracle.getUnderlyingPrice(JToken(jToken)) == 0) { return uint256(Error.PRICE_ERROR); } uint256 borrowCap = borrowCaps[jToken]; // Borrow cap of 0 corresponds to unlimited borrowing if (borrowCap != 0) { uint256 totalBorrows = JToken(jToken).totalBorrows(); uint256 nextTotalBorrows = add_(totalBorrows, borrowAmount); require(nextTotalBorrows < borrowCap, "market borrow cap reached"); } (Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal( borrower, JToken(jToken), 0, borrowAmount ); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall > 0) { return uint256(Error.INSUFFICIENT_LIQUIDITY); } // Keep the flywheel going Exp memory borrowIndex = Exp({mantissa: JToken(jToken).borrowIndex()}); RewardDistributor(rewardDistributor).updateAndDistributeBorrowerRewardsForToken(jToken, borrower, borrowIndex); return uint256(Error.NO_ERROR); } /** * @notice Validates borrow and reverts on rejection. May emit logs. * @param jToken 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 jToken, address borrower, uint256 borrowAmount ) external { // Shh - currently unused jToken; borrower; borrowAmount; // Shh - we don't ever want this hook to be marked pure if (false) { closeFactorMantissa = closeFactorMantissa; } } /** * @notice Checks if the account should be allowed to repay a borrow in the given market * @param jToken The market to verify the repay against * @param payer The account which would repay the asset * @param borrower The account which 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 jToken, address payer, address borrower, uint256 repayAmount ) external returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; if (!isMarketListed(jToken)) { return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel going Exp memory borrowIndex = Exp({mantissa: JToken(jToken).borrowIndex()}); RewardDistributor(rewardDistributor).updateAndDistributeBorrowerRewardsForToken(jToken, borrower, borrowIndex); return uint256(Error.NO_ERROR); } /** * @notice Validates repayBorrow and reverts on rejection. May emit logs. * @param jToken Asset being repaid * @param payer The address repaying the borrow * @param borrower The address of the borrower * @param actualRepayAmount The amount of underlying being repaid */ function repayBorrowVerify( address jToken, address payer, address borrower, uint256 actualRepayAmount, uint256 borrowerIndex ) external { // Shh - currently unused jToken; payer; borrower; actualRepayAmount; borrowerIndex; // Shh - we don't ever want this hook to be marked pure if (false) { closeFactorMantissa = closeFactorMantissa; } } /** * @notice Checks if the liquidation should be allowed to occur * @param jTokenBorrowed Asset which was borrowed by the borrower * @param jTokenCollateral 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 jTokenBorrowed, address jTokenCollateral, address liquidator, address borrower, uint256 repayAmount ) external returns (uint256) { require(!isCreditAccount(borrower), "cannot liquidate credit account"); // Shh - currently unused liquidator; if (!isMarketListed(jTokenBorrowed) || !isMarketListed(jTokenCollateral)) { return uint256(Error.MARKET_NOT_LISTED); } /* The borrower must have shortfall in order to be liquidatable */ (Error err, , uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall == 0) { return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ uint256 borrowBalance = JToken(jTokenBorrowed).borrowBalanceStored(borrower); uint256 maxClose = mul_ScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (repayAmount > maxClose) { return uint256(Error.TOO_MUCH_REPAY); } return uint256(Error.NO_ERROR); } /** * @notice Validates liquidateBorrow and reverts on rejection. May emit logs. * @param jTokenBorrowed Asset which was borrowed by the borrower * @param jTokenCollateral Asset which was used as collateral and will be seized * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param actualRepayAmount The amount of underlying being repaid */ function liquidateBorrowVerify( address jTokenBorrowed, address jTokenCollateral, address liquidator, address borrower, uint256 actualRepayAmount, uint256 seizeTokens ) external { // Shh - currently unused jTokenBorrowed; jTokenCollateral; liquidator; borrower; actualRepayAmount; seizeTokens; // Shh - we don't ever want this hook to be marked pure if (false) { closeFactorMantissa = closeFactorMantissa; } } /** * @notice Checks if the seizing of assets should be allowed to occur * @param jTokenCollateral Asset which was used as collateral and will be seized * @param jTokenBorrowed 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 jTokenCollateral, address jTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); require(!isCreditAccount(borrower), "cannot sieze from credit account"); // Shh - currently unused liquidator; seizeTokens; if (!isMarketListed(jTokenCollateral) || !isMarketListed(jTokenBorrowed)) { return uint256(Error.MARKET_NOT_LISTED); } if (JToken(jTokenCollateral).joetroller() != JToken(jTokenBorrowed).joetroller()) { return uint256(Error.JOETROLLER_MISMATCH); } // Keep the flywheel moving RewardDistributor(rewardDistributor).updateAndDistributeSupplierRewardsForToken(jTokenCollateral, borrower); RewardDistributor(rewardDistributor).updateAndDistributeSupplierRewardsForToken(jTokenCollateral, liquidator); return uint256(Error.NO_ERROR); } /** * @notice Validates seize and reverts on rejection. May emit logs. * @param jTokenCollateral Asset which was used as collateral and will be seized * @param jTokenBorrowed 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 jTokenCollateral, address jTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external { // Shh - currently unused jTokenCollateral; jTokenBorrowed; liquidator; borrower; seizeTokens; // Shh - we don't ever want this hook to be marked pure if (false) { closeFactorMantissa = closeFactorMantissa; } } /** * @notice Checks if the account should be allowed to transfer tokens in the given market * @param jToken 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 jTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function transferAllowed( address jToken, address src, address dst, uint256 transferTokens ) external returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); require(!isCreditAccount(dst), "cannot transfer to a credit account"); // Shh - currently unused dst; // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens uint256 allowed = redeemAllowedInternal(jToken, src, transferTokens); if (allowed != uint256(Error.NO_ERROR)) { return allowed; } // Keep the flywheel moving RewardDistributor(rewardDistributor).updateAndDistributeSupplierRewardsForToken(jToken, src); RewardDistributor(rewardDistributor).updateAndDistributeSupplierRewardsForToken(jToken, dst); return uint256(Error.NO_ERROR); } /** * @notice Validates transfer and reverts on rejection. May emit logs. * @param jToken Asset being transferred * @param src The account which sources the tokens * @param dst The account which receives the tokens * @param transferTokens The number of jTokens to transfer */ function transferVerify( address jToken, address src, address dst, uint256 transferTokens ) external { // Shh - currently unused jToken; src; dst; transferTokens; // Shh - we don't ever want this hook to be marked pure if (false) { closeFactorMantissa = closeFactorMantissa; } } /** * @notice Checks if the account should be allowed to transfer tokens in the given market * @param jToken The market to verify the transfer against * @param receiver The account which receives the tokens * @param amount The amount of the tokens * @param params The other parameters */ function flashloanAllowed( address jToken, address receiver, uint256 amount, bytes calldata params ) external view returns (bool) { return !flashloanGuardianPaused[jToken]; } /** * @notice Update JToken's version. * @param jToken Version of the asset being updated * @param newVersion The new version */ function updateJTokenVersion(address jToken, Version newVersion) external { require(msg.sender == jToken, "only jToken could update its version"); // This function will be called when a new JToken implementation becomes active. // If a new JToken is newly created, this market is not listed yet. The version of // this market will be taken care of when calling `_supportMarket`. if (isMarketListed(jToken)) { Version oldVersion = markets[jToken].version; markets[jToken].version = newVersion; emit NewJTokenVersion(JToken(jToken), oldVersion, newVersion); } } /** * @notice Check if the account is a credit account * @param account The account needs to be checked * @return The account is a credit account or not */ function isCreditAccount(address account) public view returns (bool) { return creditLimits[account] > 0; } /*** Liquidity/Liquidation Calculations ***/ /** * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. * Note that `jTokenBalance` is the number of jTokens the account owns in the market, * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { uint256 sumCollateral; uint256 sumBorrowPlusEffects; uint256 jTokenBalance; uint256 borrowBalance; uint256 exchangeRateMantissa; uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; Exp tokensToDenom; } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code (semi-opaque), account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidity(address account) public view returns ( uint256, uint256, uint256 ) { (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( account, JToken(0), 0, 0 ); return (uint256(err), liquidity, shortfall); } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code, account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidityInternal(address account) internal view returns ( Error, uint256, uint256 ) { return getHypotheticalAccountLiquidityInternal(account, JToken(0), 0, 0); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param jTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @return (possible error code (semi-opaque), hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidity( address account, address jTokenModify, uint256 redeemTokens, uint256 borrowAmount ) public view returns ( uint256, uint256, uint256 ) { (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( account, JToken(jTokenModify), redeemTokens, borrowAmount ); return (uint256(err), liquidity, shortfall); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param jTokenModify 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 jToken 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, JToken jTokenModify, uint256 redeemTokens, uint256 borrowAmount ) internal view returns ( Error, uint256, uint256 ) { // If credit limit is set to MAX, no need to check account liquidity. if (creditLimits[account] == uint256(-1)) { return (Error.NO_ERROR, uint256(-1), 0); } AccountLiquidityLocalVars memory vars; // Holds all our calculation results uint256 oErr; // For each asset the account is in JToken[] memory assets = accountAssets[account]; for (uint256 i = 0; i < assets.length; i++) { JToken asset = assets[i]; // Read the balances and exchange rate from the jToken (oErr, vars.jTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot( account ); if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } // Unlike compound protocol, getUnderlyingPrice is relatively expensive because we use ChainLink as our primary price feed. // If user has no supply / borrow balance on this asset, and user is not redeeming / borrowing this asset, skip it. if (vars.jTokenBalance == 0 && vars.borrowBalance == 0 && asset != jTokenModify) { continue; } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); // Get the normalized price of the asset vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); if (vars.oraclePriceMantissa == 0) { return (Error.PRICE_ERROR, 0, 0); } vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); // Pre-compute a conversion factor from tokens -> ether (normalized price value) vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice); // sumCollateral += tokensToDenom * jTokenBalance vars.sumCollateral = mul_ScalarTruncateAddUInt(vars.tokensToDenom, vars.jTokenBalance, vars.sumCollateral); // sumBorrowPlusEffects += oraclePrice * borrowBalance vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects ); // Calculate effects of interacting with jTokenModify if (asset == jTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects ); // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects ); } } // If credit limit is set, no need to consider collateral. if (creditLimits[account] > 0) { vars.sumCollateral = creditLimits[account]; } // 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 jToken.liquidateBorrowFresh) * @param jTokenBorrowed The address of the borrowed jToken * @param jTokenCollateral The address of the collateral jToken * @param actualRepayAmount The amount of jTokenBorrowed underlying to convert into jTokenCollateral tokens * @return (errorCode, number of jTokenCollateral tokens to be seized in a liquidation) */ function liquidateCalculateSeizeTokens( address jTokenBorrowed, address jTokenCollateral, uint256 actualRepayAmount ) external view returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(JToken(jTokenBorrowed)); uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(JToken(jTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { return (uint256(Error.PRICE_ERROR), 0); } /* * Get the exchange rate and calculate the number of collateral tokens to seize: * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ uint256 exchangeRateMantissa = JToken(jTokenCollateral).exchangeRateStored(); // Note: reverts on error Exp memory numerator = mul_( Exp({mantissa: liquidationIncentiveMantissa}), Exp({mantissa: priceBorrowedMantissa}) ); Exp memory denominator = mul_(Exp({mantissa: priceCollateralMantissa}), Exp({mantissa: exchangeRateMantissa})); Exp memory ratio = div_(numerator, denominator); uint256 seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); return (uint256(Error.NO_ERROR), seizeTokens); } /*** Admin Functions ***/ function _setRewardDistributor(address payable newRewardDistributor) public returns (uint256) { if (msg.sender != admin) { return uint256(Error.UNAUTHORIZED); } (bool success, ) = newRewardDistributor.call.value(0)(abi.encodeWithSignature("initialize()", 0)); if (!success) { return uint256(Error.REJECTION); } address oldRewardDistributor = rewardDistributor; rewardDistributor = newRewardDistributor; return uint256(Error.NO_ERROR); } /** * @notice Sets a new price oracle for the joetroller * @dev Admin function to set a new price oracle * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); } // Track the old oracle for the joetroller PriceOracle oldOracle = oracle; // Set joetroller's oracle to newOracle oracle = newOracle; // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); return uint256(Error.NO_ERROR); } /** * @notice Sets the closeFactor used when liquidating borrows * @dev Admin function to set closeFactor * @param newCloseFactorMantissa New close factor, scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); } uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); return uint256(Error.NO_ERROR); } /** * @notice Sets the collateralFactor for a market * @dev Admin function to set per-market collateralFactor * @param jToken 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(JToken jToken, uint256 newCollateralFactorMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); } // Verify market is listed Market storage market = markets[address(jToken)]; 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(jToken) == 0) { return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); } // Set market's collateral factor to new collateral factor, remember old value uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(jToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); return uint256(Error.NO_ERROR); } /** * @notice Sets liquidationIncentive * @dev Admin function to set liquidationIncentive * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); } // Save current value for use in log uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); return uint256(Error.NO_ERROR); } /** * @notice Add the market to the markets mapping and set it as listed * @dev Admin function to set isListed and add support for the market * @param jToken The address of the market (token) to list * @param version The version of the market (token) * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _supportMarket(JToken jToken, Version version) external returns (uint256) { require(msg.sender == admin, "only admin may support market"); require(!isMarketListed(address(jToken)), "market already listed"); jToken.isJToken(); // Sanity check to make sure its really a JToken markets[address(jToken)] = Market({isListed: true, collateralFactorMantissa: 0, version: version}); _addMarketInternal(address(jToken)); emit MarketListed(jToken); return uint256(Error.NO_ERROR); } /** * @notice Remove the market from the markets mapping * @param jToken The address of the market (token) to delist */ function _delistMarket(JToken jToken) external { require(msg.sender == admin, "only admin may delist market"); require(isMarketListed(address(jToken)), "market not listed"); require(jToken.totalSupply() == 0, "market not empty"); jToken.isJToken(); // Sanity check to make sure its really a JToken delete markets[address(jToken)]; for (uint256 i = 0; i < allMarkets.length; i++) { if (allMarkets[i] == jToken) { allMarkets[i] = allMarkets[allMarkets.length - 1]; delete allMarkets[allMarkets.length - 1]; allMarkets.length--; break; } } emit MarketDelisted(jToken); } function _addMarketInternal(address jToken) internal { for (uint256 i = 0; i < allMarkets.length; i++) { require(allMarkets[i] != JToken(jToken), "market already added"); } allMarkets.push(JToken(jToken)); } /** * @notice Admin function to change the Supply Cap Guardian * @param newSupplyCapGuardian The address of the new Supply Cap Guardian */ function _setSupplyCapGuardian(address newSupplyCapGuardian) external { require(msg.sender == admin, "only admin can set supply cap guardian"); // Save current value for inclusion in log address oldSupplyCapGuardian = supplyCapGuardian; // Store supplyCapGuardian with value newSupplyCapGuardian supplyCapGuardian = newSupplyCapGuardian; // Emit NewSupplyCapGuardian(OldSupplyCapGuardian, NewSupplyCapGuardian) emit NewSupplyCapGuardian(oldSupplyCapGuardian, newSupplyCapGuardian); } /** * @notice Set the given supply caps for the given jToken markets. Supplying that brings total supplys to or above supply cap will revert. * @dev Admin or supplyCapGuardian function to set the supply caps. A supply cap of 0 corresponds to unlimited supplying. If the total borrows * already exceeded the cap, it will prevent anyone to borrow. * @param jTokens The addresses of the markets (tokens) to change the supply caps for * @param newSupplyCaps The new supply cap values in underlying to be set. A value of 0 corresponds to unlimited supplying. */ function _setMarketSupplyCaps(JToken[] calldata jTokens, uint256[] calldata newSupplyCaps) external { require( msg.sender == admin || msg.sender == supplyCapGuardian, "only admin or supply cap guardian can set supply caps" ); uint256 numMarkets = jTokens.length; uint256 numSupplyCaps = newSupplyCaps.length; require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input"); for (uint256 i = 0; i < numMarkets; i++) { supplyCaps[address(jTokens[i])] = newSupplyCaps[i]; emit NewSupplyCap(jTokens[i], newSupplyCaps[i]); } } /** * @notice Set the given borrow caps for the given jToken markets. Borrowing that brings total borrows to or above borrow cap will revert. * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. If the total supplies * already exceeded the cap, it will prevent anyone to mint. * @param jTokens The addresses of the markets (tokens) to change the borrow caps for * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing. */ function _setMarketBorrowCaps(JToken[] calldata jTokens, uint256[] calldata newBorrowCaps) external { require( msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps" ); uint256 numMarkets = jTokens.length; uint256 numBorrowCaps = newBorrowCaps.length; require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); for (uint256 i = 0; i < numMarkets; i++) { borrowCaps[address(jTokens[i])] = newBorrowCaps[i]; emit NewBorrowCap(jTokens[i], newBorrowCaps[i]); } } /** * @notice Admin function to change the Borrow Cap Guardian * @param newBorrowCapGuardian The address of the new Borrow Cap Guardian */ function _setBorrowCapGuardian(address newBorrowCapGuardian) external { require(msg.sender == admin, "only admin can set borrow cap guardian"); // Save current value for inclusion in log address oldBorrowCapGuardian = borrowCapGuardian; // Store borrowCapGuardian with value newBorrowCapGuardian borrowCapGuardian = newBorrowCapGuardian; // Emit NewBorrowCapGuardian(OldBorrowCapGuardian, NewBorrowCapGuardian) emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian); } /** * @notice Admin function to change the Pause Guardian * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); } // Save current value for inclusion in log address oldPauseGuardian = pauseGuardian; // Store pauseGuardian with value newPauseGuardian pauseGuardian = newPauseGuardian; // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); return uint256(Error.NO_ERROR); } function _setMintPaused(JToken jToken, bool state) public returns (bool) { require(isMarketListed(address(jToken)), "cannot pause a market that is not listed"); require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); mintGuardianPaused[address(jToken)] = state; emit ActionPaused(jToken, "Mint", state); return state; } function _setBorrowPaused(JToken jToken, bool state) public returns (bool) { require(isMarketListed(address(jToken)), "cannot pause a market that is not listed"); require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); borrowGuardianPaused[address(jToken)] = state; emit ActionPaused(jToken, "Borrow", state); return state; } function _setFlashloanPaused(JToken jToken, bool state) public returns (bool) { require(isMarketListed(address(jToken)), "cannot pause a market that is not listed"); require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); flashloanGuardianPaused[address(jToken)] = state; emit ActionPaused(jToken, "Flashloan", state); return state; } function _setTransferPaused(bool state) public returns (bool) { require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); transferGuardianPaused = state; emit ActionPaused("Transfer", state); return state; } function _setSeizePaused(bool state) public returns (bool) { require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); require(msg.sender == admin || state == true, "only admin can unpause"); seizeGuardianPaused = state; emit ActionPaused("Seize", state); return state; } function _become(Unitroller unitroller) public { require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); require(unitroller._acceptImplementation() == 0, "change not authorized"); } /** * @notice Sets whitelisted protocol's credit limit * @param protocol The address of the protocol * @param creditLimit The credit limit */ function _setCreditLimit(address protocol, uint256 creditLimit) public { require(msg.sender == admin, "only admin can set protocol credit limit"); creditLimits[protocol] = creditLimit; emit CreditLimitChanged(protocol, creditLimit); } /** * @notice Checks caller is admin, or this contract is becoming the new implementation */ function adminOrInitializing() internal view returns (bool) { return msg.sender == admin || msg.sender == implementation; } /*** Reward distribution functions ***/ /** * @notice Claim all the JOE/AVAX accrued by holder in all markets * @param holder The address to claim JOE/AVAX for */ function claimReward(uint8 rewardType, address payable holder) public { RewardDistributor(rewardDistributor).claimReward(rewardType, holder); } /** * @notice Claim all the JOE/AVAX accrued by holder in the specified markets * @param holder The address to claim JOE/AVAX for * @param jTokens The list of markets to claim JOE/AVAX in */ function claimReward( uint8 rewardType, address payable holder, JToken[] memory jTokens ) public { RewardDistributor(rewardDistributor).claimReward(rewardType, holder, jTokens); } /** * @notice Claim all JOE/AVAX accrued by the holders * @param rewardType 0 = JOE, 1 = AVAX * @param holders The addresses to claim JOE/AVAX for * @param jTokens The list of markets to claim JOE/AVAX in * @param borrowers Whether or not to claim JOE/AVAX earned by borrowing * @param suppliers Whether or not to claim JOE/AVAX earned by supplying */ function claimReward( uint8 rewardType, address payable[] memory holders, JToken[] memory jTokens, bool borrowers, bool suppliers ) public payable { RewardDistributor(rewardDistributor).claimReward(rewardType, holders, jTokens, borrowers, suppliers); } }
[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"rewardType","type":"uint8"},{"indexed":true,"internalType":"contract JToken","name":"jToken","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardBorrowIndex","type":"uint256"}],"name":"DistributedBorrowerReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"rewardType","type":"uint8"},{"indexed":true,"internalType":"contract JToken","name":"jToken","type":"address"},{"indexed":true,"internalType":"address","name":"supplier","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardSupplyIndex","type":"uint256"}],"name":"DistributedSupplierReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"rewardType","type":"uint8"},{"indexed":true,"internalType":"contract JToken","name":"jToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSpeed","type":"uint256"}],"name":"RewardBorrowSpeedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"rewardType","type":"uint8"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"rewardType","type":"uint8"},{"indexed":true,"internalType":"contract JToken","name":"jToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSpeed","type":"uint256"}],"name":"RewardSupplySpeedUpdated","type":"event"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[{"internalType":"uint8","name":"rewardType","type":"uint8"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"_grantReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint8","name":"rewardType","type":"uint8"},{"internalType":"contract JToken","name":"jToken","type":"address"},{"internalType":"uint256","name":"rewardSupplySpeed","type":"uint256"},{"internalType":"uint256","name":"rewardBorrowSpeed","type":"uint256"}],"name":"_setRewardSpeed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint8","name":"rewardType","type":"uint8"},{"internalType":"address payable","name":"holder","type":"address"}],"name":"claimReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint8","name":"rewardType","type":"uint8"},{"internalType":"address payable","name":"holder","type":"address"},{"internalType":"contract JToken[]","name":"jTokens","type":"address[]"}],"name":"claimReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint8","name":"rewardType","type":"uint8"},{"internalType":"address payable[]","name":"holders","type":"address[]"},{"internalType":"contract JToken[]","name":"jTokens","type":"address[]"},{"internalType":"bool","name":"borrowers","type":"bool"},{"internalType":"bool","name":"suppliers","type":"bool"}],"name":"claimReward","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"getBlockTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"joeAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"joetroller","outputs":[{"internalType":"contract Joetroller","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"rewardAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"rewardBorrowSpeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"rewardBorrowState","outputs":[{"internalType":"uint224","name":"index","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"rewardBorrowerIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardInitialIndex","outputs":[{"internalType":"uint224","name":"","type":"uint224"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"rewardSupplierIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"rewardSupplySpeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"rewardSupplyState","outputs":[{"internalType":"uint224","name":"index","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newJoeAddress","type":"address"}],"name":"setJoeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_joetroller","type":"address"}],"name":"setJoetroller","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"jToken","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"components":[{"internalType":"uint256","name":"mantissa","type":"uint256"}],"internalType":"struct Exponential.Exp","name":"marketBorrowIndex","type":"tuple"}],"name":"updateAndDistributeBorrowerRewardsForToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"jToken","type":"address"},{"internalType":"address","name":"supplier","type":"address"}],"name":"updateAndDistributeSupplierRewardsForToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50600080546001600160a01b03191633179055612ca7806100326000396000f3fe6080604052600436106101405760003560e01c8063744532ae116100b65780639c39e2931161006f5780639c39e29314610367578063bf09595514610387578063cb15d8e2146103a7578063d81c5e45146103c9578063f851a440146103e9578063fcc4cfd1146103fe57610140565b8063744532ae146102ca5780637937969d146102ea578063796b89b91461030a5780638129fc1c1461031f5780638805714b1461033457806388e972b81461034757610140565b80632f32b11f116101085780632f32b11f146101f85780634b3a0a74146102185780636330533c1461024657806366f91a2414610268578063670e8afd1461028a578063704b6c02146102aa57610140565b8063030ce6381461014257806305b9783d146101785780630952c563146101985780631d94cb94146101b85780631f9f3511146101d8575b005b34801561014e57600080fd5b5061016261015d36600461242e565b61041e565b60405161016f9190612acf565b60405180910390f35b34801561018457600080fd5b5061016261019336600461242e565b61043b565b3480156101a457600080fd5b506101406101b336600461242e565b610458565b3480156101c457600080fd5b506101406101d33660046125d3565b6104eb565b3480156101e457600080fd5b506101406101f33660046122f2565b61054e565b34801561020457600080fd5b506101406102133660046124a8565b61059a565b34801561022457600080fd5b5061023861023336600461242e565b61062c565b60405161016f929190612ab4565b34801561025257600080fd5b5061025b610661565b60405161016f91906129c7565b34801561027457600080fd5b5061027d610670565b60405161016f9190612aa6565b34801561029657600080fd5b506101406102a5366004612318565b610683565b3480156102b657600080fd5b506101406102c53660046122f2565b6106d7565b3480156102d657600080fd5b506101406102e536600461244d565b610723565b3480156102f657600080fd5b506101626103053660046124eb565b610781565b34801561031657600080fd5b506101626107a4565b34801561032b57600080fd5b506101406107a8565b61014061034236600461252e565b6107f9565b34801561035357600080fd5b506101626103623660046124eb565b610b47565b34801561037357600080fd5b50610140610382366004612352565b610b6a565b34801561039357600080fd5b506101626103a236600461242e565b610bd7565b3480156103b357600080fd5b506103bc610bf4565b60405161016f9190612990565b3480156103d557600080fd5b506102386103e436600461242e565b610c03565b3480156103f557600080fd5b506103bc610c38565b34801561040a57600080fd5b506101406104193660046122f2565b610c47565b600260209081526000928352604080842090915290825290205481565b600860209081526000928352604080842090915290825290205481565b6104e78282600160009054906101000a90046001600160a01b03166001600160a01b031663b0772d0b6040518163ffffffff1660e01b815260040160006040518083038186803b1580156104ab57600080fd5b505afa1580156104bf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526102e5919081019061239f565b5050565b60018460ff1611156105185760405162461bcd60e51b815260040161050f90612a56565b60405180910390fd5b610520610c93565b61053c5760405162461bcd60e51b815260040161050f90612a96565b61054884848484610cbc565b50505050565b6000546001600160a01b031633146105785760405162461bcd60e51b815260040161050f906129e6565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105a2610c93565b6105be5760405162461bcd60e51b815260040161050f90612a36565b60006105cb848484611224565b905080156105eb5760405162461bcd60e51b815260040161050f90612a66565b7fc6d3bad890aa15362e90611f8643bfafcf5b09a9d4781295bd7ba8adffa0371184848460405161061e93929190612add565b60405180910390a150505050565b60056020908152600092835260408084209091529082529020546001600160e01b03811690600160e01b900463ffffffff1682565b6001546001600160a01b031681565b6ec097ce7bc90715b34b9f100000000081565b61068b610c93565b6106a75760405162461bcd60e51b815260040161050f90612a46565b60005b60018160ff16116106d2576106bf81846113cb565b6106ca81848461169c565b6001016106aa565b505050565b6000546001600160a01b031633146107015760405162461bcd60e51b815260040161050f90612a86565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60408051600180825281830190925260609160208083019080388339019050509050828160008151811061075357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506105488482846001806107f9565b600760209081526000938452604080852082529284528284209052825290205481565b4290565b600954600160a01b900460ff16156107d25760405162461bcd60e51b815260040161050f90612a76565b600180546001600160a01b031916331790556009805460ff60a01b1916600160a01b179055565b60018560ff16111561081d5760405162461bcd60e51b815260040161050f90612a56565b60005b8351811015610b3f57600084828151811061083757fe5b6020908102919091010151600154604051633d98a1e560e01b81529192506001600160a01b031690633d98a1e590610873908490600401612990565b60206040518083038186803b15801561088b57600080fd5b505afa15801561089f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108c391908101906123d4565b6108df5760405162461bcd60e51b815260040161050f90612a26565b60018415151415610a67576108f26120dd565b6040518060200160405280836001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561093657600080fd5b505afa15801561094a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061096e9190810190612410565b9052905061097d8883836118d6565b60005b8751811015610a64576109a889848a848151811061099a57fe5b602002602001015185611be8565b610a15898983815181106109b857fe5b6020026020010151600860008d60ff1660ff16815260200190815260200160002060008c86815181106109e757fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054611224565b60ff8a1660009081526008602052604081208a519091908b9085908110610a3857fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002055600101610980565b50505b60018315151415610b3657610a7c87826113cb565b60005b8651811015610b3457610aa68883898481518110610a9957fe5b602002602001015161169c565b610ae588888381518110610ab657fe5b6020026020010151600860008c60ff1660ff16815260200190815260200160002060008b86815181106109e757fe5b60ff8916600090815260086020526040812089519091908a9085908110610b0857fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002055600101610a7f565b505b50600101610820565b505050505050565b600660209081526000938452604080852082529284528284209052825290205481565b610b72610c93565b610b8e5760405162461bcd60e51b815260040161050f90612a06565b60005b60018160ff161161054857610bb58185610bb0368690038601866123f2565b6118d6565b610bcf818585610bca368790038701876123f2565b611be8565b600101610b91565b600360209081526000928352604080842090915290825290205481565b6009546001600160a01b031681565b60046020908152600092835260408084209091529082529020546001600160e01b03811690600160e01b900463ffffffff1682565b6000546001600160a01b031681565b6000546001600160a01b03163314610c715760405162461bcd60e51b815260040161050f906129f6565b600980546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b0316331480610cb757506001546001600160a01b031633145b905090565b60ff841660009081526002602090815260408083206001600160a01b03871684529091529020548015610cf857610cf385856113cb565b610ed0565b8215610ed057600154604051633d98a1e560e01b81526001600160a01b0390911690633d98a1e590610d2e908790600401612990565b60206040518083038186803b158015610d4657600080fd5b505afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d7e91908101906123d4565b610d9a5760405162461bcd60e51b815260040161050f90612a16565b60ff851660009081526004602090815260408083206001600160a01b03881684529091529020546001600160e01b0316158015610e08575060ff851660009081526004602090815260408083206001600160a01b0388168452909152902054600160e01b900463ffffffff16155b15610ed05760405180604001604052806ec097ce7bc90715b34b9f10000000006001600160e01b03168152602001610e6a610e416107a4565b6040518060400160405280601f8152602001600080516020612c45833981519152815250611df1565b63ffffffff90811690915260ff871660009081526004602090815260408083206001600160a01b038a1684528252909120835181549490920151909216600160e01b026001600160e01b039182166001600160e01b031990941693909317169190911790555b828114610f3c5760ff851660009081526002602090815260408083206001600160a01b03881680855292529182902085905590517f71975117d0c353e131b030f9cf69eecfded777f5ba2a036b066b90da4c89ab4290610f339088908790612b05565b60405180910390a25b60ff851660009081526003602090815260408083206001600160a01b0388168452909152902054801561100257610f716120dd565b6040518060200160405280876001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610fb557600080fd5b505afa158015610fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fed9190810190612410565b90529050610ffc8787836118d6565b506111b1565b82156111b157600154604051633d98a1e560e01b81526001600160a01b0390911690633d98a1e590611038908890600401612990565b60206040518083038186803b15801561105057600080fd5b505afa158015611064573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061108891908101906123d4565b6110a45760405162461bcd60e51b815260040161050f90612a16565b60ff861660009081526005602090815260408083206001600160a01b03891684529091529020546001600160e01b0316158015611112575060ff861660009081526005602090815260408083206001600160a01b0389168452909152902054600160e01b900463ffffffff16155b156111b15760405180604001604052806ec097ce7bc90715b34b9f10000000006001600160e01b0316815260200161114b610e416107a4565b63ffffffff90811690915260ff881660009081526005602090815260408083206001600160a01b038b1684528252909120835181549490920151909216600160e01b026001600160e01b039182166001600160e01b031990941693909317169190911790555b828114610b3f5760ff861660009081526003602090815260408083206001600160a01b03891680855292529182902085905590517ffc4f93ddd4a06ca99d4cd58e8f2a3eaec8f2f3795f127d83e0c203e08f8997e1906112149089908790612b05565b60405180910390a2505050505050565b600060ff841661135d576009546040516370a0823160e01b81526001600160a01b039091169060009082906370a082319061126390309060040161299e565b60206040518083038186803b15801561127b57600080fd5b505afa15801561128f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112b39190810190612410565b90506000841180156112c55750808411155b156113565760405163a9059cbb60e01b81526001600160a01b0383169063a9059cbb906112f890889088906004016129ac565b602060405180830381600087803b15801561131257600080fd5b505af1158015611326573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061134a91908101906123d4565b506000925050506113c4565b50506113c1565b8360ff16600114156113c1574782158015906113795750808311155b156113bf576040516001600160a01b0385169084156108fc029085906000818181858888f193505050501580156113b4573d6000803e3d6000fd5b5060009150506113c4565b505b50805b9392505050565b60018260ff1611156113ef5760405162461bcd60e51b815260040161050f90612a56565b60ff821660008181526004602090815260408083206001600160a01b03861680855290835281842094845260028352818420908452909152812054906114336107a4565b8354909150600090611453908390600160e01b900463ffffffff16611e24565b90506000811180156114655750600083115b15611642576000856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156114a557600080fd5b505afa1580156114b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114dd9190810190612410565b905060006114eb8386611e5e565b90506114f56120dd565b60008311611512576040518060200160405280600081525061151c565b61151c8284611ea0565b90506115266120dd565b604080516020810190915288546001600160e01b031681526115489083611ede565b9050604051806040016040528061159883600001516040518060400160405280601a81526020017f6e657720696e6465782065786365656473203232342062697473000000000000815250611f03565b6001600160e01b031681526020016115d3886040518060400160405280601f8152602001600080516020612c45833981519152815250611df1565b63ffffffff90811690915260ff8c1660009081526004602090815260408083206001600160a01b038f1684528252909120835181549490920151909216600160e01b026001600160e01b039182166001600160e01b0319909416939093171691909117905550610b3f92505050565b8015610b3f57611675826040518060400160405280601f8152602001600080516020612c45833981519152815250611df1565b845463ffffffff91909116600160e01b026001600160e01b03909116178455505050505050565b60018360ff1611156116c05760405162461bcd60e51b815260040161050f90612a56565b60ff831660009081526004602090815260408083206001600160a01b038616845290915290206116ee6120dd565b50604080516020810190915281546001600160e01b0316815261170f6120dd565b50604080516020808201835260ff88166000908152600682528381206001600160a01b03808a1683529083528482209088168083528184529482208054855286519590925290915291909155805115801561176a5750815115155b15611782576ec097ce7bc90715b34b9f100000000081525b61178a6120dd565b6117948383611f2a565b90506000866001600160a01b03166370a08231876040518263ffffffff1660e01b81526004016117c49190612990565b60206040518083038186803b1580156117dc57600080fd5b505afa1580156117f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118149190810190612410565b905060006118228284611f4f565b60ff8a1660009081526008602090815260408083206001600160a01b038c168452909152812054919250906118579083611f7e565b60ff8b1660009081526008602090815260408083206001600160a01b03808e168086529190935292819020849055895190519394509192908c16917faccd035d02c456be35306aecd5a5fe62320713dde09ccd68b0a5e8ed93039999916118c2918f91889190612b13565b60405180910390a350505050505050505050565b60018360ff1611156118fa5760405162461bcd60e51b815260040161050f90612a56565b60ff831660008181526005602090815260408083206001600160a01b038716808552908352818420948452600383528184209084529091528120549061193e6107a4565b835490915060009061195e908390600160e01b900463ffffffff16611e24565b90506000811180156119705750600083115b15611b8c5760006119f1876001600160a01b03166347bd37186040518163ffffffff1660e01b815260040160206040518083038186803b1580156119b357600080fd5b505afa1580156119c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119eb9190810190612410565b87611fb4565b905060006119ff8386611e5e565b9050611a096120dd565b60008311611a265760405180602001604052806000815250611a30565b611a308284611ea0565b9050611a3a6120dd565b604080516020810190915288546001600160e01b03168152611a5c9083611ede565b90506040518060400160405280611aac83600001516040518060400160405280601a81526020017f6e657720696e6465782065786365656473203232342062697473000000000000815250611f03565b6001600160e01b03168152602001611ae7886040518060400160405280601f8152602001600080516020612c45833981519152815250611df1565b63ffffffff16815250600560008d60ff1660ff16815260200190815260200160002060008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160e01b0302191690836001600160e01b03160217905550602082015181600001601c6101000a81548163ffffffff021916908363ffffffff16021790555090505050505050611bdf565b8015611bdf57611bbf826040518060400160405280601f8152602001600080516020612c45833981519152815250611df1565b845463ffffffff91909116600160e01b026001600160e01b039091161784555b50505050505050565b60018460ff161115611c0c5760405162461bcd60e51b815260040161050f90612a56565b60ff841660009081526005602090815260408083206001600160a01b03871684529091529020611c3a6120dd565b50604080516020810190915281546001600160e01b03168152611c5b6120dd565b50604080516020808201835260ff89166000908152600782528381206001600160a01b03808b1683529083528482209089168083528184529482208054855286519590925290915291909155805115611bdf57611cb66120dd565b611cc08383611f2a565b90506000611d0b886001600160a01b03166395dd9193896040518263ffffffff1660e01b8152600401611cf39190612990565b60206040518083038186803b1580156119b357600080fd5b90506000611d198284611f4f565b60ff8b1660009081526008602090815260408083206001600160a01b038d16845290915281205491925090611d4e9083611f7e565b905080600860008d60ff1660ff16815260200190815260200160002060008b6001600160a01b03166001600160a01b0316815260200190815260200160002081905550886001600160a01b03168a6001600160a01b03167fa1b6a046664a0ecf068059f26de56878f8d0e799907ca2e42d9148ccbdc717a78d858a60000151604051611ddc93929190612b13565b60405180910390a35050505050505050505050565b6000816401000000008410611e195760405162461bcd60e51b815260040161050f91906129d5565b508290505b92915050565b60006113c48383604051806040016040528060158152602001747375627472616374696f6e20756e646572666c6f7760581b815250611fd2565b60006113c483836040518060400160405280601781526020017f6d756c7469706c69636174696f6e206f766572666c6f77000000000000000000815250611ffe565b611ea86120dd565b6040518060200160405280611ed5611ecf866ec097ce7bc90715b34b9f1000000000611e5e565b8561204f565b90529392505050565b611ee66120dd565b6040518060200160405280611ed585600001518560000151611f7e565b600081600160e01b8410611e195760405162461bcd60e51b815260040161050f91906129d5565b611f326120dd565b6040518060200160405280611ed585600001518560000151611e24565b60006ec097ce7bc90715b34b9f1000000000611f6f848460000151611e5e565b81611f7657fe5b049392505050565b60006113c48383604051806040016040528060118152602001706164646974696f6e206f766572666c6f7760781b815250612082565b60006113c4611fcb84670de0b6b3a7640000611e5e565b835161204f565b60008184841115611ff65760405162461bcd60e51b815260040161050f91906129d5565b505050900390565b600083158061200b575082155b15612018575060006113c4565b8383028385828161202557fe5b041483906120465760405162461bcd60e51b815260040161050f91906129d5565b50949350505050565b60006113c483836040518060400160405280600e81526020016d646976696465206279207a65726f60901b8152506120a9565b600083830182858210156120465760405162461bcd60e51b815260040161050f91906129d5565b600081836120ca5760405162461bcd60e51b815260040161050f91906129d5565b508284816120d457fe5b04949350505050565b6040518060200160405280600081525090565b8035611e1e81612c09565b600082601f83011261210c57600080fd5b813561211f61211a82612b55565b612b2e565b9150818183526020840193506020810190508385602084028201111561214457600080fd5b60005b83811015612170578161215a88826120f0565b8452506020928301929190910190600101612147565b5050505092915050565b600082601f83011261218b57600080fd5b813561219961211a82612b55565b915081818352602084019350602081019050838560208402820111156121be57600080fd5b60005b8381101561217057816121d48882612270565b84525060209283019291909101906001016121c1565b600082601f8301126121fb57600080fd5b815161220961211a82612b55565b9150818183526020840193506020810190508385602084028201111561222e57600080fd5b60005b838110156121705781612244888261227b565b8452506020928301929190910190600101612231565b8035611e1e81612c20565b8051611e1e81612c20565b8035611e1e81612c29565b8051611e1e81612c29565b60006020828403121561229857600080fd5b50919050565b6000602082840312156122b057600080fd5b6122ba6020612b2e565b905060006122c884846122d1565b82525092915050565b8035611e1e81612c32565b8051611e1e81612c32565b8035611e1e81612c3b565b60006020828403121561230457600080fd5b600061231084846120f0565b949350505050565b6000806040838503121561232b57600080fd5b600061233785856120f0565b9250506020612348858286016120f0565b9150509250929050565b60008060006060848603121561236757600080fd5b600061237386866120f0565b9350506020612384868287016120f0565b925050604061239586828701612286565b9150509250925092565b6000602082840312156123b157600080fd5b815167ffffffffffffffff8111156123c857600080fd5b612310848285016121ea565b6000602082840312156123e657600080fd5b60006123108484612265565b60006020828403121561240457600080fd5b6000612310848461229e565b60006020828403121561242257600080fd5b600061231084846122dc565b6000806040838503121561244157600080fd5b600061233785856122e7565b60008060006060848603121561246257600080fd5b600061246e86866122e7565b935050602061247f868287016120f0565b925050604084013567ffffffffffffffff81111561249c57600080fd5b6123958682870161217a565b6000806000606084860312156124bd57600080fd5b60006124c986866122e7565b93505060206124da868287016120f0565b9250506040612395868287016122d1565b60008060006060848603121561250057600080fd5b600061250c86866122e7565b935050602061251d868287016120f0565b9250506040612395868287016120f0565b600080600080600060a0868803121561254657600080fd5b600061255288886122e7565b955050602086013567ffffffffffffffff81111561256f57600080fd5b61257b888289016120fb565b945050604086013567ffffffffffffffff81111561259857600080fd5b6125a48882890161217a565b93505060606125b58882890161225a565b92505060806125c68882890161225a565b9150509295509295909350565b600080600080608085870312156125e957600080fd5b60006125f587876122e7565b945050602061260687828801612270565b9350506040612617878288016122d1565b9250506060612628878288016122d1565b91505092959194509250565b61263d81612bc8565b82525050565b61263d81612b83565b61263d81612b93565b600061266082612b76565b61266a8185612b7a565b935061267a818560208601612bd3565b61268381612bff565b9093019392505050565b600061269a601d83612b7a565b7f6f6e6c792061646d696e2063616e20736574204a6f6574726f6c6c6572000000815260200192915050565b60006126d3601683612b7a565b756f6e6c792061646d696e2063616e20736574204a4f4560501b815260200192915050565b6000612705603583612b7a565b7f6f6e6c792061646d696e2063616e2075706461746520616e64206469737472698152746275746520626f72726f776572207265776172647360581b602082015260400192915050565b600061275c601b83612b7a565b7f726577617264206d61726b6574206973206e6f74206c69737465640000000000815260200192915050565b6000612795601583612b7a565b741b585c9ad95d081b5d5cdd081899481b1a5cdd1959605a1b815260200192915050565b60006127c6601883612b7a565b7f6f6e6c792061646d696e2063616e206772616e74206a6f650000000000000000815260200192915050565b60006127ff603583612b7a565b7f6f6e6c792061646d696e2063616e2075706461746520616e64206469737472698152746275746520737570706c696572207265776172647360581b602082015260400192915050565b6000612856601583612b7a565b741c995dd85c99151e5c19481a5cc81a5b9d985b1a59605a1b815260200192915050565b6000612887601a83612b7a565b7f696e73756666696369656e74206a6f6520666f72206772616e74000000000000815260200192915050565b60006128c0602583612b7a565b7f5265776172644469737472696275746f7220616c726561647920696e697469618152641b1a5e995960da1b602082015260400192915050565b6000612907601883612b7a565b7f6f6e6c792061646d696e2063616e207365742061646d696e0000000000000000815260200192915050565b6000612940601f83612b7a565b7f6f6e6c792061646d696e2063616e207365742072657761726420737065656400815260200192915050565b61263d81612baa565b61263d81612bb6565b61263d81612bb9565b61263d81612bc2565b60208101611e1e8284612643565b60208101611e1e8284612634565b604081016129ba8285612634565b6113c46020830184612975565b60208101611e1e828461264c565b602080825281016113c48184612655565b60208082528101611e1e8161268d565b60208082528101611e1e816126c6565b60208082528101611e1e816126f8565b60208082528101611e1e8161274f565b60208082528101611e1e81612788565b60208082528101611e1e816127b9565b60208082528101611e1e816127f2565b60208082528101611e1e81612849565b60208082528101611e1e8161287a565b60208082528101611e1e816128b3565b60208082528101611e1e816128fa565b60208082528101611e1e81612933565b60208101611e1e828461296c565b60408101612ac2828561296c565b6113c4602083018461297e565b60208101611e1e8284612975565b60608101612aeb8286612987565b612af86020830185612634565b6123106040830184612975565b604081016129ba8285612987565b60608101612b218286612987565b612af86020830185612975565b60405181810167ffffffffffffffff81118282101715612b4d57600080fd5b604052919050565b600067ffffffffffffffff821115612b6c57600080fd5b5060209081020190565b5190565b90815260200190565b6000611e1e82612b9e565b151590565b6000611e1e82612b83565b6001600160a01b031690565b6001600160e01b031690565b90565b63ffffffff1690565b60ff1690565b6000611e1e82612b93565b60005b83811015612bee578181015183820152602001612bd6565b838111156105485750506000910152565b601f01601f191690565b612c1281612b83565b8114612c1d57600080fd5b50565b612c1281612b8e565b612c1281612b93565b612c1281612bb6565b612c1281612bc256fe626c6f636b2074696d657374616d702065786365656473203332206269747300a365627a7a72315820497918685037ab14661619676e76f9b34eea84e8353626117398a7b51c4f687f6c6578706572696d656e74616cf564736f6c63430005100040
Deployed ByteCode Sourcemap
110544:18843:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;109211:71;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;109211:71:0;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;110279:66;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;110279:66:0;;;;;;;;:::i;123766:155::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;123766:155:0;;;;;;;;:::i;112532:397::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;112532:397:0;;;;;;;;:::i;128783:178::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;128783:178:0;;;;;;;;:::i;128075:408::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;128075:408:0;;;;;;;;:::i;109679:80::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;109679:80:0;;;;;;;;:::i;:::-;;;;;;;;;108843:28;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108843:28:0;;;:::i;:::-;;;;;;;;110409:49;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110409:49:0;;;:::i;:::-;;;;;;;;122376:408;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;122376:408:0;;;;;;;;:::i;129017:147::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;129017:147:0;;;;;;;;:::i;124193:301::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;124193:301:0;;;;;;;;:::i;110103:92::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;110103:92:0;;;;;;;;:::i;129284:100::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;129284:100:0;;;:::i;111765:185::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;111765:185:0;;;:::i;124903:1647::-;;;;;;;;;:::i;109885:92::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;109885:92:0;;;;;;;;:::i;123068:512::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;123068:512:0;;;;;;;;:::i;109378:71::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;109378:71:0;;;;;;;;:::i;110511:25::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110511:25:0;;;:::i;:::-;;;;;;;;109524:80;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;109524:80:0;;;;;;;;:::i;108752:20::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108752:20:0;;;:::i;128551:163::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;128551:163:0;;;;;;;;:::i;109211:71::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;110279:66::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;123766:155::-;123854:59;123866:10;123878:6;123886:10;;;;;;;;;-1:-1:-1;;;;;123886:10:0;-1:-1:-1;;;;;123886:24:0;;:26;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;123886:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;123886:26:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;123886:26:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;123886:26:0;;;;;;;;123854:59;123766:155;;:::o;112532:397::-;112727:1;112713:10;:15;;;;112705:49;;;;-1:-1:-1;;;112705:49:0;;;;;;;;;;;;;;;;;112773:21;:19;:21::i;:::-;112765:65;;;;-1:-1:-1;;;112765:65:0;;;;;;;;;112841:80;112864:10;112876:6;112884:17;112903;112841:22;:80::i;:::-;112532:397;;;;:::o;128783:178::-;128867:5;;-1:-1:-1;;;;;128867:5:0;128853:10;:19;128845:61;;;;-1:-1:-1;;;128845:61:0;;;;;;;;;128917:10;:36;;-1:-1:-1;;;;;;128917:36:0;-1:-1:-1;;;;;128917:36:0;;;;;;;;;;128783:178::o;128075:408::-;128218:21;:19;:21::i;:::-;128210:58;;;;-1:-1:-1;;;128210:58:0;;;;;;;;;128279:18;128300:50;128320:10;128332:9;128343:6;128300:19;:50::i;:::-;128279:71;-1:-1:-1;128369:15:0;;128361:54;;;;-1:-1:-1;;;128361:54:0;;;;;;;;;128431:44;128445:10;128457:9;128468:6;128431:44;;;;;;;;;;;;;;;;;128075:408;;;;:::o;109679:80::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;109679:80:0;;;-1:-1:-1;;;109679:80:0;;;;;:::o;108843:28::-;;;-1:-1:-1;;;;;108843:28:0;;:::o;110409:49::-;110454:4;110409:49;:::o;122376:408::-;122490:21;:19;:21::i;:::-;122482:87;;;;-1:-1:-1;;;122482:87:0;;;;;;;;;122585:16;122580:197;122621:1;122607:10;:15;;;122580:197;;122653:43;122677:10;122689:6;122653:23;:43::i;:::-;122711:54;122736:10;122748:6;122756:8;122711:24;:54::i;:::-;122624:12;;122580:197;;;;122376:408;;:::o;129017:147::-;129094:5;;-1:-1:-1;;;;;129094:5:0;129080:10;:19;129072:56;;;;-1:-1:-1;;;129072:56:0;;;;;;;;;129139:5;:17;;-1:-1:-1;;;;;;129139:17:0;-1:-1:-1;;;;;129139:17:0;;;;;;;;;;129017:147::o;124193:301::-;124368:24;;;124390:1;124368:24;;;;;;;;;124333:32;;124368:24;;;;;;105:10:-1;124368:24:0;88:34:-1;136:17;;-1:-1;124368:24:0;124333:59;;124416:6;124403:7;124411:1;124403:10;;;;;;;;;;;;;:19;-1:-1:-1;;;;;124403:19:0;;;-1:-1:-1;;;;;124403:19:0;;;;;124433:53;124445:10;124457:7;124466;124475:4;124481;124433:11;:53::i;110103:92::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;129284:100::-;129361:15;129284:100;:::o;111765:185::-;111814:11;;-1:-1:-1;;;111814:11:0;;;;111813:12;111805:62;;;;-1:-1:-1;;;111805:62:0;;;;;;;;;111878:10;:35;;-1:-1:-1;;;;;;111878:35:0;111902:10;111878:35;;;111924:11;:18;;-1:-1:-1;;;;111924:18:0;-1:-1:-1;;;111924:18:0;;;111765:185::o;124903:1647::-;125133:1;125119:10;:15;;;;125111:49;;;;-1:-1:-1;;;125111:49:0;;;;;;;;;125176:9;125171:1372;125195:7;:14;125191:1;:18;125171:1372;;;125231:13;125247:7;125255:1;125247:10;;;;;;;;;;;;;;;;;;125280;;:42;;-1:-1:-1;;;125280:42:0;;125247:10;;-1:-1:-1;;;;;;125280:10:0;;:25;;:42;;125247:10;;125280:42;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;125280:42:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;125280:42:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;125280:42:0;;;;;;;;;125272:76;;;;-1:-1:-1;;;125272:76:0;;;;;;;;;125380:4;125367:17;;;;125363:631;;;125405:22;;:::i;:::-;125430:37;;;;;;;;125445:6;-1:-1:-1;;;;;125445:18:0;;:20;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;125445:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;125445:20:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;125445:20:0;;;;;;;;;125430:37;;125405:62;-1:-1:-1;125486:65:0;125510:10;125530:6;125405:62;125486:23;:65::i;:::-;125575:9;125570:409;125594:7;:14;125590:1;:18;125570:409;;;125638:78;125663:10;125683:6;125692:7;125700:1;125692:10;;;;;;;;;;;;;;125704:11;125638:24;:78::i;:::-;125779:180;125825:10;125862:7;125870:1;125862:10;;;;;;;;;;;;;;125899:13;:25;125913:10;125899:25;;;;;;;;;;;;;;;:37;125925:7;125933:1;125925:10;;;;;;;;;;;;;;-1:-1:-1;;;;;125899:37:0;-1:-1:-1;;;;;125899:37:0;;;;;;;;;;;;;125779:19;:180::i;:::-;125739:25;;;;;;;:13;:25;;;;;125765:10;;125739:25;;;125765:7;;125773:1;;125765:10;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;125739:37:0;;;;;;;;;;;-1:-1:-1;125739:37:0;:220;125610:3;;125570:409;;;;125363:631;;126025:4;126012:17;;;;126008:524;;;126050:52;126074:10;126094:6;126050:23;:52::i;:::-;126126:9;126121:396;126145:7;:14;126141:1;:18;126121:396;;;126189:65;126214:10;126234:6;126243:7;126251:1;126243:10;;;;;;;;;;;;;;126189:24;:65::i;:::-;126317:180;126363:10;126400:7;126408:1;126400:10;;;;;;;;;;;;;;126437:13;:25;126451:10;126437:25;;;;;;;;;;;;;;;:37;126463:7;126471:1;126463:10;;;;;;;126317:180;126277:25;;;;;;;:13;:25;;;;;126303:10;;126277:25;;;126303:7;;126311:1;;126303:10;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;126277:37:0;;;;;;;;;;;-1:-1:-1;126277:37:0;:220;126161:3;;126121:396;;;;126008:524;-1:-1:-1;125211:3:0;;125171:1372;;;;124903:1647;;;;;:::o;109885:92::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;123068:512::-;123248:21;:19;:21::i;:::-;123240:87;;;;-1:-1:-1;;;123240:87:0;;;;;;;;;123343:16;123338:235;123379:1;123365:10;:15;;;123338:235;;123411:62;123435:10;123447:6;123411:62;;;;;;;123455:17;123411:62;;;:23;:62::i;:::-;123488:73;123513:10;123525:6;123533:8;123488:73;;;;;;;123543:17;123488:73;;;:24;:73::i;:::-;123382:12;;123338:235;;109378:71;;;;;;;;;;;;;;;;;;;;;;;;:::o;110511:25::-;;;-1:-1:-1;;;;;110511:25:0;;:::o;109524:80::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;109524:80:0;;;-1:-1:-1;;;109524:80:0;;;;;:::o;108752:20::-;;;-1:-1:-1;;;;;108752:20:0;;:::o;128551:163::-;128637:5;;-1:-1:-1;;;;;128637:5:0;128623:10;:19;128615:54;;;;-1:-1:-1;;;128615:54:0;;;;;;;;;128680:10;:26;;-1:-1:-1;;;;;;128680:26:0;-1:-1:-1;;;;;128680:26:0;;;;;;;;;;128551:163::o;112068:142::-;112122:4;112160:5;;-1:-1:-1;;;;;112160:5:0;112146:10;:19;;:56;;-1:-1:-1;112191:10:0;;-1:-1:-1;;;;;112191:10:0;112169;:33;112146:56;112139:63;;112068:142;:::o;113248:2688::-;113496:30;;;113461:32;113496:30;;;:18;:30;;;;;;;;-1:-1:-1;;;;;113496:47:0;;;;;;;;;;113558:29;;113554:841;;113697:52;113721:10;113741:6;113697:23;:52::i;:::-;113554:841;;;113771:19;;113767:628;;113850:10;;:42;;-1:-1:-1;;;113850:42:0;;-1:-1:-1;;;;;113850:10:0;;;;:25;;:42;;113884:6;;113850:42;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;113850:42:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;113850:42:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;113850:42:0;;;;;;;;;113842:82;;;;-1:-1:-1;;;113842:82:0;;;;;;;;;113963:29;;;;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;113963:46:0;;;;;;;;;:52;-1:-1:-1;;;;;113963:52:0;:57;:139;;;;-1:-1:-1;114041:29:0;;;;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;114041:46:0;;;;;;;;;:56;-1:-1:-1;;;114041:56:0;;;;:61;113963:139;113941:443;;;114186:182;;;;;;;;110454:4;-1:-1:-1;;;;;114186:182:0;;;;;114286:62;114293:19;:17;:19::i;:::-;114286:62;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;114286:62:0;;;:6;:62::i;:::-;114186:182;;;;;;;114137:29;;;;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;114137:46:0;;;;;;;;;:231;;;;;;;;;;;;-1:-1:-1;;;114137:231:0;-1:-1:-1;;;;;114137:231:0;;;-1:-1:-1;;;;;;114137:231:0;;;;;;;;;;;;;;113941:443;114439:14;114411:24;:42;114407:219;;114470:30;;;;;;;:18;:30;;;;;;;;-1:-1:-1;;;;;114470:47:0;;;;;;;;;;;:64;;;114554:60;;;;;;114489:10;;114520:14;;114554:60;;;;;;;;;;114407:219;114709:30;;;114674:32;114709:30;;;:18;:30;;;;;;;;-1:-1:-1;;;;;114709:47:0;;;;;;;;;;114771:29;;114767:931;;114910:22;;:::i;:::-;114935:37;;;;;;;;114950:6;-1:-1:-1;;;;;114950:18:0;;:20;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;114950:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;114950:20:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;114950:20:0;;;;;;;;;114935:37;;114910:62;-1:-1:-1;114987:65:0;115011:10;115031:6;114910:62;114987:23;:65::i;:::-;114767:931;;;;115074:19;;115070:628;;115153:10;;:42;;-1:-1:-1;;;115153:42:0;;-1:-1:-1;;;;;115153:10:0;;;;:25;;:42;;115187:6;;115153:42;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;115153:42:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;115153:42:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;115153:42:0;;;;;;;;;115145:82;;;;-1:-1:-1;;;115145:82:0;;;;;;;;;115266:29;;;;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;115266:46:0;;;;;;;;;:52;-1:-1:-1;;;;;115266:52:0;:57;:139;;;;-1:-1:-1;115344:29:0;;;;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;115344:46:0;;;;;;;;;:56;-1:-1:-1;;;115344:56:0;;;;:61;115266:139;115244:443;;;115489:182;;;;;;;;110454:4;-1:-1:-1;;;;;115489:182:0;;;;;115589:62;115596:19;:17;:19::i;115589:62::-;115489:182;;;;;;;115440:29;;;;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;115440:46:0;;;;;;;;;:231;;;;;;;;;;;;-1:-1:-1;;;115440:231:0;-1:-1:-1;;;;;115440:231:0;;;-1:-1:-1;;;;;;115440:231:0;;;;;;;;;;;;;;115244:443;115742:14;115714:24;:42;115710:219;;115773:30;;;;;;;:18;:30;;;;;;;;-1:-1:-1;;;;;115773:47:0;;;;;;;;;;;:64;;;115857:60;;;;;;115792:10;;115823:14;;115857:60;;;;;;;;;;113248:2688;;;;;;:::o;126968:732::-;127105:7;127129:15;;;127125:544;;127197:10;;127246:28;;-1:-1:-1;;;127246:28:0;;-1:-1:-1;;;;;127197:10:0;;;;127161:18;;127197:10;;127246:13;;:28;;127268:4;;127246:28;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;127246:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;127246:28:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;127246:28:0;;;;;;;;;127223:51;;127302:1;127293:6;:10;:36;;;;;127317:12;127307:6;:22;;127293:36;127289:130;;;127350:26;;-1:-1:-1;;;127350:26:0;;-1:-1:-1;;;;;127350:12:0;;;;;:26;;127363:4;;127369:6;;127350:26;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;127350:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;127350:26:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;127350:26:0;;;;;;;;;;127402:1;127395:8;;;;;;127289:130;127125:544;;;;;127440:10;:15;;127454:1;127440:15;127436:233;;;127496:21;127536:10;;;;;:37;;;127560:13;127550:6;:23;;127536:37;127532:126;;;127594:21;;-1:-1:-1;;;;;127594:13:0;;;:21;;;;;127608:6;;127594:21;;;;127608:6;127594:13;:21;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;127594:21:0;127641:1;127634:8;;;;;127532:126;127436:233;;-1:-1:-1;127686:6:0;126968:732;;;;;;:::o;116142:1250::-;116251:1;116237:10;:15;;;;116229:49;;;;-1:-1:-1;;;116229:49:0;;;;;;;;;116329:29;;;116289:37;116329:29;;;:17;:29;;;;;;;;-1:-1:-1;;;;;116329:37:0;;;;;;;;;;;116399:30;;;:18;:30;;;;;:38;;;;;;;;;;116473:19;:17;:19::i;:::-;116558:21;;116448:44;;-1:-1:-1;116503:23:0;;116529:52;;116448:44;;-1:-1:-1;;;116558:21:0;;;;116529:4;:52::i;:::-;116503:78;;116614:1;116596:15;:19;:38;;;;;116633:1;116619:11;:15;116596:38;116592:793;;;116651:20;116681:6;-1:-1:-1;;;;;116674:26:0;;:28;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;116674:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;116674:28:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;116674:28:0;;;;;;;;;116651:51;;116717:21;116741:34;116746:15;116763:11;116741:4;:34::i;:::-;116717:58;;116790:19;;:::i;:::-;116827:1;116812:12;:16;:80;;116871:21;;;;;;;;116889:1;116871:21;;;116812:80;;;116831:37;116840:13;116855:12;116831:8;:37::i;:::-;116790:102;;116907:19;;:::i;:::-;116934:37;;;;;;;;;116952:17;;-1:-1:-1;;;;;116952:17:0;116934:37;;116929:50;;116973:5;116929:4;:50::i;:::-;116907:72;;117034:200;;;;;;;;117078:53;117086:5;:14;;;117078:53;;;;;;;;;;;;;;;;;:7;:53::i;:::-;-1:-1:-1;;;;;117034:200:0;;;;;117161:57;117168:14;117161:57;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;117161:57:0;;;:6;:57::i;:::-;117034:200;;;;;;;116994:29;;;;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;116994:37:0;;;;;;;;;:240;;;;;;;;;;;;-1:-1:-1;;;116994:240:0;-1:-1:-1;;;;;116994:240:0;;;-1:-1:-1;;;;;;116994:240:0;;;;;;;;;;;;;;-1:-1:-1;116592:793:0;;-1:-1:-1;;;116592:793:0;;117256:19;;117252:133;;117316:57;117323:14;117316:57;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;117316:57:0;;;:6;:57::i;:::-;117292:81;;;;;;;-1:-1:-1;;;117292:81:0;-1:-1:-1;;;;;117292:81:0;;;;;;116142:1250;;;;;;:::o;119313:1179::-;119475:1;119461:10;:15;;;;119453:49;;;;-1:-1:-1;;;119453:49:0;;;;;;;;;119553:29;;;119513:37;119553:29;;;:17;:29;;;;;;;;-1:-1:-1;;;;;119553:37:0;;;;;;;;;119601:25;;:::i;:::-;-1:-1:-1;119629:37:0;;;;;;;;;119647:17;;-1:-1:-1;;;;;119647:17:0;119629:37;;119677:27;;:::i;:::-;-1:-1:-1;119707:69:0;;;;;;;;;119725:31;;;-1:-1:-1;119725:31:0;;;:19;:31;;;;;-1:-1:-1;;;;;119725:39:0;;;;;;;;;;;:49;;;;;;;;;;;;;;119707:69;;119839:20;;119787:49;;;;;;;:72;;;;119876:22;;:27;:55;;;;-1:-1:-1;119907:20:0;;:24;;119876:55;119872:131;;;110454:4;119948:43;;119872:131;120015:24;;:::i;:::-;120042:32;120047:11;120060:13;120042:4;:32::i;:::-;120015:59;;120085:22;120117:6;-1:-1:-1;;;;;120110:24:0;;120135:8;120110:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;120110:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;120110:34:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;120110:34:0;;;;;;;;;120085:59;;120155:21;120179:32;120184:14;120200:10;120179:4;:32::i;:::-;120253:25;;;120222:23;120253:25;;;:13;:25;;;;;;;;-1:-1:-1;;;;;120253:35:0;;;;;;;;;;120155:56;;-1:-1:-1;120222:23:0;120248:56;;120155;120248:4;:56::i;:::-;120315:25;;;;;;;:13;:25;;;;;;;;-1:-1:-1;;;;;120315:35:0;;;;;;;;;;;;;;:53;;;120463:20;;120384:100;;120222:82;;-1:-1:-1;120315:35:0;;120384:100;;;;;;;;120329:10;;120448:13;;120463:20;120384:100;;;;;;;;;;119313:1179;;;;;;;;;;:::o;117666:1340::-;117839:1;117825:10;:15;;;;117817:49;;;;-1:-1:-1;;;117817:49:0;;;;;;;;;117917:29;;;117877:37;117917:29;;;:17;:29;;;;;;;;-1:-1:-1;;;;;117917:37:0;;;;;;;;;;;117987:30;;;:18;:30;;;;;:38;;;;;;;;;;118061:19;:17;:19::i;:::-;118146:21;;118036:44;;-1:-1:-1;118091:23:0;;118117:52;;118036:44;;-1:-1:-1;;;118146:21:0;;;;118117:4;:52::i;:::-;118091:78;;118202:1;118184:15;:19;:38;;;;;118221:1;118207:11;:15;118184:38;118180:819;;;118239:20;118262:54;118274:6;-1:-1:-1;;;;;118267:27:0;;:29;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;118267:29:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;118267:29:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;118267:29:0;;;;;;;;;118298:17;118262:4;:54::i;:::-;118239:77;;118331:21;118355:34;118360:15;118377:11;118355:4;:34::i;:::-;118331:58;;118404:19;;:::i;:::-;118441:1;118426:12;:16;:80;;118485:21;;;;;;;;118503:1;118485:21;;;118426:80;;;118445:37;118454:13;118469:12;118445:8;:37::i;:::-;118404:102;;118521:19;;:::i;:::-;118548:37;;;;;;;;;118566:17;;-1:-1:-1;;;;;118566:17:0;118548:37;;118543:50;;118587:5;118543:4;:50::i;:::-;118521:72;;118648:200;;;;;;;;118692:53;118700:5;:14;;;118692:53;;;;;;;;;;;;;;;;;:7;:53::i;:::-;-1:-1:-1;;;;;118648:200:0;;;;;118775:57;118782:14;118775:57;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;118775:57:0;;;:6;:57::i;:::-;118648:200;;;;;118608:17;:29;118626:10;118608:29;;;;;;;;;;;;;;;:37;118638:6;-1:-1:-1;;;;;118608:37:0;-1:-1:-1;;;;;118608:37:0;;;;;;;;;;;;:240;;;;;;;;;;;;;-1:-1:-1;;;;;118608:240:0;;;;;-1:-1:-1;;;;;118608:240:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;118180:819;;;;;;;118870:19;;118866:133;;118930:57;118937:14;118930:57;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;118930:57:0;;;:6;:57::i;:::-;118906:81;;;;;;;-1:-1:-1;;;118906:81:0;-1:-1:-1;;;;;118906:81:0;;;;;;118866:133;117666:1340;;;;;;;:::o;120968:1188::-;121169:1;121155:10;:15;;;;121147:49;;;;-1:-1:-1;;;121147:49:0;;;;;;;;;121247:29;;;121207:37;121247:29;;;:17;:29;;;;;;;;-1:-1:-1;;;;;121247:37:0;;;;;;;;;121295:25;;:::i;:::-;-1:-1:-1;121323:37:0;;;;;;;;;121341:17;;-1:-1:-1;;;;;121341:17:0;121323:37;;121371:27;;:::i;:::-;-1:-1:-1;121401:69:0;;;;;;;;;121419:31;;;-1:-1:-1;121419:31:0;;;:19;:31;;;;;-1:-1:-1;;;;;121419:39:0;;;;;;;;;;;:49;;;;;;;;;;;;;;121401:69;;121533:20;;121481:49;;;;;;;:72;;;;121570:22;;:26;121566:583;;121613:24;;:::i;:::-;121640:32;121645:11;121658:13;121640:4;:32::i;:::-;121613:59;;121687:22;121712:69;121724:6;-1:-1:-1;;;;;121717:34:0;;121752:8;121717:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;121712:69:0;121687:94;;121796:21;121820:32;121825:14;121841:10;121820:4;:32::i;:::-;121898:25;;;121867:23;121898:25;;;:13;:25;;;;;;;;-1:-1:-1;;;;;121898:35:0;;;;;;;;;;121796:56;;-1:-1:-1;121867:23:0;121893:56;;121796;121893:4;:56::i;:::-;121867:82;;122002:15;121964:13;:25;121978:10;121964:25;;;;;;;;;;;;;;;:35;121990:8;-1:-1:-1;;;;;121964:35:0;-1:-1:-1;;;;;121964:35:0;;;;;;;;;;;;:53;;;;122091:8;-1:-1:-1;;;;;122037:100:0;122082:6;-1:-1:-1;;;;;122037:100:0;;122063:10;122101:13;122116:11;:20;;;122037:100;;;;;;;;;;;;;;;;;121566:583;;;;120968:1188;;;;;;;:::o;24604:164::-;24682:6;24720:12;24713:5;24709:9;;24701:32;;;;-1:-1:-1;;;24701:32:0;;;;;;;;;;;24758:1;24744:16;;24604:164;;;;;:::o;25790:129::-;25849:7;25876:35;25881:1;25884;25876:35;;;;;;;;;;;;;-1:-1:-1;;;25876:35:0;;;:4;:35::i;27058:131::-;27117:7;27144:37;27149:1;27152;27144:37;;;;;;;;;;;;;;;;;:4;:37::i;28781:153::-;28844:13;;:::i;:::-;28877:49;;;;;;;;28895:29;28900:20;28905:1;14896:4;28900;:20::i;:::-;28922:1;28895:4;:29::i;:::-;28877:49;;28870:56;28781:153;-1:-1:-1;;;28781:153:0:o;24932:160::-;25003:13;;:::i;:::-;25036:48;;;;;;;;25054:28;25059:1;:10;;;25071:1;:10;;;25054:4;:28::i;24428:168::-;24507:7;24547:12;-1:-1:-1;;;24535:10:0;;24527:33;;;;-1:-1:-1;;;24527:33:0;;;;;;;;;25622:160;25693:13;;:::i;:::-;25726:48;;;;;;;;25744:28;25749:1;:10;;;25761:1;:10;;;25744:4;:28::i;26917:133::-;26982:7;14896:4;27009:19;27014:1;27017;:10;;;27009:4;:19::i;:::-;:33;;;;;;;26917:133;-1:-1:-1;;;26917:133:0:o;25100:125::-;25159:7;25186:31;25191:1;25194;25186:31;;;;;;;;;;;;;-1:-1:-1;;;25186:31:0;;;:4;:31::i;27817:132::-;27879:7;27906:35;27911:17;27916:1;14854:4;27911;:17::i;:::-;27930:10;;27906:4;:35::i;25927:201::-;26048:7;26084:12;26076:6;;;;26068:29;;;;-1:-1:-1;;;26068:29:0;;;;;;;;;;-1:-1:-1;;;26115:5:0;;;25927:201::o;27197:296::-;27318:7;27342:6;;;:16;;-1:-1:-1;27352:6:0;;27342:16;27338:57;;;-1:-1:-1;27382:1:0;27375:8;;27338:57;27417:5;;;27421:1;27417;:5;:1;27441:5;;;;;:10;27453:12;27433:33;;;;;-1:-1:-1;;;27433:33:0;;;;;;;;;;-1:-1:-1;27484:1:0;27197:296;-1:-1:-1;;;;27197:296:0:o;28443:122::-;28502:7;28529:28;28534:1;28537;28529:28;;;;;;;;;;;;;-1:-1:-1;;;28529:28:0;;;:4;:28::i;25233:225::-;25354:7;25386:5;;;25418:12;25410:6;;;;25402:29;;;;-1:-1:-1;;;25402:29:0;;;;;;;;;28573:200;28694:7;28729:12;28722:5;28714:28;;;;-1:-1:-1;;;28714:28:0;;;;;;;;;;;28764:1;28760;:5;;;;;;;28573:200;-1:-1:-1;;;;28573:200:0:o;110544:18843::-;;;;;;;;;;;;;;:::o;5:130:-1:-;72:20;;97:33;72:20;97:33;;321:731;;446:3;439:4;431:6;427:17;423:27;413:2;;464:1;461;454:12;413:2;501:6;488:20;523:88;538:72;603:6;538:72;;;523:88;;;514:97;;628:5;653:6;646:5;639:21;683:4;675:6;671:17;661:27;;705:4;700:3;696:14;689:21;;758:6;805:3;797:4;789:6;785:17;780:3;776:27;773:36;770:2;;;822:1;819;812:12;770:2;847:1;832:214;857:6;854:1;851:13;832:214;;;915:3;937:45;978:3;966:10;937:45;;;925:58;;-1:-1;1006:4;997:14;;;;1025;;;;;879:1;872:9;832:214;;;836:14;406:646;;;;;;;;1086:752;;1218:3;1211:4;1203:6;1199:17;1195:27;1185:2;;1236:1;1233;1226:12;1185:2;1273:6;1260:20;1295:95;1310:79;1382:6;1310:79;;1295:95;1286:104;;1407:5;1432:6;1425:5;1418:21;1462:4;1454:6;1450:17;1440:27;;1484:4;1479:3;1475:14;1468:21;;1537:6;1584:3;1576:4;1568:6;1564:17;1559:3;1555:27;1552:36;1549:2;;;1601:1;1598;1591:12;1549:2;1626:1;1611:221;1636:6;1633:1;1630:13;1611:221;;;1694:3;1716:52;1764:3;1752:10;1716:52;;;1704:65;;-1:-1;1792:4;1783:14;;;;1811;;;;;1658:1;1651:9;1611:221;;1872:767;;2015:3;2008:4;2000:6;1996:17;1992:27;1982:2;;2033:1;2030;2023:12;1982:2;2063:6;2057:13;2085:95;2100:79;2172:6;2100:79;;2085:95;2076:104;;2197:5;2222:6;2215:5;2208:21;2252:4;2244:6;2240:17;2230:27;;2274:4;2269:3;2265:14;2258:21;;2327:6;2374:3;2366:4;2358:6;2354:17;2349:3;2345:27;2342:36;2339:2;;;2391:1;2388;2381:12;2339:2;2416:1;2401:232;2426:6;2423:1;2420:13;2401:232;;;2484:3;2506:63;2565:3;2553:10;2506:63;;;2494:76;;-1:-1;2593:4;2584:14;;;;2612;;;;;2448:1;2441:9;2401:232;;2647:124;2711:20;;2736:30;2711:20;2736:30;;2778:128;2853:13;;2871:30;2853:13;2871:30;;2913:160;2995:20;;3020:48;2995:20;3020:48;;3080:164;3173:13;;3191:48;3173:13;3191:48;;3282:153;;3388:2;3379:6;3374:3;3370:16;3366:25;3363:2;;;3404:1;3401;3394:12;3363:2;-1:-1;3423:6;3356:79;-1:-1;3356:79;3471:322;;3580:4;3568:9;3563:3;3559:19;3555:30;3552:2;;;3598:1;3595;3588:12;3552:2;3616:20;3631:4;3616:20;;;3607:29;-1:-1;3690:1;3722:49;3767:3;3747:9;3722:49;;;3697:75;;-1:-1;3708:5;3546:247;-1:-1;;3546:247;3800:130;3867:20;;3892:33;3867:20;3892:33;;3937:134;4015:13;;4033:33;4015:13;4033:33;;4078:126;4143:20;;4168:31;4143:20;4168:31;;4211:241;;4315:2;4303:9;4294:7;4290:23;4286:32;4283:2;;;4331:1;4328;4321:12;4283:2;4366:1;4383:53;4428:7;4408:9;4383:53;;;4373:63;4277:175;-1:-1;;;;4277:175;4459:366;;;4580:2;4568:9;4559:7;4555:23;4551:32;4548:2;;;4596:1;4593;4586:12;4548:2;4631:1;4648:53;4693:7;4673:9;4648:53;;;4638:63;;4610:97;4738:2;4756:53;4801:7;4792:6;4781:9;4777:22;4756:53;;;4746:63;;4717:98;4542:283;;;;;;4832:535;;;;4992:2;4980:9;4971:7;4967:23;4963:32;4960:2;;;5008:1;5005;4998:12;4960:2;5043:1;5060:53;5105:7;5085:9;5060:53;;;5050:63;;5022:97;5150:2;5168:53;5213:7;5204:6;5193:9;5189:22;5168:53;;;5158:63;;5129:98;5258:2;5276:75;5343:7;5334:6;5323:9;5319:22;5276:75;;;5266:85;;5237:120;4954:413;;;;;;5374:422;;5529:2;5517:9;5508:7;5504:23;5500:32;5497:2;;;5545:1;5542;5535:12;5497:2;5580:24;;5624:18;5613:30;;5610:2;;;5656:1;5653;5646:12;5610:2;5676:104;5772:7;5763:6;5752:9;5748:22;5676:104;;5803:257;;5915:2;5903:9;5894:7;5890:23;5886:32;5883:2;;;5931:1;5928;5921:12;5883:2;5966:1;5983:61;6036:7;6016:9;5983:61;;6067:281;;6191:2;6179:9;6170:7;6166:23;6162:32;6159:2;;;6207:1;6204;6197:12;6159:2;6242:1;6259:73;6324:7;6304:9;6259:73;;6355:263;;6470:2;6458:9;6449:7;6445:23;6441:32;6438:2;;;6486:1;6483;6476:12;6438:2;6521:1;6538:64;6594:7;6574:9;6538:64;;6625:362;;;6744:2;6732:9;6723:7;6719:23;6715:32;6712:2;;;6760:1;6757;6750:12;6712:2;6795:1;6812:51;6855:7;6835:9;6812:51;;7379:669;;;;7563:2;7551:9;7542:7;7538:23;7534:32;7531:2;;;7579:1;7576;7569:12;7531:2;7614:1;7631:51;7674:7;7654:9;7631:51;;;7621:61;;7593:95;7719:2;7737:61;7790:7;7781:6;7770:9;7766:22;7737:61;;;7727:71;;7698:106;7863:2;7852:9;7848:18;7835:32;7887:18;7879:6;7876:30;7873:2;;;7919:1;7916;7909:12;7873:2;7939:93;8024:7;8015:6;8004:9;8000:22;7939:93;;8055:503;;;;8199:2;8187:9;8178:7;8174:23;8170:32;8167:2;;;8215:1;8212;8205:12;8167:2;8250:1;8267:51;8310:7;8290:9;8267:51;;;8257:61;;8229:95;8355:2;8373:61;8426:7;8417:6;8406:9;8402:22;8373:61;;;8363:71;;8334:106;8471:2;8489:53;8534:7;8525:6;8514:9;8510:22;8489:53;;8565:487;;;;8701:2;8689:9;8680:7;8676:23;8672:32;8669:2;;;8717:1;8714;8707:12;8669:2;8752:1;8769:51;8812:7;8792:9;8769:51;;;8759:61;;8731:95;8857:2;8875:53;8920:7;8911:6;8900:9;8896:22;8875:53;;;8865:63;;8836:98;8965:2;8983:53;9028:7;9019:6;9008:9;9004:22;8983:53;;9059:1045;;;;;;9296:3;9284:9;9275:7;9271:23;9267:33;9264:2;;;9313:1;9310;9303:12;9264:2;9348:1;9365:51;9408:7;9388:9;9365:51;;;9355:61;;9327:95;9481:2;9470:9;9466:18;9453:32;9505:18;9497:6;9494:30;9491:2;;;9537:1;9534;9527:12;9491:2;9557:86;9635:7;9626:6;9615:9;9611:22;9557:86;;;9547:96;;9432:217;9708:2;9697:9;9693:18;9680:32;9732:18;9724:6;9721:30;9718:2;;;9764:1;9761;9754:12;9718:2;9784:93;9869:7;9860:6;9849:9;9845:22;9784:93;;;9774:103;;9659:224;9914:2;9932:50;9974:7;9965:6;9954:9;9950:22;9932:50;;;9922:60;;9893:95;10019:3;10038:50;10080:7;10071:6;10060:9;10056:22;10038:50;;;10028:60;;9998:96;9258:846;;;;;;;;;10111:643;;;;;10279:3;10267:9;10258:7;10254:23;10250:33;10247:2;;;10296:1;10293;10286:12;10247:2;10331:1;10348:51;10391:7;10371:9;10348:51;;;10338:61;;10310:95;10436:2;10454:68;10514:7;10505:6;10494:9;10490:22;10454:68;;;10444:78;;10415:113;10559:2;10577:53;10622:7;10613:6;10602:9;10598:22;10577:53;;;10567:63;;10538:98;10667:2;10685:53;10730:7;10721:6;10710:9;10706:22;10685:53;;;10675:63;;10646:98;10241:513;;;;;;;;10761:142;10852:45;10891:5;10852:45;;;10847:3;10840:58;10834:69;;;10910:113;10993:24;11011:5;10993:24;;11030:166;11133:57;11184:5;11133:57;;11203:347;;11315:39;11348:5;11315:39;;;11366:71;11430:6;11425:3;11366:71;;;11359:78;;11442:52;11487:6;11482:3;11475:4;11468:5;11464:16;11442:52;;;11515:29;11537:6;11515:29;;;11506:39;;;;11295:255;-1:-1;;;11295:255;11558:329;;11718:67;11782:2;11777:3;11718:67;;;11818:31;11798:52;;11878:2;11869:12;;11704:183;-1:-1;;11704:183;11896:322;;12056:67;12120:2;12115:3;12056:67;;;-1:-1;;;12136:45;;12209:2;12200:12;;12042:176;-1:-1;;12042:176;12227:390;;12387:67;12451:2;12446:3;12387:67;;;12487:34;12467:55;;-1:-1;;;12551:2;12542:12;;12535:45;12608:2;12599:12;;12373:244;-1:-1;;12373:244;12626:327;;12786:67;12850:2;12845:3;12786:67;;;12886:29;12866:50;;12944:2;12935:12;;12772:181;-1:-1;;12772:181;12962:321;;13122:67;13186:2;13181:3;13122:67;;;-1:-1;;;13202:44;;13274:2;13265:12;;13108:175;-1:-1;;13108:175;13292:324;;13452:67;13516:2;13511:3;13452:67;;;13552:26;13532:47;;13607:2;13598:12;;13438:178;-1:-1;;13438:178;13625:390;;13785:67;13849:2;13844:3;13785:67;;;13885:34;13865:55;;-1:-1;;;13949:2;13940:12;;13933:45;14006:2;13997:12;;13771:244;-1:-1;;13771:244;14024:321;;14184:67;14248:2;14243:3;14184:67;;;-1:-1;;;14264:44;;14336:2;14327:12;;14170:175;-1:-1;;14170:175;14354:326;;14514:67;14578:2;14573:3;14514:67;;;14614:28;14594:49;;14671:2;14662:12;;14500:180;-1:-1;;14500:180;14689:374;;14849:67;14913:2;14908:3;14849:67;;;14949:34;14929:55;;-1:-1;;;15013:2;15004:12;;14997:29;15054:2;15045:12;;14835:228;-1:-1;;14835:228;15072:324;;15232:67;15296:2;15291:3;15232:67;;;15332:26;15312:47;;15387:2;15378:12;;15218:178;-1:-1;;15218:178;15405:331;;15565:67;15629:2;15624:3;15565:67;;;15665:33;15645:54;;15727:2;15718:12;;15551:185;-1:-1;;15551:185;15744:113;15827:24;15845:5;15827:24;;15864:113;15947:24;15965:5;15947:24;;15984:110;16065:23;16082:5;16065:23;;16101:107;16180:22;16196:5;16180:22;;16215:213;16333:2;16318:18;;16347:71;16322:9;16391:6;16347:71;;16435:229;16561:2;16546:18;;16575:79;16550:9;16627:6;16575:79;;16671:340;16825:2;16810:18;;16839:79;16814:9;16891:6;16839:79;;;16929:72;16997:2;16986:9;16982:18;16973:6;16929:72;;17018:253;17156:2;17141:18;;17170:91;17145:9;17234:6;17170:91;;17278:301;17416:2;17430:47;;;17401:18;;17491:78;17401:18;17555:6;17491:78;;17586:407;17777:2;17791:47;;;17762:18;;17852:131;17762:18;17852:131;;18000:407;18191:2;18205:47;;;18176:18;;18266:131;18176:18;18266:131;;18414:407;18605:2;18619:47;;;18590:18;;18680:131;18590:18;18680:131;;18828:407;19019:2;19033:47;;;19004:18;;19094:131;19004:18;19094:131;;19242:407;19433:2;19447:47;;;19418:18;;19508:131;19418:18;19508:131;;19656:407;19847:2;19861:47;;;19832:18;;19922:131;19832:18;19922:131;;20070:407;20261:2;20275:47;;;20246:18;;20336:131;20246:18;20336:131;;20484:407;20675:2;20689:47;;;20660:18;;20750:131;20660:18;20750:131;;20898:407;21089:2;21103:47;;;21074:18;;21164:131;21074:18;21164:131;;21312:407;21503:2;21517:47;;;21488:18;;21578:131;21488:18;21578:131;;21726:407;21917:2;21931:47;;;21902:18;;21992:131;21902:18;21992:131;;22140:407;22331:2;22345:47;;;22316:18;;22406:131;22316:18;22406:131;;22554:213;22672:2;22657:18;;22686:71;22661:9;22730:6;22686:71;;22774:320;22918:2;22903:18;;22932:71;22907:9;22976:6;22932:71;;;23014:70;23080:2;23069:9;23065:18;23056:6;23014:70;;23101:213;23219:2;23204:18;;23233:71;23208:9;23277:6;23233:71;;23321:443;23499:2;23484:18;;23513:67;23488:9;23553:6;23513:67;;;23591:80;23667:2;23656:9;23652:18;23643:6;23591:80;;;23682:72;23750:2;23739:9;23735:18;23726:6;23682:72;;23771:316;23913:2;23898:18;;23927:67;23902:9;23967:6;23927:67;;24094:427;24264:2;24249:18;;24278:67;24253:9;24318:6;24278:67;;;24356:72;24424:2;24413:9;24409:18;24400:6;24356:72;;24528:256;24590:2;24584:9;24616:17;;;24691:18;24676:34;;24712:22;;;24673:62;24670:2;;;24748:1;24745;24738:12;24670:2;24764;24757:22;24568:216;;-1:-1;24568:216;24791:312;;24958:18;24950:6;24947:30;24944:2;;;24990:1;24987;24980:12;24944:2;-1:-1;25025:4;25013:17;;;25078:15;;24881:222;25436:122;25524:12;;25495:63;25566:163;25669:19;;;25718:4;25709:14;;25662:67;25737:91;;25799:24;25817:5;25799:24;;25941:85;26007:13;26000:21;;25983:43;26033:106;;26110:24;26128:5;26110:24;;26146:121;-1:-1;;;;;26208:54;;26191:76;26274:137;-1:-1;;;;;26336:70;;26319:92;26418:72;26480:5;26463:27;26497:88;26569:10;26558:22;;26541:44;26592:81;26663:4;26652:16;;26635:38;26680:129;;26767:37;26798:5;26767:37;;27363:268;27428:1;27435:101;27449:6;27446:1;27443:13;27435:101;;;27516:11;;;27510:18;27497:11;;;27490:39;27471:2;27464:10;27435:101;;;27551:6;27548:1;27545:13;27542:2;;;-1:-1;;27616:1;27598:16;;27591:27;27412:219;27639:97;27727:2;27707:14;-1:-1;;27703:28;;27687:49;27744:117;27813:24;27831:5;27813:24;;;27806:5;27803:35;27793:2;;27852:1;27849;27842:12;27793:2;27787:74;;28008:111;28074:21;28089:5;28074:21;;28126:147;28210:39;28243:5;28210:39;;28280:117;28349:24;28367:5;28349:24;;28404:113;28471:22;28487:5;28471:22;
Swarm Source
bzzr://497918685037ab14661619676e76f9b34eea84e8353626117398a7b51c4f687f
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.