Source Code
Overview
ETH Balance
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
RErc20Delegate
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
import "./RErc20.sol";
/**
* @title RErc20Delegate Contract
* @notice RTokens which wrap an EIP-20 underlying and are delegated to
*/
contract RErc20Delegate is RErc20, RDelegateInterface {
/**
* @notice Construct an empty delegate
*/
constructor() {}
/**
* @notice Called by the delegator on a delegate to initialize it for duty
* @param data The encoded bytes data for any initialization
*/
function _becomeImplementation(bytes memory data) virtual override public {
// Shh -- currently unused
data;
// Shh -- we don't ever want this hook to be marked pure
if (false) {
implementation = address(0);
}
require(msg.sender == admin, "only the admin may call _becomeImplementation");
}
/**
* @notice Called by the delegator on a delegate to forfeit its responsibility
*/
function _resignImplementation() virtual override public {
// Shh -- we don't ever want this hook to be marked pure
if (false) {
implementation = address(0);
}
require(msg.sender == admin, "only the admin may call _resignImplementation");
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "./RToken.sol";
/**
* @title RErc20 Contract
* @notice RTokens which wrap an EIP-20 underlying
*/
contract RErc20 is RToken, RErc20Interface {
/**
* @notice Initialize the new money market
* @param underlying_ The address of the underlying asset
* @param comptroller_ The address of the Comptroller
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ ERC-20 name of this token
* @param symbol_ ERC-20 symbol of this token
* @param decimals_ ERC-20 decimal precision of this token
*/
function initialize(
address underlying_,
ComptrollerInterface comptroller_,
InterestRateModel interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_
) public {
// RToken initialize does the bulk of the work
super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_);
// Set underlying and sanity check it
underlying = underlying_;
EIP20Interface(underlying).totalSupply();
}
/**
* User Interface **
*/
/**
* @notice Sender supplies assets into the market and receives rTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param mintAmount The amount of the underlying asset to supply
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function mint(uint256 mintAmount) external override returns (uint256) {
(uint256 err,) = mintInternal(mintAmount);
return err;
}
/**
* @notice Supply assets but without a 2-step approval process, EIP-2612
* @dev Simply calls the underlying token's `permit()` function and then assumes things worked
* @param mintAmount The amount of the underlying asset to supply
* @param deadline The amount of the underlying asset to supply
* @param v ECDSA recovery id for the signature
* @param r ECDSA r parameter for the signature
* @param s ECDSA s parameter for the signature
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function mintWithPermit(uint256 mintAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
external
override
returns (uint256)
{
IERC20Permit token = IERC20Permit(underlying);
// Go submit our pre-approval signature data to the underlying token, but
// explicitly fail if there is an issue.
token.permit(msg.sender, address(this), mintAmount, deadline, v, r, s);
(uint256 err,) = mintInternal(mintAmount);
return err;
}
/**
* @notice Sender redeems rTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of rTokens to redeem into underlying
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeem(uint256 redeemTokens) external override returns (uint256) {
return redeemInternal(redeemTokens);
}
/**
* @notice Sender redeems rTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemAmount The amount of underlying to redeem
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeemUnderlying(uint256 redeemAmount) external override returns (uint256) {
return redeemUnderlyingInternal(redeemAmount);
}
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function borrow(uint256 borrowAmount) external override returns (uint256) {
return borrowInternal(borrowAmount);
}
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay, or uint.max for the full outstanding amount
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function repayBorrow(uint256 repayAmount) external override returns (uint256) {
(uint256 err,) = repayBorrowInternal(repayAmount);
return err;
}
/**
* @notice Sender repays a borrow belonging to borrower
* @param borrower the account with the debt being payed off
* @param repayAmount The amount to repay, or uint.max for the full outstanding amount
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function repayBorrowBehalf(address borrower, uint256 repayAmount) external override returns (uint256) {
(uint256 err,) = repayBorrowBehalfInternal(borrower, repayAmount);
return err;
}
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this rToken to be liquidated
* @param repayAmount The amount of the underlying borrowed asset to repay
* @param rTokenCollateral The market in which to seize collateral from the borrower
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function liquidateBorrow(address borrower, uint256 repayAmount, RTokenInterface rTokenCollateral)
external
override
returns (uint256)
{
(uint256 err,) = liquidateBorrowInternal(borrower, repayAmount, rTokenCollateral);
return err;
}
/**
* @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)
* @param token The address of the ERC-20 token to sweep
*/
function sweepToken(EIP20NonStandardInterface token) external override {
require(msg.sender == admin, "RErc20::sweepToken: only admin can sweep tokens");
require(address(token) != underlying, "RErc20::sweepToken: can not sweep underlying token");
uint256 balance = token.balanceOf(address(this));
token.transfer(admin, balance);
}
/**
* @notice The sender adds to reserves.
* @param addAmount The amount fo underlying token to add as reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _addReserves(uint256 addAmount) external override returns (uint256) {
return _addReservesInternal(addAmount);
}
/**
* Safe Token **
*/
/**
* @notice Gets balance of this contract in terms of the underlying
* @dev This excludes the value of the current message, if any
* @return The quantity of underlying tokens owned by this contract
*/
function getCashPrior() internal view virtual override returns (uint256) {
EIP20Interface token = EIP20Interface(underlying);
return token.balanceOf(address(this));
}
/// @notice Indicator that this is a ETH Derivative
function isEthDerivative() public view virtual override returns (bool) {
return false;
}
/**
* @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case.
* This will revert due to insufficient balance or insufficient allowance.
* This function returns the actual amount received,
* which may be less than `amount` if there is a fee attached to the transfer.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) {
// Read from storage once
address underlying_ = underlying;
EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying_);
uint256 balanceBefore = EIP20Interface(underlying_).balanceOf(address(this));
token.transferFrom(from, address(this), amount);
bool success;
assembly {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a compliant ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of external call
}
default {
// This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
require(success, "TOKEN_TRANSFER_IN_FAILED");
// Calculate the amount that was *actually* transferred
uint256 balanceAfter = EIP20Interface(underlying_).balanceOf(address(this));
require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW");
return balanceAfter - balanceBefore; // underflow already checked above, just subtract
}
/**
* @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory
* error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
* insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
* it is >= amount, this should not revert in normal conditions.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferOut(address payable to, uint256 amount) internal virtual override {
EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
token.transfer(to, amount);
bool success;
assembly {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a compliant ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of override external call
}
default {
// This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
require(success, "TOKEN_TRANSFER_OUT_FAILED");
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
import "./ComptrollerInterface.sol";
import "./RTokenInterfaces.sol";
import "./ErrorReporter.sol";
import "./Exponential.sol";
import "./EIP20Interface.sol";
import "./irm/InterestRateModel.sol";
/**
* @title RToken Contract
* @notice Abstract base for RTokens
*/
abstract contract RToken is RTokenInterface, Exponential, TokenErrorReporter {
/**
* @notice Initialize the money market
* @param comptroller_ The address of the Comptroller
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ EIP-20 name of this token
* @param symbol_ EIP-20 symbol of this token
* @param decimals_ EIP-20 decimal precision of this token
*/
function initialize(
ComptrollerInterface comptroller_,
InterestRateModel interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_
) public {
require(msg.sender == admin, "only admin may initialize the market");
require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");
// Set initial exchange rate
initialExchangeRateMantissa = initialExchangeRateMantissa_;
require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");
// Set the comptroller
uint256 err = _setComptroller(comptroller_);
require(err == uint256(Error.NO_ERROR), "setting comptroller failed");
// Initialize block timestamp and borrow index (block timestamp mocks depend on comptroller being set)
accrualBlockNumber = getBlockNumber();
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 `tokens` tokens from `src` to `dst` by `spender`
* @dev Called by both `transfer` and `transferFrom` internally
* @param spender The address of the account performing the transfer
* @param src The address of the source account
* @param dst The address of the destination account
* @param tokens The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transferTokens(address spender, address src, address dst, uint256 tokens) internal returns (uint256) {
/* Fail if transfer not allowed */
uint256 allowed = comptroller.transferAllowed(address(this), src, dst, tokens);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed);
}
/* Do not allow self-transfers */
if (src == dst) {
return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED);
}
/* Get the allowance, infinite for the account owner */
uint256 startingAllowance = 0;
if (spender == src) {
startingAllowance = type(uint256).max;
} else {
startingAllowance = transferAllowances[src][spender];
}
/* Do the calculations, checking for {under,over}flow */
MathError mathErr;
uint256 allowanceNew;
uint256 srcTokensNew;
uint256 dstTokensNew;
(mathErr, allowanceNew) = subUInt(startingAllowance, tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ALLOWED);
}
(mathErr, srcTokensNew) = subUInt(accountTokens[src], tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ENOUGH);
}
(mathErr, dstTokensNew) = addUInt(accountTokens[dst], tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_TOO_MUCH);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
accountTokens[src] = srcTokensNew;
accountTokens[dst] = dstTokensNew;
/* Eat some of the allowance (if necessary) */
if (startingAllowance != type(uint256).max) {
transferAllowances[src][spender] = allowanceNew;
}
/* We emit a Transfer event */
emit Transfer(src, dst, tokens);
// unused function
// comptroller.transferVerify(address(this), src, dst, tokens);
return uint256(Error.NO_ERROR);
}
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transfer(address dst, uint256 amount) external override 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 override 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 (uint.max means infinite)
* @return Whether or not the approval succeeded
*/
function approve(address spender, uint256 amount) external override returns (bool) {
address src = msg.sender;
transferAllowances[src][spender] = amount;
emit Approval(src, spender, amount);
return true;
}
/// @notice Indicator that this is a ETH Derivative
function isEthDerivative() public view virtual override returns (bool) {
return false;
}
/**
* @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 (uint.max means infinite)
*/
function allowance(address owner, address spender) external view override returns (uint256) {
return transferAllowances[owner][spender];
}
/**
* @notice Get the token balance of the `owner`
* @param owner The address of the account to query
* @return The number of tokens owned by `owner`
*/
function balanceOf(address owner) external view override returns (uint256) {
return accountTokens[owner];
}
/**
* @notice Get the underlying balance of the `owner`
* @dev This also accrues interest in a transaction
* @param owner The address of the account to query
* @return The amount of underlying owned by `owner`
*/
function balanceOfUnderlying(address owner) external override returns (uint256) {
Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
(MathError mErr, uint256 balance) = mulScalarTruncate(exchangeRate, accountTokens[owner]);
require(mErr == MathError.NO_ERROR, "balance could not be calculated");
return balance;
}
/**
* @notice Get a snapshot of the account's balances, and the cached exchange rate
* @dev This is used by comptroller to more efficiently perform liquidity checks.
* @param account Address of the account to snapshot
* @return (possible error, token balance, borrow balance, exchange rate mantissa)
*/
function getAccountSnapshot(address account) external view override returns (uint256, uint256, uint256, uint256) {
uint256 rTokenBalance = accountTokens[account];
uint256 borrowBalance;
uint256 exchangeRateMantissa;
MathError mErr;
(mErr, borrowBalance) = borrowBalanceStoredInternal(account);
if (mErr != MathError.NO_ERROR) {
return (uint256(Error.MATH_ERROR), 0, 0, 0);
}
(mErr, exchangeRateMantissa) = exchangeRateStoredInternal();
if (mErr != MathError.NO_ERROR) {
return (uint256(Error.MATH_ERROR), 0, 0, 0);
}
return (uint256(Error.NO_ERROR), rTokenBalance, borrowBalance, exchangeRateMantissa);
}
/**
* @dev Function to simply retrieve block timestamp
* This exists mainly for inheriting test contracts to stub this result.
*/
function getBlockNumber() internal view virtual returns (uint256) {
return block.number;
}
/**
* @notice Returns the current per-timestamp borrow interest rate for this rToken
* @return The borrow interest rate per timestamp, scaled by 1e18
*/
function borrowRatePerBlock() external view override returns (uint256) {
return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves);
}
/**
* @notice Returns the current per-timestamp supply interest rate for this rToken
* @return The supply interest rate per timestamp, scaled by 1e18
*/
function supplyRatePerBlock() external view override returns (uint256) {
return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa);
}
/**
* @notice Returns the current total borrows plus accrued interest
* @return The total borrows with interest
*/
function totalBorrowsCurrent() external override nonReentrant returns (uint256) {
require(accrueInterest() == 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 override 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 override returns (uint256) {
(MathError err, uint256 result) = borrowBalanceStoredInternal(account);
require(err == MathError.NO_ERROR, "borrowBalanceStored: borrowBalanceStoredInternal failed");
return result;
}
/**
* @notice Return the borrow balance of account based on stored data
* @param account The address whose balance should be calculated
* @return (error code, the calculated balance or 0 if error code is non-zero)
*/
function borrowBalanceStoredInternal(address account) internal view returns (MathError, uint256) {
/* Note: we do not assert that the market is up to date */
MathError mathErr;
uint256 principalTimesIndex;
uint256 result;
/* 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 (MathError.NO_ERROR, 0);
}
/* Calculate new borrow balance using the interest index:
* recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
*/
(mathErr, principalTimesIndex) = mulUInt(borrowSnapshot.principal, borrowIndex);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
(mathErr, result) = divUInt(principalTimesIndex, borrowSnapshot.interestIndex);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
return (MathError.NO_ERROR, result);
}
/**
* @notice Accrue interest then return the up-to-date exchange rate
* @return Calculated exchange rate scaled by 1e18
*/
function exchangeRateCurrent() public override nonReentrant returns (uint256) {
require(accrueInterest() == uint256(Error.NO_ERROR), "accrue interest failed");
return exchangeRateStored();
}
/**
* @notice Calculates the exchange rate from the underlying to the RToken
* @dev This function does not accrue interest before calculating the exchange rate
* @return Calculated exchange rate scaled by 1e18
*/
function exchangeRateStored() public view override returns (uint256) {
(MathError err, uint256 result) = exchangeRateStoredInternal();
require(err == MathError.NO_ERROR, "exchangeRateStored: exchangeRateStoredInternal failed");
return result;
}
/**
* @notice Calculates the exchange rate from the underlying to the RToken
* @dev This function does not accrue interest before calculating the exchange rate
* @return (error code, calculated exchange rate scaled by 1e18)
*/
function exchangeRateStoredInternal() internal view virtual returns (MathError, uint256) {
uint256 _totalSupply = totalSupply;
if (_totalSupply == 0) {
/*
* If there are no tokens minted:
* exchangeRate = initialExchangeRate
*/
return (MathError.NO_ERROR, initialExchangeRateMantissa);
} else {
/*
* Otherwise:
* exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
*/
uint256 totalCash = getCashPrior();
uint256 cashPlusBorrowsMinusReserves;
Exp memory exchangeRate;
MathError mathErr;
(mathErr, cashPlusBorrowsMinusReserves) = addThenSubUInt(totalCash, totalBorrows, totalReserves);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
(mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, _totalSupply);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
return (MathError.NO_ERROR, exchangeRate.mantissa);
}
}
/**
* @notice Get cash balance of this rToken in the underlying asset
* @return The quantity of underlying asset owned by this contract
*/
function getCash() external view override returns (uint256) {
return getCashPrior();
}
/**
* @notice Applies accrued interest to total borrows and reserves
* @dev This calculates interest accrued from the last checkpointed block
* up to the current block and writes new checkpoint to storage.
*/
function accrueInterest() public virtual override returns (uint256) {
/* Remember the initial block timestamp */
uint256 currentBlockNumber = getBlockNumber();
uint256 accrualBlockNumberPrior = accrualBlockNumber;
/* Short-circuit accumulating 0 interest */
if (accrualBlockNumberPrior == currentBlockNumber) {
return uint256(Error.NO_ERROR);
}
/* Read the previous values out of storage */
uint256 cashPrior = getCashPrior();
uint256 borrowsPrior = totalBorrows;
uint256 reservesPrior = totalReserves;
uint256 borrowIndexPrior = borrowIndex;
/* Calculate the current borrow interest rate */
uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior);
require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
/* Calculate the number of blocks elapsed since the last accrual */
(MathError mathErr, uint256 blockDelta) = subUInt(currentBlockNumber, accrualBlockNumberPrior);
require(mathErr == MathError.NO_ERROR, "could not calculate block delta");
/*
* Calculate the interest accumulated into borrows and reserves and the new index:
* simpleInterestFactor = borrowRate * blockDelta
* interestAccumulated = simpleInterestFactor * totalBorrows
* totalBorrowsNew = interestAccumulated + totalBorrows
* totalReservesNew = interestAccumulated * reserveFactor + totalReserves
* borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
*/
Exp memory simpleInterestFactor;
uint256 interestAccumulated;
uint256 totalBorrowsNew;
uint256 totalReservesNew;
uint256 borrowIndexNew;
(mathErr, simpleInterestFactor) = mulScalar(Exp({mantissa: borrowRateMantissa}), blockDelta);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR,
FailureInfo.ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED,
uint256(mathErr)
);
}
(mathErr, interestAccumulated) = mulScalarTruncate(simpleInterestFactor, borrowsPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, uint256(mathErr)
);
}
(mathErr, totalBorrowsNew) = addUInt(interestAccumulated, borrowsPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, uint256(mathErr)
);
}
(mathErr, totalReservesNew) =
mulScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, uint256(mathErr)
);
}
(mathErr, borrowIndexNew) = mulScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, uint256(mathErr)
);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
accrualBlockNumber = currentBlockNumber;
borrowIndex = borrowIndexNew;
totalBorrows = totalBorrowsNew;
totalReserves = totalReservesNew;
/* We emit an AccrueInterest event */
emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);
return uint256(Error.NO_ERROR);
}
/**
* @notice Sender supplies assets into the market and receives rTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param mintAmount The amount of the underlying asset to supply
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
*/
function mintInternal(uint256 mintAmount) internal nonReentrant returns (uint256, uint256) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0);
}
// mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
return mintFresh(msg.sender, mintAmount);
}
struct MintLocalVars {
Error err;
MathError mathErr;
uint256 exchangeRateMantissa;
uint256 mintTokens;
uint256 totalSupplyNew;
uint256 accountTokensNew;
uint256 actualMintAmount;
}
/**
* @notice User supplies assets into the market and receives rTokens in exchange
* @dev Assumes interest has already been accrued up to the current block
* @param minter The address of the account which is supplying the assets
* @param mintAmount The amount of the underlying asset to supply
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
*/
function mintFresh(address minter, uint256 mintAmount) internal returns (uint256, uint256) {
/* Fail if mint not allowed */
uint256 allowed = comptroller.mintAllowed(address(this), minter, mintAmount);
if (allowed != 0) {
return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0);
}
/* Verify market's block timestamp equals current block timestamp */
if (accrualBlockNumber != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0);
}
MintLocalVars memory vars;
(vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
if (vars.mathErr != MathError.NO_ERROR) {
return (failOpaque(Error.MATH_ERROR, FailureInfo.MINT_EXCHANGE_RATE_READ_FAILED, uint256(vars.mathErr)), 0);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call `doTransferIn` for the minter and the mintAmount.
* Note: The rToken must handle variations between ERC-20 and GLMR underlying.
* `doTransferIn` reverts if anything goes wrong, since we can't be sure if
* side-effects occurred. The function returns the amount actually transferred,
* in case of a fee. On success, the rToken holds an additional `actualMintAmount`
* of cash.
*/
vars.actualMintAmount = doTransferIn(minter, mintAmount);
/*
* We get the current exchange rate and calculate the number of rTokens to be minted:
* mintTokens = actualMintAmount / exchangeRate
*/
(vars.mathErr, vars.mintTokens) =
divScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa}));
require(vars.mathErr == MathError.NO_ERROR, "MINT_EXCHANGE_CALCULATION_FAILED");
/*
* We calculate the new total supply of rTokens and minter token balance, checking for overflow:
* totalSupplyNew = totalSupply + mintTokens
* accountTokensNew = accountTokens[minter] + mintTokens
*/
(vars.mathErr, vars.totalSupplyNew) = addUInt(totalSupply, vars.mintTokens);
require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED");
(vars.mathErr, vars.accountTokensNew) = addUInt(accountTokens[minter], vars.mintTokens);
require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED");
/* We write previously calculated values into storage */
totalSupply = vars.totalSupplyNew;
accountTokens[minter] = vars.accountTokensNew;
/* We emit a Mint event, and a Transfer event */
emit Mint(minter, vars.actualMintAmount, vars.mintTokens);
emit Transfer(address(this), minter, vars.mintTokens);
/* We call the defense hook */
// unused function
comptroller.enterAllMarkets(minter);
return (uint256(Error.NO_ERROR), vars.actualMintAmount);
}
/**
* @notice Sender redeems rTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of rTokens to redeem into underlying
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeemInternal(uint256 redeemTokens) internal nonReentrant returns (uint256) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
}
// redeemFresh emits redeem-specific logs on errors, so we don't need to
return redeemFresh(payable(msg.sender), redeemTokens, 0);
}
/**
* @notice Sender redeems rTokens 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 rTokens
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeemUnderlyingInternal(uint256 redeemAmount) internal nonReentrant returns (uint256) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
}
// redeemFresh emits redeem-specific logs on errors, so we don't need to
return redeemFresh(payable(msg.sender), 0, redeemAmount);
}
struct RedeemLocalVars {
Error err;
MathError mathErr;
uint256 exchangeRateMantissa;
uint256 redeemTokens;
uint256 redeemAmount;
uint256 totalSupplyNew;
uint256 accountTokensNew;
}
/**
* @notice User redeems rTokens in exchange for the underlying asset
* @dev Assumes interest has already been accrued up to the current block
* @param redeemer The address of the account which is redeeming the tokens
* @param redeemTokensIn The number of rTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)
* @param redeemAmountIn The number of underlying tokens to receive from redeeming rTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeemFresh(address payable redeemer, uint256 redeemTokensIn, uint256 redeemAmountIn)
internal
returns (uint256)
{
require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");
RedeemLocalVars memory vars;
/* exchangeRate = invoke Exchange Rate Stored() */
(vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_RATE_READ_FAILED, uint256(vars.mathErr));
}
/* If redeemTokensIn > 0: */
if (redeemTokensIn > 0) {
/*
* We calculate the exchange rate and the amount of underlying to be redeemed:
* redeemTokens = redeemTokensIn
* redeemAmount = redeemTokensIn x exchangeRateCurrent
*/
if (redeemAmountIn == type(uint256).max) {
vars.redeemTokens = accountTokens[redeemer];
} else {
vars.redeemTokens = redeemTokensIn;
}
(vars.mathErr, vars.redeemAmount) =
mulScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), vars.redeemTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, uint256(vars.mathErr)
);
}
} else {
/*
* We get the current exchange rate and calculate the amount to be redeemed:
* redeemTokens = redeemAmountIn / exchangeRate
* redeemAmount = redeemAmountIn
*/
if (redeemAmountIn == type(uint256).max) {
vars.redeemTokens = accountTokens[redeemer];
(vars.mathErr, vars.redeemAmount) =
mulScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), vars.redeemTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, uint256(vars.mathErr)
);
}
} else {
vars.redeemAmount = redeemAmountIn;
(vars.mathErr, vars.redeemTokens) =
divScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa}));
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, uint256(vars.mathErr)
);
}
}
}
/* Fail if redeem not allowed */
uint256 allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed);
}
/* Verify market's block timestamp equals current block timestamp */
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK);
}
/*
* We calculate the new total supply and redeemer balance, checking for underflow:
* totalSupplyNew = totalSupply - redeemTokens
* accountTokensNew = accountTokens[redeemer] - redeemTokens
*/
(vars.mathErr, vars.totalSupplyNew) = subUInt(totalSupply, vars.redeemTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, uint256(vars.mathErr)
);
}
(vars.mathErr, vars.accountTokensNew) = subUInt(accountTokens[redeemer], vars.redeemTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr)
);
}
/* Fail gracefully if protocol has insufficient cash */
if (getCashPrior() < vars.redeemAmount) {
return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write previously calculated values into storage */
totalSupply = vars.totalSupplyNew;
accountTokens[redeemer] = vars.accountTokensNew;
/* We emit a Transfer event, and a Redeem event */
emit Transfer(redeemer, address(this), vars.redeemTokens);
emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens);
/* We call the defense hook */
comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens);
/*
* We invoke doTransferOut for the redeemer and the redeemAmount.
* Note: The rToken must handle variations between ERC-20 and GLMR underlying.
* On success, the rToken has redeemAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(redeemer, vars.redeemAmount);
return uint256(Error.NO_ERROR);
}
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function borrowInternal(uint256 borrowAmount) internal nonReentrant returns (uint256) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED);
}
// borrowFresh emits borrow-specific logs on errors, so we don't need to
return borrowFresh(payable(msg.sender), borrowAmount);
}
struct BorrowLocalVars {
MathError mathErr;
uint256 accountBorrows;
uint256 accountBorrowsNew;
uint256 totalBorrowsNew;
}
/**
* @notice Users borrow assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function borrowFresh(address payable borrower, uint256 borrowAmount) internal returns (uint256) {
/* Fail if borrow not allowed */
uint256 allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.BORROW_COMPTROLLER_REJECTION, allowed);
}
/* Verify market's block timestamp equals current block timestamp */
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK);
}
/* Fail gracefully if protocol has insufficient underlying cash */
if (getCashPrior() < borrowAmount) {
return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.BORROW_CASH_NOT_AVAILABLE);
}
BorrowLocalVars memory vars;
/*
* We calculate the new borrower and total borrow balances, failing on overflow:
* accountBorrowsNew = accountBorrows + borrowAmount
* totalBorrowsNew = totalBorrows + borrowAmount
*/
(vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr)
);
}
(vars.mathErr, vars.accountBorrowsNew) = addUInt(vars.accountBorrows, borrowAmount);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR,
FailureInfo.BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
uint256(vars.mathErr)
);
}
(vars.mathErr, vars.totalBorrowsNew) = addUInt(totalBorrows, borrowAmount);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr)
);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* 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 invoke doTransferOut for the borrower and the borrowAmount.
* Note: The rToken must handle variations between ERC-20 and GLMR underlying.
* On success, the rToken borrowAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(borrower, borrowAmount);
/* We call the defense hook */
// unused function
// comptroller.borrowVerify(address(this), borrower, borrowAmount);
return uint256(Error.NO_ERROR);
}
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function repayBorrowInternal(uint256 repayAmount) internal nonReentrant returns (uint256, uint256) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0);
}
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
return repayBorrowFresh(msg.sender, msg.sender, repayAmount);
}
/**
* @notice Sender repays a borrow belonging to borrower
* @param borrower the account with the debt being payed off
* @param repayAmount The amount to repay
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function repayBorrowBehalfInternal(address borrower, uint256 repayAmount)
internal
nonReentrant
returns (uint256, uint256)
{
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0);
}
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
return repayBorrowFresh(msg.sender, borrower, repayAmount);
}
struct RepayBorrowLocalVars {
Error err;
MathError mathErr;
uint256 repayAmount;
uint256 borrowerIndex;
uint256 accountBorrows;
uint256 accountBorrowsNew;
uint256 totalBorrowsNew;
uint256 actualRepayAmount;
}
/**
* @notice Borrows are repaid by another user (possibly the borrower).
* @param payer the account paying off the borrow
* @param borrower the account with the debt being payed off
* @param repayAmount the amount of underlying tokens being returned
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function repayBorrowFresh(address payer, address borrower, uint256 repayAmount)
internal
returns (uint256, uint256)
{
/* Fail if repayBorrow not allowed */
uint256 allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount);
if (allowed != 0) {
return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), 0);
}
/* Verify market's block timestamp equals current block timestamp */
if (accrualBlockNumber != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.REPAY_BORROW_FRESHNESS_CHECK), 0);
}
RepayBorrowLocalVars memory vars;
/* We remember the original borrowerIndex for verification purposes */
vars.borrowerIndex = accountBorrows[borrower].interestIndex;
/* We fetch the amount the borrower owes, with accumulated interest */
(vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
if (vars.mathErr != MathError.NO_ERROR) {
return (
failOpaque(
Error.MATH_ERROR,
FailureInfo.REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
uint256(vars.mathErr)
),
0
);
}
/* If repayAmount == uint.max, repayAmount = accountBorrows */
if (repayAmount == type(uint256).max) {
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 rToken must handle variations between ERC-20 and GLMR underlying.
* On success, the rToken holds an additional repayAmount of cash.
* doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
* it returns the amount actually transferred, in case of a fee.
*/
vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount);
/*
* We calculate the new borrower and total borrow balances, failing on underflow:
* accountBorrowsNew = accountBorrows - actualRepayAmount
* totalBorrowsNew = totalBorrows - actualRepayAmount
*/
(vars.mathErr, vars.accountBorrowsNew) = subUInt(vars.accountBorrows, vars.actualRepayAmount);
require(vars.mathErr == MathError.NO_ERROR, "REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED");
(vars.mathErr, vars.totalBorrowsNew) = subUInt(totalBorrows, vars.actualRepayAmount);
require(vars.mathErr == MathError.NO_ERROR, "REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED");
/* 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
// comptroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex);
return (uint256(Error.NO_ERROR), vars.actualRepayAmount);
}
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this rToken to be liquidated
* @param rTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function liquidateBorrowInternal(address borrower, uint256 repayAmount, RTokenInterface rTokenCollateral)
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 = rTokenCollateral.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, rTokenCollateral);
}
/**
* @notice The liquidator liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this rToken to be liquidated
* @param liquidator The address repaying the borrow and seizing collateral
* @param rTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function liquidateBorrowFresh(
address liquidator,
address borrower,
uint256 repayAmount,
RTokenInterface rTokenCollateral
) internal returns (uint256, uint256) {
/* Fail if liquidate not allowed */
uint256 allowed = comptroller.liquidateBorrowAllowed(
address(this), address(rTokenCollateral), liquidator, borrower, repayAmount
);
if (allowed != 0) {
return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_COMPTROLLER_REJECTION, allowed), 0);
}
/* Verify market's block timestamp equals current block timestamp */
if (accrualBlockNumber != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_FRESHNESS_CHECK), 0);
}
/* Verify rTokenCollateral market's block timestamp equals current block timestamp */
if (rTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_COLLATERAL_FRESHNESS_CHECK), 0);
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
return (fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_LIQUIDATOR_IS_BORROWER), 0);
}
/* Fail if repayAmount = 0 */
if (repayAmount == 0) {
return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_ZERO), 0);
}
/* Fail if repayAmount = uint.max */
if (repayAmount == type(uint256).max) {
return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0);
}
/* Fail if repayBorrow fails */
(uint256 repayBorrowError, uint256 actualRepayAmount) = repayBorrowFresh(liquidator, borrower, repayAmount);
if (repayBorrowError != uint256(Error.NO_ERROR)) {
return (fail(Error(repayBorrowError), FailureInfo.LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We calculate the number of collateral tokens that will be seized */
(uint256 amountSeizeError, uint256 seizeTokens) =
comptroller.liquidateCalculateSeizeTokens(address(this), address(rTokenCollateral), actualRepayAmount);
require(amountSeizeError == uint256(Error.NO_ERROR), "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");
/* Revert if borrower collateral token balance < seizeTokens */
require(rTokenCollateral.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(rTokenCollateral) == address(this)) {
seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens);
} else {
seizeError = rTokenCollateral.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(rTokenCollateral), seizeTokens);
/* We call the defense hook */
// unused function
// comptroller.liquidateBorrowVerify(address(this), address(rTokenCollateral), 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 rToken during the process of liquidation.
* Its absolutely critical to use msg.sender as the borrowed rToken and not a parameter.
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of rTokens to seize
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function seize(address liquidator, address borrower, uint256 seizeTokens)
external
override
nonReentrant
returns (uint256)
{
return seizeInternal(msg.sender, liquidator, borrower, seizeTokens);
}
struct SeizeInternalLocalVars {
MathError mathErr;
uint256 borrowerTokensNew;
uint256 liquidatorTokensNew;
uint256 liquidatorSeizeTokens;
uint256 protocolSeizeTokens;
uint256 protocolSeizeAmount;
uint256 exchangeRateMantissa;
uint256 totalReservesNew;
uint256 totalSupplyNew;
}
/**
* @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 RToken.
* Its absolutely critical to use msg.sender as the seizer rToken and not a parameter.
* @param seizerToken The contract seizing the collateral (i.e. borrowed rToken)
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of rTokens to seize
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function seizeInternal(address seizerToken, address liquidator, address borrower, uint256 seizeTokens)
internal
returns (uint256)
{
/* Fail if seize not allowed */
uint256 allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed);
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER);
}
SeizeInternalLocalVars memory vars;
/*
* We calculate the new borrower and liquidator token balances, failing on underflow/overflow:
* borrowerTokensNew = accountTokens[borrower] - seizeTokens
* liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
*/
(vars.mathErr, vars.borrowerTokensNew) = subUInt(accountTokens[borrower], seizeTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED, uint256(vars.mathErr)
);
}
vars.protocolSeizeTokens = mul_(seizeTokens, Exp({mantissa: protocolSeizeShareMantissa}));
vars.liquidatorSeizeTokens = sub_(seizeTokens, vars.protocolSeizeTokens);
(vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
require(vars.mathErr == MathError.NO_ERROR, "exchange rate math error");
vars.protocolSeizeAmount =
mul_ScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), vars.protocolSeizeTokens);
vars.totalReservesNew = add_(totalReserves, vars.protocolSeizeAmount);
vars.totalSupplyNew = sub_(totalSupply, vars.protocolSeizeTokens);
(vars.mathErr, vars.liquidatorTokensNew) = addUInt(accountTokens[liquidator], vars.liquidatorSeizeTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(
Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, uint256(vars.mathErr)
);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
totalReserves = vars.totalReservesNew;
totalSupply = vars.totalSupplyNew;
accountTokens[borrower] = vars.borrowerTokensNew;
accountTokens[liquidator] = vars.liquidatorTokensNew;
/* Emit a Transfer event */
emit Transfer(borrower, liquidator, vars.liquidatorSeizeTokens);
emit Transfer(borrower, address(this), vars.protocolSeizeTokens);
emit ReservesAdded(address(this), vars.protocolSeizeAmount, vars.totalReservesNew);
/* We call the defense hook */
// unused function
// comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens);
return uint256(Error.NO_ERROR);
}
/**
* Admin Functions **
*/
/**
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @param newPendingAdmin New pending admin.
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setPendingAdmin(address payable newPendingAdmin) external virtual override 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 override 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 = payable(address(0));
emit NewAdmin(oldAdmin, admin);
emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
return uint256(Error.NO_ERROR);
}
/**
* @notice Sets a new comptroller for the market
* @dev Admin function to set a new comptroller
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setComptroller(ComptrollerInterface newComptroller) public override returns (uint256) {
// Check caller is admin
if (msg.sender != admin) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK);
}
ComptrollerInterface oldComptroller = comptroller;
// Ensure invoke comptroller.isComptroller() returns true
require(newComptroller.isComptroller(), "marker method returned false");
// Set market's comptroller to newComptroller
comptroller = newComptroller;
// Emit NewComptroller(oldComptroller, newComptroller)
emit NewComptroller(oldComptroller, newComptroller);
return uint256(Error.NO_ERROR);
}
/**
* @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
* @dev Admin function to accrue interest and set a new reserve factor
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setReserveFactor(uint256 newReserveFactorMantissa) external override 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 (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK);
}
// Check newReserveFactor ≤ maxReserveFactor
if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK);
}
uint256 oldReserveFactorMantissa = reserveFactorMantissa;
reserveFactorMantissa = newReserveFactorMantissa;
emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);
return uint256(Error.NO_ERROR);
}
/**
* @notice Accrues interest and reduces reserves by transferring from msg.sender
* @param addAmount Amount of addition to reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _addReservesInternal(uint256 addAmount) internal nonReentrant returns (uint256) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED);
}
// _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
(error,) = _addReservesFresh(addAmount);
return error;
}
/**
* @notice Add reserves by transferring from caller
* @dev Requires fresh interest accrual
* @param addAmount Amount of addition to reserves
* @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees
*/
function _addReservesFresh(uint256 addAmount) internal returns (uint256, uint256) {
// totalReserves + actualAddAmount
uint256 totalReservesNew;
uint256 actualAddAmount;
// We fail gracefully unless market's block timestamp equals current block timestamp
if (accrualBlockNumber != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.ADD_RESERVES_FRESH_CHECK), actualAddAmount);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the caller and the addAmount
* Note: The rToken must handle variations between ERC-20 and GLMR underlying.
* On success, the rToken holds an additional addAmount of cash.
* doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
* it returns the amount actually transferred, in case of a fee.
*/
actualAddAmount = doTransferIn(msg.sender, addAmount);
totalReservesNew = totalReserves + actualAddAmount;
/* Revert on overflow */
require(totalReservesNew >= totalReserves, "add reserves unexpected overflow");
// 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 override 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 (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK);
}
// Fail gracefully if protocol has insufficient underlying cash
if (getCashPrior() < reduceAmount) {
return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_AVAILABLE);
}
// Check reduceAmount ≤ reserves[n] (totalReserves)
if (reduceAmount > totalReserves) {
return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
totalReservesNew = totalReserves - reduceAmount;
// We checked reduceAmount <= totalReserves above, so this should never revert.
require(totalReservesNew <= totalReserves, "reduce reserves unexpected underflow");
// Store reserves[n+1] = reserves[n] - reduceAmount
totalReserves = totalReservesNew;
// doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
doTransferOut(admin, reduceAmount);
emit ReservesReduced(admin, reduceAmount, totalReservesNew);
return 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 override 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 (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK);
}
// Track the market's current interest rate model
oldInterestRateModel = interestRateModel;
// Ensure invoke newInterestRateModel.isInterestRateModel() returns true
require(newInterestRateModel.isInterestRateModel(), "marker method returned false");
// Set the interest rate model to newInterestRateModel
interestRateModel = newInterestRateModel;
// Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)
emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);
return uint256(Error.NO_ERROR);
}
/**
* @notice accrues interest and updates the protocol seize share using _setProtocolSeizeShareFresh
* @dev Admin function to accrue interest and update the protocol seize share
* @param newProtocolSeizeShareMantissa the new protocol seize share to use
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa)
external
override
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 change of protocol seize share failed
return fail(Error(error), FailureInfo.SET_PROTOCOL_SEIZE_SHARE_ACCRUE_INTEREST_FAILED);
}
// _setProtocolSeizeShareFresh emits protocol-seize-share-update-specific logs on errors, so we don't need to.
return _setProtocolSeizeShareFresh(newProtocolSeizeShareMantissa);
}
/**
* @notice updates the protocol seize share (*requires fresh interest accrual)
* @dev Admin function to update the protocol seize share
* @param newProtocolSeizeShareMantissa the new protocol seize share to use
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setProtocolSeizeShareFresh(uint256 newProtocolSeizeShareMantissa) internal returns (uint256) {
// Used to store old share for use in the event that is emitted on success
uint256 oldProtocolSeizeShareMantissa;
// Check caller is admin
if (msg.sender != admin) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_PROTOCOL_SEIZE_SHARE_OWNER_CHECK);
}
// We fail gracefully unless market's block timestamp equals current block timestamp
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_PROTOCOL_SEIZE_SHARE_FRESH_CHECK);
}
// Track the market's current protocol seize share
oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa;
// Set the protocol seize share to newProtocolSeizeShareMantissa
protocolSeizeShareMantissa = newProtocolSeizeShareMantissa;
// Emit NewProtocolSeizeShareMantissa(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa)
emit NewProtocolSeizeShare(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa);
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 virtual returns (uint256);
/**
* @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
* This may revert due to insufficient balance or insufficient allowance.
*/
function doTransferIn(address from, uint256 amount) internal virtual returns (uint256);
/**
* @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting.
* If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
* If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
*/
function doTransferOut(address payable to, uint256 amount) internal virtual;
/**
* Reentrancy Guard **
*/
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
*/
modifier nonReentrant() {
require(_notEntered, "re-entered");
_notEntered = false;
_;
_notEntered = true; // get a gas-refund post-Istanbul
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
abstract contract ComptrollerInterface {
/// @notice Indicator that this is a Comptroller contract (for inspection)
bool public constant isComptroller = true;
/**
* Assets You Are In **
*/
function enterMarkets(address[] calldata rTokens) external virtual returns (uint256[] memory);
function exitMarket(address rToken) external virtual returns (uint256);
/**
* Policy Hooks **
*/
function mintAllowed(address rToken, address minter, uint256 mintAmount) external virtual returns (uint256);
function redeemAllowed(address rToken, address redeemer, uint256 redeemTokens) external virtual returns (uint256);
// Do not remove, still used by RToken
function redeemVerify(address rToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens)
external
pure
virtual;
function borrowAllowed(address rToken, address borrower, uint256 borrowAmount) external virtual returns (uint256);
function enterAllMarkets(address account) external virtual returns (uint256[] memory);
function repayBorrowAllowed(address rToken, address payer, address borrower, uint256 repayAmount)
external
virtual
returns (uint256);
function liquidateBorrowAllowed(
address rTokenBorrowed,
address rTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount
) external view virtual returns (uint256);
function seizeAllowed(
address rTokenCollateral,
address rTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) external virtual returns (uint256);
function transferAllowed(address rToken, address src, address dst, uint256 transferTokens)
external
virtual
returns (uint256);
/**
* Liquidity/Liquidation Calculations **
*/
function liquidateCalculateSeizeTokens(address rTokenBorrowed, address rTokenCollateral, uint256 repayAmount)
external
view
virtual
returns (uint256, uint256);
}
// The hooks that were patched out of the comptroller to make room for the supply caps, if we need them
abstract contract ComptrollerInterfaceWithAllVerificationHooks is ComptrollerInterface {
function mintVerify(address rToken, address minter, uint256 mintAmount, uint256 mintTokens) external virtual;
// Included in ComptrollerInterface already
// function redeemVerify(address rToken, address redeemer, uint redeemAmount, uint redeemTokens) virtual external;
function borrowVerify(address rToken, address borrower, uint256 borrowAmount) external virtual;
function repayBorrowVerify(
address rToken,
address payer,
address borrower,
uint256 repayAmount,
uint256 borrowerIndex
) external virtual;
function liquidateBorrowVerify(
address rTokenBorrowed,
address rTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount,
uint256 seizeTokens
) external virtual;
function seizeVerify(
address rTokenCollateral,
address rTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) external virtual;
function transferVerify(address rToken, address src, address dst, uint256 transferTokens) external virtual;
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
import "./ComptrollerInterface.sol";
import "./irm/InterestRateModel.sol";
import "./EIP20NonStandardInterface.sol";
import "./ErrorReporter.sol";
contract RTokenStorage {
/// @dev Guard variable for re-entrancy checks
bool internal _notEntered;
/// @notice EIP-20 token name for this token
string public name;
/// @notice EIP-20 token symbol for this token
string public symbol;
/// @notice EIP-20 token decimals for this token
uint8 public decimals;
/// @notice Maximum borrow rate that can ever be applied (.0005% / block)
uint256 internal constant borrowRateMaxMantissa = 0.0005e16;
// @notice Maximum fraction of interest that can be set aside for reserves
uint256 internal constant reserveFactorMaxMantissa = 1e18;
// @notice Maximum fraction of redemption factor that can be set aside for reserves
uint256 internal constant redemptionReserveFactorMaxMantissa = 0.05 ether;
uint256 internal constant minRedemptionCashRequire = 1 wei;
/// @notice Administrator for this contract
address payable public admin;
/// @notice Pending administrator for this contract
address payable public pendingAdmin;
/// @notice Contract which oversees inter-rToken operations
ComptrollerInterface public comptroller;
/// @notice Model which tells what the current interest rate should be
InterestRateModel public interestRateModel;
// @notice Initial exchange rate used when minting the first RTokens (used when totalSupply = 0)
uint256 internal initialExchangeRateMantissa;
/// @notice Fraction of interest currently set aside for reserves
uint256 public reserveFactorMantissa;
/// @notice Block number that interest was last accrued at
uint256 public accrualBlockNumber;
/// @notice Accumulator of the total earned interest rate since the opening of the market
uint256 public borrowIndex;
/// @notice Total amount of outstanding borrows of the underlying in this market
uint256 public totalBorrows;
/// @notice Total amount of reserves of the underlying held in this market
uint256 public totalReserves;
/// @notice Total number of tokens in circulation
uint256 public totalSupply;
/// @notice Official record of token balances for each account
mapping(address => uint256) internal accountTokens;
/// @notice Approved token transfer amounts on behalf of others
mapping(address => mapping(address => uint256)) internal transferAllowances;
/**
* @notice Container for borrow balance information
* @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
* @member interestIndex Global borrowIndex as of the most recent balance-changing action
*/
struct BorrowSnapshot {
uint256 principal;
uint256 interestIndex;
}
// @notice Mapping of account addresses to outstanding borrow balances
mapping(address => BorrowSnapshot) internal accountBorrows;
/// @notice Share of seized collateral that is added to reserves
uint256 public protocolSeizeShareMantissa;
}
abstract contract RTokenInterface is RTokenStorage {
/// @notice Indicator that this is a RToken contract (for inspection)
bool public constant isRToken = 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 rTokenCollateral, uint256 seizeTokens
);
/**
* Admin Events **
*/
/// @notice Event emitted when pendingAdmin is changed
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
/// @notice Event emitted when pendingAdmin is accepted, which means admin is updated
event NewAdmin(address oldAdmin, address newAdmin);
/// @notice Event emitted when comptroller is changed
event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller);
/// @notice Event emitted when interestRateModel is changed
event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel);
/// @notice Event emitted when the reserve factor is changed
event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);
/// @notice Event emitted when the redemption reserve factor is changed
event NewRedemptionReserveFactor(uint256 oldRedemptionReserveFactor, uint256 newRedemptionReserveFactor);
/// @notice Event emitted when the protocol seize share is changed
event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa);
/// @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);
/**
* User Interface **
*/
function isEthDerivative() external view virtual returns (bool);
function transfer(address dst, uint256 amount) external virtual returns (bool);
function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);
function approve(address spender, uint256 amount) external virtual returns (bool);
function allowance(address owner, address spender) external view virtual returns (uint256);
function balanceOf(address owner) external view virtual returns (uint256);
function balanceOfUnderlying(address owner) external virtual returns (uint256);
function getAccountSnapshot(address account) external view virtual returns (uint256, uint256, uint256, uint256);
function borrowRatePerBlock() external view virtual returns (uint256);
function supplyRatePerBlock() external view virtual returns (uint256);
function totalBorrowsCurrent() external virtual returns (uint256);
function borrowBalanceCurrent(address account) external virtual returns (uint256);
function borrowBalanceStored(address account) external view virtual returns (uint256);
function exchangeRateCurrent() external virtual returns (uint256);
function exchangeRateStored() external view virtual returns (uint256);
function getCash() external view virtual returns (uint256);
function accrueInterest() external virtual returns (uint256);
function seize(address liquidator, address borrower, uint256 seizeTokens) external virtual returns (uint256);
/**
* Admin Functions **
*/
function _setPendingAdmin(address payable newPendingAdmin) external virtual returns (uint256);
function _acceptAdmin() external virtual returns (uint256);
function _setComptroller(ComptrollerInterface newComptroller) external virtual returns (uint256);
function _setReserveFactor(uint256 newReserveFactorMantissa) external virtual returns (uint256);
function _reduceReserves(uint256 reduceAmount) external virtual returns (uint256);
function _setInterestRateModel(InterestRateModel newInterestRateModel) external virtual returns (uint256);
function _setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa) external virtual returns (uint256);
}
contract RErc20Storage {
/// @notice Underlying asset for this RToken
address public underlying;
}
abstract contract RErc20Interface is RErc20Storage {
/**
* User Interface **
*/
function mint(uint256 mintAmount) external virtual returns (uint256);
function mintWithPermit(uint256 mintAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
external
virtual
returns (uint256);
function redeem(uint256 redeemTokens) external virtual returns (uint256);
function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);
function borrow(uint256 borrowAmount) external virtual returns (uint256);
function repayBorrow(uint256 repayAmount) external virtual returns (uint256);
function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);
function liquidateBorrow(address borrower, uint256 repayAmount, RTokenInterface rTokenCollateral)
external
virtual
returns (uint256);
function sweepToken(EIP20NonStandardInterface token) external virtual;
/**
* Admin Functions **
*/
function _addReserves(uint256 addAmount) external virtual returns (uint256);
}
contract RDelegationStorage {
/// @notice Implementation address for this contract
address public implementation;
}
abstract contract RDelegatorInterface is RDelegationStorage {
/// @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)
external
virtual;
}
abstract contract RDelegateInterface is RDelegationStorage {
/**
* @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) external virtual;
/// @notice Called by the delegator on a delegate to forfeit its responsibility
function _resignImplementation() external virtual;
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
contract ComptrollerErrorReporter {
enum Error {
NO_ERROR,
UNAUTHORIZED,
COMPTROLLER_MISMATCH,
INSUFFICIENT_SHORTFALL,
INSUFFICIENT_LIQUIDITY,
INVALID_CLOSE_FACTOR,
INVALID_COLLATERAL_FACTOR,
INVALID_LIQUIDATION_INCENTIVE,
MARKET_NOT_ENTERED, // no longer possible
MARKET_NOT_LISTED,
MARKET_ALREADY_LISTED,
MATH_ERROR,
NONZERO_BORROW_BALANCE,
PRICE_ERROR,
REJECTION,
SNAPSHOT_ERROR,
TOO_MANY_ASSETS,
TOO_MUCH_REPAY
}
enum FailureInfo {
ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
EXIT_MARKET_BALANCE_OWED,
EXIT_MARKET_REJECTION,
SET_CLOSE_FACTOR_OWNER_CHECK,
SET_CLOSE_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_OWNER_CHECK,
SET_COLLATERAL_FACTOR_NO_EXISTS,
SET_COLLATERAL_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
SET_IMPLEMENTATION_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_VALIDATION,
SET_MAX_ASSETS_OWNER_CHECK,
SET_PENDING_ADMIN_OWNER_CHECK,
SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
SET_PRICE_ORACLE_OWNER_CHECK,
SUPPORT_MARKET_EXISTS,
SUPPORT_MARKET_OWNER_CHECK,
SET_PAUSE_GUARDIAN_OWNER_CHECK,
SET_GAS_AMOUNT_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(uint error, uint info, uint detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/
function fail(Error err, FailureInfo info) internal returns (uint) {
emit Failure(uint(err), uint(info), 0);
return uint(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/
function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
emit Failure(uint(err), uint(info), opaqueError);
return uint(err);
}
}
contract TokenErrorReporter {
enum Error {
NO_ERROR,
UNAUTHORIZED,
BAD_INPUT,
COMPTROLLER_REJECTION,
COMPTROLLER_CALCULATION_ERROR,
INTEREST_RATE_MODEL_ERROR,
INVALID_ACCOUNT_PAIR,
INVALID_CLOSE_AMOUNT_REQUESTED,
INVALID_COLLATERAL_FACTOR,
MATH_ERROR,
MARKET_NOT_FRESH,
MARKET_NOT_LISTED,
MARKET_LIQUIDITY_LOW,
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_ACCUMULATED_INTEREST_CALCULATION_FAILED,
ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED,
ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED,
BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
BORROW_ACCRUE_INTEREST_FAILED,
BORROW_CASH_NOT_AVAILABLE,
BORROW_FRESHNESS_CHECK,
BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
BORROW_MARKET_NOT_LISTED,
BORROW_COMPTROLLER_REJECTION,
LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED,
LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED,
LIQUIDATE_COLLATERAL_FRESHNESS_CHECK,
LIQUIDATE_COMPTROLLER_REJECTION,
LIQUIDATE_COMPTROLLER_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_BALANCE_INCREMENT_FAILED,
LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED,
LIQUIDATE_SEIZE_COMPTROLLER_REJECTION,
LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER,
LIQUIDATE_SEIZE_TOO_MUCH,
MINT_ACCRUE_INTEREST_FAILED,
MINT_COMPTROLLER_REJECTION,
MINT_EXCHANGE_CALCULATION_FAILED,
MINT_EXCHANGE_RATE_READ_FAILED,
MINT_FRESHNESS_CHECK,
MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
MINT_TRANSFER_IN_FAILED,
MINT_TRANSFER_IN_NOT_POSSIBLE,
REDEEM_MIN_BALANCE_NOT_MET,
REDEEM_ACCRUE_INTEREST_FAILED,
REDEEM_COMPTROLLER_REJECTION,
REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED,
REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED,
REDEEM_RESERVE_FEE_AMOUNT_CALCULATION_FAILED,
REDEEM_EXCHANGE_RATE_READ_FAILED,
REDEEM_FRESHNESS_CHECK,
REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
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_ACCUMULATED_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_COMPTROLLER_REJECTION,
REPAY_BORROW_FRESHNESS_CHECK,
REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE,
SET_COLLATERAL_FACTOR_OWNER_CHECK,
SET_COLLATERAL_FACTOR_VALIDATION,
SET_COMPTROLLER_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,
SET_REDEMPTION_RESERVE_FACTOR_ADMIN_CHECK,
SET_REDEMPTION_RESERVE_FACTOR_FRESH_CHECK,
SET_REDEMPTION_RESERVE_FACTOR_BOUNDS_CHECK,
TRANSFER_COMPTROLLER_REJECTION,
TRANSFER_NOT_ALLOWED,
TRANSFER_NOT_ENOUGH,
TRANSFER_TOO_MUCH,
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_OWNER_CHECK,
SET_PROTOCOL_SEIZE_SHARE_FRESH_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(uint error, uint info, uint detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/
function fail(Error err, FailureInfo info) internal returns (uint) {
emit Failure(uint(err), uint(info), 0);
return uint(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/
function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
emit Failure(uint(err), uint(info), opaqueError);
return uint(err);
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
import "./CarefulMath.sol";
import "./ExponentialNoError.sol";
/**
* @title Exponential module for storing fixed-precision decimals
* @dev Legacy contract for compatibility reasons with existing contracts that still use MathError
* @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, ExponentialNoError {
/**
* @dev Creates an exponential from numerator and denominator values.
* Note: Returns an error if (`num` * 10e18) > MAX_INT,
* or if `denom` is zero.
*/
function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) {
(MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
(MathError err1, uint 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) pure internal returns (MathError, Exp memory) {
(MathError error, uint 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) pure internal returns (MathError, Exp memory) {
(MathError error, uint 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, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint 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, uint scalar) pure internal returns (MathError, uint) {
(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, uint scalar, uint addend) pure internal returns (MathError, uint) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return addUInt(truncate(product), addend);
}
/**
* @dev Divide an Exp by a scalar, returning a new Exp.
*/
function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar);
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(uint scalar, Exp memory divisor) pure internal 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, uint 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(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) {
(MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(fraction));
}
/**
* @dev Multiplies two exponentials, returning a new exponential.
*/
function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
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, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
(MathError err2, uint 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(uint a, uint b) pure internal returns (MathError, Exp memory) {
return mulExp(Exp({mantissa: a}), Exp({mantissa: b}));
}
/**
* @dev Multiplies three exponentials, returning a new exponential.
*/
function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) {
(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) pure internal returns (MathError, Exp memory) {
return getExp(a.mantissa, b.mantissa);
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
/**
* @title ERC 20 Token Standard Interface
* https://eips.ethereum.org/EIPS/eip-20
*/
interface EIP20Interface {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance The balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return success Whether or not the transfer succeeded
*/
function transfer(address dst, uint256 amount) external returns (bool success);
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return success Whether or not the transfer succeeded
*/
function transferFrom(address src, address dst, uint256 amount) external returns (bool success);
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved (-1 means infinite)
* @return success Whether or not the approval succeeded
*/
function approve(address spender, uint256 amount) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent (-1 means infinite)
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
/**
* @title InterestRateModel Interface
*/
abstract contract InterestRateModel {
/// @notice Indicator that this is an InterestRateModel contract (for inspection)
bool public constant isInterestRateModel = true;
/**
* @notice Calculates the current borrow interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @return The borrow rate per block (as a percentage, and scaled by 1e18)
*/
function getBorrowRate(
uint cash,
uint borrows,
uint reserves
) external view virtual returns (uint);
/**
* @notice Calculates the current supply interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @param reserveFactorMantissa The current reserve factor the market has
* @return The supply rate per block (as a percentage, and scaled by 1e18)
*/
function getSupplyRate(
uint cash,
uint borrows,
uint reserves,
uint reserveFactorMantissa
) external view virtual returns (uint);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
/**
* @title EIP20NonStandardInterface
* @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
* See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
interface EIP20NonStandardInterface {
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance The balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transfer(address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transferFrom(address src, address dst, uint256 amount) external;
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved
* @return success Whether or not the approval succeeded
*/
function approve(address spender, uint256 amount) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
/**
* @title Careful Math
*/
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(uint a, uint b) internal pure returns (MathError, uint) {
if (a == 0) {
return (MathError.NO_ERROR, 0);
}
uint 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(uint a, uint b) internal pure returns (MathError, uint) {
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(uint a, uint b) internal pure returns (MathError, uint) {
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(uint a, uint b) internal pure returns (MathError, uint) {
uint 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(uint a, uint b, uint c) internal pure returns (MathError, uint) {
(MathError err0, uint sum) = addUInt(a, b);
if (err0 != MathError.NO_ERROR) {
return (err0, 0);
}
return subUInt(sum, c);
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;
/**
* @title Exponential module for storing fixed-precision decimals
* @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
* Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
* `Exp({mantissa: 5100000000000000000})`.
*/
contract ExponentialNoError {
uint constant expScale = 1e18;
uint constant doubleScale = 1e36;
uint constant halfExpScale = expScale/2;
uint constant mantissaOne = expScale;
struct Exp {
uint mantissa;
}
struct Double {
uint mantissa;
}
/**
* @dev Truncates the given exp to a whole number value.
* For example, truncate(Exp{mantissa: 15 * expScale}) = 15
*/
function truncate(Exp memory exp) pure internal returns (uint) {
// Note: We are not using careful math here as we're performing a division that cannot fail
return exp.mantissa / expScale;
}
/**
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
*/
function mul_ScalarTruncate(Exp memory a, uint scalar) pure internal returns (uint) {
Exp memory product = mul_(a, scalar);
return truncate(product);
}
/**
* @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
*/
function mul_ScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (uint) {
Exp memory product = mul_(a, scalar);
return add_(truncate(product), addend);
}
/**
* @dev Checks if first Exp is less than second Exp.
*/
function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa < right.mantissa;
}
/**
* @dev Checks if left Exp <= right Exp.
*/
function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa <= right.mantissa;
}
/**
* @dev Checks if left Exp > right Exp.
*/
function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa > right.mantissa;
}
/**
* @dev returns true if Exp is exactly zero
*/
function isZeroExp(Exp memory value) pure internal returns (bool) {
return value.mantissa == 0;
}
function safe224(uint n, string memory errorMessage) pure internal returns (uint224) {
require(n < 2**224, errorMessage);
return uint224(n);
}
function safe32(uint n, string memory errorMessage) pure internal returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(uint a, uint b) pure internal returns (uint) {
return add_(a, b, "addition overflow");
}
function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
uint c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(uint a, uint b) pure internal returns (uint) {
return sub_(a, b, "subtraction underflow");
}
function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b <= a, errorMessage);
return a - b;
}
function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
}
function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Exp memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / expScale;
}
function mul_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
}
function mul_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Double memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / doubleScale;
}
function mul_(uint a, uint b) pure internal returns (uint) {
return mul_(a, b, "multiplication overflow");
}
function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
if (a == 0 || b == 0) {
return 0;
}
uint c = a * b;
require(c / a == b, errorMessage);
return c;
}
function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
}
function div_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Exp memory b) pure internal returns (uint) {
return div_(mul_(a, expScale), b.mantissa);
}
function div_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
}
function div_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Double memory b) pure internal returns (uint) {
return div_(mul_(a, doubleScale), b.mantissa);
}
function div_(uint a, uint b) pure internal returns (uint) {
return div_(a, b, "divide by zero");
}
function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b > 0, errorMessage);
return a / b;
}
function fraction(uint a, uint b) pure internal returns (Double memory) {
return Double({mantissa: div_(mul_(a, doubleScale), b)});
}
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"rTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ComptrollerInterface","name":"oldComptroller","type":"address"},{"indexed":false,"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldProtocolSeizeShareMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newProtocolSeizeShareMantissa","type":"uint256"}],"name":"NewProtocolSeizeShare","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldRedemptionReserveFactor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRedemptionReserveFactor","type":"uint256"}],"name":"NewRedemptionReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"_becomeImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_resignImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newProtocolSeizeShareMantissa","type":"uint256"}],"name":"_setProtocolSeizeShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isEthDerivative","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract RTokenInterface","name":"rTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"mintWithPermit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolSeizeShareMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract EIP20NonStandardInterface","name":"token","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080806040523461001757615b6090816200001d8239f35b600080fdfe6080604052600436101561001257600080fd5b60003560e01c806306fdde0314610397578063095ea7b3146103925780630e7527021461038d578063153ab50514610388578063173b99041461038357806317bfdfbc1461037e57806318160ddd14610379578063182df0f5146103745780631a31d4651461036f5780631be195601461036a57806323b872dd146103655780632608f81814610360578063267822471461035b578063313ce567146103565780633630a6b5146103515780633af9e6691461034c5780633b1d21a2146103475780633e941010146103425780634576b5db1461033d57806347bd37181461033857806356e67728146103335780635c60da1b1461032e5780635fe3b56714610329578063601a0bf1146103245780636752e7021461031f5780636c540baf1461031a5780636f307dc31461031557806370a082311461031057806373acee981461030b5780638303084614610306578063852a12e314610301578063895dabad146102fc5780638f840ddd146102f757806395d89b41146102f257806395dd9193146102ed57806399d8c1b4146102e8578063a0712d68146102e3578063a6afed95146102de578063a9059cbb146102d9578063aa5af0fd146102d4578063ae9d70b0146102cf578063b2a02ff1146102ca578063b71d1a0c146102c5578063bd6d894d146102c0578063c37f68e2146102bb578063c5ebeaec146102b6578063d0248fb4146102b1578063db006a75146102ac578063dd62ed3e146102a7578063e9c714f2146102a2578063f2b3abbd1461029d578063f3fdb15a14610298578063f5e3c46214610293578063f851a4401461028e578063f8f9da28146102895763fca7820b1461028457600080fd5b611583565b611520565b6114f3565b6114a1565b611478565b611451565b611436565b6113d3565b61139b565b6112f1565b6112b9565b61126f565b611254565b61122d565b6111f5565b61114e565b611130565b6110e8565b6110cd565b611095565b611016565b610ff4565b610f4c565b610f2e565b610f12565b610eda565b610ea2565b610e58565b610e1b565b610df2565b610dd4565b610db6565b610d7e565b610d55565b610d2c565b610c78565b610c5a565b610c33565b610bfb565b610be0565b610b27565b610b0b565b610aea565b610ac1565b610a7b565b610a31565b6108dd565b61084f565b6107a9565b61078b565b610727565b610709565b610686565b61063c565b6105ad565b6104b4565b60009103126103a757565b600080fd5b90600182811c921680156103dc575b60208310146103c657565b634e487b7160e01b600052602260045260246000fd5b91607f16916103bb565b634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161040f57604052565b6103e6565b602081019081106001600160401b0382111761040f57604052565b604081019081106001600160401b0382111761040f57604052565b90601f801991011681019081106001600160401b0382111761040f57604052565b6020808252825181830181905290939260005b8281106104a057505060409293506000838284010152601f8019910116010190565b81810186015184820160400152850161047e565b346103a75760008060031936011261059957604051908060018054906104d9826103ac565b808652926020926001811690811561056c5750600114610514575b610510866105048188038261044a565b6040519182918261046b565b0390f35b9350600184527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8385106105595750505050810160200161050482610510386104f4565b805486860184015293820193810161053c565b90508695506105109693506020925061050494915060ff191682840152151560051b8201019293386104f4565b80fd5b6001600160a01b038116036103a757565b346103a75760403660031901126103a7576004356105ca8161059c565b6024359033600052600f602052816105f88260406000209060018060a01b0316600052602052604060002090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3602060405160018152f35b346103a75760203660031901126103a757602060005461065e60ff82166115bb565b60ff1990811660005560016106746004356125f1565b50916000541617600055604051908152f35b346103a75760003660031901126103a75760035460081c6001600160a01b031633036106ae57005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f72657369676e4960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346103a75760003660031901126103a7576020600854604051908152f35b346103a75760203660031901126103a75760206004356107468161059c565b600161077a6000549261075b60ff85166115bb565b60ff1993841660005561077561076f612084565b15611614565b611a2c565b916000541617600055604051908152f35b346103a75760003660031901126103a7576020600d54604051908152f35b346103a75760003660031901126103a75760206107c4611663565b604051908152f35b604051906107d982610414565b565b9291926001600160401b03821161040f5760405191610804601f8201601f19166020018461044a565b8294818452818301116103a7578281602093846000960137010152565b9080601f830112156103a75781602061083c933591016107db565b90565b6044359060ff821682036103a757565b346103a75760e03660031901126103a75760043561086c8161059c565b602435906108798261059c565b604435906108868261059c565b6001600160401b036084358181116103a7576108a6903690600401610821565b9060a4359081116103a7576108bf903690600401610821565b9160c4359360ff851685036103a7576108db95606435926116fa565b005b346103a75760203660031901126103a7576004356108fa8161059c565b60035460081c6001600160a01b0316906001600160a01b03906109203383851614611767565b6012549116919061094d90610945906001600160a01b03165b6001600160a01b031690565b8314156117cb565b6040516370a0823160e01b8152306004820152602081602481865afa9081156109d1576000916109d6575b50823b156103a75760405163a9059cbb60e01b81526001600160a01b039290921660048301526024820152906000908290604490829084905af180156109d1576109be57005b806109cb6108db926103fc565b8061039c565b6116ee565b6109f8915060203d6020116109fe575b6109f0818361044a565b8101906116df565b38610978565b503d6109e6565b60609060031901126103a757600435610a1d8161059c565b90602435610a2a8161059c565b9060443590565b346103a7576020610a696001610a4636610a05565b9060009492945494610a5a60ff87166115bb565b60ff19958616600055336126bb565b15916000541617600055604051908152f35b346103a75760403660031901126103a7576020600435610a9a8161059c565b600161067460005492610aaf60ff85166115bb565b60ff1993841660005560243590612917565b346103a75760003660031901126103a7576004546040516001600160a01b039091168152602090f35b346103a75760003660031901126103a757602060ff60035416604051908152f35b346103a75760003660031901126103a757602060405160018152f35b346103a75760203660031901126103a757610b7d600435610b478161059c565b610b4f612384565b9060405191610b5d83610414565b82526001600160a01b03166000908152600e602052604090205490612968565b906004811015610bdb57610b9657604051908152602090f35b60405162461bcd60e51b815260206004820152601f60248201527f62616c616e636520636f756c64206e6f742062652063616c63756c61746564006044820152606490fd5b6115f4565b346103a75760003660031901126103a75760206107c461299b565b346103a75760203660031901126103a7576020600054610c1d60ff82166115bb565b60ff19908116600055600161077a6004356129f2565b346103a75760203660031901126103a75760206107c4600435610c558161059c565b611896565b346103a75760003660031901126103a7576020600b54604051908152f35b346103a75760203660031901126103a7576004356001600160401b0381116103a757366023820112156103a757610cb99036906024816004013591016107db565b5060035460081c6001600160a01b03163303610cd157005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f6265636f6d654960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346103a75760003660031901126103a7576013546040516001600160a01b039091168152602090f35b346103a75760003660031901126103a7576005546040516001600160a01b039091168152602090f35b346103a75760203660031901126103a7576020600054610da060ff82166115bb565b60ff19908116600055600161077a600435611996565b346103a75760003660031901126103a7576020601154604051908152f35b346103a75760003660031901126103a7576020600954604051908152f35b346103a75760003660031901126103a7576012546040516001600160a01b039091168152602090f35b346103a75760203660031901126103a757600435610e388161059c565b60018060a01b0316600052600e6020526020604060002054604051908152f35b346103a757600080600319360112610599578054602091610e7b60ff83166115bb565b60ff199182168155610e8e61076f612084565b6001600b5492825416179055604051908152f35b346103a75760203660031901126103a7576020600054610ec460ff82166115bb565b60ff19908116600055600161077a6004356119e1565b346103a75760203660031901126103a7576020600054610efc60ff82166115bb565b60ff19908116600055600161077a6004356131a9565b346103a75760003660031901126103a757602060405160008152f35b346103a75760003660031901126103a7576020600c54604051908152f35b346103a75760008060031936011261059957604051908060025490610f70826103ac565b8085529160209160019182811690811561056c5750600114610f9c57610510866105048188038261044a565b9350600284527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b838510610fe15750505050810160200161050482610510386104f4565b8054868601840152938201938101610fc4565b346103a75760203660031901126103a75760206107c46004356107758161059c565b346103a75760c03660031901126103a7576004356110338161059c565b602435906110408261059c565b6001600160401b036064358181116103a757611060903690600401610821565b906084359081116103a757611079903690600401610821565b9060a4359260ff841684036103a7576108db9460443591611ecc565b346103a75760203660031901126103a75760206000546110b760ff82166115bb565b60ff19908116600055600161067460043561333a565b346103a75760003660031901126103a75760206107c4612084565b346103a75760403660031901126103a75760206004356111078161059c565b6001610a696000549261111c60ff85166115bb565b60ff199384166000556024359033806126bb565b346103a75760003660031901126103a7576020600a54604051908152f35b346103a75760003660031901126103a7576006546001600160a01b0316602061117561299b565b600b54600c54600854604051635c0b440b60e11b815260048101949094526024840192909252604483015260648201529182908180608481015b03915afa80156109d157610510916000916111d6575b506040519081529081906020820190565b6111ef915060203d6020116109fe576109f0818361044a565b386111c5565b346103a757602061077a600161120a36610a05565b906000949294549461121e60ff87166115bb565b60ff199586166000553361390b565b346103a75760203660031901126103a75760206107c460043561124f8161059c565b6122e1565b346103a75760003660031901126103a75760206107c4612384565b346103a75760203660031901126103a7576105106112976004356112928161059c565b6123bd565b6040805194855260208501939093529183015260608201529081906080820190565b346103a75760203660031901126103a75760206000546112db60ff82166115bb565b60ff19908116600055600161077a600435613c55565b346103a75760a03660031901126103a75760043561130d61083f565b6012546001600160a01b03169190823b156103a75760ff60e460009283604051968794859363d505accf60e01b8552336004860152306024860152886044860152602435606486015216608484015260643560a484015260843560c48401525af19081156109d157610510926111c592611388575b50613308565b806109cb611395926103fc565b38611382565b346103a75760203660031901126103a75760206000546113bd60ff82166115bb565b60ff19908116600055600161077a600435613ca1565b346103a75760403660031901126103a757602061142d6004356113f58161059c565b602435906114028261059c565b60018060a01b0316600052600f835260406000209060018060a01b0316600052602052604060002090565b54604051908152f35b346103a75760003660031901126103a75760206107c4612427565b346103a75760203660031901126103a75760206107c46004356114738161059c565b61255b565b346103a75760003660031901126103a7576006546040516001600160a01b039091168152602090f35b346103a75760603660031901126103a75760206004356114c08161059c565b60016106746044356114d18161059c565b600054936114e160ff86166115bb565b60ff1994851660005560243590613cba565b346103a75760003660031901126103a75760035460405160089190911c6001600160a01b03168152602090f35b346103a75760003660031901126103a7576006546111af906020906001600160a01b031661154c61299b565b600b54600c546040516315f2405360e01b815260048101939093526024830191909152604482015292839190829081906064820190565b346103a75760203660031901126103a75760206000546115a560ff82166115bb565b60ff19908116600055600161077a6004356125a6565b156115c257565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b634e487b7160e01b600052602160045260246000fd5b60121115610bdb57565b1561161b57565b60405162461bcd60e51b81526020600482015260166024820152751858d8dc9d59481a5b9d195c995cdd0819985a5b195960521b6044820152606490fd5b60041115610bdb57565b61166b612645565b906004811015610bdb5761167c5790565b60405162461bcd60e51b815260206004820152603560248201527f65786368616e67655261746553746f7265643a2065786368616e67655261746560448201527414dd1bdc9959125b9d195c9b985b0819985a5b1959605a1b6064820152608490fd5b908160209103126103a7575190565b6040513d6000823e3d90fd5b9361170c936004979360209793611ecc565b601280546001600160a01b0319166001600160a01b039290921691821790556040516318160ddd60e01b815292839182905afa80156109d15761174c5750565b6117649060203d6020116109fe576109f0818361044a565b50565b1561176e57565b60405162461bcd60e51b815260206004820152602f60248201527f5245726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b156117d257565b60405162461bcd60e51b815260206004820152603260248201527f5245726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b908160209103126103a7575180151581036103a75790565b1561185157565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b6003546118ae9060081c6001600160a01b0316610939565b330361198d576005546001600160a01b0316604051623f1ee960e11b815291906020836004816001600160a01b0386165afa9283156109d1577f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d9361191b9160009161195e575b5061184a565b600580546001600160a01b0319166001600160a01b0384161790555b604080516001600160a01b039283168152929091166020830152819081015b0390a1600090565b611980915060203d602011611986575b611978818361044a565b810190611832565b38611915565b503d61196e565b5061083c612a41565b61199e612084565b90816119ae5761083c9150613001565b506012811015610bdb57600080516020615aeb83398151915260606040518381526032602082015260006040820152a190565b6119e9612084565b90816119f95761083c91506130ef565b506012811015610bdb57600080516020615aeb83398151915260606040518381526056602082015260006040820152a190565b611a35906131d5565b906004811015610bdb57611a465790565b60405162461bcd60e51b815260206004820152603760248201527f626f72726f7742616c616e636553746f7265643a20626f72726f7742616c616e60448201527f636553746f726564496e7465726e616c206661696c65640000000000000000006064820152608490fd5b15611ab857565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b15611b1057565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b15611b7557565b60405162461bcd60e51b815260206004820152601a60248201527f73657474696e6720636f6d7074726f6c6c6572206661696c65640000000000006044820152606490fd5b15611bc157565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b90601f8211611c1e575050565b60019160009060016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6906020601f850160051c83019410611c7d575b601f0160051c01915b828110611c735750505050565b8181558301611c66565b9092508290611c5d565b601f8111611c93575050565b60009060026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace906020601f850160051c83019410611cef575b601f0160051c01915b828110611ce457505050565b818155600101611cd8565b9092508290611ccf565b9081516001600160401b03811161040f57600190611d2081611d1b84546103ac565b611c11565b602080601f8311600114611d6357508190611d54939495600092611d58575b50508160011b916000199060031b1c19161790565b9055565b015190503880611d3f565b90601f19831695611d9660016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf690565b926000905b888210611dd15750508385969710611db8575b505050811b019055565b015160001960f88460031b161c19169055388080611dae565b808785968294968601518155019501930190611d9b565b9081516001600160401b03811161040f57611e0d81611e086002546103ac565b611c87565b602080601f8311600114611e4457508190611e3f9394600092611d585750508160011b916000199060031b1c19161790565b600255565b90601f19831694611e7760026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace90565b926000905b878210611eb4575050836001959610611e9b575b505050811b01600255565b015160001960f88460031b161c19169055388080611e90565b80600185968294968601518155019501930190611e7c565b60035490959492939060081c6001600160a01b03163303611f8857611f52611f4c611f5793611f2d611f27611f6c9a611f1a611f5c9a6009541580611f7e575b611f1590611ab1565b600755565b610c556007541515611b09565b15611b6e565b611f3643600955565b611f47670de0b6b3a7640000600a55565b613248565b15611bba565b611cf9565b611de8565b60ff1660ff196003541617600355565b6107d9600160ff196000541617600055565b50600a5415611f0c565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b15611fe057565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b1561202c57565b60405162461bcd60e51b815260206004820152601f60248201527f636f756c64206e6f742063616c63756c61746520626c6f636b2064656c7461006044820152606490fd5b6040519061207e82610414565b60008252565b6009544381146122db5761209661299b565b600b5490600c5492600a54906120b661093960065460018060a01b031690565b6040516315f2405360e01b815260048101859052602481018690526044810187905290602090829060649082905afa80156109d15761214292612129926000926122b6575b506121189061211265048c27395000841115611fd9565b4361336c565b929061212381611659565b15612025565b612131612071565b5061213a6107cc565b90815261338e565b919061214d81611659565b8061229e575061215d8483612968565b949061216881611659565b8061228557506121789085613804565b959061218381611659565b8061226c57506121a190856008546121996107cc565b908152613825565b92906121ac81611659565b80612253575090806121bd92613825565b91906121c881611659565b8061223b575093611956916122187f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049661220143600955565b61220a83600a55565b61221384600b55565b600c55565b604051948594859094939260609260808301968352602083015260408201520152565b935050505061083c915061224e81611659565b613494565b94505050505061083c915061226781611659565b613468565b94505050505061083c915061228081611659565b61343c565b94505050505061083c915061229981611659565b613410565b935050505061083c91506122b181611659565b6133e4565b6121189192506122d49060203d6020116109fe576109f0818361044a565b91906120fb565b50600090565b6003546001600160a01b039060081c8116330361235657600480548383166001600160a01b031982161790915560408051929091166001600160a01b03908116835290921660208201527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99181908101611956565b5050600080516020615aeb8339815191526060604051600181526047602082015260006040820152a1600190565b6000549061239460ff83166115bb565b60ff199182166000556123a861076f612084565b60016123b2611663565b926000541617600055565b6001600160a01b0381166000908152600e602052604081205492916123e1906131d5565b92906004811015610bdb5761241a576123f8612645565b929061240381611659565b61240d5793929190565b6009945092508291508190565b9150915060099180918190565b6004546001600160a01b0316338114801590612553575b612526576003547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a991907ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc9061249f9060081c6001600160a01b0316610939565b60038054610100600160a81b031916600885901b610100600160a81b0316179055600480546001600160a01b031916905560035460081c6001600160a01b0316604080516001600160a01b039384168152929091166020830152819081010390a1600454604080516001600160a01b03938416815292909116602083015281908101611956565b50600080516020615aeb8339815191526060604051600181526000602082015260006040820152a1600190565b50331561243e565b612563612084565b90816125735761083c9150613248565b506012811015610bdb57600080516020615aeb83398151915260606040518381526042602082015260006040820152a190565b6125ae612084565b90816125be5761083c9150613d84565b506012811015610bdb57600080516020615aeb83398151915260606040518381526048602082015260006040820152a190565b6125f9612084565b908161260f5761260b91503333613f5e565b9091565b506012811015610bdb57600080516020615aeb83398151915260606040518381526038602082015260006040820152a190600090565b600d54806126565750600754600091565b61267c61266161299b565b600060405161266f81610414565b52600b54600c54916141db565b612687829392611659565b826126b3576126969250614235565b906126a081611659565b806126ac575051600091565b9160009150565b505090600090565b600554909392906126d4906001600160a01b0316610939565b6040516317b9b84b60e31b81523060048201526001600160a01b038481166024830152831660448201526064810185905290602090829060849082906000905af19081156109d1576000916128f8575b50806128ea57506001600160a01b038181169483821694918587146128db57811685036128b157600019935b61275a838661336c565b929061276581611659565b6128a1576001600160a01b0382166000908152600e6020526040902061278e9085905b5461336c565b969061279981611659565b612890576001600160a01b0386166000908152600e602052604090206127c190869054613804565b906127cb81611659565b61287e5761281d61283797600080516020615b0b833981519152996128028760018060a01b0316600052600e602052604060002090565b556001600160a01b03166000908152600e6020526040902090565b551961283f575b5050604051918252509081906020820190565b0390a3600090565b61285e6128759260018060a01b0316600052600f602052604060002090565b9060018060a01b0316600052602052604060002090565b55388080612824565b5050505050505050505061083c612af1565b50505050505050505061083c612ac5565b505050505050505061083c612a99565b6001600160a01b0384166000908152600f602052604090206128d490829061285e565b5493612750565b5050505050505061083c612a6d565b9250505061083c91506134c0565b612911915060203d6020116109fe576109f0818361044a565b38612724565b90612920612084565b91826129315761260b925033613f5e565b50506012811015610bdb57600080516020615aeb83398151915260606040518381526037602082015260006040820152a190600090565b906129729161338e565b6004821015610bdb578161299457670de0b6b3a7640000915051049060009190565b5090600090565b6012546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa9081156109d1576000916129d9575090565b61083c915060203d6020116109fe576109f0818361044a565b6129fa612084565b9081612a0e57612a0a915061429a565b5090565b506012811015610bdb57600080516020615aeb83398151915260606040518381526053602082015260006040820152a190565b600080516020615aeb8339815191526060604051600181526041602082015260006040820152a1600190565b600080516020615aeb8339815191526060604051600281526050602082015260006040820152a1600290565b600080516020615aeb8339815191526060604051600981526050602082015260006040820152a1600990565b600080516020615aeb8339815191526060604051600981526051602082015260006040820152a1600990565b600080516020615aeb8339815191526060604051600981526052602082015260006040820152a1600990565b600080516020615aeb8339815191526060604051600181526033602082015260006040820152a1600190565b600080516020615aeb8339815191526060604051600a81526035602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600f81526034602082015260006040820152a1600f90565b600080516020615aeb8339815191526060604051600281526036602082015260006040820152a1600290565b6012811015610bdb57600080516020615aeb83398151915260606040518381526028602082015260006040820152a190565b600080516020615aeb8339815191526060604051600181526044602082015260006040820152a1600190565b600080516020615aeb8339815191526060604051600a81526043602082015260006040820152a1600a90565b6012811015610bdb57600080516020615aeb8339815191526060604051838152601e602082015260006040820152a190565b600080516020615aeb833981519152606060405160068152601c602082015260006040820152a1600690565b6012811015610bdb57600080516020615aeb83398151915260606040518381526010602082015260006040820152a190565b600080516020615aeb8339815191526060604051600a8152604a602082015260006040820152a1600a90565b600080516020615aeb833981519152606060405160028152604b602082015260006040820152a1600290565b600080516020615aeb8339815191526060604051600a8152603b602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a81526054602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a8152602e602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600f81526031602082015260006040820152a1600f90565b600080516020615aeb8339815191526060604051600a81526022602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a8152600a602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600f81526009602082015260006040820152a1600f90565b600080516020615aeb8339815191526060604051600a81526016602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a81526011602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600681526017602082015260006040820152a1600690565b600080516020615aeb8339815191526060604051600781526015602082015260006040820152a1600790565b600080516020615aeb8339815191526060604051600781526014602082015260006040820152a1600790565b6012811015610bdb57600080516020615aeb83398151915260606040518381526018602082015260006040820152a190565b634e487b7160e01b600052601160045260246000fd5b91908203918211612fa457565b612f81565b15612fb057565b60405162461bcd60e51b8152602060048201526024808201527f72656475636520726573657276657320756e657870656374656420756e646572604482015263666c6f7760e01b6064820152608490fd5b6003546130199060081c6001600160a01b0316610939565b33036130e65760095443036130dd578061303161299b565b106130d457600c54908181116130ca5781613079613070837f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e95612f97565b91821115612fa9565b61308281600c55565b60035461309c90839060081c6001600160a01b0316614365565b6003546040805160089290921c6001600160a01b031682526020820193909352918201528060608101611956565b505061083c612ba1565b5061083c612b75565b5061083c612b49565b5061083c612b1d565b60035460081c6001600160a01b0316330361317c57600954430361314f5760118054908290556040805191825260208201929092527ff5815f353a60e815cce7553e4f60c533a59d26b1b5504ea4b6db8d60da3e4da29181908101611956565b50600080516020615aeb8339815191526060604051600a81526058602082015260006040820152a1600a90565b50600080516020615aeb8339815191526060604051600181526057602082015260006040820152a1600190565b6131b1612084565b90816131c25761083c9150336144eb565b506012811015610bdb5761083c90612bcd565b6001600160a01b031660009081526010602052604081208054919291801561324257600a5461320391614b43565b906004811015610bdb578061323b5750600161322192015490614b7d565b929061322c81611659565b8061323657509190565b925090565b9150509190565b50508190565b6003546132609060081c6001600160a01b0316610939565b33036132ff5760095443036132f6576006546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa9283156109d1577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936132d69160009161195e575061184a565b600680546001600160a01b0319166001600160a01b038416179055611937565b5061083c612c2b565b5061083c612bff565b60019161332c6000549261331e60ff85166115bb565b60ff1993841660005561333a565b939093926000541617600055565b613342612084565b90816133535761260b915033614d1a565b506012811015610bdb5761336690612c57565b90600090565b9190828111613384578203918211612fa45760009190565b5060039150600090565b906133a89160006040516133a181610414565b5251614b43565b91906004811015610bdb57806133ce5750604051916133c683610414565b825260009190565b60405192506133dc83610414565b600083529190565b6060600080516020615aeb833981519152916040519060098252600660208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600160208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600460208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600560208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600360208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252604f60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060038252601b60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252601a60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252601960208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252603a60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252603960208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602d60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602b60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602a60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252602960208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252603060208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602f60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252601f60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252602160208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252600e60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252600760208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600c60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600b60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252601260208301526040820152a1600390565b9190820191828111612fa457821061381c5760009190565b60029150600090565b9061382f9161338e565b91906004811015610bdb57806138545750670de0b6b3a764000061260b925104613804565b91505090600090565b6040519061012082018281106001600160401b0382111761040f57604052816101006000918281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201520152565b6004821015610bdb5752565b156138c657565b60405162461bcd60e51b815260206004820152601860248201527f65786368616e67652072617465206d617468206572726f7200000000000000006044820152606490fd5b9260208382939586600061392961093960055460018060a01b031690565b60405163d02f735160e01b81523060048201526001600160a01b03948516602482015292841660448401529390921660648201526084810194909452839160a49183915af19081156109d157600091613c36575b5080613c2957506001600160a01b038381169190831690818314613c1c576139d7926139a761385d565b946139c8836127888360018060a01b0316600052600e602052604060002090565b959060208801968752876138b3565b85516139e281611659565b6139eb81611659565b613bfa57613ae396613a23670de0b6b3a7640000613a1560115480613a0e6107cc565b5287615a0e565b046080890195818752614ff7565b9160608801928352613a79613a48613a39612645565b919060c08c019283528b6138b3565b613a658a51613a5681611659565b613a5f81611659565b156138bf565b51613a6e6107cc565b90815286519061503e565b96613a8d60a08a0198808a52600c54615073565b9860e08101998a52613aa3600d54885190614ff7565b906101008101918252613ad4613acb8660018060a01b0316600052600e602052604060002090565b54875190613804565b9c90604083019d8e52826138b3565b8051613aee81611659565b613af781611659565b613bd257507fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59a8897969593613b61613ba29a9694613b44613b7f958f613b3e9051600c55565b51600d55565b516001600160a01b039092166000908152600e6020526040902090565b55516001600160a01b039092166000908152600e6020526040902090565b555160405180600080516020615b0b833981519152978893829190602083019252565b0390a351604051908152309290602090a35190516040805130815260208101939093528201528060608101611956565b995050505050505050505061083c915051613bec81611659565b613bf581611659565b613544565b505050505061083c915051613c0e81611659565b613c1781611659565b613518565b505050505061083c612c89565b91505061083c91506134ec565b613c4f915060203d6020116109fe576109f0818361044a565b3861397d565b613c5d612084565b9081613c6e5761083c9150336150e9565b506012811015610bdb57600080516020615aeb83398151915260606040518381526008602082015260006040820152a190565b613ca9612084565b90816131c25761083c9150336148ee565b9190613cc4612084565b80613d42575060405163a6afed9560e01b81529260208460048160006001600160a01b0388165af19384156109d157600094613d21575b5083613d0c5761260b93503361546a565b50505080613d1c6133669261160a565b612cb5565b613d3b91945060203d6020116109fe576109f0818361044a565b9238613cfb565b92505050613d4f8161160a565b6012811015610bdb57600080516020615aeb8339815191526060604051838152600f602082015260006040820152a190600090565b60035460081c6001600160a01b03163303613e0a576009544303613e0157670de0b6b3a76400008111613df8577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600854613ddf82600855565b6040805191825260208201929092529081908101611956565b5061083c612d13565b5061083c612ce7565b50600080516020615aeb8339815191526060604051600181526049602082015260006040820152a1600190565b6040519061010082018281106001600160401b0382111761040f576040528160e06000918281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b15613e8d57565b60405162461bcd60e51b815260206004820152603a60248201527f52455041595f424f52524f575f4e45575f4143434f554e545f424f52524f575f60448201527f42414c414e43455f43414c43554c4154494f4e5f4641494c45440000000000006064820152608490fd5b15613eff57565b60405162461bcd60e51b815260206004820152603160248201527f52455041595f424f52524f575f4e45575f544f54414c5f42414c414e43455f43604482015270105310d55310551253d397d19052531151607a1b6064820152608490fd5b60055491939291613f77906001600160a01b0316610939565b604051631200453160e11b81523060048201526001600160a01b038381166024830152861660448201526064810184905260009591602090829060849082908a905af19081156109d15786916141bc575b50806141af575060095443036141a457613fe0613e37565b916001613fff8360018060a01b03166000526010602052604060002090565b0154606084015261400f826131d5565b939094614027602083019660808401968752876138b3565b855161403281611659565b61403b81611659565b61417f5760c06141067f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a196976140ed61416e956140b595600019811460001461417557508a5160408201525b6140a661409860408301518a61589f565b60e083018181529c5161336c565b969060a08301978852846138b3565b6140d283516140c381611659565b6140cc81611659565b15613e86565b6140e0600b548c519061336c565b95909101948552826138b3565b516140f781611659565b61410081611659565b15613ef8565b81516001600160a01b0386166000908152601060205260409020908155600a5460019091015551600b8190559086519051604080516001600160a01b0396871681529690951660208701529385015260608401929092526080830191909152819060a0820190565b0390a15190565b6040820152614087565b50505050506141a0905161419281611659565b61419b81611659565b61359c565b9190565b5050506141a0612d3f565b9150506141a09150613570565b6141d5915060203d6020116109fe576109f0818361044a565b38613fc8565b906141e591613804565b60048293921015610bdb57826126b35761260b925061336c565b61421790600060405161421181610414565b52614b68565b919061422281611659565b806133ce5750604051916133c683610414565b61425090929192600060405161424a81610414565b52614adc565b60048294921015610bdb578361426b57614217929350614b7d565b5050604051916133dc83610414565b8115614284570490565b634e487b7160e01b600052601260045260246000fd5b600954430361435c576142ad903361589f565b90600c5482810190818111612fa45781106143185761430f816142f07fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc593600c55565b6040805133815260208101879052908101919091529081906060820190565b0390a160009190565b606460405162461bcd60e51b815260206004820152602060248201527f61646420726573657276657320756e6578706563746564206f766572666c6f776044820152fd5b50613366612d6b565b919060018060a01b036012541690813b156103a75760405163a9059cbb60e01b81526001600160a01b0394909416600485015260248401526000929083908290604490829084905af180156109d15761442b575b503d8015614420576020146143cc575080fd5b90602081803e515b156143db57565b60405162461bcd60e51b815260206004820152601960248201527f544f4b454e5f5452414e534645525f4f55545f4641494c4544000000000000006044820152606490fd5b5090506000196143d4565b614434906103fc565b386143b9565b1561444157565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6040519060e082018281106001600160401b0382111761040f576040528160c06000918281528260208201528260408201528260608201528260808201528260a08201520152565b6144f36144a3565b6144fb612645565b9093614512602084019560408501938452866138b3565b845161451d81611659565b61452681611659565b6148ce57600019810361486c57506001600160a01b0383166000908152600e6020526040902061457c916145719154906060850191825251906145676107cc565b9182525190612968565b6080840152846138b3565b825161458781611659565b61459081611659565b61484e575b6005546145aa906001600160a01b0316610939565b60608201805160405163eabe7d9160e01b81523060048201526001600160a01b038616602482015260448101919091526000959192602090829060649082908a905af19081156109d157869161482f575b5080614820575060095443036148135761462c9061461d600d5484519061336c565b929060a08601938452826138b3565b805161463781611659565b61464081611659565b6147f1576001600160a01b0385166000908152600e6020526040902061467e92919061466f905485519061336c565b939060c08701948552826138b3565b805161468981611659565b61469281611659565b6147ce575060806146a161299b565b9401938451116147c05751600d55516001600160a01b0384166000818152600e602090815260409182902093909355835190519081523092600080516020615b0b83398151915291a381518151604080516001600160a01b038716815260208101939093528201527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a92990606090a1600554614744906001600160a01b0316610939565b8251915191813b156147bc576040516351dff98960e01b81523060048201526001600160a01b038616602482015260448101919091526064810192909252909291908490849060849082905afa9283156109d15761083c936147a9575b505190614365565b806109cb6147b6926103fc565b386147a1565b8580fd5b50505050505061083c612dc3565b94505050505061083c9150516147e381611659565b6147ec81611659565b6136a4565b935050505061083c91505161480581611659565b61480e81611659565b613678565b505050505061083c612d97565b935050505061083c915061364c565b614848915060203d6020116109fe576109f0818361044a565b386145fb565b505061083c905161485e81611659565b61486781611659565b613620565b806148979261488c92608086015251906148846107cc565b918252615a04565b6060840152846138b3565b82516148a281611659565b6148ab81611659565b1561459557505061083c90516148c081611659565b6148c981611659565b6135f4565b5050505061083c90516148e081611659565b6148e981611659565b6135c8565b600091801580159081614ac1575b6149059061443a565b61490d6144a3565b91614916612645565b91909261492e602086019460408701948552856138b3565b835161493981611659565b61494281611659565b614aac5715614a555761499b91614990918715614a4b57506001600160a01b0386166000908152600e602052604090205460608601525b516149826107cc565b908152606085015190612968565b6080850152826138b3565b80516149a681611659565b6149af81611659565b614a39575b6005546149c9906001600160a01b0316610939565b60608301805160405163eabe7d9160e01b81523060048201526001600160a01b03871660248201526044810191909152909291602090829060649082908a905af19081156109d157869161482f575080614820575060095443036148135761462c9061461d600d5484519061336c565b91505061083c91505161485e81611659565b6060860152614979565b506000608084015251614a8190614a7690614a6e6107cc565b9081526159eb565b6060850152826138b3565b8051614a8c81611659565b614a9581611659565b156149b45791505061083c9150516148c081611659565b50505091505061083c9150516148e081611659565b5060016148fc565b81810292918115918404141715612fa457565b8015614b1557670de0b6b3a7640000808202918083048203612fa457614b02908361427a565b14614b105750600290600090565b600091565b50600090600090565b670de0b6b3a764000090808202918204808203612fa45714614b105750600290600090565b908115614b5e57614b02614b578284614ac9565b928361427a565b5050600090600090565b15614b7557600090600090565b600190600090565b908015614b8d57614b109161427a565b5050600190600090565b15614b9e57565b606460405162461bcd60e51b815260206004820152602060248201527f4d494e545f45584348414e47455f43414c43554c4154494f4e5f4641494c45446044820152fd5b15614be957565b60405162461bcd60e51b815260206004820152602860248201527f4d494e545f4e45575f544f54414c5f535550504c595f43414c43554c4154494f6044820152671397d1905253115160c21b6064820152608490fd5b15614c4657565b60405162461bcd60e51b815260206004820152602b60248201527f4d494e545f4e45575f4143434f554e545f42414c414e43455f43414c43554c4160448201526a151253d397d1905253115160aa1b6064820152608490fd5b9060209081838203126103a75782516001600160401b03938482116103a7570181601f820112156103a757805193841161040f578360051b9060405194614ce88584018761044a565b855283808601928201019283116103a7578301905b828210614d0b575050505090565b81518152908301908301614cfd565b60055490929190614d33906001600160a01b0316610939565b604051634ef4c3e160e01b81523060048201526001600160a01b038516602482015260448101839052600094916020908290606490829089905af19081156109d1578591614fd8575b5080614fcc57506009544303614fc25783614d956144a3565b91614d9e612645565b93614db4602082019260408301968752836138b3565b8151614dbf81611659565b614dc881611659565b614fa057614dd88495968461589f565b60c082019681885251614de96107cc565b908152614df591615a04565b929060608301938452614e0890826138b3565b8051614e1381611659565b614e1c81611659565b15614e2690614b97565b600d548351614e3491613804565b919060808401928352614e4790826138b3565b8051614e5281611659565b614e5b81611659565b15614e6590614be2565b6001600160a01b0385166000908152600e60205260409020548451614e8991613804565b9360a001938452614e9a90826138b3565b51614ea481611659565b614ead81611659565b15614eb790614c3f565b51600d55516001600160a01b0383166000908152600e602052604090205584518151604080516001600160a01b038616815260208101939093528201528060608101037f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f91a1516040519081526001600160a01b038216903090600080516020615b0b83398151915290602090a3600554604051631ea016b760e21b81526001600160a01b039283166004820152938492602492849291165af180156109d157614f8057505190565b614f9b903d8086833e614f93818361044a565b810190614c9f565b505190565b50925050506141a0915051614fb481611659565b614fbd81611659565b6136fc565b50506141a0612def565b90506141a091506136d0565b614ff1915060203d6020116109fe576109f0818361044a565b38614d7c565b906150326040516150078161042f565b60158152747375627472616374696f6e20756e646572666c6f7760581b602082015283831115615a4e565b8103908111612fa45790565b670de0b6b3a76400009161506191600060405161505a81610414565b5251615a0e565b8060405161506e81610414565b520490565b604051916150808361042f565b60118352706164646974696f6e206f766572666c6f7760781b6020840152810191828211612fa45761083c91831015615a4e565b60405190608082018281106001600160401b0382111761040f5760405260006060838281528260208201528260408201520152565b600554909190615101906001600160a01b0316610939565b60405163368f515360e21b81523060048201526001600160a01b03841660248201526044810183905290602090829060649082906000905af19081156109d15760009161533e575b508061533257506009544303615328578061516261299b565b1061531e5761516f6150b4565b916151946151856001600160a01b0383166131d5565b919060208601928352856138b3565b835161519f81611659565b6151a881611659565b6152ff57926151bb836151ca9551613804565b949060408301958652826138b3565b80516151d581611659565b6151de81611659565b6152df57615201906151f284600b54613804565b929060608301938452826138b3565b805161520c81611659565b61521581611659565b6152be5750837f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80916152b9955161525e8560018060a01b03166000526010602052604060002090565b55600a546001600160a01b038516600090815260106020526040902060010155519061528982600b55565b51604080516001600160a01b038616815260208101879052908101919091526060810191909152608090a1614365565b600090565b9250505061083c9150516152d181611659565b6152da81611659565b6137ac565b91505061083c9150516152f181611659565b6152fa81611659565b613780565b50505061083c905161531081611659565b61531981611659565b613754565b505061083c612e47565b505061083c612e1b565b905061083c9150613728565b615357915060203d6020116109fe576109f0818361044a565b38615149565b91908260409103126103a7576020825192015190565b1561537a57565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b156153e257565b60405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606490fd5b1561542e57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b60055492949392615483906001600160a01b0316610939565b60408051632fe3f38f60e11b81523060048201526001600160a01b03958616602482018190528487166044830152858716606483015260848201899052602098929690939091908990829060a49082905afa9081156109d1576000916157ea575b50806157d8575060095443036157c8578551636c540baf60e01b81528881600481875afa9081156109d1576000916157ab575b50430361579b578084169085161461578c57801561577d57600019811461576e57615543908484613f5e565b9690806157545750600554615560906001600160a01b0316610939565b855163c488847b60e01b81523060048201526001600160a01b03841660248201526044810189905295908190879060649082905afa9586156109d157600090600097615722575b506155b29015615373565b80516370a0823160e01b81526001600160a01b0386166004820152918083602481875afa80156109d157876155f3918b95600091615705575b5010156153db565b30840361566c575091859161430f936156386156327f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529989893061390b565b15615427565b516001600160a01b0395861681529585166020870152604086015290921660608401526080830191909152819060a0820190565b815163b2a02ff160e01b81526001600160a01b03868116600483015287166024820152604481018890529096919391925086816064816000865af19384156109d15761563861430f958b937f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529a6000926156e8575b5050615632565b6156fe9250803d106109fe576109f0818361044a565b38806156e1565b61571c9150843d86116109fe576109f0818361044a565b386155eb565b6155b297506157479150823d841161574d575b61573f818361044a565b81019061535d565b966155a7565b503d615735565b9450505050506133669192506157698161160a565b612f4f565b50505050509050613366612f23565b50505050509050613366612ef7565b50505050509050613366612ecb565b5050505050509050613366612e9f565b6157c29150893d8b116109fe576109f0818361044a565b38615517565b5050505050509050613366612e73565b955050505050506133669192506137d8565b6158019150893d8b116109fe576109f0818361044a565b386154e4565b1561580e57565b60405162461bcd60e51b815260206004820152601860248201527f544f4b454e5f5452414e534645525f494e5f4641494c454400000000000000006044820152606490fd5b1561585a57565b60405162461bcd60e51b815260206004820152601a60248201527f544f4b454e5f5452414e534645525f494e5f4f564552464c4f570000000000006044820152606490fd5b6012546040516370a0823160e01b80825230600483015260209491936001600160a01b03909316929091908585602481875afa9485156109d1576000956159cc575b50833b156103a7576040516323b872dd60e01b81526001600160a01b03919091166004820152306024820152604481019190915260008160648183875af180156109d1576159b9575b503d8481156159ab575060201461594057600080fd5b8390816000803e615952600051615807565b60405190815230600482015291829060249082905afa9081156109d15761083c9360009261598e575b505061598982821015615853565b612f97565b6159a49250803d106109fe576109f0818361044a565b388061597b565b919050615952600019615807565b806109cb6159c6926103fc565b3861592a565b6159e4919550863d88116109fe576109f0818361044a565b93386158e1565b6129729060006040516159fd81610414565b52516141ff565b9061297291615a76565b61083c9160405191615a1f8361042f565b601783527f6d756c7469706c69636174696f6e206f766572666c6f770000000000000000006020840152615aae565b15615a565750565b60405162461bcd60e51b8152908190615a72906004830161046b565b0390fd5b615a91909291926000604051615a8b81610414565b52614b1e565b90926004841015610bdb578361426b5761260b9293505190614235565b9182158015615ae2575b615ada5781615ad4615acd61083c9486614ac9565b948561427a565b14615a4e565b505050600090565b508115615ab856fe45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212209b70a804d1f58250f63dc48c29e1d9d2ff15d307aa7586b47ff68bb042592e4564736f6c63430008170033
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c806306fdde0314610397578063095ea7b3146103925780630e7527021461038d578063153ab50514610388578063173b99041461038357806317bfdfbc1461037e57806318160ddd14610379578063182df0f5146103745780631a31d4651461036f5780631be195601461036a57806323b872dd146103655780632608f81814610360578063267822471461035b578063313ce567146103565780633630a6b5146103515780633af9e6691461034c5780633b1d21a2146103475780633e941010146103425780634576b5db1461033d57806347bd37181461033857806356e67728146103335780635c60da1b1461032e5780635fe3b56714610329578063601a0bf1146103245780636752e7021461031f5780636c540baf1461031a5780636f307dc31461031557806370a082311461031057806373acee981461030b5780638303084614610306578063852a12e314610301578063895dabad146102fc5780638f840ddd146102f757806395d89b41146102f257806395dd9193146102ed57806399d8c1b4146102e8578063a0712d68146102e3578063a6afed95146102de578063a9059cbb146102d9578063aa5af0fd146102d4578063ae9d70b0146102cf578063b2a02ff1146102ca578063b71d1a0c146102c5578063bd6d894d146102c0578063c37f68e2146102bb578063c5ebeaec146102b6578063d0248fb4146102b1578063db006a75146102ac578063dd62ed3e146102a7578063e9c714f2146102a2578063f2b3abbd1461029d578063f3fdb15a14610298578063f5e3c46214610293578063f851a4401461028e578063f8f9da28146102895763fca7820b1461028457600080fd5b611583565b611520565b6114f3565b6114a1565b611478565b611451565b611436565b6113d3565b61139b565b6112f1565b6112b9565b61126f565b611254565b61122d565b6111f5565b61114e565b611130565b6110e8565b6110cd565b611095565b611016565b610ff4565b610f4c565b610f2e565b610f12565b610eda565b610ea2565b610e58565b610e1b565b610df2565b610dd4565b610db6565b610d7e565b610d55565b610d2c565b610c78565b610c5a565b610c33565b610bfb565b610be0565b610b27565b610b0b565b610aea565b610ac1565b610a7b565b610a31565b6108dd565b61084f565b6107a9565b61078b565b610727565b610709565b610686565b61063c565b6105ad565b6104b4565b60009103126103a757565b600080fd5b90600182811c921680156103dc575b60208310146103c657565b634e487b7160e01b600052602260045260246000fd5b91607f16916103bb565b634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161040f57604052565b6103e6565b602081019081106001600160401b0382111761040f57604052565b604081019081106001600160401b0382111761040f57604052565b90601f801991011681019081106001600160401b0382111761040f57604052565b6020808252825181830181905290939260005b8281106104a057505060409293506000838284010152601f8019910116010190565b81810186015184820160400152850161047e565b346103a75760008060031936011261059957604051908060018054906104d9826103ac565b808652926020926001811690811561056c5750600114610514575b610510866105048188038261044a565b6040519182918261046b565b0390f35b9350600184527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8385106105595750505050810160200161050482610510386104f4565b805486860184015293820193810161053c565b90508695506105109693506020925061050494915060ff191682840152151560051b8201019293386104f4565b80fd5b6001600160a01b038116036103a757565b346103a75760403660031901126103a7576004356105ca8161059c565b6024359033600052600f602052816105f88260406000209060018060a01b0316600052602052604060002090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3602060405160018152f35b346103a75760203660031901126103a757602060005461065e60ff82166115bb565b60ff1990811660005560016106746004356125f1565b50916000541617600055604051908152f35b346103a75760003660031901126103a75760035460081c6001600160a01b031633036106ae57005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f72657369676e4960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346103a75760003660031901126103a7576020600854604051908152f35b346103a75760203660031901126103a75760206004356107468161059c565b600161077a6000549261075b60ff85166115bb565b60ff1993841660005561077561076f612084565b15611614565b611a2c565b916000541617600055604051908152f35b346103a75760003660031901126103a7576020600d54604051908152f35b346103a75760003660031901126103a75760206107c4611663565b604051908152f35b604051906107d982610414565b565b9291926001600160401b03821161040f5760405191610804601f8201601f19166020018461044a565b8294818452818301116103a7578281602093846000960137010152565b9080601f830112156103a75781602061083c933591016107db565b90565b6044359060ff821682036103a757565b346103a75760e03660031901126103a75760043561086c8161059c565b602435906108798261059c565b604435906108868261059c565b6001600160401b036084358181116103a7576108a6903690600401610821565b9060a4359081116103a7576108bf903690600401610821565b9160c4359360ff851685036103a7576108db95606435926116fa565b005b346103a75760203660031901126103a7576004356108fa8161059c565b60035460081c6001600160a01b0316906001600160a01b03906109203383851614611767565b6012549116919061094d90610945906001600160a01b03165b6001600160a01b031690565b8314156117cb565b6040516370a0823160e01b8152306004820152602081602481865afa9081156109d1576000916109d6575b50823b156103a75760405163a9059cbb60e01b81526001600160a01b039290921660048301526024820152906000908290604490829084905af180156109d1576109be57005b806109cb6108db926103fc565b8061039c565b6116ee565b6109f8915060203d6020116109fe575b6109f0818361044a565b8101906116df565b38610978565b503d6109e6565b60609060031901126103a757600435610a1d8161059c565b90602435610a2a8161059c565b9060443590565b346103a7576020610a696001610a4636610a05565b9060009492945494610a5a60ff87166115bb565b60ff19958616600055336126bb565b15916000541617600055604051908152f35b346103a75760403660031901126103a7576020600435610a9a8161059c565b600161067460005492610aaf60ff85166115bb565b60ff1993841660005560243590612917565b346103a75760003660031901126103a7576004546040516001600160a01b039091168152602090f35b346103a75760003660031901126103a757602060ff60035416604051908152f35b346103a75760003660031901126103a757602060405160018152f35b346103a75760203660031901126103a757610b7d600435610b478161059c565b610b4f612384565b9060405191610b5d83610414565b82526001600160a01b03166000908152600e602052604090205490612968565b906004811015610bdb57610b9657604051908152602090f35b60405162461bcd60e51b815260206004820152601f60248201527f62616c616e636520636f756c64206e6f742062652063616c63756c61746564006044820152606490fd5b6115f4565b346103a75760003660031901126103a75760206107c461299b565b346103a75760203660031901126103a7576020600054610c1d60ff82166115bb565b60ff19908116600055600161077a6004356129f2565b346103a75760203660031901126103a75760206107c4600435610c558161059c565b611896565b346103a75760003660031901126103a7576020600b54604051908152f35b346103a75760203660031901126103a7576004356001600160401b0381116103a757366023820112156103a757610cb99036906024816004013591016107db565b5060035460081c6001600160a01b03163303610cd157005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f6265636f6d654960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346103a75760003660031901126103a7576013546040516001600160a01b039091168152602090f35b346103a75760003660031901126103a7576005546040516001600160a01b039091168152602090f35b346103a75760203660031901126103a7576020600054610da060ff82166115bb565b60ff19908116600055600161077a600435611996565b346103a75760003660031901126103a7576020601154604051908152f35b346103a75760003660031901126103a7576020600954604051908152f35b346103a75760003660031901126103a7576012546040516001600160a01b039091168152602090f35b346103a75760203660031901126103a757600435610e388161059c565b60018060a01b0316600052600e6020526020604060002054604051908152f35b346103a757600080600319360112610599578054602091610e7b60ff83166115bb565b60ff199182168155610e8e61076f612084565b6001600b5492825416179055604051908152f35b346103a75760203660031901126103a7576020600054610ec460ff82166115bb565b60ff19908116600055600161077a6004356119e1565b346103a75760203660031901126103a7576020600054610efc60ff82166115bb565b60ff19908116600055600161077a6004356131a9565b346103a75760003660031901126103a757602060405160008152f35b346103a75760003660031901126103a7576020600c54604051908152f35b346103a75760008060031936011261059957604051908060025490610f70826103ac565b8085529160209160019182811690811561056c5750600114610f9c57610510866105048188038261044a565b9350600284527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b838510610fe15750505050810160200161050482610510386104f4565b8054868601840152938201938101610fc4565b346103a75760203660031901126103a75760206107c46004356107758161059c565b346103a75760c03660031901126103a7576004356110338161059c565b602435906110408261059c565b6001600160401b036064358181116103a757611060903690600401610821565b906084359081116103a757611079903690600401610821565b9060a4359260ff841684036103a7576108db9460443591611ecc565b346103a75760203660031901126103a75760206000546110b760ff82166115bb565b60ff19908116600055600161067460043561333a565b346103a75760003660031901126103a75760206107c4612084565b346103a75760403660031901126103a75760206004356111078161059c565b6001610a696000549261111c60ff85166115bb565b60ff199384166000556024359033806126bb565b346103a75760003660031901126103a7576020600a54604051908152f35b346103a75760003660031901126103a7576006546001600160a01b0316602061117561299b565b600b54600c54600854604051635c0b440b60e11b815260048101949094526024840192909252604483015260648201529182908180608481015b03915afa80156109d157610510916000916111d6575b506040519081529081906020820190565b6111ef915060203d6020116109fe576109f0818361044a565b386111c5565b346103a757602061077a600161120a36610a05565b906000949294549461121e60ff87166115bb565b60ff199586166000553361390b565b346103a75760203660031901126103a75760206107c460043561124f8161059c565b6122e1565b346103a75760003660031901126103a75760206107c4612384565b346103a75760203660031901126103a7576105106112976004356112928161059c565b6123bd565b6040805194855260208501939093529183015260608201529081906080820190565b346103a75760203660031901126103a75760206000546112db60ff82166115bb565b60ff19908116600055600161077a600435613c55565b346103a75760a03660031901126103a75760043561130d61083f565b6012546001600160a01b03169190823b156103a75760ff60e460009283604051968794859363d505accf60e01b8552336004860152306024860152886044860152602435606486015216608484015260643560a484015260843560c48401525af19081156109d157610510926111c592611388575b50613308565b806109cb611395926103fc565b38611382565b346103a75760203660031901126103a75760206000546113bd60ff82166115bb565b60ff19908116600055600161077a600435613ca1565b346103a75760403660031901126103a757602061142d6004356113f58161059c565b602435906114028261059c565b60018060a01b0316600052600f835260406000209060018060a01b0316600052602052604060002090565b54604051908152f35b346103a75760003660031901126103a75760206107c4612427565b346103a75760203660031901126103a75760206107c46004356114738161059c565b61255b565b346103a75760003660031901126103a7576006546040516001600160a01b039091168152602090f35b346103a75760603660031901126103a75760206004356114c08161059c565b60016106746044356114d18161059c565b600054936114e160ff86166115bb565b60ff1994851660005560243590613cba565b346103a75760003660031901126103a75760035460405160089190911c6001600160a01b03168152602090f35b346103a75760003660031901126103a7576006546111af906020906001600160a01b031661154c61299b565b600b54600c546040516315f2405360e01b815260048101939093526024830191909152604482015292839190829081906064820190565b346103a75760203660031901126103a75760206000546115a560ff82166115bb565b60ff19908116600055600161077a6004356125a6565b156115c257565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b634e487b7160e01b600052602160045260246000fd5b60121115610bdb57565b1561161b57565b60405162461bcd60e51b81526020600482015260166024820152751858d8dc9d59481a5b9d195c995cdd0819985a5b195960521b6044820152606490fd5b60041115610bdb57565b61166b612645565b906004811015610bdb5761167c5790565b60405162461bcd60e51b815260206004820152603560248201527f65786368616e67655261746553746f7265643a2065786368616e67655261746560448201527414dd1bdc9959125b9d195c9b985b0819985a5b1959605a1b6064820152608490fd5b908160209103126103a7575190565b6040513d6000823e3d90fd5b9361170c936004979360209793611ecc565b601280546001600160a01b0319166001600160a01b039290921691821790556040516318160ddd60e01b815292839182905afa80156109d15761174c5750565b6117649060203d6020116109fe576109f0818361044a565b50565b1561176e57565b60405162461bcd60e51b815260206004820152602f60248201527f5245726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b156117d257565b60405162461bcd60e51b815260206004820152603260248201527f5245726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b908160209103126103a7575180151581036103a75790565b1561185157565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b6003546118ae9060081c6001600160a01b0316610939565b330361198d576005546001600160a01b0316604051623f1ee960e11b815291906020836004816001600160a01b0386165afa9283156109d1577f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d9361191b9160009161195e575b5061184a565b600580546001600160a01b0319166001600160a01b0384161790555b604080516001600160a01b039283168152929091166020830152819081015b0390a1600090565b611980915060203d602011611986575b611978818361044a565b810190611832565b38611915565b503d61196e565b5061083c612a41565b61199e612084565b90816119ae5761083c9150613001565b506012811015610bdb57600080516020615aeb83398151915260606040518381526032602082015260006040820152a190565b6119e9612084565b90816119f95761083c91506130ef565b506012811015610bdb57600080516020615aeb83398151915260606040518381526056602082015260006040820152a190565b611a35906131d5565b906004811015610bdb57611a465790565b60405162461bcd60e51b815260206004820152603760248201527f626f72726f7742616c616e636553746f7265643a20626f72726f7742616c616e60448201527f636553746f726564496e7465726e616c206661696c65640000000000000000006064820152608490fd5b15611ab857565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b15611b1057565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b15611b7557565b60405162461bcd60e51b815260206004820152601a60248201527f73657474696e6720636f6d7074726f6c6c6572206661696c65640000000000006044820152606490fd5b15611bc157565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b90601f8211611c1e575050565b60019160009060016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6906020601f850160051c83019410611c7d575b601f0160051c01915b828110611c735750505050565b8181558301611c66565b9092508290611c5d565b601f8111611c93575050565b60009060026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace906020601f850160051c83019410611cef575b601f0160051c01915b828110611ce457505050565b818155600101611cd8565b9092508290611ccf565b9081516001600160401b03811161040f57600190611d2081611d1b84546103ac565b611c11565b602080601f8311600114611d6357508190611d54939495600092611d58575b50508160011b916000199060031b1c19161790565b9055565b015190503880611d3f565b90601f19831695611d9660016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf690565b926000905b888210611dd15750508385969710611db8575b505050811b019055565b015160001960f88460031b161c19169055388080611dae565b808785968294968601518155019501930190611d9b565b9081516001600160401b03811161040f57611e0d81611e086002546103ac565b611c87565b602080601f8311600114611e4457508190611e3f9394600092611d585750508160011b916000199060031b1c19161790565b600255565b90601f19831694611e7760026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace90565b926000905b878210611eb4575050836001959610611e9b575b505050811b01600255565b015160001960f88460031b161c19169055388080611e90565b80600185968294968601518155019501930190611e7c565b60035490959492939060081c6001600160a01b03163303611f8857611f52611f4c611f5793611f2d611f27611f6c9a611f1a611f5c9a6009541580611f7e575b611f1590611ab1565b600755565b610c556007541515611b09565b15611b6e565b611f3643600955565b611f47670de0b6b3a7640000600a55565b613248565b15611bba565b611cf9565b611de8565b60ff1660ff196003541617600355565b6107d9600160ff196000541617600055565b50600a5415611f0c565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b15611fe057565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b1561202c57565b60405162461bcd60e51b815260206004820152601f60248201527f636f756c64206e6f742063616c63756c61746520626c6f636b2064656c7461006044820152606490fd5b6040519061207e82610414565b60008252565b6009544381146122db5761209661299b565b600b5490600c5492600a54906120b661093960065460018060a01b031690565b6040516315f2405360e01b815260048101859052602481018690526044810187905290602090829060649082905afa80156109d15761214292612129926000926122b6575b506121189061211265048c27395000841115611fd9565b4361336c565b929061212381611659565b15612025565b612131612071565b5061213a6107cc565b90815261338e565b919061214d81611659565b8061229e575061215d8483612968565b949061216881611659565b8061228557506121789085613804565b959061218381611659565b8061226c57506121a190856008546121996107cc565b908152613825565b92906121ac81611659565b80612253575090806121bd92613825565b91906121c881611659565b8061223b575093611956916122187f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049661220143600955565b61220a83600a55565b61221384600b55565b600c55565b604051948594859094939260609260808301968352602083015260408201520152565b935050505061083c915061224e81611659565b613494565b94505050505061083c915061226781611659565b613468565b94505050505061083c915061228081611659565b61343c565b94505050505061083c915061229981611659565b613410565b935050505061083c91506122b181611659565b6133e4565b6121189192506122d49060203d6020116109fe576109f0818361044a565b91906120fb565b50600090565b6003546001600160a01b039060081c8116330361235657600480548383166001600160a01b031982161790915560408051929091166001600160a01b03908116835290921660208201527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99181908101611956565b5050600080516020615aeb8339815191526060604051600181526047602082015260006040820152a1600190565b6000549061239460ff83166115bb565b60ff199182166000556123a861076f612084565b60016123b2611663565b926000541617600055565b6001600160a01b0381166000908152600e602052604081205492916123e1906131d5565b92906004811015610bdb5761241a576123f8612645565b929061240381611659565b61240d5793929190565b6009945092508291508190565b9150915060099180918190565b6004546001600160a01b0316338114801590612553575b612526576003547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a991907ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc9061249f9060081c6001600160a01b0316610939565b60038054610100600160a81b031916600885901b610100600160a81b0316179055600480546001600160a01b031916905560035460081c6001600160a01b0316604080516001600160a01b039384168152929091166020830152819081010390a1600454604080516001600160a01b03938416815292909116602083015281908101611956565b50600080516020615aeb8339815191526060604051600181526000602082015260006040820152a1600190565b50331561243e565b612563612084565b90816125735761083c9150613248565b506012811015610bdb57600080516020615aeb83398151915260606040518381526042602082015260006040820152a190565b6125ae612084565b90816125be5761083c9150613d84565b506012811015610bdb57600080516020615aeb83398151915260606040518381526048602082015260006040820152a190565b6125f9612084565b908161260f5761260b91503333613f5e565b9091565b506012811015610bdb57600080516020615aeb83398151915260606040518381526038602082015260006040820152a190600090565b600d54806126565750600754600091565b61267c61266161299b565b600060405161266f81610414565b52600b54600c54916141db565b612687829392611659565b826126b3576126969250614235565b906126a081611659565b806126ac575051600091565b9160009150565b505090600090565b600554909392906126d4906001600160a01b0316610939565b6040516317b9b84b60e31b81523060048201526001600160a01b038481166024830152831660448201526064810185905290602090829060849082906000905af19081156109d1576000916128f8575b50806128ea57506001600160a01b038181169483821694918587146128db57811685036128b157600019935b61275a838661336c565b929061276581611659565b6128a1576001600160a01b0382166000908152600e6020526040902061278e9085905b5461336c565b969061279981611659565b612890576001600160a01b0386166000908152600e602052604090206127c190869054613804565b906127cb81611659565b61287e5761281d61283797600080516020615b0b833981519152996128028760018060a01b0316600052600e602052604060002090565b556001600160a01b03166000908152600e6020526040902090565b551961283f575b5050604051918252509081906020820190565b0390a3600090565b61285e6128759260018060a01b0316600052600f602052604060002090565b9060018060a01b0316600052602052604060002090565b55388080612824565b5050505050505050505061083c612af1565b50505050505050505061083c612ac5565b505050505050505061083c612a99565b6001600160a01b0384166000908152600f602052604090206128d490829061285e565b5493612750565b5050505050505061083c612a6d565b9250505061083c91506134c0565b612911915060203d6020116109fe576109f0818361044a565b38612724565b90612920612084565b91826129315761260b925033613f5e565b50506012811015610bdb57600080516020615aeb83398151915260606040518381526037602082015260006040820152a190600090565b906129729161338e565b6004821015610bdb578161299457670de0b6b3a7640000915051049060009190565b5090600090565b6012546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa9081156109d1576000916129d9575090565b61083c915060203d6020116109fe576109f0818361044a565b6129fa612084565b9081612a0e57612a0a915061429a565b5090565b506012811015610bdb57600080516020615aeb83398151915260606040518381526053602082015260006040820152a190565b600080516020615aeb8339815191526060604051600181526041602082015260006040820152a1600190565b600080516020615aeb8339815191526060604051600281526050602082015260006040820152a1600290565b600080516020615aeb8339815191526060604051600981526050602082015260006040820152a1600990565b600080516020615aeb8339815191526060604051600981526051602082015260006040820152a1600990565b600080516020615aeb8339815191526060604051600981526052602082015260006040820152a1600990565b600080516020615aeb8339815191526060604051600181526033602082015260006040820152a1600190565b600080516020615aeb8339815191526060604051600a81526035602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600f81526034602082015260006040820152a1600f90565b600080516020615aeb8339815191526060604051600281526036602082015260006040820152a1600290565b6012811015610bdb57600080516020615aeb83398151915260606040518381526028602082015260006040820152a190565b600080516020615aeb8339815191526060604051600181526044602082015260006040820152a1600190565b600080516020615aeb8339815191526060604051600a81526043602082015260006040820152a1600a90565b6012811015610bdb57600080516020615aeb8339815191526060604051838152601e602082015260006040820152a190565b600080516020615aeb833981519152606060405160068152601c602082015260006040820152a1600690565b6012811015610bdb57600080516020615aeb83398151915260606040518381526010602082015260006040820152a190565b600080516020615aeb8339815191526060604051600a8152604a602082015260006040820152a1600a90565b600080516020615aeb833981519152606060405160028152604b602082015260006040820152a1600290565b600080516020615aeb8339815191526060604051600a8152603b602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a81526054602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a8152602e602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600f81526031602082015260006040820152a1600f90565b600080516020615aeb8339815191526060604051600a81526022602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a8152600a602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600f81526009602082015260006040820152a1600f90565b600080516020615aeb8339815191526060604051600a81526016602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600a81526011602082015260006040820152a1600a90565b600080516020615aeb8339815191526060604051600681526017602082015260006040820152a1600690565b600080516020615aeb8339815191526060604051600781526015602082015260006040820152a1600790565b600080516020615aeb8339815191526060604051600781526014602082015260006040820152a1600790565b6012811015610bdb57600080516020615aeb83398151915260606040518381526018602082015260006040820152a190565b634e487b7160e01b600052601160045260246000fd5b91908203918211612fa457565b612f81565b15612fb057565b60405162461bcd60e51b8152602060048201526024808201527f72656475636520726573657276657320756e657870656374656420756e646572604482015263666c6f7760e01b6064820152608490fd5b6003546130199060081c6001600160a01b0316610939565b33036130e65760095443036130dd578061303161299b565b106130d457600c54908181116130ca5781613079613070837f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e95612f97565b91821115612fa9565b61308281600c55565b60035461309c90839060081c6001600160a01b0316614365565b6003546040805160089290921c6001600160a01b031682526020820193909352918201528060608101611956565b505061083c612ba1565b5061083c612b75565b5061083c612b49565b5061083c612b1d565b60035460081c6001600160a01b0316330361317c57600954430361314f5760118054908290556040805191825260208201929092527ff5815f353a60e815cce7553e4f60c533a59d26b1b5504ea4b6db8d60da3e4da29181908101611956565b50600080516020615aeb8339815191526060604051600a81526058602082015260006040820152a1600a90565b50600080516020615aeb8339815191526060604051600181526057602082015260006040820152a1600190565b6131b1612084565b90816131c25761083c9150336144eb565b506012811015610bdb5761083c90612bcd565b6001600160a01b031660009081526010602052604081208054919291801561324257600a5461320391614b43565b906004811015610bdb578061323b5750600161322192015490614b7d565b929061322c81611659565b8061323657509190565b925090565b9150509190565b50508190565b6003546132609060081c6001600160a01b0316610939565b33036132ff5760095443036132f6576006546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa9283156109d1577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936132d69160009161195e575061184a565b600680546001600160a01b0319166001600160a01b038416179055611937565b5061083c612c2b565b5061083c612bff565b60019161332c6000549261331e60ff85166115bb565b60ff1993841660005561333a565b939093926000541617600055565b613342612084565b90816133535761260b915033614d1a565b506012811015610bdb5761336690612c57565b90600090565b9190828111613384578203918211612fa45760009190565b5060039150600090565b906133a89160006040516133a181610414565b5251614b43565b91906004811015610bdb57806133ce5750604051916133c683610414565b825260009190565b60405192506133dc83610414565b600083529190565b6060600080516020615aeb833981519152916040519060098252600660208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600160208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600460208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600560208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600360208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252604f60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060038252601b60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252601a60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252601960208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252603a60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252603960208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602d60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602b60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602a60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252602960208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252603060208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252602f60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252601f60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252602160208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252600e60208301526040820152a1600390565b6060600080516020615aeb833981519152916040519060098252600760208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600c60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060098252600b60208301526040820152a1600990565b6060600080516020615aeb833981519152916040519060038252601260208301526040820152a1600390565b9190820191828111612fa457821061381c5760009190565b60029150600090565b9061382f9161338e565b91906004811015610bdb57806138545750670de0b6b3a764000061260b925104613804565b91505090600090565b6040519061012082018281106001600160401b0382111761040f57604052816101006000918281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201520152565b6004821015610bdb5752565b156138c657565b60405162461bcd60e51b815260206004820152601860248201527f65786368616e67652072617465206d617468206572726f7200000000000000006044820152606490fd5b9260208382939586600061392961093960055460018060a01b031690565b60405163d02f735160e01b81523060048201526001600160a01b03948516602482015292841660448401529390921660648201526084810194909452839160a49183915af19081156109d157600091613c36575b5080613c2957506001600160a01b038381169190831690818314613c1c576139d7926139a761385d565b946139c8836127888360018060a01b0316600052600e602052604060002090565b959060208801968752876138b3565b85516139e281611659565b6139eb81611659565b613bfa57613ae396613a23670de0b6b3a7640000613a1560115480613a0e6107cc565b5287615a0e565b046080890195818752614ff7565b9160608801928352613a79613a48613a39612645565b919060c08c019283528b6138b3565b613a658a51613a5681611659565b613a5f81611659565b156138bf565b51613a6e6107cc565b90815286519061503e565b96613a8d60a08a0198808a52600c54615073565b9860e08101998a52613aa3600d54885190614ff7565b906101008101918252613ad4613acb8660018060a01b0316600052600e602052604060002090565b54875190613804565b9c90604083019d8e52826138b3565b8051613aee81611659565b613af781611659565b613bd257507fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59a8897969593613b61613ba29a9694613b44613b7f958f613b3e9051600c55565b51600d55565b516001600160a01b039092166000908152600e6020526040902090565b55516001600160a01b039092166000908152600e6020526040902090565b555160405180600080516020615b0b833981519152978893829190602083019252565b0390a351604051908152309290602090a35190516040805130815260208101939093528201528060608101611956565b995050505050505050505061083c915051613bec81611659565b613bf581611659565b613544565b505050505061083c915051613c0e81611659565b613c1781611659565b613518565b505050505061083c612c89565b91505061083c91506134ec565b613c4f915060203d6020116109fe576109f0818361044a565b3861397d565b613c5d612084565b9081613c6e5761083c9150336150e9565b506012811015610bdb57600080516020615aeb83398151915260606040518381526008602082015260006040820152a190565b613ca9612084565b90816131c25761083c9150336148ee565b9190613cc4612084565b80613d42575060405163a6afed9560e01b81529260208460048160006001600160a01b0388165af19384156109d157600094613d21575b5083613d0c5761260b93503361546a565b50505080613d1c6133669261160a565b612cb5565b613d3b91945060203d6020116109fe576109f0818361044a565b9238613cfb565b92505050613d4f8161160a565b6012811015610bdb57600080516020615aeb8339815191526060604051838152600f602082015260006040820152a190600090565b60035460081c6001600160a01b03163303613e0a576009544303613e0157670de0b6b3a76400008111613df8577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600854613ddf82600855565b6040805191825260208201929092529081908101611956565b5061083c612d13565b5061083c612ce7565b50600080516020615aeb8339815191526060604051600181526049602082015260006040820152a1600190565b6040519061010082018281106001600160401b0382111761040f576040528160e06000918281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b15613e8d57565b60405162461bcd60e51b815260206004820152603a60248201527f52455041595f424f52524f575f4e45575f4143434f554e545f424f52524f575f60448201527f42414c414e43455f43414c43554c4154494f4e5f4641494c45440000000000006064820152608490fd5b15613eff57565b60405162461bcd60e51b815260206004820152603160248201527f52455041595f424f52524f575f4e45575f544f54414c5f42414c414e43455f43604482015270105310d55310551253d397d19052531151607a1b6064820152608490fd5b60055491939291613f77906001600160a01b0316610939565b604051631200453160e11b81523060048201526001600160a01b038381166024830152861660448201526064810184905260009591602090829060849082908a905af19081156109d15786916141bc575b50806141af575060095443036141a457613fe0613e37565b916001613fff8360018060a01b03166000526010602052604060002090565b0154606084015261400f826131d5565b939094614027602083019660808401968752876138b3565b855161403281611659565b61403b81611659565b61417f5760c06141067f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a196976140ed61416e956140b595600019811460001461417557508a5160408201525b6140a661409860408301518a61589f565b60e083018181529c5161336c565b969060a08301978852846138b3565b6140d283516140c381611659565b6140cc81611659565b15613e86565b6140e0600b548c519061336c565b95909101948552826138b3565b516140f781611659565b61410081611659565b15613ef8565b81516001600160a01b0386166000908152601060205260409020908155600a5460019091015551600b8190559086519051604080516001600160a01b0396871681529690951660208701529385015260608401929092526080830191909152819060a0820190565b0390a15190565b6040820152614087565b50505050506141a0905161419281611659565b61419b81611659565b61359c565b9190565b5050506141a0612d3f565b9150506141a09150613570565b6141d5915060203d6020116109fe576109f0818361044a565b38613fc8565b906141e591613804565b60048293921015610bdb57826126b35761260b925061336c565b61421790600060405161421181610414565b52614b68565b919061422281611659565b806133ce5750604051916133c683610414565b61425090929192600060405161424a81610414565b52614adc565b60048294921015610bdb578361426b57614217929350614b7d565b5050604051916133dc83610414565b8115614284570490565b634e487b7160e01b600052601260045260246000fd5b600954430361435c576142ad903361589f565b90600c5482810190818111612fa45781106143185761430f816142f07fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc593600c55565b6040805133815260208101879052908101919091529081906060820190565b0390a160009190565b606460405162461bcd60e51b815260206004820152602060248201527f61646420726573657276657320756e6578706563746564206f766572666c6f776044820152fd5b50613366612d6b565b919060018060a01b036012541690813b156103a75760405163a9059cbb60e01b81526001600160a01b0394909416600485015260248401526000929083908290604490829084905af180156109d15761442b575b503d8015614420576020146143cc575080fd5b90602081803e515b156143db57565b60405162461bcd60e51b815260206004820152601960248201527f544f4b454e5f5452414e534645525f4f55545f4641494c4544000000000000006044820152606490fd5b5090506000196143d4565b614434906103fc565b386143b9565b1561444157565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6040519060e082018281106001600160401b0382111761040f576040528160c06000918281528260208201528260408201528260608201528260808201528260a08201520152565b6144f36144a3565b6144fb612645565b9093614512602084019560408501938452866138b3565b845161451d81611659565b61452681611659565b6148ce57600019810361486c57506001600160a01b0383166000908152600e6020526040902061457c916145719154906060850191825251906145676107cc565b9182525190612968565b6080840152846138b3565b825161458781611659565b61459081611659565b61484e575b6005546145aa906001600160a01b0316610939565b60608201805160405163eabe7d9160e01b81523060048201526001600160a01b038616602482015260448101919091526000959192602090829060649082908a905af19081156109d157869161482f575b5080614820575060095443036148135761462c9061461d600d5484519061336c565b929060a08601938452826138b3565b805161463781611659565b61464081611659565b6147f1576001600160a01b0385166000908152600e6020526040902061467e92919061466f905485519061336c565b939060c08701948552826138b3565b805161468981611659565b61469281611659565b6147ce575060806146a161299b565b9401938451116147c05751600d55516001600160a01b0384166000818152600e602090815260409182902093909355835190519081523092600080516020615b0b83398151915291a381518151604080516001600160a01b038716815260208101939093528201527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a92990606090a1600554614744906001600160a01b0316610939565b8251915191813b156147bc576040516351dff98960e01b81523060048201526001600160a01b038616602482015260448101919091526064810192909252909291908490849060849082905afa9283156109d15761083c936147a9575b505190614365565b806109cb6147b6926103fc565b386147a1565b8580fd5b50505050505061083c612dc3565b94505050505061083c9150516147e381611659565b6147ec81611659565b6136a4565b935050505061083c91505161480581611659565b61480e81611659565b613678565b505050505061083c612d97565b935050505061083c915061364c565b614848915060203d6020116109fe576109f0818361044a565b386145fb565b505061083c905161485e81611659565b61486781611659565b613620565b806148979261488c92608086015251906148846107cc565b918252615a04565b6060840152846138b3565b82516148a281611659565b6148ab81611659565b1561459557505061083c90516148c081611659565b6148c981611659565b6135f4565b5050505061083c90516148e081611659565b6148e981611659565b6135c8565b600091801580159081614ac1575b6149059061443a565b61490d6144a3565b91614916612645565b91909261492e602086019460408701948552856138b3565b835161493981611659565b61494281611659565b614aac5715614a555761499b91614990918715614a4b57506001600160a01b0386166000908152600e602052604090205460608601525b516149826107cc565b908152606085015190612968565b6080850152826138b3565b80516149a681611659565b6149af81611659565b614a39575b6005546149c9906001600160a01b0316610939565b60608301805160405163eabe7d9160e01b81523060048201526001600160a01b03871660248201526044810191909152909291602090829060649082908a905af19081156109d157869161482f575080614820575060095443036148135761462c9061461d600d5484519061336c565b91505061083c91505161485e81611659565b6060860152614979565b506000608084015251614a8190614a7690614a6e6107cc565b9081526159eb565b6060850152826138b3565b8051614a8c81611659565b614a9581611659565b156149b45791505061083c9150516148c081611659565b50505091505061083c9150516148e081611659565b5060016148fc565b81810292918115918404141715612fa457565b8015614b1557670de0b6b3a7640000808202918083048203612fa457614b02908361427a565b14614b105750600290600090565b600091565b50600090600090565b670de0b6b3a764000090808202918204808203612fa45714614b105750600290600090565b908115614b5e57614b02614b578284614ac9565b928361427a565b5050600090600090565b15614b7557600090600090565b600190600090565b908015614b8d57614b109161427a565b5050600190600090565b15614b9e57565b606460405162461bcd60e51b815260206004820152602060248201527f4d494e545f45584348414e47455f43414c43554c4154494f4e5f4641494c45446044820152fd5b15614be957565b60405162461bcd60e51b815260206004820152602860248201527f4d494e545f4e45575f544f54414c5f535550504c595f43414c43554c4154494f6044820152671397d1905253115160c21b6064820152608490fd5b15614c4657565b60405162461bcd60e51b815260206004820152602b60248201527f4d494e545f4e45575f4143434f554e545f42414c414e43455f43414c43554c4160448201526a151253d397d1905253115160aa1b6064820152608490fd5b9060209081838203126103a75782516001600160401b03938482116103a7570181601f820112156103a757805193841161040f578360051b9060405194614ce88584018761044a565b855283808601928201019283116103a7578301905b828210614d0b575050505090565b81518152908301908301614cfd565b60055490929190614d33906001600160a01b0316610939565b604051634ef4c3e160e01b81523060048201526001600160a01b038516602482015260448101839052600094916020908290606490829089905af19081156109d1578591614fd8575b5080614fcc57506009544303614fc25783614d956144a3565b91614d9e612645565b93614db4602082019260408301968752836138b3565b8151614dbf81611659565b614dc881611659565b614fa057614dd88495968461589f565b60c082019681885251614de96107cc565b908152614df591615a04565b929060608301938452614e0890826138b3565b8051614e1381611659565b614e1c81611659565b15614e2690614b97565b600d548351614e3491613804565b919060808401928352614e4790826138b3565b8051614e5281611659565b614e5b81611659565b15614e6590614be2565b6001600160a01b0385166000908152600e60205260409020548451614e8991613804565b9360a001938452614e9a90826138b3565b51614ea481611659565b614ead81611659565b15614eb790614c3f565b51600d55516001600160a01b0383166000908152600e602052604090205584518151604080516001600160a01b038616815260208101939093528201528060608101037f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f91a1516040519081526001600160a01b038216903090600080516020615b0b83398151915290602090a3600554604051631ea016b760e21b81526001600160a01b039283166004820152938492602492849291165af180156109d157614f8057505190565b614f9b903d8086833e614f93818361044a565b810190614c9f565b505190565b50925050506141a0915051614fb481611659565b614fbd81611659565b6136fc565b50506141a0612def565b90506141a091506136d0565b614ff1915060203d6020116109fe576109f0818361044a565b38614d7c565b906150326040516150078161042f565b60158152747375627472616374696f6e20756e646572666c6f7760581b602082015283831115615a4e565b8103908111612fa45790565b670de0b6b3a76400009161506191600060405161505a81610414565b5251615a0e565b8060405161506e81610414565b520490565b604051916150808361042f565b60118352706164646974696f6e206f766572666c6f7760781b6020840152810191828211612fa45761083c91831015615a4e565b60405190608082018281106001600160401b0382111761040f5760405260006060838281528260208201528260408201520152565b600554909190615101906001600160a01b0316610939565b60405163368f515360e21b81523060048201526001600160a01b03841660248201526044810183905290602090829060649082906000905af19081156109d15760009161533e575b508061533257506009544303615328578061516261299b565b1061531e5761516f6150b4565b916151946151856001600160a01b0383166131d5565b919060208601928352856138b3565b835161519f81611659565b6151a881611659565b6152ff57926151bb836151ca9551613804565b949060408301958652826138b3565b80516151d581611659565b6151de81611659565b6152df57615201906151f284600b54613804565b929060608301938452826138b3565b805161520c81611659565b61521581611659565b6152be5750837f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80916152b9955161525e8560018060a01b03166000526010602052604060002090565b55600a546001600160a01b038516600090815260106020526040902060010155519061528982600b55565b51604080516001600160a01b038616815260208101879052908101919091526060810191909152608090a1614365565b600090565b9250505061083c9150516152d181611659565b6152da81611659565b6137ac565b91505061083c9150516152f181611659565b6152fa81611659565b613780565b50505061083c905161531081611659565b61531981611659565b613754565b505061083c612e47565b505061083c612e1b565b905061083c9150613728565b615357915060203d6020116109fe576109f0818361044a565b38615149565b91908260409103126103a7576020825192015190565b1561537a57565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b156153e257565b60405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606490fd5b1561542e57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b60055492949392615483906001600160a01b0316610939565b60408051632fe3f38f60e11b81523060048201526001600160a01b03958616602482018190528487166044830152858716606483015260848201899052602098929690939091908990829060a49082905afa9081156109d1576000916157ea575b50806157d8575060095443036157c8578551636c540baf60e01b81528881600481875afa9081156109d1576000916157ab575b50430361579b578084169085161461578c57801561577d57600019811461576e57615543908484613f5e565b9690806157545750600554615560906001600160a01b0316610939565b855163c488847b60e01b81523060048201526001600160a01b03841660248201526044810189905295908190879060649082905afa9586156109d157600090600097615722575b506155b29015615373565b80516370a0823160e01b81526001600160a01b0386166004820152918083602481875afa80156109d157876155f3918b95600091615705575b5010156153db565b30840361566c575091859161430f936156386156327f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529989893061390b565b15615427565b516001600160a01b0395861681529585166020870152604086015290921660608401526080830191909152819060a0820190565b815163b2a02ff160e01b81526001600160a01b03868116600483015287166024820152604481018890529096919391925086816064816000865af19384156109d15761563861430f958b937f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529a6000926156e8575b5050615632565b6156fe9250803d106109fe576109f0818361044a565b38806156e1565b61571c9150843d86116109fe576109f0818361044a565b386155eb565b6155b297506157479150823d841161574d575b61573f818361044a565b81019061535d565b966155a7565b503d615735565b9450505050506133669192506157698161160a565b612f4f565b50505050509050613366612f23565b50505050509050613366612ef7565b50505050509050613366612ecb565b5050505050509050613366612e9f565b6157c29150893d8b116109fe576109f0818361044a565b38615517565b5050505050509050613366612e73565b955050505050506133669192506137d8565b6158019150893d8b116109fe576109f0818361044a565b386154e4565b1561580e57565b60405162461bcd60e51b815260206004820152601860248201527f544f4b454e5f5452414e534645525f494e5f4641494c454400000000000000006044820152606490fd5b1561585a57565b60405162461bcd60e51b815260206004820152601a60248201527f544f4b454e5f5452414e534645525f494e5f4f564552464c4f570000000000006044820152606490fd5b6012546040516370a0823160e01b80825230600483015260209491936001600160a01b03909316929091908585602481875afa9485156109d1576000956159cc575b50833b156103a7576040516323b872dd60e01b81526001600160a01b03919091166004820152306024820152604481019190915260008160648183875af180156109d1576159b9575b503d8481156159ab575060201461594057600080fd5b8390816000803e615952600051615807565b60405190815230600482015291829060249082905afa9081156109d15761083c9360009261598e575b505061598982821015615853565b612f97565b6159a49250803d106109fe576109f0818361044a565b388061597b565b919050615952600019615807565b806109cb6159c6926103fc565b3861592a565b6159e4919550863d88116109fe576109f0818361044a565b93386158e1565b6129729060006040516159fd81610414565b52516141ff565b9061297291615a76565b61083c9160405191615a1f8361042f565b601783527f6d756c7469706c69636174696f6e206f766572666c6f770000000000000000006020840152615aae565b15615a565750565b60405162461bcd60e51b8152908190615a72906004830161046b565b0390fd5b615a91909291926000604051615a8b81610414565b52614b1e565b90926004841015610bdb578361426b5761260b9293505190614235565b9182158015615ae2575b615ada5781615ad4615acd61083c9486614ac9565b948561427a565b14615a4e565b505050600090565b508115615ab856fe45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212209b70a804d1f58250f63dc48c29e1d9d2ff15d307aa7586b47ff68bb042592e4564736f6c63430008170033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.