Transactions
Token Transfers
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been partially verified via Sourcify.
View contract in Sourcify repository
- Contract name:
- AuETH
- Optimization enabled
- true
- Compiler version
- v0.8.11+commit.d7f03943
- Optimization runs
- 3000
- EVM Version
- london
- Verified at
- 2023-09-03T21:22:48.939936Z
Constructor Arguments
000000000000000000000000817af6cfaf35bdc1a634d6cc94ee9e4c68369aeb0000000000000000000000000bc03bd8d4af1e8bab2194860268f5774c9d400a000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000b969526ebc8dd4b1d816da846ee08180515ffbbc0000000000000000000000000000000000000000000000000000000000000005617545544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056175455448000000000000000000000000000000000000000000000000000000
Arg [0] (address) : 0x817af6cfaf35bdc1a634d6cc94ee9e4c68369aeb
Arg [1] (address) : 0x0bc03bd8d4af1e8bab2194860268f5774c9d400a
Arg [2] (uint256) : 200000000000000000000000000
Arg [3] (string) : auETH
Arg [4] (string) : auETH
Arg [5] (uint8) : 8
Arg [6] (address) : 0xb969526ebc8dd4b1d816da846ee08180515ffbbc
AuETH.sol
pragma solidity 0.8.11; 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 plyTokens) external virtual; function exitMarket(address plyToken) external virtual; /*** Policy Hooks ***/ function mintAllowed(address plyToken, address minter, uint mintAmount) external virtual; function redeemAllowed(address plyToken, address redeemer, uint redeemTokens) external virtual; function borrowAllowed(address plyToken, address borrower, uint borrowAmount) external virtual; function repayBorrowAllowed( address plyToken, address payer, address borrower, uint repayAmount) external virtual; function liquidateBorrowAllowed( address plyTokenBorrowed, address plyTokenCollateral, address liquidator, address borrower, uint repayAmount) external virtual; function seizeAllowed( address plyTokenCollateral, address plyTokenBorrowed, address liquidator, address borrower, uint seizeTokens) external virtual; function transferAllowed(address plyToken, address src, address dst, uint transferTokens) external virtual; /*** Liquidity/Liquidation Calculations ***/ function liquidateCalculateSeizeTokens( address plyTokenBorrowed, address plyTokenCollateral, uint repayAmount) external view virtual returns (uint); } /** * @title Aurigami Finance's 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 timestmp * @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 timestmp (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 timestmp * @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 timestmp (as a percentage, and scaled by 1e18) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view virtual returns (uint); } /** * @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); } /** * @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); } // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } } contract AuTokenStorage is ReentrancyGuard { /** * @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 immutable public decimals; /** * @notice Maximum borrow rate that can ever be applied (.0005% / block) */ uint internal constant borrowRateMaxMantissa = 0.0005e16; /** * @notice Maximum fraction of interest that can be set aside for reserves */ uint internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract */ address payable public admin; /** * @notice Pending administrator for this contract */ address payable public pendingAdmin; /** * @notice Contract which oversees inter-auToken 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 AuTokens (used when totalSupply = 0) */ uint internal immutable initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves */ uint public reserveFactorMantissa; /** * @notice Block number that interest was last accrued at */ uint public accrualBlockTimestamp; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ uint public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ uint public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ uint public totalReserves; /** * @notice Total number of tokens in circulation */ uint public totalSupply; /** * @notice Official record of token balances for each account */ mapping (address => uint) internal accountTokens; /** * @notice Approved token transfer amounts on behalf of others */ mapping (address => mapping (address => uint)) 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 { uint principal; uint 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 */ uint public protocolSeizeShareMantissa; constructor(uint8 decimals_, uint256 initialExchangeRateMantissa_) ReentrancyGuard() { require(initialExchangeRateMantissa_ > 0, "initial exchange rate must be greater than zero."); decimals = decimals_; initialExchangeRateMantissa = initialExchangeRateMantissa_; } } abstract contract AuTokenInterface is AuTokenStorage { /** * @notice Indicator that this is a AuToken contract (for inspection) */ bool public constant isAuToken = true; /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); /** * @notice Event emitted when tokens are minted */ event Mint(address minter, uint mintAmount, uint mintTokens); /** * @notice Event emitted when tokens are redeemed */ event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); /** * @notice Event emitted when underlying is borrowed */ event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is repaid */ event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is liquidated */ event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address auTokenCollateral, uint 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(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); /** * @notice Event emitted when the protocol seize share is changed */ event NewProtocolSeizeShare(uint oldProtocolSeizeShareMantissa, uint newProtocolSeizeShareMantissa); /** * @notice Event emitted when the reserves are added */ event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); /** * @notice Event emitted when the reserves are reduced */ event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); /** * @notice EIP20 Transfer event */ event Transfer(address indexed from, address indexed to, uint amount); /** * @notice EIP20 Approval event */ event Approval(address indexed owner, address indexed spender, uint amount); /*** User Interface ***/ function transfer(address dst, uint amount) external virtual returns (bool); function transferFrom(address src, address dst, uint amount) external virtual returns (bool); function approve(address spender, uint amount) external virtual returns (bool); function allowance(address owner, address spender) external virtual view returns (uint); function balanceOf(address owner) external virtual view returns (uint); function balanceOfUnderlying(address owner) external virtual returns (uint); function getAccountSnapshot(address account) external virtual view returns (uint, uint, uint); function borrowRatePerTimestamp() external virtual view returns (uint); function supplyRatePerTimestamp() external virtual view returns (uint); function totalBorrowsCurrent() external virtual returns (uint); function borrowBalanceCurrent(address account) external virtual returns (uint); function borrowBalanceStored(address account) public view virtual returns (uint); function exchangeRateCurrent() public virtual returns (uint); function exchangeRateStored() public view virtual returns (uint); function getBorrowDataOfAccount(address account) public view virtual returns (uint, uint); function getSupplyDataOfOneAccount(address account) public view virtual returns (uint, uint); function getSupplyDataOfTwoAccount(address account1, address account2) public view virtual returns (uint, uint, uint); function getCash() external virtual view returns (uint); function accrueInterest() public virtual; function seize(address liquidator, address borrower, uint seizeTokens) external virtual; /*** Admin Functions ***/ function _setPendingAdmin(address payable newPendingAdmin) external virtual; function _acceptAdmin() external virtual; function _setComptroller(ComptrollerInterface newComptroller) public virtual; function _setReserveFactor(uint newReserveFactorMantissa) external virtual; function _reduceReserves(uint reduceAmount) external virtual; function _setInterestRateModel(InterestRateModel newInterestRateModel) public virtual; function _setProtocolSeizeShare(uint newProtocolSeizeShareMantissa) external virtual; } contract AuErc20Storage { /** * @notice Underlying asset for this AuToken */ address public immutable underlying; constructor(address underlying_) { underlying = underlying_; EIP20Interface(underlying).totalSupply(); } } abstract contract AuErc20Interface is AuErc20Storage { /*** User Interface ***/ function mint(uint mintAmount) external virtual; function redeem(uint redeemTokens) external virtual; function redeemUnderlying(uint redeemAmount) external virtual; function borrow(uint borrowAmount) external virtual; function repayBorrow(uint repayAmount) external virtual; function repayBorrowBehalf(address borrower, uint repayAmount) external virtual; function liquidateBorrow(address borrower, uint repayAmount, AuTokenInterface auTokenCollateral) external virtual; function sweepToken(EIP20NonStandardInterface token) external virtual; /*** Admin Functions ***/ function _addReserves(uint addAmount) external virtual; } /** * @title Exponential module for storing fixed-precision decimals * @author Compound * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places. * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is: * `Exp({mantissa: 5100000000000000000})`. */ contract ExponentialNoError { type Exp is uint; type Double is uint; uint constant internal expScale = 1e18; uint constant internal doubleScale = 1e36; uint constant internal halfExpScale = expScale/2; uint constant internal mantissaOne = expScale; /** * @dev Truncates the given exp to a whole number value. * For example, truncate(Exp{mantissa: 15 * expScale}) = 15 */ function truncate(Exp exp) pure internal returns (uint) { return Exp.unwrap(exp) / expScale; } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mul_ScalarTruncate(Exp a, uint scalar) pure internal returns (uint) { return truncate(mul_(a, scalar)); } /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ function mul_ScalarTruncateAddUInt(Exp a, uint scalar, uint addend) pure internal returns (uint) { return truncate(mul_(a, scalar)) + addend; } /** * @dev Checks if first Exp is less than second Exp. */ function lessThanExp(Exp left, Exp right) pure internal returns (bool) { return Exp.unwrap(left) < Exp.unwrap(right); } /** * @dev Checks if left Exp <= right Exp. */ function lessThanOrEqualExp(Exp left, Exp right) pure internal returns (bool) { return Exp.unwrap(left) <= Exp.unwrap(right); } /** * @dev Checks if left Exp > right Exp. */ function greaterThanExp(Exp left, Exp right) pure internal returns (bool) { return Exp.unwrap(left) > Exp.unwrap(right); } /** * @dev returns true if Exp is exactly zero */ function isZeroExp(Exp value) pure internal returns (bool) { return Exp.unwrap(value) == 0; } function safe224(uint n) pure internal returns (uint224) { require(n <= type(uint224).max, "safe224"); return uint224(n); } function add_(Exp a, Exp b) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) + Exp.unwrap(b)); } function add_(Double a, Double b) pure internal returns (Double) { return Double.wrap(Double.unwrap(a) + Double.unwrap(b)); } function sub_(Exp a, Exp b) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) - Exp.unwrap(b)); } function sub_(Double a, Double b) pure internal returns (Double) { return Double.wrap(Double.unwrap(a) - Double.unwrap(b)); } function mul_(Exp a, Exp b) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) * Exp.unwrap(b) / expScale); } function mul_(Exp a, uint b) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) * b); } function mul_(uint a, Exp b) pure internal returns (uint) { return a * Exp.unwrap(b) / expScale; } function mul_(Double a, Double b) pure internal returns (Double) { return Double.wrap(Double.unwrap(a) * Double.unwrap(b) / doubleScale); } function mul_(Double a, uint b) pure internal returns (Double) { return Double.wrap(Double.unwrap(a) * b); } function mul_(uint a, Double b) pure internal returns (uint) { return a * Double.unwrap(b) / doubleScale; } function div_(Exp a, Exp b) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) * expScale / Exp.unwrap(b)); } function div_(Exp a, uint b) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) / b); } function div_(uint a, Exp b) pure internal returns (uint) { return a * expScale / Exp.unwrap(b); } function div_(Double a, Double b) pure internal returns (Double) { return Double.wrap(Double.unwrap(a) * doubleScale / Double.unwrap(b)); } function div_(Double a, uint b) pure internal returns (Double) { return Double.wrap(Double.unwrap(a) / b); } function div_(uint a, Double b) pure internal returns (uint) { return a * doubleScale / Double.unwrap(b); } function fraction(uint a, uint b) pure internal returns (Double) { return Double.wrap(a * doubleScale / b); } } /** * @title Exponential module for storing fixed-precision decimals * @notice Exp is a user-defined type 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.wrap(5100000000000000000)`. * @notice All the Math errors were removed from this contract. Every math error will now cause the transaction to be reverted. */ contract Exponential is ExponentialNoError { /** * @dev Creates an exponential from numerator and denominator values. */ function getExp(uint num, uint denom) pure internal returns (Exp) { return Exp.wrap(num * expScale / denom); } /** * @dev Multiply an Exp by a scalar, returning a new Exp. */ function mulScalar(Exp a, uint scalar) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) * scalar); } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mulScalarTruncate(Exp a, uint scalar) pure internal returns (uint) { return truncate(mulScalar(a, scalar)); } /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ function mulScalarTruncateAddUInt(Exp a, uint scalar, uint addend) pure internal returns (uint) { return truncate(mulScalar(a, scalar)) + addend; } /** * @dev Divide an Exp by a scalar, returning a new Exp. */ function divScalar(Exp a, uint scalar) pure internal returns (Exp) { return Exp.wrap(Exp.unwrap(a) / scalar); } /** * @dev Divide a scalar by an Exp, returning a new Exp. */ function divScalarByExp(uint scalar, Exp divisor) pure internal returns (Exp) { /* We are doing this as: getExp(expScale * scalar, divisor) How it works: Exp = a / b; Scalar = s; `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` */ return getExp(expScale * scalar, Exp.unwrap(divisor)); } /** * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. */ function divScalarByExpTruncate(uint scalar, Exp divisor) pure internal returns (uint) { return truncate(divScalarByExp(scalar, divisor)); } /** * @dev Multiplies two exponentials, returning a new exponential. */ function mulExp(Exp a, Exp b) pure internal returns (Exp) { uint doubleScaledProduct = Exp.unwrap(a) * Exp.unwrap(b); // 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. uint doubleScaledProductWithHalfScale = halfExpScale + doubleScaledProduct; uint product = doubleScaledProductWithHalfScale / expScale; return Exp.wrap(product); } /** * @dev Multiplies two exponentials given their mantissas, returning a new exponential. */ function mulExp(uint a, uint b) pure internal returns (Exp) { return mulExp(Exp.wrap(a), Exp.wrap(b)); } /** * @dev Multiplies three exponentials, returning a new exponential. */ function mulExp3(Exp a, Exp b, Exp c) pure internal returns (Exp) { return mulExp(mulExp(a, b), 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 a, Exp b) pure internal returns (Exp) { return getExp(Exp.unwrap(a), Exp.unwrap(b)); } } /** * @title Aurigami Finance's AuToken Contract * @notice Abstract base for auTokens */ abstract contract AuToken is AuTokenInterface, Exponential { error MarketNotFresh(); error TokenInsufficientCash(); error Unauthorized(); error BadInput(); error InvalidCloseAmountRequested(); error InvalidAccountPair(); constructor( ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_, address admin_ ) AuTokenStorage(decimals_, initialExchangeRateMantissa_) { // set admin temporarily admin = payable(msg.sender); // Set the comptroller _setComptroller(comptroller_); // Initialize block timestamp and borrow index (block timestamp mocks depend on comptroller being set) accrualBlockTimestamp = getBlockTimestamp(); borrowIndex = mantissaOne; // Set the interest rate model (depends on block timestamp / borrow index) _setInterestRateModelFresh(interestRateModel_); name = name_; symbol = symbol_; admin = payable(admin_); } /** * @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 */ function transferTokens(address spender, address src, address dst, uint tokens) internal { /* Fail if transfer not allowed */ comptroller.transferAllowed(address(this), src, dst, tokens); /* Do not allow self-transfers */ if (src == dst) { revert BadInput(); } /* Get the allowance, infinite for the account owner */ uint startingAllowance = 0; if (spender == src) { startingAllowance = type(uint256).max; } else { startingAllowance = transferAllowances[src][spender]; } /* Do the calculations, checking for {under,over}flow */ uint allowanceNew; uint srcTokensNew; uint dstTokensNew; allowanceNew = startingAllowance - tokens; srcTokensNew = accountTokens[src] - tokens; dstTokensNew = accountTokens[dst] + tokens; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) accountTokens[src] = srcTokensNew; accountTokens[dst] = dstTokensNew; /* Eat some of the allowance (if necessary) */ if (startingAllowance != type(uint256).max) { transferAllowances[src][spender] = allowanceNew; } /* We emit a Transfer event */ emit Transfer(src, dst, tokens); // unused function // comptroller.transferVerify(address(this), src, dst, tokens); } /** * @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) { transferTokens(msg.sender, msg.sender, dst, amount); return true; } /** * @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) { transferTokens(msg.sender, src, dst, amount); return true; } /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external override returns (bool) { address src = msg.sender; transferAllowances[src][spender] = amount; emit Approval(src, spender, amount); return true; } /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view override returns (uint256) { return transferAllowances[owner][spender]; } /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ function balanceOf(address owner) external view override returns (uint256) { return accountTokens[owner]; } /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ function balanceOfUnderlying(address owner) external override returns (uint) { Exp exchangeRate = Exp.wrap(exchangeRateCurrent()); uint balance = mulScalarTruncate(exchangeRate, accountTokens[owner]); 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 (token balance, borrow balance, exchange rate mantissa) */ function getAccountSnapshot(address account) external view override returns (uint, uint, uint) { uint auTokenBalance = accountTokens[account]; uint borrowBalance = borrowBalanceStoredInternal(account); uint exchangeRateMantissa = exchangeRateStoredInternal(); return (auTokenBalance, borrowBalance, exchangeRateMantissa); } /** * @dev Function to simply retrieve block timestamp * This exists mainly for inheriting test contracts to stub this result. */ function getBlockTimestamp() internal view returns (uint) { return block.timestamp; } /** * @notice Returns the current per-timestamp borrow interest rate for this auToken * @return The borrow interest rate per timestmp, scaled by 1e18 */ function borrowRatePerTimestamp() external view override returns (uint) { return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves); } /** * @notice Returns the current per-timestamp supply interest rate for this auToken * @return The supply interest rate per timestmp, scaled by 1e18 */ function supplyRatePerTimestamp() external view override returns (uint) { 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 (uint) { accrueInterest(); 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 (uint) { accrueInterest(); return borrowBalanceStored(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return The calculated balance */ function borrowBalanceStored(address account) public view override returns (uint) { uint result = borrowBalanceStoredInternal(account); return result; } /** * @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 borrowBalanceStoredInternal(address account) internal view returns (uint) { uint principalTimesIndex; uint 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 0; } /* Calculate new borrow balance using the interest index: * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex */ principalTimesIndex = borrowSnapshot.principal * borrowIndex; result = principalTimesIndex / borrowSnapshot.interestIndex; return result; } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() public override nonReentrant returns (uint) { accrueInterest(); return exchangeRateStored(); } /** * @notice Calculates the exchange rate from the underlying to the AuToken * @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 (uint) { return exchangeRateStoredInternal(); } /** * @notice Retrieve the totalBorrows & borrowBalance of account * @param account The address whose data to be retrieved * @return (totalBorrows, borrowBalance of account) */ function getBorrowDataOfAccount(address account) public view override returns (uint, uint) { return (totalBorrows, borrowBalanceStored(account)); } /** * @notice Retrieve the totalSupply & auTokenBalance of account * @param account The address whose data to be retrieved * @return (totalSupply, auTokenBalance of account) */ function getSupplyDataOfOneAccount(address account) public view override returns (uint, uint) { return (totalSupply, accountTokens[account]); } /** * @notice Retrieve the totalSupply & auTokenBalance of two accounts * @param account1 The address whose data to be retrieved * @param account2 The address whose data to be retrieved * @return (totalSupply, auTokenBalance of account1, auTokenBalance of account2) */ function getSupplyDataOfTwoAccount(address account1, address account2) public view override returns (uint, uint, uint) { return (totalSupply, accountTokens[account1], accountTokens[account2]); } /** * @notice Calculates the exchange rate from the underlying to the AuToken * @dev This function does not accrue interest before calculating the exchange rate * @return (calculated exchange rate scaled by 1e18) */ function exchangeRateStoredInternal() internal view returns (uint) { uint _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return initialExchangeRateMantissa; } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ uint totalCash = getCashPrior(); uint cashPlusBorrowsMinusReserves; Exp exchangeRate; cashPlusBorrowsMinusReserves = totalCash + totalBorrows - totalReserves; exchangeRate = getExp(cashPlusBorrowsMinusReserves, _totalSupply); return Exp.unwrap(exchangeRate); } } /** * @notice Get cash balance of this auToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ function getCash() external view override returns (uint) { 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 override{ /* Remember the initial block timestamp */ uint currentBlockTimestamp = getBlockTimestamp(); uint accrualBlockTimestampPrior = accrualBlockTimestamp; /* Short-circuit accumulating 0 interest */ if (accrualBlockTimestampPrior == currentBlockTimestamp){ return; } /* Read the previous values out of storage */ uint cashPrior = getCashPrior(); uint borrowsPrior = totalBorrows; uint reservesPrior = totalReserves; uint borrowIndexPrior = borrowIndex; /* Calculate the current borrow interest rate */ uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high"); /* Calculate the number of timestamp elapsed since the last accrual */ uint timestampDelta = currentBlockTimestamp - accrualBlockTimestampPrior; /* * Calculate the interest accumulated into borrows and reserves and the new index: * simpleInterestFactor = borrowRate * timestampDelta * interestAccumulated = simpleInterestFactor * totalBorrows * totalBorrowsNew = interestAccumulated + totalBorrows * totalReservesNew = interestAccumulated * reserveFactor + totalReserves * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex */ Exp simpleInterestFactor; uint interestAccumulated; uint totalBorrowsNew; uint totalReservesNew; uint borrowIndexNew; simpleInterestFactor = mulScalar(Exp.wrap(borrowRateMantissa), timestampDelta); interestAccumulated = mulScalarTruncate(simpleInterestFactor, borrowsPrior); totalBorrowsNew = interestAccumulated + borrowsPrior; totalReservesNew = mulScalarTruncateAddUInt(Exp.wrap(reserveFactorMantissa), interestAccumulated, reservesPrior); borrowIndexNew = mulScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ accrualBlockTimestamp = currentBlockTimestamp; borrowIndex = borrowIndexNew; totalBorrows = totalBorrowsNew; totalReserves = totalReservesNew; /* We emit an AccrueInterest event */ emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew); } /** * @notice Sender supplies assets into the market and receives auTokens in exchange * @param mintAmount The amount of the underlying asset to supply * @return the actual mint amount. */ function mintInternal(uint mintAmount) internal nonReentrant returns (uint) { accrueInterest(); return mintFresh(msg.sender, mintAmount); } struct MintLocalVars { uint exchangeRateMantissa; uint mintTokens; uint totalSupplyNew; uint accountTokensNew; uint actualMintAmount; } /** * @notice User supplies assets into the market and receives auTokens 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 the actual mint amount. */ function mintFresh(address minter, uint mintAmount) internal returns (uint) { /* Fail if mint not allowed */ comptroller.mintAllowed(address(this), minter, mintAmount); /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } MintLocalVars memory vars; vars.exchangeRateMantissa = exchangeRateStoredInternal(); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call `doTransferIn` for the minter and the mintAmount. * Note: The auToken must handle variations between ERC-20 and ETH underlying. * `doTransferIn` reverts if anything goes wrong, since we can't be sure if * side-effects occurred. The function returns the amount actually transferred, * in case of a fee. On success, the auToken holds an additional `actualMintAmount` * of cash. */ vars.actualMintAmount = doTransferIn(minter, mintAmount); /* * We get the current exchange rate and calculate the number of auTokens to be minted: * mintTokens = actualMintAmount / exchangeRate */ vars.mintTokens = divScalarByExpTruncate(vars.actualMintAmount, Exp.wrap(vars.exchangeRateMantissa)); /* * We calculate the new total supply of auTokens and minter token balance, checking for overflow: * totalSupplyNew = totalSupply + mintTokens * accountTokensNew = accountTokens[minter] + mintTokens */ vars.totalSupplyNew = totalSupply + vars.mintTokens; vars.accountTokensNew = accountTokens[minter] + vars.mintTokens; /* We write previously calculated values into storage */ totalSupply = vars.totalSupplyNew; accountTokens[minter] = vars.accountTokensNew; /* We emit a Mint event, and a Transfer event */ emit Mint(minter, vars.actualMintAmount, vars.mintTokens); emit Transfer(address(this), minter, vars.mintTokens); /* We call the defense hook */ // unused function // comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens); return vars.actualMintAmount; } /** * @notice Sender redeems auTokens in exchange for the underlying asset * @param redeemTokens The number of auTokens to redeem into underlying */ function redeemInternal(uint redeemTokens) internal nonReentrant { accrueInterest(); redeemFresh(payable(msg.sender), redeemTokens, 0); } /** * @notice Sender redeems auTokens in exchange for a specified amount of underlying asset * @param redeemAmount The amount of underlying to receive from redeeming auTokens */ function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant { accrueInterest(); redeemFresh(payable(msg.sender), 0, redeemAmount); } struct RedeemLocalVars { uint exchangeRateMantissa; uint redeemTokens; uint redeemAmount; uint totalSupplyNew; uint accountTokensNew; } /** * @notice User redeems auTokens 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 auTokens 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 auTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) */ function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal{ require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); RedeemLocalVars memory vars; /* exchangeRate = invoke Exchange Rate Stored() */ vars.exchangeRateMantissa = exchangeRateStoredInternal(); /* If redeemTokensIn > 0: */ if (redeemTokensIn > 0) { /* * We calculate the exchange rate and the amount of underlying to be redeemed: * redeemTokens = redeemTokensIn * redeemAmount = redeemTokensIn x exchangeRateCurrent */ if (redeemTokensIn == type(uint256).max) { vars.redeemTokens = accountTokens[redeemer]; } else { vars.redeemTokens = redeemTokensIn; } vars.redeemAmount = mulScalarTruncate(Exp.wrap(vars.exchangeRateMantissa), vars.redeemTokens); } 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.redeemAmount = mulScalarTruncate(Exp.wrap(vars.exchangeRateMantissa), vars.redeemTokens); } else { vars.redeemAmount = redeemAmountIn; vars.redeemTokens = divScalarByExpTruncate(redeemAmountIn, Exp.wrap(vars.exchangeRateMantissa)); } } /* Fail if redeem not allowed */ comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens); /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } /* * We calculate the new total supply and redeemer balance, checking for underflow: * totalSupplyNew = totalSupply - redeemTokens * accountTokensNew = accountTokens[redeemer] - redeemTokens */ vars.totalSupplyNew = totalSupply - vars.redeemTokens; vars.accountTokensNew = accountTokens[redeemer] - vars.redeemTokens; /* Revert if protocol has insufficient cash */ if (getCashPrior() < vars.redeemAmount) { revert TokenInsufficientCash(); } ///////////////////////// // 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); // the comptroller's redeemVerify hook is inlined in here to save external call if (vars.redeemTokens == 0 && vars.redeemAmount > 0) { revert("redeemTokens zero"); } /* * We invoke doTransferOut for the redeemer and the redeemAmount. * Note: The auToken must handle variations between ERC-20 and ETH underlying. * On success, the auToken has redeemAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. * Note: This doTransferOut is moved here to prevent exploits similar to the CREAM hack. */ doTransferOut(redeemer, vars.redeemAmount); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowInternal(uint borrowAmount) internal nonReentrant { accrueInterest(); borrowFresh(payable(msg.sender), borrowAmount); } struct BorrowLocalVars { uint accountBorrows; uint accountBorrowsNew; uint totalBorrowsNew; } /** * @notice Users borrow assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowFresh(address payable borrower, uint borrowAmount) internal { /* Fail if borrow not allowed */ comptroller.borrowAllowed(address(this), borrower, borrowAmount); /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } /* Revert if protocol has insufficient underlying cash */ if (getCashPrior() < borrowAmount) { revert TokenInsufficientCash(); } BorrowLocalVars memory vars; /* * We calculate the new borrower and total borrow balances, failing on overflow: * accountBorrowsNew = accountBorrows + borrowAmount * totalBorrowsNew = totalBorrows + borrowAmount */ vars.accountBorrows = borrowBalanceStoredInternal(borrower); vars.accountBorrowsNew = vars.accountBorrows + borrowAmount; vars.totalBorrowsNew = totalBorrows + borrowAmount; ///////////////////////// // 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 call the defense hook */ // unused function // comptroller.borrowVerify(address(this), borrower, borrowAmount); /* * We invoke doTransferOut for the borrower and the borrowAmount. * Note: The auToken must handle variations between ERC-20 and ETH underlying. * On success, the auToken borrowAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. * Note: This doTransferOut is moved here to prevent exploits similar to the CREAM hack. */ doTransferOut(borrower, borrowAmount); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay * @return the actual repayment amount. */ function repayBorrowInternal(uint repayAmount) internal nonReentrant returns (uint) { accrueInterest(); 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 the actual repayment amount. */ function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant returns (uint) { accrueInterest(); return repayBorrowFresh(msg.sender, borrower, repayAmount); } struct RepayBorrowLocalVars { uint repayAmount; uint borrowerIndex; uint accountBorrows; uint accountBorrowsNew; uint totalBorrowsNew; uint actualRepayAmount; } /** * @notice Borrows are repaid by another user (possibly the borrower). * @param payer the account paying off the borrow * @param borrower the account with the debt being payed off * @param repayAmount the amount of undelrying tokens being returned * @return the actual repayment amount. */ function repayBorrowFresh(address payer, address borrower, uint repayAmount) internal returns (uint) { /* Fail if repayBorrow not allowed */ comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } RepayBorrowLocalVars memory vars; /* We remember the original borrowerIndex for verification purposes */ vars.borrowerIndex = accountBorrows[borrower].interestIndex; /* We fetch the amount the borrower owes, with accumulated interest */ vars.accountBorrows = borrowBalanceStoredInternal(borrower); /* If repayAmount == type(uint256).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 auToken must handle variations between ERC-20 and ETH underlying. * On success, the auToken holds an additional repayAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount); /* * We calculate the new borrower and total borrow balances, failing on underflow: * accountBorrowsNew = accountBorrows - actualRepayAmount * totalBorrowsNew = totalBorrows - actualRepayAmount */ vars.accountBorrowsNew = vars.accountBorrows - vars.actualRepayAmount; vars.totalBorrowsNew = totalBorrows - vars.actualRepayAmount; /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = vars.accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = vars.totalBorrowsNew; /* We emit a RepayBorrow event */ emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); /* We call the defense hook */ // unused function // comptroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex); return vars.actualRepayAmount; } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this auToken to be liquidated * @param auTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @return the actual repayment amount. */ function liquidateBorrowInternal(address borrower, uint repayAmount, AuTokenInterface auTokenCollateral) internal nonReentrant returns (uint) { accrueInterest(); auTokenCollateral.accrueInterest(); return liquidateBorrowFresh(msg.sender, borrower, repayAmount, auTokenCollateral); } /** * @notice The liquidator liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this auToken to be liquidated * @param liquidator The address repaying the borrow and seizing collateral * @param auTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @return the actual repayment amount. */ function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, AuTokenInterface auTokenCollateral) internal returns (uint) { /* Fail if liquidate not allowed */ comptroller.liquidateBorrowAllowed(address(this), address(auTokenCollateral), liquidator, borrower, repayAmount); /* Verify market's block timestamp equals current block timestamp */ if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } /* Verify auTokenCollateral market's block timestamp equals current block timestamp */ if (auTokenCollateral.accrualBlockTimestamp() != getBlockTimestamp()) { revert MarketNotFresh(); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert InvalidAccountPair(); } /* Fail if repayAmount = 0 */ if (repayAmount == 0) { revert InvalidCloseAmountRequested(); } /* Fail if repayAmount = type(uint256).max */ if (repayAmount == type(uint256).max) { revert InvalidCloseAmountRequested(); } /* Fail if repayBorrow fails */ uint actualRepayAmount = repayBorrowFresh(liquidator, borrower, repayAmount); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We calculate the number of collateral tokens that will be seized */ uint seizeTokens = comptroller.liquidateCalculateSeizeTokens(address(this), address(auTokenCollateral), actualRepayAmount); /* Revert if borrower collateral token balance < seizeTokens */ require(auTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH"); // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call if (address(auTokenCollateral) == address(this)) { seizeInternal(address(this), liquidator, borrower, seizeTokens); } else { auTokenCollateral.seize(liquidator, borrower, seizeTokens); } /* We emit a LiquidateBorrow event */ emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(auTokenCollateral), seizeTokens); /* We call the defense hook */ // unused function // comptroller.liquidateBorrowVerify(address(this), address(auTokenCollateral), liquidator, borrower, actualRepayAmount, seizeTokens); return actualRepayAmount; } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Will fail unless called by another auToken during the process of liquidation. * Its absolutely critical to use msg.sender as the borrowed auToken and not a parameter. * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of auTokens to seize */ function seize(address liquidator, address borrower, uint seizeTokens) external override nonReentrant { seizeInternal(msg.sender, liquidator, borrower, seizeTokens); } struct SeizeInternalLocalVars { uint borrowerTokensNew; uint liquidatorTokensNew; uint liquidatorSeizeTokens; uint protocolSeizeTokens; uint protocolSeizeAmount; uint exchangeRateMantissa; uint totalReservesNew; uint 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 AuToken. * Its absolutely critical to use msg.sender as the seizer auToken and not a parameter. * @param seizerToken The contract seizing the collateral (i.e. borrowed auToken) * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of auTokens to seize */ function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal{ /* Fail if seize not allowed */ comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert InvalidAccountPair(); } 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.borrowerTokensNew = accountTokens[borrower] - seizeTokens; vars.protocolSeizeTokens = mul_(seizeTokens, Exp.wrap(protocolSeizeShareMantissa)); vars.liquidatorSeizeTokens = seizeTokens - vars.protocolSeizeTokens; vars.exchangeRateMantissa = exchangeRateStoredInternal(); vars.protocolSeizeAmount = mul_ScalarTruncate(Exp.wrap(vars.exchangeRateMantissa), vars.protocolSeizeTokens); vars.totalReservesNew = totalReserves + vars.protocolSeizeAmount; vars.totalSupplyNew = totalSupply - vars.protocolSeizeTokens; vars.liquidatorTokensNew = accountTokens[liquidator] + vars.liquidatorSeizeTokens; ///////////////////////// // 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); } /*** 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. */ function _setPendingAdmin(address payable newPendingAdmin) external override { // Check caller = admin if (msg.sender != admin) { revert Unauthorized(); } // 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); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin */ function _acceptAdmin() external override{ // Check caller is pendingAdmin if (msg.sender != pendingAdmin) { revert Unauthorized(); } // 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(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); } /** * @notice Sets a new comptroller for the market * @dev Admin function to set a new comptroller */ function _setComptroller(ComptrollerInterface newComptroller) public override{ // Check caller is admin if (msg.sender != admin) { revert Unauthorized(); } 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); } /** * @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 */ function _setReserveFactor(uint newReserveFactorMantissa) external override nonReentrant{ accrueInterest(); _setReserveFactorFresh(newReserveFactorMantissa); } /** * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) * @dev Admin function to set a new reserve factor */ function _setReserveFactorFresh(uint newReserveFactorMantissa) internal{ // Check caller is admin if (msg.sender != admin) { revert Unauthorized(); } // Verify market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } // Check newReserveFactor ≤ maxReserveFactor if (newReserveFactorMantissa > reserveFactorMaxMantissa) { revert BadInput(); } uint oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); } /** * @notice Accrues interest and reduces reserves by transferring from msg.sender * @param addAmount Amount of addition to reserves */ function _addReservesInternal(uint addAmount) internal nonReentrant{ accrueInterest(); _addReservesFresh(addAmount); } /** * @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(uint addAmount) internal returns (uint) { // totalReserves + actualAddAmount uint totalReservesNew; uint actualAddAmount; // We revert unless market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the caller and the addAmount * Note: The auToken must handle variations between ERC-20 and ETH underlying. * On success, the auToken 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 actualAddAmount; } /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves */ function _reduceReserves(uint reduceAmount) external override nonReentrant{ accrueInterest(); _reduceReservesFresh(reduceAmount); } /** * @notice Reduces reserves by transferring to admin * @dev Requires fresh interest accrual * @param reduceAmount Amount of reduction to reserves */ function _reduceReservesFresh(uint reduceAmount) internal{ // totalReserves - reduceAmount uint totalReservesNew; // Check caller is admin if (msg.sender != admin) { revert Unauthorized(); } // We revert unless market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } // Revert if protocol has insufficient underlying cash if (getCashPrior() < reduceAmount) { revert TokenInsufficientCash(); } // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { revert BadInput(); } ///////////////////////// // 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); } /** * @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 */ function _setInterestRateModel(InterestRateModel newInterestRateModel) public override { accrueInterest(); _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 */ function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal { // Used to store old model for use in the event that is emitted on success InterestRateModel oldInterestRateModel; // Check caller is admin if (msg.sender != admin) { revert Unauthorized(); } // We revert unless market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } // 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); } /** * @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 */ function _setProtocolSeizeShare(uint newProtocolSeizeShareMantissa) external override nonReentrant{ accrueInterest(); _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 */ function _setProtocolSeizeShareFresh(uint newProtocolSeizeShareMantissa) internal{ // Used to store old share for use in the event that is emitted on success uint oldProtocolSeizeShareMantissa; // Check caller is admin if (msg.sender != admin) { revert Unauthorized(); } // We revert unless market's block timestamp equals current block timestamp if (accrualBlockTimestamp != getBlockTimestamp()) { revert MarketNotFresh(); } // 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); } /*** 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 (uint); /** * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee. * This may revert due to insufficient balance or insufficient allowance. */ function doTransferIn(address from, uint amount) internal virtual returns (uint); /** * @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting. * If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract. * If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions. */ function doTransferOut(address payable to, uint amount) internal virtual; } contract AuETH is AuToken { /** * @notice Construct a new AuETH 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_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token * @param admin_ Address of the administrator of this token */ constructor( ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_, address payable admin_ ) AuToken( comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_, admin_ ) {} /*** User Interface ***/ /** * @notice Sender supplies assets into the market and receives auTokens in exchange * @dev Reverts upon any failure */ function mint() external payable { mintInternal(msg.value); } /** * @notice Sender redeems auTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of auTokens to redeem into underlying */ function redeem(uint256 redeemTokens) external { redeemInternal(redeemTokens); } /** * @notice Sender redeems auTokens 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 */ function redeemUnderlying(uint256 redeemAmount) external { redeemUnderlyingInternal(redeemAmount); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrow(uint256 borrowAmount) external { borrowInternal(borrowAmount); } /** * @notice Sender repays their own borrow * @dev Reverts upon any failure */ function repayBorrow() external payable { repayBorrowInternal(msg.value); } /** * @notice Sender repays a borrow belonging to borrower * @dev Reverts upon any failure * @param borrower the account with the debt being payed off */ function repayBorrowBehalf(address borrower) external payable { repayBorrowBehalfInternal(borrower, msg.value); } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @dev Reverts upon any failure * @param borrower The borrower of this auToken to be liquidated * @param auTokenCollateral The market in which to seize collateral from the borrower */ function liquidateBorrow(address borrower, AuToken auTokenCollateral) external payable { liquidateBorrowInternal(borrower, msg.value, auTokenCollateral); } /** * @notice The sender adds to reserves. */ function _addReserves() external payable { _addReservesInternal(msg.value); } /** * @notice Send Ether to AuETH to mint */ fallback() external payable { mintInternal(msg.value); } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of Ether, before this message * @dev This excludes the value of the current message, if any * @return The quantity of Ether owned by this contract */ function getCashPrior() internal view override returns (uint256) { uint256 startingBalance = address(this).balance - msg.value; return startingBalance; } /** * @notice Perform the actual transfer in, which is a no-op * @param from Address sending the Ether * @param amount Amount of Ether being sent * @return The actual amount of Ether transferred */ function doTransferIn(address from, uint256 amount) internal override returns (uint256) { // Sanity checks require(msg.sender == from, "sender mismatch"); require(msg.value == amount, "value mismatch"); return amount; } function doTransferOut(address payable to, uint256 amount) internal override { /* Send the Ether, with minimal gas and revert on failure */ to.transfer(amount); } }
Compiler Settings
{"remappings":[],"optimizer":{"runs":3000,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london","compilationTarget":{"AuETH.sol":"AuETH"}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"comptroller_","internalType":"contract ComptrollerInterface"},{"type":"address","name":"interestRateModel_","internalType":"contract InterestRateModel"},{"type":"uint256","name":"initialExchangeRateMantissa_","internalType":"uint256"},{"type":"string","name":"name_","internalType":"string"},{"type":"string","name":"symbol_","internalType":"string"},{"type":"uint8","name":"decimals_","internalType":"uint8"},{"type":"address","name":"admin_","internalType":"address payable"}]},{"type":"error","name":"BadInput","inputs":[]},{"type":"error","name":"InvalidAccountPair","inputs":[]},{"type":"error","name":"InvalidCloseAmountRequested","inputs":[]},{"type":"error","name":"MarketNotFresh","inputs":[]},{"type":"error","name":"TokenInsufficientCash","inputs":[]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"event","name":"AccrueInterest","inputs":[{"type":"uint256","name":"cashPrior","internalType":"uint256","indexed":false},{"type":"uint256","name":"interestAccumulated","internalType":"uint256","indexed":false},{"type":"uint256","name":"borrowIndex","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalBorrows","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Borrow","inputs":[{"type":"address","name":"borrower","internalType":"address","indexed":false},{"type":"uint256","name":"borrowAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"accountBorrows","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalBorrows","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LiquidateBorrow","inputs":[{"type":"address","name":"liquidator","internalType":"address","indexed":false},{"type":"address","name":"borrower","internalType":"address","indexed":false},{"type":"uint256","name":"repayAmount","internalType":"uint256","indexed":false},{"type":"address","name":"auTokenCollateral","internalType":"address","indexed":false},{"type":"uint256","name":"seizeTokens","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Mint","inputs":[{"type":"address","name":"minter","internalType":"address","indexed":false},{"type":"uint256","name":"mintAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"mintTokens","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NewAdmin","inputs":[{"type":"address","name":"oldAdmin","internalType":"address","indexed":false},{"type":"address","name":"newAdmin","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"NewComptroller","inputs":[{"type":"address","name":"oldComptroller","internalType":"contract ComptrollerInterface","indexed":false},{"type":"address","name":"newComptroller","internalType":"contract ComptrollerInterface","indexed":false}],"anonymous":false},{"type":"event","name":"NewMarketInterestRateModel","inputs":[{"type":"address","name":"oldInterestRateModel","internalType":"contract InterestRateModel","indexed":false},{"type":"address","name":"newInterestRateModel","internalType":"contract InterestRateModel","indexed":false}],"anonymous":false},{"type":"event","name":"NewPendingAdmin","inputs":[{"type":"address","name":"oldPendingAdmin","internalType":"address","indexed":false},{"type":"address","name":"newPendingAdmin","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"NewProtocolSeizeShare","inputs":[{"type":"uint256","name":"oldProtocolSeizeShareMantissa","internalType":"uint256","indexed":false},{"type":"uint256","name":"newProtocolSeizeShareMantissa","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NewReserveFactor","inputs":[{"type":"uint256","name":"oldReserveFactorMantissa","internalType":"uint256","indexed":false},{"type":"uint256","name":"newReserveFactorMantissa","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Redeem","inputs":[{"type":"address","name":"redeemer","internalType":"address","indexed":false},{"type":"uint256","name":"redeemAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"redeemTokens","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RepayBorrow","inputs":[{"type":"address","name":"payer","internalType":"address","indexed":false},{"type":"address","name":"borrower","internalType":"address","indexed":false},{"type":"uint256","name":"repayAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"accountBorrows","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalBorrows","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ReservesAdded","inputs":[{"type":"address","name":"benefactor","internalType":"address","indexed":false},{"type":"uint256","name":"addAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"newTotalReserves","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ReservesReduced","inputs":[{"type":"address","name":"admin","internalType":"address","indexed":false},{"type":"uint256","name":"reduceAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"newTotalReserves","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"fallback","stateMutability":"payable"},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_acceptAdmin","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"_addReserves","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_reduceReserves","inputs":[{"type":"uint256","name":"reduceAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_setComptroller","inputs":[{"type":"address","name":"newComptroller","internalType":"contract ComptrollerInterface"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_setInterestRateModel","inputs":[{"type":"address","name":"newInterestRateModel","internalType":"contract InterestRateModel"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_setPendingAdmin","inputs":[{"type":"address","name":"newPendingAdmin","internalType":"address payable"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_setProtocolSeizeShare","inputs":[{"type":"uint256","name":"newProtocolSeizeShareMantissa","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_setReserveFactor","inputs":[{"type":"uint256","name":"newReserveFactorMantissa","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"accrualBlockTimestamp","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"accrueInterest","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address payable"}],"name":"admin","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOfUnderlying","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"borrow","inputs":[{"type":"uint256","name":"borrowAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"borrowBalanceCurrent","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"borrowBalanceStored","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"borrowIndex","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"borrowRatePerTimestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ComptrollerInterface"}],"name":"comptroller","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"exchangeRateCurrent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"exchangeRateStored","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAccountSnapshot","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBorrowDataOfAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCash","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getSupplyDataOfOneAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getSupplyDataOfTwoAccount","inputs":[{"type":"address","name":"account1","internalType":"address"},{"type":"address","name":"account2","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract InterestRateModel"}],"name":"interestRateModel","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isAuToken","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"liquidateBorrow","inputs":[{"type":"address","name":"borrower","internalType":"address"},{"type":"address","name":"auTokenCollateral","internalType":"contract AuToken"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"mint","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address payable"}],"name":"pendingAdmin","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"protocolSeizeShareMantissa","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"redeem","inputs":[{"type":"uint256","name":"redeemTokens","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"redeemUnderlying","inputs":[{"type":"uint256","name":"redeemAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"repayBorrow","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"repayBorrowBehalf","inputs":[{"type":"address","name":"borrower","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"reserveFactorMantissa","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"seize","inputs":[{"type":"address","name":"liquidator","internalType":"address"},{"type":"address","name":"borrower","internalType":"address"},{"type":"uint256","name":"seizeTokens","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"supplyRatePerTimestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalBorrows","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalBorrowsCurrent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalReserves","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"dst","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"src","internalType":"address"},{"type":"address","name":"dst","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]}]
Contract Creation Code
0x60c06040523480156200001157600080fd5b5060405162003eaa38038062003eaa8339810160408190526200003491620005ae565b600160005586868686868686818580620000ae5760405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b60648201526084015b60405180910390fd5b60ff90911660805260a052600380546001600160a01b03191633179055620000d68762000151565b42600855670de0b6b3a7640000600955620000f1866200029b565b83516200010690600190602087019062000410565b5082516200011c90600290602086019062000410565b50600380546001600160a01b0319166001600160a01b039290921691909117905550620006e19b505050505050505050505050565b6003546001600160a01b031633146200017c576040516282b42960e81b815260040160405180910390fd5b60055460408051623f1ee960e11b815290516001600160a01b0392831692841691627e3dd29160048083019260209291908290030181865afa158015620001c7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ed919062000679565b6200023b5760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606401620000a5565b600580546001600160a01b0319166001600160a01b0384811691821790925560408051928416835260208301919091527f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d91015b60405180910390a15050565b6003546000906001600160a01b03163314620002c9576040516282b42960e81b815260040160405180910390fd5b4260085414620002ec57604051638e10cbd360e01b815260040160405180910390fd5b600660009054906101000a90046001600160a01b03169050816001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000343573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000369919062000679565b620003b75760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606401620000a5565b600680546001600160a01b0319166001600160a01b0384811691821790925560408051928416835260208301919091527fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f92691016200028f565b8280546200041e90620006a4565b90600052602060002090601f0160209004810192826200044257600085556200048d565b82601f106200045d57805160ff19168380011785556200048d565b828001600101855582156200048d579182015b828111156200048d57825182559160200191906001019062000470565b506200049b9291506200049f565b5090565b5b808211156200049b5760008155600101620004a0565b6001600160a01b0381168114620004cc57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620004f757600080fd5b81516001600160401b0380821115620005145762000514620004cf565b604051601f8301601f19908116603f011681019082821181831017156200053f576200053f620004cf565b816040528381526020925086838588010111156200055c57600080fd5b600091505b8382101562000580578582018301518183018401529082019062000561565b83821115620005925760008385830101525b9695505050505050565b8051620005a981620004b6565b919050565b600080600080600080600060e0888a031215620005ca57600080fd5b8751620005d781620004b6565b6020890151909750620005ea81620004b6565b604089015160608a015191975095506001600160401b03808211156200060f57600080fd5b6200061d8b838c01620004e5565b955060808a01519150808211156200063457600080fd5b50620006438a828b01620004e5565b93505060a088015160ff811681146200065b57600080fd5b91506200066b60c089016200059c565b905092959891949750929550565b6000602082840312156200068c57600080fd5b815180151581146200069d57600080fd5b9392505050565b600181811c90821680620006b957607f821691505b60208210811415620006db57634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a0516137a362000707600039600061182c0152600061047301526137a36000f3fe6080604052600436106103295760003560e01c806395dd9193116101a5578063d1d86742116100ec578063e597461911610095578063f3fdb15a1161006f578063f3fdb15a14610910578063f851a44014610930578063fca7820b14610950578063fcb641471461097057610329565b8063e5974619146108c8578063e9c714f2146108db578063f2b3abbd146108f057610329565b8063dd62ed3e116100c6578063dd62ed3e14610825578063dec720a51461086b578063e107c8f31461088057610329565b8063d1d86742146107d0578063d3bd2c72146107f0578063db006a751461080557610329565b8063b71d1a0c1161014e578063c5ebeaec11610128578063c5ebeaec14610785578063cd91801c146107a5578063cfa99201146107ba57610329565b8063b71d1a0c14610715578063bd6d894d14610735578063c37f68e21461074a57610329565b8063aa5af0fd1161017f578063aa5af0fd146106cc578063aae40a2a146106e2578063b2a02ff1146106f557610329565b806395dd919314610677578063a6afed9514610697578063a9059cbb146106ac57610329565b80633b1d21a2116102745780636752e7021161021d57806383030846116101f7578063830308461461060c578063852a12e31461062c5780638f840ddd1461064c57806395d89b411461066257610329565b80636752e702146105ab57806370a08231146105c157806373acee98146105f757610329565b80634e4d9fea1161024e5780634e4d9fea146105635780635fe3b5671461056b578063601a0bf11461058b57610329565b80633b1d21a2146105185780634576b5db1461052d57806347bd37181461054d57610329565b8063182df0f5116102d6578063313ce567116102b0578063313ce567146104615780633a117d42146104a75780633af9e669146104f857610329565b8063182df0f5146103f457806323b872dd14610409578063267822471461042957610329565b8063173b990411610307578063173b99041461039a57806317bfdfbc146103be57806318160ddd146103de57610329565b806306fdde0314610335578063095ea7b3146103605780631249c58b14610390575b61033234610978565b50005b34801561034157600080fd5b5061034a6109f4565b60405161035791906134c8565b60405180910390f35b34801561036c57600080fd5b5061038061037b366004613550565b610a82565b6040519015158152602001610357565b610398610af0565b005b3480156103a657600080fd5b506103b060075481565b604051908152602001610357565b3480156103ca57600080fd5b506103b06103d936600461357c565b610afc565b3480156103ea57600080fd5b506103b0600c5481565b34801561040057600080fd5b506103b0610b67565b34801561041557600080fd5b50610380610424366004613599565b610b76565b34801561043557600080fd5b50600454610449906001600160a01b031681565b6040516001600160a01b039091168152602001610357565b34801561046d57600080fd5b506104957f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610357565b3480156104b357600080fd5b506104e36104c236600461357c565b600c546001600160a01b039091166000908152600d60205260409020549091565b60408051928352602083019190915201610357565b34801561050457600080fd5b506103b061051336600461357c565b610bea565b34801561052457600080fd5b506103b0610c25565b34801561053957600080fd5b5061039861054836600461357c565b610c2f565b34801561055957600080fd5b506103b0600a5481565b610398610da4565b34801561057757600080fd5b50600554610449906001600160a01b031681565b34801561059757600080fd5b506103986105a63660046135da565b610dad565b3480156105b757600080fd5b506103b060105481565b3480156105cd57600080fd5b506103b06105dc36600461357c565b6001600160a01b03166000908152600d602052604090205490565b34801561060357600080fd5b506103b0610e1e565b34801561061857600080fd5b506103986106273660046135da565b610e8c565b34801561063857600080fd5b506103986106473660046135da565b610ef5565b34801561065857600080fd5b506103b0600b5481565b34801561066e57600080fd5b5061034a610efe565b34801561068357600080fd5b506103b061069236600461357c565b610f0b565b3480156106a357600080fd5b50610398610f1e565b3480156106b857600080fd5b506103806106c7366004613550565b6110fa565b3480156106d857600080fd5b506103b060095481565b6103986106f03660046135f3565b61116d565b34801561070157600080fd5b50610398610710366004613599565b61117d565b34801561072157600080fd5b5061039861073036600461357c565b6111eb565b34801561074157600080fd5b506103b0611287565b34801561075657600080fd5b5061076a61076536600461357c565b6112fb565b60408051938452602084019290925290820152606001610357565b34801561079157600080fd5b506103986107a03660046135da565b61133c565b3480156107b157600080fd5b506103b0611345565b3480156107c657600080fd5b506103b060085481565b3480156107dc57600080fd5b506104e36107eb36600461357c565b6113ed565b3480156107fc57600080fd5b506103b0611405565b34801561081157600080fd5b506103986108203660046135da565b611478565b34801561083157600080fd5b506103b06108403660046135f3565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b34801561087757600080fd5b50610380600181565b34801561088c57600080fd5b5061076a61089b3660046135f3565b600c546001600160a01b038084166000908152600d60205260408082205492851682529020549250925092565b6103986108d636600461357c565b611481565b3480156108e757600080fd5b5061039861148f565b3480156108fc57600080fd5b5061039861090b36600461357c565b61157f565b34801561091c57600080fd5b50600654610449906001600160a01b031681565b34801561093c57600080fd5b50600354610449906001600160a01b031681565b34801561095c57600080fd5b5061039861096b3660046135da565b611590565b6103986115f9565b6000600260005414156109d25760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026000556109df610f1e565b6109e93383611604565b600160005592915050565b60018054610a019061362c565b80601f0160208091040260200160405190810160405280929190818152602001828054610a2d9061362c565b8015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b336000818152600e602090815260408083206001600160a01b03871680855292528083208590555191929182907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610ade9087815260200190565b60405180910390a35060019392505050565b610af934610978565b50565b600060026000541415610b515760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610b5e610f1e565b6109e982610f0b565b6000610b7161181f565b905090565b600060026000541415610bcb5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610bdc33858585611890565b506001806000559392505050565b600080610bf5611287565b6001600160a01b0384166000908152600d602052604081205491925090610c1d908390611ac3565b949350505050565b6000610b71611ad7565b6003546001600160a01b03163314610c59576040516282b42960e81b815260040160405180910390fd5b600554604080517e7e3dd200000000000000000000000000000000000000000000000000000000815290516001600160a01b0392831692841691627e3dd29160048083019260209291908290030181865afa158015610cbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce0919061367a565b610d2c5760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c73650000000060448201526064016109c9565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384811691821790925560408051928416835260208301919091527f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d91015b60405180910390a15050565b610af934611aea565b60026000541415610e005760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610e0d610f1e565b610e1681611b57565b506001600055565b600060026000541415610e735760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610e80610f1e565b50600a54600160005590565b60026000541415610edf5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610eec610f1e565b610e1681611d0f565b610af981611d9d565b60028054610a019061362c565b600080610f1783611e09565b9392505050565b600854429080821415610f2f575050565b6000610f39611ad7565b600a54600b546009546006546040517f15f240530000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604481018490529495509293919290916000916001600160a01b0316906315f2405390606401602060405180830381865afa158015610fbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe0919061369c565b905065048c273950008111156110385760405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c7920686967680000000060448201526064016109c9565b600061104487896136e4565b905060008060008060006110588787611e57565b9450611064858b611ac3565b93506110708a856136fb565b925061107f600754858b611e63565b915061108c85898a611e63565b60088e90556009819055600a849055600b839055604080518d815260208101879052908101829052606081018590529091507f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049060800160405180910390a150505050505050505050505050565b60006002600054141561114f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b600260005561116033808585611890565b5060018060005592915050565b611178823483611e7d565b505050565b600260005414156111d05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556111e133848484611f4b565b5050600160005550565b6003546001600160a01b03163314611215576040516282b42960e81b815260040160405180910390fd5b600480546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99101610d98565b6000600260005414156112dc5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556112e9610f1e565b6112f1610b67565b9050600160005590565b6001600160a01b0381166000908152600d6020526040812054819081908161132286611e09565b9050600061132e61181f565b929791965091945092505050565b610af981612252565b6006546000906001600160a01b03166315f24053611361611ad7565b600a54600b546040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526004810193909352602483019190915260448201526064015b602060405180830381865afa1580156113c9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b71919061369c565b600080600a546113fc84610f0b565b91509150915091565b6006546000906001600160a01b031663b8168816611421611ad7565b600a54600b546007546040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815260048101949094526024840192909252604483015260648201526084016113ac565b610af9816122bc565b61148b8134612328565b5050565b6004546001600160a01b031633146114b9576040516282b42960e81b815260040160405180910390fd5b60038054600480546001600160a01b038082167fffffffffffffffffffffffff000000000000000000000000000000000000000080861682179096559490911690915560408051919092168082526020820184905292917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc910160405180910390a1600454604080516001600160a01b03808516825290921660208301527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99101610d98565b611587610f1e565b610af9816123a1565b600260005414156115e35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556115f0610f1e565b610e1681612526565b611602346125f2565b565b6005546040517f4ef4c3e10000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038481166024830152604482018490526000921690634ef4c3e190606401600060405180830381600087803b15801561167257600080fd5b505af1158015611686573d6000803e3d6000fd5b505050506116914290565b600854146116b257604051638e10cbd360e01b815260040160405180910390fd5b6116e46040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6116ec61181f565b81526116f88484612664565b60808201819052815161170b9190612713565b60208201819052600c5461171f91906136fb565b6040808301919091526020808301516001600160a01b0387166000908152600d9092529190205461175091906136fb565b6060828101829052604080840151600c556001600160a01b0387166000818152600d60209081529083902094909455608085015184860151835192835294820152908101929092527f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a1836001600160a01b0316306001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836020015160405161180c91815260200190565b60405180910390a3608001519392505050565b600c5460009080611851577f000000000000000000000000000000000000000000000000000000000000000091505090565b600061185b611ad7565b9050600080600b54600a548461187191906136fb565b61187b91906136e4565b91506118878285612722565b95945050505050565b6005546040517fbdcdc2580000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0385811660248301528481166044830152606482018490529091169063bdcdc25890608401600060405180830381600087803b15801561190557600080fd5b505af1158015611919573d6000803e3d6000fd5b50505050816001600160a01b0316836001600160a01b03161415611969576040517f2bb9acf700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000836001600160a01b0316856001600160a01b0316141561198e57506000196119b6565b506001600160a01b038084166000908152600e60209081526040808320938816835292905220545b600080806119c485856136e4565b6001600160a01b0388166000908152600d60205260409020549093506119eb9086906136e4565b6001600160a01b0387166000908152600d6020526040902054909250611a129086906136fb565b6001600160a01b038089166000908152600d602052604080822086905591891681522081905590506000198414611a6c576001600160a01b038088166000908152600e60209081526040808320938c168352929052208390555b856001600160a01b0316876001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef87604051611ab191815260200190565b60405180910390a35050505050505050565b6000610f17611ad28484611e57565b612741565b600080611ae434476136e4565b92915050565b600060026000541415611b3f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055611b4c610f1e565b6109e9333384612755565b6003546000906001600160a01b03163314611b84576040516282b42960e81b815260040160405180910390fd5b4260085414611ba657604051638e10cbd360e01b815260040160405180910390fd5b81611baf611ad7565b1015611be7576040517f7541f53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54821115611c23576040517f2bb9acf700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600b54611c3191906136e4565b9050600b54811115611caa5760405162461bcd60e51b8152602060048201526024808201527f72656475636520726573657276657320756e657870656374656420756e64657260448201527f666c6f770000000000000000000000000000000000000000000000000000000060648201526084016109c9565b600b819055600354611cc5906001600160a01b03168361296b565b600354604080516001600160a01b0390921682526020820184905281018290527f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e90606001610d98565b6003546000906001600160a01b03163314611d3c576040516282b42960e81b815260040160405180910390fd5b4260085414611d5e57604051638e10cbd360e01b815260040160405180910390fd5b50601080549082905560408051828152602081018490527ff5815f353a60e815cce7553e4f60c533a59d26b1b5504ea4b6db8d60da3e4da29101610d98565b60026000541415611df05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055611dfd610f1e565b610e16336000836129a1565b6001600160a01b0381166000908152600f60205260408120805482918291611e3657506000949350505050565b6009548154611e459190613713565b92508060010154836118879190613732565b6000610f178284613713565b600081611e73611ad28686611e57565b610c1d91906136fb565b600060026000541415611ed25760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055611edf610f1e565b816001600160a01b031663a6afed956040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611f1a57600080fd5b505af1158015611f2e573d6000803e3d6000fd5b50505050611f3e33858585612d6d565b6001600055949350505050565b6005546040517fd02f73510000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03868116602483015285811660448301528481166064830152608482018490529091169063d02f73519060a401600060405180830381600087803b158015611fc857600080fd5b505af1158015611fdc573d6000803e3d6000fd5b50505050826001600160a01b0316826001600160a01b0316141561202c576040517f856b276500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61207460405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b0383166000908152600d60205260409020546120989083906136e4565b81526010546120a89083906131f1565b606082018190526120b990836136e4565b60408201526120c661181f565b60a0820181905260608201516120dc9190611ac3565b60808201819052600b546120f091906136fb565b60c08201526060810151600c5461210791906136e4565b60e08201526040808201516001600160a01b0386166000908152600d602052919091205461213591906136fb565b602082810191825260c0830151600b5560e0830151600c5582516001600160a01b038681166000818152600d855260408082209490945594519189168086529483902091909155818501519151918252917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3306001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83606001516040516121fa91815260200190565b60405180910390a3608081015160c08201516040805130815260208101939093528201527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59060600160405180910390a15050505050565b600260005414156122a55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556122b2610f1e565b610e163382613206565b6002600054141561230f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b600260005561231c610f1e565b610e16338260006129a1565b60006002600054141561237d5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b600260005561238a610f1e565b612395338484612755565b60016000559392505050565b6003546000906001600160a01b031633146123ce576040516282b42960e81b815260040160405180910390fd5b42600854146123f057604051638e10cbd360e01b815260040160405180910390fd5b600660009054906101000a90046001600160a01b03169050816001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612446573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246a919061367a565b6124b65760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c73650000000060448201526064016109c9565b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384811691821790925560408051928416835260208301919091527fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f9269101610d98565b6003546001600160a01b03163314612550576040516282b42960e81b815260040160405180910390fd5b426008541461257257604051638e10cbd360e01b815260040160405180910390fd5b670de0b6b3a76400008111156125b4576040517f2bb9acf700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780549082905560408051828152602081018490527faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f8214609101610d98565b600260005414156126455760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055612652610f1e565b61265b816133ca565b50506001600055565b6000336001600160a01b038416146126be5760405162461bcd60e51b815260206004820152600f60248201527f73656e646572206d69736d61746368000000000000000000000000000000000060448201526064016109c9565b81341461270d5760405162461bcd60e51b815260206004820152600e60248201527f76616c7565206d69736d6174636800000000000000000000000000000000000060448201526064016109c9565b50919050565b6000610f17611ad284846134ab565b600081612737670de0b6b3a764000085613713565b610f179190613732565b6000611ae4670de0b6b3a764000083613732565b6005546040517f24008a620000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03858116602483015284811660448301526064820184905260009216906324008a6290608401600060405180830381600087803b1580156127cb57600080fd5b505af11580156127df573d6000803e3d6000fd5b505050506127ea4290565b6008541461280b57604051638e10cbd360e01b815260040160405180910390fd5b6128446040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b0384166000908152600f60209081526040909120600101549082015261287084611e09565b604082015260001983141561288b576040810151815261288f565b8281525b61289d858260000151612664565b60a0820181905260408201516128b391906136e4565b606082015260a0810151600a546128ca91906136e4565b6080828101918252606080840180516001600160a01b038981166000818152600f60209081526040918290209485556009546001909501949094559651600a81905560a0808a015195518951948f168552948401929092529682019390935292830152918101929092527f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1910160405180910390a160a00151949350505050565b6040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611178573d6000803e3d6000fd5b8115806129ac575080155b612a1e5760405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d4160448201527f6d6f756e74496e206d757374206265207a65726f00000000000000000000000060648201526084016109c9565b612a506040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b612a5861181f565b81528215612ab357600019831415612a8f576001600160a01b0384166000908152600d602090815260409091205490820152612a97565b602081018390525b612aa981600001518260200151611ac3565b6040820152612b04565b600019821415612aea576001600160a01b0384166000908152600d60209081526040909120549082018190528151612aa991611ac3565b604081018290528051612afe908390612713565b60208201525b60055460208201516040517feabe7d910000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038781166024830152604482019290925291169063eabe7d9190606401600060405180830381600087803b158015612b7657600080fd5b505af1158015612b8a573d6000803e3d6000fd5b50505050612b954290565b60085414612bb657604051638e10cbd360e01b815260040160405180910390fd5b8060200151600c54612bc891906136e4565b60608201526020808201516001600160a01b0386166000908152600d909252604090912054612bf791906136e4565b60808201526040810151612c09611ad7565b1015612c41576040517f7541f53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060810151600c5560808101516001600160a01b0385166000818152600d60209081526040918290209390935582840151905190815230927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a360408082015160208084015183516001600160a01b038916815291820192909252918201527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a9299060600160405180910390a16020810151158015612d0c575060008160400151115b15612d595760405162461bcd60e51b815260206004820152601160248201527f72656465656d546f6b656e73207a65726f00000000000000000000000000000060448201526064016109c9565b612d6784826040015161296b565b50505050565b6005546040517f5fc7e71e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015286811660448301528581166064830152608482018590526000921690635fc7e71e9060a401600060405180830381600087803b158015612deb57600080fd5b505af1158015612dff573d6000803e3d6000fd5b50505050612e0a4290565b60085414612e2b57604051638e10cbd360e01b815260040160405180910390fd5b42826001600160a01b031663cfa992016040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8e919061369c565b14612eac57604051638e10cbd360e01b815260040160405180910390fd5b846001600160a01b0316846001600160a01b03161415612ef8576040517f856b276500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82612f2f576040517f2feff84300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600019831415612f6b576040517f2feff84300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612f78868686612755565b6005546040517fc488847b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038681166024830152604482018490529293506000929091169063c488847b90606401602060405180830381865afa158015612fee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613012919061369c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015291925082918616906370a0823190602401602060405180830381865afa158015613076573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061309a919061369c565b10156130e85760405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d554348000000000000000060448201526064016109c9565b6001600160a01b03841630141561310a5761310530888884611f4b565b61318e565b6040517fb2a02ff10000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015287811660248301526044820183905285169063b2a02ff190606401600060405180830381600087803b15801561317557600080fd5b505af1158015613189573d6000803e3d6000fd5b505050505b604080516001600160a01b0389811682528881166020830152818301859052861660608201526080810183905290517f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529181900360a00190a15095945050505050565b6000670de0b6b3a76400006127378385613713565b6005546040517fda3d454c0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038481166024830152604482018490529091169063da3d454c90606401600060405180830381600087803b15801561327357600080fd5b505af1158015613287573d6000803e3d6000fd5b505050506132924290565b600854146132b357604051638e10cbd360e01b815260040160405180910390fd5b806132bc611ad7565b10156132f4576040517f7541f53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61331860405180606001604052806000815260200160008152602001600081525090565b61332183611e09565b80825261332f9083906136fb565b6020820152600a546133429083906136fb565b6040828101918252602080840180516001600160a01b0388166000818152600f85528590209182556009546001909201919091559351600a819055905183519485529184018690529183015260608201527f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809060800160405180910390a1611178838361296b565b6000808042600854146133f057604051638e10cbd360e01b815260040160405180910390fd5b6133fa3385612664565b905080600b5461340a91906136fb565b9150600b5482101561345e5760405162461bcd60e51b815260206004820181905260248201527f61646420726573657276657320756e6578706563746564206f766572666c6f7760448201526064016109c9565b600b82905560408051338152602081018390529081018390527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59060600160405180910390a19392505050565b6000610f176134c284670de0b6b3a7640000613713565b83612722565b600060208083528351808285015260005b818110156134f5578581018301518582016040015282016134d9565b81811115613507576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b6001600160a01b0381168114610af957600080fd5b6000806040838503121561356357600080fd5b823561356e8161353b565b946020939093013593505050565b60006020828403121561358e57600080fd5b8135610f178161353b565b6000806000606084860312156135ae57600080fd5b83356135b98161353b565b925060208401356135c98161353b565b929592945050506040919091013590565b6000602082840312156135ec57600080fd5b5035919050565b6000806040838503121561360657600080fd5b82356136118161353b565b915060208301356136218161353b565b809150509250929050565b600181811c9082168061364057607f821691505b6020821081141561270d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006020828403121561368c57600080fd5b81518015158114610f1757600080fd5b6000602082840312156136ae57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156136f6576136f66136b5565b500390565b6000821982111561370e5761370e6136b5565b500190565b600081600019048311821515161561372d5761372d6136b5565b500290565b600082613768577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea2646970667358221220b8200d19653d5d2d8373a9410a82393d2422bdfa5de283404b6dde609b9ccc8764736f6c634300080b0033000000000000000000000000817af6cfaf35bdc1a634d6cc94ee9e4c68369aeb0000000000000000000000000bc03bd8d4af1e8bab2194860268f5774c9d400a000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000b969526ebc8dd4b1d816da846ee08180515ffbbc0000000000000000000000000000000000000000000000000000000000000005617545544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056175455448000000000000000000000000000000000000000000000000000000
Deployed ByteCode
0x6080604052600436106103295760003560e01c806395dd9193116101a5578063d1d86742116100ec578063e597461911610095578063f3fdb15a1161006f578063f3fdb15a14610910578063f851a44014610930578063fca7820b14610950578063fcb641471461097057610329565b8063e5974619146108c8578063e9c714f2146108db578063f2b3abbd146108f057610329565b8063dd62ed3e116100c6578063dd62ed3e14610825578063dec720a51461086b578063e107c8f31461088057610329565b8063d1d86742146107d0578063d3bd2c72146107f0578063db006a751461080557610329565b8063b71d1a0c1161014e578063c5ebeaec11610128578063c5ebeaec14610785578063cd91801c146107a5578063cfa99201146107ba57610329565b8063b71d1a0c14610715578063bd6d894d14610735578063c37f68e21461074a57610329565b8063aa5af0fd1161017f578063aa5af0fd146106cc578063aae40a2a146106e2578063b2a02ff1146106f557610329565b806395dd919314610677578063a6afed9514610697578063a9059cbb146106ac57610329565b80633b1d21a2116102745780636752e7021161021d57806383030846116101f7578063830308461461060c578063852a12e31461062c5780638f840ddd1461064c57806395d89b411461066257610329565b80636752e702146105ab57806370a08231146105c157806373acee98146105f757610329565b80634e4d9fea1161024e5780634e4d9fea146105635780635fe3b5671461056b578063601a0bf11461058b57610329565b80633b1d21a2146105185780634576b5db1461052d57806347bd37181461054d57610329565b8063182df0f5116102d6578063313ce567116102b0578063313ce567146104615780633a117d42146104a75780633af9e669146104f857610329565b8063182df0f5146103f457806323b872dd14610409578063267822471461042957610329565b8063173b990411610307578063173b99041461039a57806317bfdfbc146103be57806318160ddd146103de57610329565b806306fdde0314610335578063095ea7b3146103605780631249c58b14610390575b61033234610978565b50005b34801561034157600080fd5b5061034a6109f4565b60405161035791906134c8565b60405180910390f35b34801561036c57600080fd5b5061038061037b366004613550565b610a82565b6040519015158152602001610357565b610398610af0565b005b3480156103a657600080fd5b506103b060075481565b604051908152602001610357565b3480156103ca57600080fd5b506103b06103d936600461357c565b610afc565b3480156103ea57600080fd5b506103b0600c5481565b34801561040057600080fd5b506103b0610b67565b34801561041557600080fd5b50610380610424366004613599565b610b76565b34801561043557600080fd5b50600454610449906001600160a01b031681565b6040516001600160a01b039091168152602001610357565b34801561046d57600080fd5b506104957f000000000000000000000000000000000000000000000000000000000000000881565b60405160ff9091168152602001610357565b3480156104b357600080fd5b506104e36104c236600461357c565b600c546001600160a01b039091166000908152600d60205260409020549091565b60408051928352602083019190915201610357565b34801561050457600080fd5b506103b061051336600461357c565b610bea565b34801561052457600080fd5b506103b0610c25565b34801561053957600080fd5b5061039861054836600461357c565b610c2f565b34801561055957600080fd5b506103b0600a5481565b610398610da4565b34801561057757600080fd5b50600554610449906001600160a01b031681565b34801561059757600080fd5b506103986105a63660046135da565b610dad565b3480156105b757600080fd5b506103b060105481565b3480156105cd57600080fd5b506103b06105dc36600461357c565b6001600160a01b03166000908152600d602052604090205490565b34801561060357600080fd5b506103b0610e1e565b34801561061857600080fd5b506103986106273660046135da565b610e8c565b34801561063857600080fd5b506103986106473660046135da565b610ef5565b34801561065857600080fd5b506103b0600b5481565b34801561066e57600080fd5b5061034a610efe565b34801561068357600080fd5b506103b061069236600461357c565b610f0b565b3480156106a357600080fd5b50610398610f1e565b3480156106b857600080fd5b506103806106c7366004613550565b6110fa565b3480156106d857600080fd5b506103b060095481565b6103986106f03660046135f3565b61116d565b34801561070157600080fd5b50610398610710366004613599565b61117d565b34801561072157600080fd5b5061039861073036600461357c565b6111eb565b34801561074157600080fd5b506103b0611287565b34801561075657600080fd5b5061076a61076536600461357c565b6112fb565b60408051938452602084019290925290820152606001610357565b34801561079157600080fd5b506103986107a03660046135da565b61133c565b3480156107b157600080fd5b506103b0611345565b3480156107c657600080fd5b506103b060085481565b3480156107dc57600080fd5b506104e36107eb36600461357c565b6113ed565b3480156107fc57600080fd5b506103b0611405565b34801561081157600080fd5b506103986108203660046135da565b611478565b34801561083157600080fd5b506103b06108403660046135f3565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b34801561087757600080fd5b50610380600181565b34801561088c57600080fd5b5061076a61089b3660046135f3565b600c546001600160a01b038084166000908152600d60205260408082205492851682529020549250925092565b6103986108d636600461357c565b611481565b3480156108e757600080fd5b5061039861148f565b3480156108fc57600080fd5b5061039861090b36600461357c565b61157f565b34801561091c57600080fd5b50600654610449906001600160a01b031681565b34801561093c57600080fd5b50600354610449906001600160a01b031681565b34801561095c57600080fd5b5061039861096b3660046135da565b611590565b6103986115f9565b6000600260005414156109d25760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026000556109df610f1e565b6109e93383611604565b600160005592915050565b60018054610a019061362c565b80601f0160208091040260200160405190810160405280929190818152602001828054610a2d9061362c565b8015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b336000818152600e602090815260408083206001600160a01b03871680855292528083208590555191929182907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610ade9087815260200190565b60405180910390a35060019392505050565b610af934610978565b50565b600060026000541415610b515760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610b5e610f1e565b6109e982610f0b565b6000610b7161181f565b905090565b600060026000541415610bcb5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610bdc33858585611890565b506001806000559392505050565b600080610bf5611287565b6001600160a01b0384166000908152600d602052604081205491925090610c1d908390611ac3565b949350505050565b6000610b71611ad7565b6003546001600160a01b03163314610c59576040516282b42960e81b815260040160405180910390fd5b600554604080517e7e3dd200000000000000000000000000000000000000000000000000000000815290516001600160a01b0392831692841691627e3dd29160048083019260209291908290030181865afa158015610cbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce0919061367a565b610d2c5760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c73650000000060448201526064016109c9565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384811691821790925560408051928416835260208301919091527f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d91015b60405180910390a15050565b610af934611aea565b60026000541415610e005760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610e0d610f1e565b610e1681611b57565b506001600055565b600060026000541415610e735760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610e80610f1e565b50600a54600160005590565b60026000541415610edf5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055610eec610f1e565b610e1681611d0f565b610af981611d9d565b60028054610a019061362c565b600080610f1783611e09565b9392505050565b600854429080821415610f2f575050565b6000610f39611ad7565b600a54600b546009546006546040517f15f240530000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604481018490529495509293919290916000916001600160a01b0316906315f2405390606401602060405180830381865afa158015610fbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe0919061369c565b905065048c273950008111156110385760405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c7920686967680000000060448201526064016109c9565b600061104487896136e4565b905060008060008060006110588787611e57565b9450611064858b611ac3565b93506110708a856136fb565b925061107f600754858b611e63565b915061108c85898a611e63565b60088e90556009819055600a849055600b839055604080518d815260208101879052908101829052606081018590529091507f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049060800160405180910390a150505050505050505050505050565b60006002600054141561114f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b600260005561116033808585611890565b5060018060005592915050565b611178823483611e7d565b505050565b600260005414156111d05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556111e133848484611f4b565b5050600160005550565b6003546001600160a01b03163314611215576040516282b42960e81b815260040160405180910390fd5b600480546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99101610d98565b6000600260005414156112dc5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556112e9610f1e565b6112f1610b67565b9050600160005590565b6001600160a01b0381166000908152600d6020526040812054819081908161132286611e09565b9050600061132e61181f565b929791965091945092505050565b610af981612252565b6006546000906001600160a01b03166315f24053611361611ad7565b600a54600b546040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526004810193909352602483019190915260448201526064015b602060405180830381865afa1580156113c9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b71919061369c565b600080600a546113fc84610f0b565b91509150915091565b6006546000906001600160a01b031663b8168816611421611ad7565b600a54600b546007546040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815260048101949094526024840192909252604483015260648201526084016113ac565b610af9816122bc565b61148b8134612328565b5050565b6004546001600160a01b031633146114b9576040516282b42960e81b815260040160405180910390fd5b60038054600480546001600160a01b038082167fffffffffffffffffffffffff000000000000000000000000000000000000000080861682179096559490911690915560408051919092168082526020820184905292917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc910160405180910390a1600454604080516001600160a01b03808516825290921660208301527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99101610d98565b611587610f1e565b610af9816123a1565b600260005414156115e35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556115f0610f1e565b610e1681612526565b611602346125f2565b565b6005546040517f4ef4c3e10000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038481166024830152604482018490526000921690634ef4c3e190606401600060405180830381600087803b15801561167257600080fd5b505af1158015611686573d6000803e3d6000fd5b505050506116914290565b600854146116b257604051638e10cbd360e01b815260040160405180910390fd5b6116e46040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6116ec61181f565b81526116f88484612664565b60808201819052815161170b9190612713565b60208201819052600c5461171f91906136fb565b6040808301919091526020808301516001600160a01b0387166000908152600d9092529190205461175091906136fb565b6060828101829052604080840151600c556001600160a01b0387166000818152600d60209081529083902094909455608085015184860151835192835294820152908101929092527f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a1836001600160a01b0316306001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836020015160405161180c91815260200190565b60405180910390a3608001519392505050565b600c5460009080611851577f000000000000000000000000000000000000000000a56fa5b99019a5c800000091505090565b600061185b611ad7565b9050600080600b54600a548461187191906136fb565b61187b91906136e4565b91506118878285612722565b95945050505050565b6005546040517fbdcdc2580000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0385811660248301528481166044830152606482018490529091169063bdcdc25890608401600060405180830381600087803b15801561190557600080fd5b505af1158015611919573d6000803e3d6000fd5b50505050816001600160a01b0316836001600160a01b03161415611969576040517f2bb9acf700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000836001600160a01b0316856001600160a01b0316141561198e57506000196119b6565b506001600160a01b038084166000908152600e60209081526040808320938816835292905220545b600080806119c485856136e4565b6001600160a01b0388166000908152600d60205260409020549093506119eb9086906136e4565b6001600160a01b0387166000908152600d6020526040902054909250611a129086906136fb565b6001600160a01b038089166000908152600d602052604080822086905591891681522081905590506000198414611a6c576001600160a01b038088166000908152600e60209081526040808320938c168352929052208390555b856001600160a01b0316876001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef87604051611ab191815260200190565b60405180910390a35050505050505050565b6000610f17611ad28484611e57565b612741565b600080611ae434476136e4565b92915050565b600060026000541415611b3f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055611b4c610f1e565b6109e9333384612755565b6003546000906001600160a01b03163314611b84576040516282b42960e81b815260040160405180910390fd5b4260085414611ba657604051638e10cbd360e01b815260040160405180910390fd5b81611baf611ad7565b1015611be7576040517f7541f53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54821115611c23576040517f2bb9acf700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600b54611c3191906136e4565b9050600b54811115611caa5760405162461bcd60e51b8152602060048201526024808201527f72656475636520726573657276657320756e657870656374656420756e64657260448201527f666c6f770000000000000000000000000000000000000000000000000000000060648201526084016109c9565b600b819055600354611cc5906001600160a01b03168361296b565b600354604080516001600160a01b0390921682526020820184905281018290527f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e90606001610d98565b6003546000906001600160a01b03163314611d3c576040516282b42960e81b815260040160405180910390fd5b4260085414611d5e57604051638e10cbd360e01b815260040160405180910390fd5b50601080549082905560408051828152602081018490527ff5815f353a60e815cce7553e4f60c533a59d26b1b5504ea4b6db8d60da3e4da29101610d98565b60026000541415611df05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055611dfd610f1e565b610e16336000836129a1565b6001600160a01b0381166000908152600f60205260408120805482918291611e3657506000949350505050565b6009548154611e459190613713565b92508060010154836118879190613732565b6000610f178284613713565b600081611e73611ad28686611e57565b610c1d91906136fb565b600060026000541415611ed25760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055611edf610f1e565b816001600160a01b031663a6afed956040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611f1a57600080fd5b505af1158015611f2e573d6000803e3d6000fd5b50505050611f3e33858585612d6d565b6001600055949350505050565b6005546040517fd02f73510000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03868116602483015285811660448301528481166064830152608482018490529091169063d02f73519060a401600060405180830381600087803b158015611fc857600080fd5b505af1158015611fdc573d6000803e3d6000fd5b50505050826001600160a01b0316826001600160a01b0316141561202c576040517f856b276500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61207460405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b0383166000908152600d60205260409020546120989083906136e4565b81526010546120a89083906131f1565b606082018190526120b990836136e4565b60408201526120c661181f565b60a0820181905260608201516120dc9190611ac3565b60808201819052600b546120f091906136fb565b60c08201526060810151600c5461210791906136e4565b60e08201526040808201516001600160a01b0386166000908152600d602052919091205461213591906136fb565b602082810191825260c0830151600b5560e0830151600c5582516001600160a01b038681166000818152600d855260408082209490945594519189168086529483902091909155818501519151918252917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3306001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83606001516040516121fa91815260200190565b60405180910390a3608081015160c08201516040805130815260208101939093528201527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59060600160405180910390a15050505050565b600260005414156122a55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b60026000556122b2610f1e565b610e163382613206565b6002600054141561230f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b600260005561231c610f1e565b610e16338260006129a1565b60006002600054141561237d5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b600260005561238a610f1e565b612395338484612755565b60016000559392505050565b6003546000906001600160a01b031633146123ce576040516282b42960e81b815260040160405180910390fd5b42600854146123f057604051638e10cbd360e01b815260040160405180910390fd5b600660009054906101000a90046001600160a01b03169050816001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612446573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246a919061367a565b6124b65760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c73650000000060448201526064016109c9565b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384811691821790925560408051928416835260208301919091527fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f9269101610d98565b6003546001600160a01b03163314612550576040516282b42960e81b815260040160405180910390fd5b426008541461257257604051638e10cbd360e01b815260040160405180910390fd5b670de0b6b3a76400008111156125b4576040517f2bb9acf700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780549082905560408051828152602081018490527faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f8214609101610d98565b600260005414156126455760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c9565b6002600055612652610f1e565b61265b816133ca565b50506001600055565b6000336001600160a01b038416146126be5760405162461bcd60e51b815260206004820152600f60248201527f73656e646572206d69736d61746368000000000000000000000000000000000060448201526064016109c9565b81341461270d5760405162461bcd60e51b815260206004820152600e60248201527f76616c7565206d69736d6174636800000000000000000000000000000000000060448201526064016109c9565b50919050565b6000610f17611ad284846134ab565b600081612737670de0b6b3a764000085613713565b610f179190613732565b6000611ae4670de0b6b3a764000083613732565b6005546040517f24008a620000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03858116602483015284811660448301526064820184905260009216906324008a6290608401600060405180830381600087803b1580156127cb57600080fd5b505af11580156127df573d6000803e3d6000fd5b505050506127ea4290565b6008541461280b57604051638e10cbd360e01b815260040160405180910390fd5b6128446040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b0384166000908152600f60209081526040909120600101549082015261287084611e09565b604082015260001983141561288b576040810151815261288f565b8281525b61289d858260000151612664565b60a0820181905260408201516128b391906136e4565b606082015260a0810151600a546128ca91906136e4565b6080828101918252606080840180516001600160a01b038981166000818152600f60209081526040918290209485556009546001909501949094559651600a81905560a0808a015195518951948f168552948401929092529682019390935292830152918101929092527f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1910160405180910390a160a00151949350505050565b6040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611178573d6000803e3d6000fd5b8115806129ac575080155b612a1e5760405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d4160448201527f6d6f756e74496e206d757374206265207a65726f00000000000000000000000060648201526084016109c9565b612a506040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b612a5861181f565b81528215612ab357600019831415612a8f576001600160a01b0384166000908152600d602090815260409091205490820152612a97565b602081018390525b612aa981600001518260200151611ac3565b6040820152612b04565b600019821415612aea576001600160a01b0384166000908152600d60209081526040909120549082018190528151612aa991611ac3565b604081018290528051612afe908390612713565b60208201525b60055460208201516040517feabe7d910000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038781166024830152604482019290925291169063eabe7d9190606401600060405180830381600087803b158015612b7657600080fd5b505af1158015612b8a573d6000803e3d6000fd5b50505050612b954290565b60085414612bb657604051638e10cbd360e01b815260040160405180910390fd5b8060200151600c54612bc891906136e4565b60608201526020808201516001600160a01b0386166000908152600d909252604090912054612bf791906136e4565b60808201526040810151612c09611ad7565b1015612c41576040517f7541f53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060810151600c5560808101516001600160a01b0385166000818152600d60209081526040918290209390935582840151905190815230927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a360408082015160208084015183516001600160a01b038916815291820192909252918201527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a9299060600160405180910390a16020810151158015612d0c575060008160400151115b15612d595760405162461bcd60e51b815260206004820152601160248201527f72656465656d546f6b656e73207a65726f00000000000000000000000000000060448201526064016109c9565b612d6784826040015161296b565b50505050565b6005546040517f5fc7e71e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015286811660448301528581166064830152608482018590526000921690635fc7e71e9060a401600060405180830381600087803b158015612deb57600080fd5b505af1158015612dff573d6000803e3d6000fd5b50505050612e0a4290565b60085414612e2b57604051638e10cbd360e01b815260040160405180910390fd5b42826001600160a01b031663cfa992016040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8e919061369c565b14612eac57604051638e10cbd360e01b815260040160405180910390fd5b846001600160a01b0316846001600160a01b03161415612ef8576040517f856b276500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82612f2f576040517f2feff84300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600019831415612f6b576040517f2feff84300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612f78868686612755565b6005546040517fc488847b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038681166024830152604482018490529293506000929091169063c488847b90606401602060405180830381865afa158015612fee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613012919061369c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015291925082918616906370a0823190602401602060405180830381865afa158015613076573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061309a919061369c565b10156130e85760405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d554348000000000000000060448201526064016109c9565b6001600160a01b03841630141561310a5761310530888884611f4b565b61318e565b6040517fb2a02ff10000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015287811660248301526044820183905285169063b2a02ff190606401600060405180830381600087803b15801561317557600080fd5b505af1158015613189573d6000803e3d6000fd5b505050505b604080516001600160a01b0389811682528881166020830152818301859052861660608201526080810183905290517f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529181900360a00190a15095945050505050565b6000670de0b6b3a76400006127378385613713565b6005546040517fda3d454c0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038481166024830152604482018490529091169063da3d454c90606401600060405180830381600087803b15801561327357600080fd5b505af1158015613287573d6000803e3d6000fd5b505050506132924290565b600854146132b357604051638e10cbd360e01b815260040160405180910390fd5b806132bc611ad7565b10156132f4576040517f7541f53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61331860405180606001604052806000815260200160008152602001600081525090565b61332183611e09565b80825261332f9083906136fb565b6020820152600a546133429083906136fb565b6040828101918252602080840180516001600160a01b0388166000818152600f85528590209182556009546001909201919091559351600a819055905183519485529184018690529183015260608201527f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809060800160405180910390a1611178838361296b565b6000808042600854146133f057604051638e10cbd360e01b815260040160405180910390fd5b6133fa3385612664565b905080600b5461340a91906136fb565b9150600b5482101561345e5760405162461bcd60e51b815260206004820181905260248201527f61646420726573657276657320756e6578706563746564206f766572666c6f7760448201526064016109c9565b600b82905560408051338152602081018390529081018390527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59060600160405180910390a19392505050565b6000610f176134c284670de0b6b3a7640000613713565b83612722565b600060208083528351808285015260005b818110156134f5578581018301518582016040015282016134d9565b81811115613507576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b6001600160a01b0381168114610af957600080fd5b6000806040838503121561356357600080fd5b823561356e8161353b565b946020939093013593505050565b60006020828403121561358e57600080fd5b8135610f178161353b565b6000806000606084860312156135ae57600080fd5b83356135b98161353b565b925060208401356135c98161353b565b929592945050506040919091013590565b6000602082840312156135ec57600080fd5b5035919050565b6000806040838503121561360657600080fd5b82356136118161353b565b915060208301356136218161353b565b809150509250929050565b600181811c9082168061364057607f821691505b6020821081141561270d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006020828403121561368c57600080fd5b81518015158114610f1757600080fd5b6000602082840312156136ae57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156136f6576136f66136b5565b500390565b6000821982111561370e5761370e6136b5565b500190565b600081600019048311821515161561372d5761372d6136b5565b500290565b600082613768577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea2646970667358221220b8200d19653d5d2d8373a9410a82393d2422bdfa5de283404b6dde609b9ccc8764736f6c634300080b0033