Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- AuriOracle
- Optimization enabled
- true
- Compiler version
- v0.8.11+commit.d7f03943
- Optimization runs
- 3000
- EVM Version
- default
- Verified at
- 2023-05-06T10:22:58.609456Z
Constructor Arguments
0x000000000000000000000000f2b7e347ca4ec2a0f139b1d3073d679911295a2a000000000000000000000000b3072378821cdafac340bf18a0fbf15c72feb83b000000000000000000000000f2f3776b9f69a9302c897aec26b59027e01d36cc
Arg [0] (address) : <a href=/address/0xf2b7e347ca4ec2a0f139b1d3073d679911295a2a>0xf2b7e347ca4ec2a0f139b1d3073d679911295a2a</a>
Arg [1] (address) : <a href=/address/0xb3072378821cdafac340bf18a0fbf15c72feb83b>0xb3072378821cdafac340bf18a0fbf15c72feb83b</a>
Arg [2] (address) : <a href=/address/0xf2f3776b9f69a9302c897aec26b59027e01d36cc>0xf2f3776b9f69a9302c897aec26b59027e01d36cc</a>
Contract source code
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; } abstract contract PriceOracle { /// @notice Indicator that this is a PriceOracle contract (for inspection) bool public constant isPriceOracle = true; /** * @notice Get the underlying price of a AuToken asset * @param auToken The AuToken to get the underlying price of * @return The underlying asset price mantissa (scaled by 1e18). * Zero means the price is unavailable. */ function getUnderlyingPrice(AuToken auToken) external view virtual returns (uint); function getUnderlyingPrices(AuToken[] calldata auTokens) external view virtual returns (uint256[] memory res); } // Audit on 5-Jan-2021 by Keno and BoringCrypto // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol // Edited by BoringCrypto contract BoringOwnableData { address public owner; address public pendingOwner; } contract BoringOwnable is BoringOwnableData { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor(){ owner = msg.sender; } /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner. /// Can only be invoked by the current `owner`. /// @param newOwner Address of the new owner. /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`. /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise. function transferOwnership( address newOwner, bool direct, bool renounce ) public onlyOwner { if (direct) { // Checks require(newOwner != address(0) || renounce, "Ownable: zero address"); // Effects emit OwnershipTransferred(owner, newOwner); owner = newOwner; pendingOwner = address(0); } else { // Effects pendingOwner = newOwner; } } /// @notice Needs to be called by `pendingOwner` to claim ownership. function claimOwnership() public { address _pendingOwner = pendingOwner; // Checks require(msg.sender == _pendingOwner, "Ownable: caller != pending owner"); // Effects emit OwnershipTransferred(owner, _pendingOwner); owner = _pendingOwner; pendingOwner = address(0); } /// @notice Only allows the `owner` to execute the function. modifier onlyOwner() { require(msg.sender == owner, "Ownable: caller is not the owner"); _; } } // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); } interface AggregatorV3Interface { // // V3 Interface: // function decimals() external view returns (uint8); function description() external view returns (string memory); // latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); } library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } function max( uint256 a, uint256 b, uint256 c ) internal pure returns (uint256) { return max(a, max(b, c)); } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } function min( uint256 a, uint256 b, uint256 c ) internal pure returns (uint256) { return min(min(a, b), c); } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } /** * @dev Returns the abs of the difference of two numbers. */ function absDiff(uint256 a, uint256 b) internal pure returns (uint256) { return (a > b) ? (a - b) : (b - a); } function safe216(uint256 n) internal pure returns (uint216) { require(n <= type(uint216).max, "safe216"); return uint216(n); } function safe224(uint256 n) internal pure returns (uint224) { require(n <= type(uint224).max, "safe224"); return uint224(n); } function safe32(uint256 n) internal pure returns (uint32) { require(n <= type(uint32).max, "safe32"); return uint32(n); } } /* Before chainlink is available on Aurora, Aurigami will host its own price feeds by running 3 bot independently, forking price from chainlink on Eth mainnet and update it onto this contract. How it works: 1. Liquidator bots (updator1, updatetor2, updator3) forks chainlink's price feed from eth mainnet (ignoring roundId and updatedAt) 2. Liquidator bots sends its collected price through a transaction "updateMainFeedData", the updatedAt is set to block.timestamp 3. During the transaction, the contract will consider the latest updates from 3 bots 3.1. Update from a bot is considered invalid if (block.timestamp - updatedAt) > validPeriod (= 5 minutes by default) 3.2. If the number of valid updates <= 1, does nothing 3.3. If the number of valid updates >= 2, return the average of 2 closest prices, .i.e, (x+y)/2 with minimum (x-y) among prices[]. And updatedAt is set to min of participating answers. Why 3.3: 1. A price is considered valid only if its updated in less than 5 minutes before the transaction 2. A price is considered trustworthy if another updator also has an answer close to it How a query works: 1. The Oracle checks for the latest mainFeedAnswers (calculated from updates above), if its not outdated, return the price 2. If mainFeeds answer is outdated, return the backupFeed's price (a third-party's feed) */ contract AuriOracle is PriceOracle, BoringOwnable { struct AggregatedData { uint216 answer; uint8 underlyingDecimal; // must not be used anyway except in setUnderlyingDecimals uint32 updatedAt; // sum = 256 bit } struct RawData { uint216 answer; uint32 updatedAt; // sum < 256 bit } address public immutable updator1; address public immutable updator2; address public immutable updator3; mapping(address => mapping(address => RawData)) public mainFeedRaw; // updator => auToken => MainFeedRawData mapping(address => AggregatedData) public mainFeed; // auToken => MainFeedData mapping(address => address) public backupFeedAddr; // auToken => backupFeedAddr uint256 private constant _DEFAULT_PRICE_VALID_PERIOD = 5 minutes; uint256 private constant _DEFAULT_FUTURE_TOLERANCE = 3 seconds; uint256 private constant _1E10 = 1e10; uint256 public validPeriod; uint256 public futureTolerance; event MainFeedSync(address indexed auToken, uint256 indexed answer, address indexed updator); event MainFeedFail(address indexed auToken, address indexed updator); event BackupFeedUpdated(address indexed auToken, address indexed backupFeed); event DecimalsSet(address indexed auToken, uint8 indexed decimals); modifier onlyUpdator() { require( msg.sender == updator1 || msg.sender == updator2 || msg.sender == updator3, "not allowed" ); _; } constructor( address _updator1, address _updator2, address _updator3 ) BoringOwnable() { validPeriod = _DEFAULT_PRICE_VALID_PERIOD; futureTolerance = _DEFAULT_FUTURE_TOLERANCE; updator1 = _updator1; updator2 = _updator2; updator3 = _updator3; } function getUnderlyingPrices(AuToken[] calldata auTokens) external view override returns (uint256[] memory res) { res = new uint256[](auTokens.length); uint256 cachedValidPeriod = validPeriod; for (uint256 i = 0; i < auTokens.length; i++) { res[i] = _getUnderlyingPrice(auTokens[i], cachedValidPeriod); } } function getUnderlyingPrice(AuToken auToken) external view override returns (uint256) { return _getUnderlyingPrice(auToken, validPeriod); } function _getUnderlyingPrice(AuToken auToken, uint256 cachedValidPeriod) internal view returns (uint256) { AggregatedData memory mainFeedData = mainFeed[address(auToken)]; (uint256 rawPrice, uint256 underlyingDecimal, uint256 updatedAt) = ( mainFeedData.answer, mainFeedData.underlyingDecimal, mainFeedData.updatedAt ); require(underlyingDecimal != 0, "underlyingDecimal not set"); require(rawPrice != 0 && updatedAt != 0, "mainFeed data zero"); if (isOutdated(updatedAt, cachedValidPeriod)) { (rawPrice, ) = fetchBackupFeed(backupFeedAddr[address(auToken)]); } // feed's price is 8 decimals, so we will multiply it by 1e10 to get 1e18 decimals uint256 rawPrice18Decimals = rawPrice * _1E10; // scale the price to price for 1e18 units if (underlyingDecimal <= 18) { return rawPrice18Decimals * (10**(18 - underlyingDecimal)); } else { return rawPrice18Decimals / (10**(underlyingDecimal - 18)); } } /** * @dev out of the 3 feeds, we will take the 2 non-outdated feeds with minimum difference between them, and then the final price will be the average of the two * @dev by doing this, even if one feed is down (outdated) or one feed is malicious, the system would still be able to run normally * @param newUpdatedAt the timestamp of the block that we query this price from */ function updateMainFeedData( address auToken, int256 newUnderlyingPrice, uint256 newUpdatedAt ) external onlyUpdator { require(newUnderlyingPrice > 0, "bad price"); if (block.timestamp > newUpdatedAt) { // reject stale price require(!isOutdated(newUpdatedAt, validPeriod), "price outdated"); } else { // reject future timestamp (but accept a small delta of future time) require(newUpdatedAt - block.timestamp < futureTolerance, "future time rejected"); newUpdatedAt = block.timestamp; } // safe uint256 cast since answer > 0 mainFeedRaw[msg.sender][auToken] = RawData( Math.safe216(uint256(newUnderlyingPrice)), Math.safe32(newUpdatedAt) ); (uint256 answer, uint256 updatedAt) = aggregateAllRawData(auToken); if (updatedAt != 0) { // updatedAt == 0 only if 2 out of 3 feeds are outdated // answer != 0 since updatedAt != 0 mainFeed[auToken].answer = Math.safe216(answer); mainFeed[auToken].updatedAt = Math.safe32(updatedAt); emit MainFeedSync(auToken, uint256(answer), msg.sender); } else { emit MainFeedFail(auToken, msg.sender); } } /** @notice updatedAt & answer can be both zero when at least 2 of 3 feeds are outdated */ function aggregateAllRawData(address auToken) internal view returns (uint256 answer, uint256 updatedAt) { RawData[3] memory data = [ mainFeedRaw[updator1][auToken], mainFeedRaw[updator2][auToken], mainFeedRaw[updator3][auToken] ]; uint256 cachedValidPeriod = validPeriod; uint256 mnDiff = type(uint256).max; // Looping through all pairs of prices to find the closest double for (uint8 i = 0; i < 3; i++) { uint8 j = (i + 1) % 3; if ( isOutdated(data[i].updatedAt, cachedValidPeriod) || isOutdated(data[j].updatedAt, cachedValidPeriod) ) continue; uint256 diff = Math.absDiff(data[i].answer, data[j].answer); if (diff < mnDiff) { (answer, updatedAt) = mergeTwoFeeds(data[i], data[j]); mnDiff = diff; } } } function mergeTwoFeeds(RawData memory feed1, RawData memory feed2) internal pure returns (uint256 answer, uint256 updatedAt) { answer = Math.average(feed1.answer, feed2.answer); updatedAt = Math.min(feed1.updatedAt, feed2.updatedAt); } function fetchBackupFeed(address feed) public view returns (uint256 answer, uint256 updatedAt) { // prettier-ignore ( /*uint80 roundId*/, int256 rawAnswer, /*uint256 startedAt*/, uint256 rawUpdatedAt, /*uint80 answeredInRound*/ ) = AggregatorV3Interface(feed).latestRoundData(); require(rawAnswer > 0, "bad price"); // safe cast since rawAnswer > 0 answer = uint256(rawAnswer); updatedAt = rawUpdatedAt; } function isOutdated(uint256 lastUpdateTimestamp, uint256 localValidPeriod) internal view returns (bool res) { res = (block.timestamp - lastUpdateTimestamp) > localValidPeriod; } function setPriceValidity(uint256 _validPeriod) external onlyOwner { require(_validPeriod != 0, "bad input"); validPeriod = _validPeriod; } function setFutureTolerance(uint256 _futureTolerance) external onlyOwner { require(_futureTolerance != 0, "bad input"); futureTolerance = _futureTolerance; } function setBackupFeedAddr(address[] calldata auTokens, address[] calldata backupFeeds) external onlyOwner { require(auTokens.length == backupFeeds.length, "invalid length"); for (uint256 i = 0; i < auTokens.length; i++) { backupFeedAddr[auTokens[i]] = backupFeeds[i]; emit BackupFeedUpdated(auTokens[i], backupFeeds[i]); } } function setUnderlyingDecimals(address[] calldata auTokens, uint8[] calldata decimals) external onlyOwner { require(auTokens.length == decimals.length, "invalid length"); for (uint256 i = 0; i < auTokens.length; i++) { mainFeed[auTokens[i]].underlyingDecimal = decimals[i]; emit DecimalsSet(auTokens[i], decimals[i]); } } /** @dev ONLY USED FOR MONITORING PURPOSE @dev get the raw underlying price without any scaling */ function _getRawUnderlyingPrice(AuToken auToken) external view returns ( uint256 price, uint256 lastUpdated, bool isFromMainFeed ) { AggregatedData memory mainFeedData = mainFeed[address(auToken)]; (price, lastUpdated) = (mainFeedData.answer, mainFeedData.updatedAt); isFromMainFeed = true; if (isOutdated(lastUpdated, validPeriod)) { (price, lastUpdated) = fetchBackupFeed(backupFeedAddr[address(auToken)]); isFromMainFeed = false; } } }
Contract ABI
[{"type":"constructor","inputs":[{"type":"address","name":"_updator1","internalType":"address"},{"type":"address","name":"_updator2","internalType":"address"},{"type":"address","name":"_updator3","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"price","internalType":"uint256"},{"type":"uint256","name":"lastUpdated","internalType":"uint256"},{"type":"bool","name":"isFromMainFeed","internalType":"bool"}],"name":"_getRawUnderlyingPrice","inputs":[{"type":"address","name":"auToken","internalType":"contract AuToken"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"backupFeedAddr","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"answer","internalType":"uint256"},{"type":"uint256","name":"updatedAt","internalType":"uint256"}],"name":"fetchBackupFeed","inputs":[{"type":"address","name":"feed","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"futureTolerance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUnderlyingPrice","inputs":[{"type":"address","name":"auToken","internalType":"contract AuToken"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"res","internalType":"uint256[]"}],"name":"getUnderlyingPrices","inputs":[{"type":"address[]","name":"auTokens","internalType":"contract AuToken[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isPriceOracle","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint216","name":"answer","internalType":"uint216"},{"type":"uint8","name":"underlyingDecimal","internalType":"uint8"},{"type":"uint32","name":"updatedAt","internalType":"uint32"}],"name":"mainFeed","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint216","name":"answer","internalType":"uint216"},{"type":"uint32","name":"updatedAt","internalType":"uint32"}],"name":"mainFeedRaw","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pendingOwner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBackupFeedAddr","inputs":[{"type":"address[]","name":"auTokens","internalType":"address[]"},{"type":"address[]","name":"backupFeeds","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFutureTolerance","inputs":[{"type":"uint256","name":"_futureTolerance","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPriceValidity","inputs":[{"type":"uint256","name":"_validPeriod","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setUnderlyingDecimals","inputs":[{"type":"address[]","name":"auTokens","internalType":"address[]"},{"type":"uint8[]","name":"decimals","internalType":"uint8[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"},{"type":"bool","name":"direct","internalType":"bool"},{"type":"bool","name":"renounce","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateMainFeedData","inputs":[{"type":"address","name":"auToken","internalType":"address"},{"type":"int256","name":"newUnderlyingPrice","internalType":"int256"},{"type":"uint256","name":"newUpdatedAt","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"updator1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"updator2","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"updator3","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"validPeriod","inputs":[]},{"type":"event","name":"BackupFeedUpdated","inputs":[{"type":"address","name":"auToken","indexed":true},{"type":"address","name":"backupFeed","indexed":true}],"anonymous":false},{"type":"event","name":"DecimalsSet","inputs":[{"type":"address","name":"auToken","indexed":true},{"type":"uint8","name":"decimals","indexed":true}],"anonymous":false},{"type":"event","name":"MainFeedFail","inputs":[{"type":"address","name":"auToken","indexed":true},{"type":"address","name":"updator","indexed":true}],"anonymous":false},{"type":"event","name":"MainFeedSync","inputs":[{"type":"address","name":"auToken","indexed":true},{"type":"uint256","name":"answer","indexed":true},{"type":"address","name":"updator","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","indexed":true},{"type":"address","name":"newOwner","indexed":true}],"anonymous":false}]
Contract Creation Code
0x60e06040523480156200001157600080fd5b5060405162001df738038062001df783398101604081905262000034916200008c565b600080546001600160a01b0319163317905561012c60055560036006556001600160a01b0392831660805290821660a0521660c052620000d6565b80516001600160a01b03811681146200008757600080fd5b919050565b600080600060608486031215620000a257600080fd5b620000ad846200006f565b9250620000bd602085016200006f565b9150620000cd604085016200006f565b90509250925092565b60805160a05160c051611cc762000130600039600081816103e001528181610ea7015261154201526000818161041001528181610e7401526114f40152600081816101b401528181610e4201526114850152611cc76000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c806386f94f1e116100d8578063bd23262a1161008c578063f0d15d2011610066578063f0d15d2014610445578063f4eb73d414610458578063fc57d4df1461046b57600080fd5b8063bd23262a14610402578063dc37e3281461040b578063e30c39781461043257600080fd5b80639eef6d74116100bd5780639eef6d7414610358578063b1de581f1461036b578063b39a3a67146103db57600080fd5b806386f94f1e146103155780638da5cb5b1461034557600080fd5b806348a1371b1161013a57806366331bba1161011457806366331bba146102d35780637efcc3a2146102eb5780637f004c171461030257600080fd5b806348a1371b146102825780634e71e0c8146102a2578063505fd5b5146102aa57600080fd5b8063196045e81161016b578063196045e8146101af578063316aa63e146101f35780633d6789a31461021b57600080fd5b8063078dfbe714610187578063159d51961461019c575b600080fd5b61019a6101953660046117b1565b61047e565b005b61019a6101aa3660046117f6565b6105fa565b6101d67f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61020661020136600461180f565b6106a6565b604080519283526020830191909152016101ea565b61025e61022936600461182c565b60026020908152600092835260408084209091529082529020546001600160d81b03811690600160d81b900463ffffffff1682565b604080516001600160d81b03909316835263ffffffff9091166020830152016101ea565b6102956102903660046118b1565b61076f565b6040516101ea91906118f3565b61019a61082c565b6101d66102b836600461180f565b6004602052600090815260409020546001600160a01b031681565b6102db600181565b60405190151581526020016101ea565b6102f460055481565b6040519081526020016101ea565b61019a610310366004611937565b610901565b61032861032336600461180f565b610af5565b6040805193845260208401929092521515908201526060016101ea565b6000546101d6906001600160a01b031681565b61019a610366366004611937565b610b9c565b6103ae61037936600461180f565b6003602052600090815260409020546001600160d81b03811690600160d81b810460ff1690600160e01b900463ffffffff1683565b604080516001600160d81b03909416845260ff909216602084015263ffffffff16908201526060016101ea565b6101d67f000000000000000000000000000000000000000000000000000000000000000081565b6102f460065481565b6101d67f000000000000000000000000000000000000000000000000000000000000000081565b6001546101d6906001600160a01b031681565b61019a6104533660046117f6565b610d8b565b61019a6104663660046119a3565b610e37565b6102f461047936600461180f565b6111fc565b6000546001600160a01b031633146104dd5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b81156105c0576001600160a01b0383161515806104f75750805b6105435760405162461bcd60e51b815260206004820152601560248201527f4f776e61626c653a207a65726f2061646472657373000000000000000000000060448201526064016104d4565b600080546040516001600160a01b03808716939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0385167fffffffffffffffffffffffff000000000000000000000000000000000000000091821617909155600180549091169055505050565b600180546001600160a01b0385167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116179055505050565b6000546001600160a01b031633146106545760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b806106a15760405162461bcd60e51b815260206004820152600960248201527f62616420696e707574000000000000000000000000000000000000000000000060448201526064016104d4565b600555565b600080600080846001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156106ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070e91906119f2565b50935050925050600082136107655760405162461bcd60e51b815260206004820152600960248201527f626164207072696365000000000000000000000000000000000000000000000060448201526064016104d4565b9094909350915050565b60608167ffffffffffffffff81111561078a5761078a611a42565b6040519080825280602002602001820160405280156107b3578160200160208202803683370190505b5060055490915060005b83811015610824576107f58585838181106107da576107da611a58565b90506020020160208101906107ef919061180f565b83611210565b83828151811061080757610807611a58565b60209081029190910101528061081c81611a84565b9150506107bd565b505092915050565b6001546001600160a01b03163381146108875760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e657260448201526064016104d4565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b039092167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179055600180549091169055565b6000546001600160a01b0316331461095b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b8281146109aa5760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964206c656e67746800000000000000000000000000000000000060448201526064016104d4565b60005b83811015610aee578282828181106109c7576109c7611a58565b90506020020160208101906109dc919061180f565b600460008787858181106109f2576109f2611a58565b9050602002016020810190610a07919061180f565b6001600160a01b039081168252602082019290925260400160002080547fffffffffffffffffffffffff00000000000000000000000000000000000000001692909116919091179055828282818110610a6257610a62611a58565b9050602002016020810190610a77919061180f565b6001600160a01b0316858583818110610a9257610a92611a58565b9050602002016020810190610aa7919061180f565b6001600160a01b03167fd1c7fcab4d88416c4af1487be589ad0b28063f3740132e5c53e2b14c0b97bda360405160405180910390a380610ae681611a84565b9150506109ad565b5050505050565b6001600160a01b038116600090815260036020908152604091829020825160608101845290546001600160d81b038116808352600160d81b820460ff1693830193909352600160e01b900463ffffffff1692810183905260055491929160019190610b619084906113af565b15610b94576001600160a01b03808616600090815260046020526040902054610b8a91166106a6565b9094509250600091505b509193909250565b6000546001600160a01b03163314610bf65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b828114610c455760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964206c656e67746800000000000000000000000000000000000060448201526064016104d4565b60005b83811015610aee57828282818110610c6257610c62611a58565b9050602002016020810190610c779190611a9f565b60036000878785818110610c8d57610c8d611a58565b9050602002016020810190610ca2919061180f565b6001600160a01b031681526020810191909152604001600020805460ff92909216600160d81b027fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055828282818110610d0557610d05611a58565b9050602002016020810190610d1a9190611a9f565b60ff16858583818110610d2f57610d2f611a58565b9050602002016020810190610d44919061180f565b6001600160a01b03167f236e771abc981332874697c18edcefe526b1a17f2110c64f5d067a4638d9d8bd60405160405180910390a380610d8381611a84565b915050610c48565b6000546001600160a01b03163314610de55760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b80610e325760405162461bcd60e51b815260206004820152600960248201527f62616420696e707574000000000000000000000000000000000000000000000060448201526064016104d4565b600655565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480610e965750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b80610ec95750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b610f155760405162461bcd60e51b815260206004820152600b60248201527f6e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016104d4565b60008213610f655760405162461bcd60e51b815260206004820152600960248201527f626164207072696365000000000000000000000000000000000000000000000060448201526064016104d4565b80421115610fcb57610f79816005546113af565b15610fc65760405162461bcd60e51b815260206004820152600e60248201527f7072696365206f7574646174656400000000000000000000000000000000000060448201526064016104d4565b611028565b600654610fd84283611ac2565b106110255760405162461bcd60e51b815260206004820152601460248201527f6675747572652074696d652072656a656374656400000000000000000000000060448201526064016104d4565b50425b604051806040016040528061103c846113c4565b6001600160d81b0316815260200161105383611421565b63ffffffff9081169091523360009081526002602090815260408083206001600160a01b038916845282528220845181549590920151909316600160d81b027fff000000000000000000000000000000000000000000000000000000000000009094166001600160d81b0390911617929092179055806110d285611477565b91509150806000146111bf576110e7826113c4565b6001600160a01b038616600090815260036020526040902080547fffffffffff000000000000000000000000000000000000000000000000000000166001600160d81b039290921691909117905561113e81611421565b6001600160a01b038616600081815260036020526040808220805463ffffffff95909516600160e01b027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90951694909417909355915133928592917f2d452398cb8bb9e5cdadeaabfe0a5d66bf83abdf317ed5633a64528f5bee74589190a4610aee565b60405133906001600160a01b038716907f08715290864ad7454226834e42c0a556570b46d883d377ff1489ff3d52b3176c90600090a35050505050565b600061120a82600554611210565b92915050565b6001600160a01b0382166000908152600360209081526040808320815160608101835290546001600160d81b038116808352600160d81b820460ff16948301859052600160e01b90910463ffffffff1692820183905290929091816112b75760405162461bcd60e51b815260206004820152601960248201527f756e6465726c79696e67446563696d616c206e6f74207365740000000000000060448201526064016104d4565b82158015906112c557508015155b6113115760405162461bcd60e51b815260206004820152601260248201527f6d61696e466565642064617461207a65726f000000000000000000000000000060448201526064016104d4565b61131b81876113af565b15611348576001600160a01b0380881660009081526004602052604090205461134491166106a6565b5092505b60006113596402540be40085611ad9565b90506012831161138f5761136e836012611ac2565b61137990600a611bdc565b6113839082611ad9565b9550505050505061120a565b61139a601284611ac2565b6113a590600a611bdc565b6113839082611bfe565b6000816113bc8442611ac2565b119392505050565b60006001600160d81b0382111561141d5760405162461bcd60e51b815260206004820152600760248201527f736166653231360000000000000000000000000000000000000000000000000060448201526064016104d4565b5090565b600063ffffffff82111561141d5760405162461bcd60e51b815260206004820152600660248201527f736166653332000000000000000000000000000000000000000000000000000060448201526064016104d4565b604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660009081526002602081815285832087851680855290825286842060a087018852546001600160d81b038082166060890190815263ffffffff600160d81b93849004811660808b01529089527f00000000000000000000000000000000000000000000000000000000000000008816875285855289872084885285528987208a51808c018c5290548084168252849004821681870152858a01527f00000000000000000000000000000000000000000000000000000000000000009097168652938352878520918552908252868420875180890189529054938416815292049093169281019290925292820152600554829190600019835b60038160ff1610156116d157600060036115b9836001611c12565b6115c39190611c37565b90506115f2858360ff16600381106115dd576115dd611a58565b60200201516020015163ffffffff16856113af565b806116105750611610858260ff16600381106115dd576115dd611a58565b1561161b57506116bf565b600061166d868460ff166003811061163557611635611a58565b6020020151516001600160d81b03168760ff85166003811061165957611659611a58565b6020020151516001600160d81b03166116da565b9050838110156116bc576116b3868460ff166003811061168f5761168f611a58565b6020020151878460ff16600381106116a9576116a9611a58565b6020020151611703565b90985096509250825b50505b806116c981611c59565b91505061159e565b50505050915091565b60008183116116f2576116ed8383611ac2565b6116fc565b6116fc8284611ac2565b9392505050565b60008061172a84600001516001600160d81b031684600001516001600160d81b0316611753565b915061174a846020015163ffffffff16846020015163ffffffff1661176e565b90509250929050565b60006117626002848418611bfe565b6116fc90848416611c79565b600081831061177d57816116fc565b5090919050565b6001600160a01b038116811461179957600080fd5b50565b803580151581146117ac57600080fd5b919050565b6000806000606084860312156117c657600080fd5b83356117d181611784565b92506117df6020850161179c565b91506117ed6040850161179c565b90509250925092565b60006020828403121561180857600080fd5b5035919050565b60006020828403121561182157600080fd5b81356116fc81611784565b6000806040838503121561183f57600080fd5b823561184a81611784565b9150602083013561185a81611784565b809150509250929050565b60008083601f84011261187757600080fd5b50813567ffffffffffffffff81111561188f57600080fd5b6020830191508360208260051b85010111156118aa57600080fd5b9250929050565b600080602083850312156118c457600080fd5b823567ffffffffffffffff8111156118db57600080fd5b6118e785828601611865565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b8181101561192b5783518352928401929184019160010161190f565b50909695505050505050565b6000806000806040858703121561194d57600080fd5b843567ffffffffffffffff8082111561196557600080fd5b61197188838901611865565b9096509450602087013591508082111561198a57600080fd5b5061199787828801611865565b95989497509550505050565b6000806000606084860312156119b857600080fd5b83356119c381611784565b95602085013595506040909401359392505050565b805169ffffffffffffffffffff811681146117ac57600080fd5b600080600080600060a08688031215611a0a57600080fd5b611a13866119d8565b9450602086015193506040860151925060608601519150611a36608087016119d8565b90509295509295909350565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415611a9857611a98611a6e565b5060010190565b600060208284031215611ab157600080fd5b813560ff811681146116fc57600080fd5b600082821015611ad457611ad4611a6e565b500390565b6000816000190483118215151615611af357611af3611a6e565b500290565b600181815b80851115611b33578160001904821115611b1957611b19611a6e565b80851615611b2657918102915b93841c9390800290611afd565b509250929050565b600082611b4a5750600161120a565b81611b575750600061120a565b8160018114611b6d5760028114611b7757611b93565b600191505061120a565b60ff841115611b8857611b88611a6e565b50506001821b61120a565b5060208310610133831016604e8410600b8410161715611bb6575081810a61120a565b611bc08383611af8565b8060001904821115611bd457611bd4611a6e565b029392505050565b60006116fc8383611b3b565b634e487b7160e01b600052601260045260246000fd5b600082611c0d57611c0d611be8565b500490565b600060ff821660ff84168060ff03821115611c2f57611c2f611a6e565b019392505050565b600060ff831680611c4a57611c4a611be8565b8060ff84160691505092915050565b600060ff821660ff811415611c7057611c70611a6e565b60010192915050565b60008219821115611c8c57611c8c611a6e565b50019056fea26469706673582212209e1eb41d13d359a822d7c42dfca2291581e4f07d8087d24d352430aa1d403be364736f6c634300080b0033000000000000000000000000f2b7e347ca4ec2a0f139b1d3073d679911295a2a000000000000000000000000b3072378821cdafac340bf18a0fbf15c72feb83b000000000000000000000000f2f3776b9f69a9302c897aec26b59027e01d36cc
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101825760003560e01c806386f94f1e116100d8578063bd23262a1161008c578063f0d15d2011610066578063f0d15d2014610445578063f4eb73d414610458578063fc57d4df1461046b57600080fd5b8063bd23262a14610402578063dc37e3281461040b578063e30c39781461043257600080fd5b80639eef6d74116100bd5780639eef6d7414610358578063b1de581f1461036b578063b39a3a67146103db57600080fd5b806386f94f1e146103155780638da5cb5b1461034557600080fd5b806348a1371b1161013a57806366331bba1161011457806366331bba146102d35780637efcc3a2146102eb5780637f004c171461030257600080fd5b806348a1371b146102825780634e71e0c8146102a2578063505fd5b5146102aa57600080fd5b8063196045e81161016b578063196045e8146101af578063316aa63e146101f35780633d6789a31461021b57600080fd5b8063078dfbe714610187578063159d51961461019c575b600080fd5b61019a6101953660046117b1565b61047e565b005b61019a6101aa3660046117f6565b6105fa565b6101d67f000000000000000000000000f2b7e347ca4ec2a0f139b1d3073d679911295a2a81565b6040516001600160a01b0390911681526020015b60405180910390f35b61020661020136600461180f565b6106a6565b604080519283526020830191909152016101ea565b61025e61022936600461182c565b60026020908152600092835260408084209091529082529020546001600160d81b03811690600160d81b900463ffffffff1682565b604080516001600160d81b03909316835263ffffffff9091166020830152016101ea565b6102956102903660046118b1565b61076f565b6040516101ea91906118f3565b61019a61082c565b6101d66102b836600461180f565b6004602052600090815260409020546001600160a01b031681565b6102db600181565b60405190151581526020016101ea565b6102f460055481565b6040519081526020016101ea565b61019a610310366004611937565b610901565b61032861032336600461180f565b610af5565b6040805193845260208401929092521515908201526060016101ea565b6000546101d6906001600160a01b031681565b61019a610366366004611937565b610b9c565b6103ae61037936600461180f565b6003602052600090815260409020546001600160d81b03811690600160d81b810460ff1690600160e01b900463ffffffff1683565b604080516001600160d81b03909416845260ff909216602084015263ffffffff16908201526060016101ea565b6101d67f000000000000000000000000f2f3776b9f69a9302c897aec26b59027e01d36cc81565b6102f460065481565b6101d67f000000000000000000000000b3072378821cdafac340bf18a0fbf15c72feb83b81565b6001546101d6906001600160a01b031681565b61019a6104533660046117f6565b610d8b565b61019a6104663660046119a3565b610e37565b6102f461047936600461180f565b6111fc565b6000546001600160a01b031633146104dd5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b81156105c0576001600160a01b0383161515806104f75750805b6105435760405162461bcd60e51b815260206004820152601560248201527f4f776e61626c653a207a65726f2061646472657373000000000000000000000060448201526064016104d4565b600080546040516001600160a01b03808716939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0385167fffffffffffffffffffffffff000000000000000000000000000000000000000091821617909155600180549091169055505050565b600180546001600160a01b0385167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116179055505050565b6000546001600160a01b031633146106545760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b806106a15760405162461bcd60e51b815260206004820152600960248201527f62616420696e707574000000000000000000000000000000000000000000000060448201526064016104d4565b600555565b600080600080846001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156106ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070e91906119f2565b50935050925050600082136107655760405162461bcd60e51b815260206004820152600960248201527f626164207072696365000000000000000000000000000000000000000000000060448201526064016104d4565b9094909350915050565b60608167ffffffffffffffff81111561078a5761078a611a42565b6040519080825280602002602001820160405280156107b3578160200160208202803683370190505b5060055490915060005b83811015610824576107f58585838181106107da576107da611a58565b90506020020160208101906107ef919061180f565b83611210565b83828151811061080757610807611a58565b60209081029190910101528061081c81611a84565b9150506107bd565b505092915050565b6001546001600160a01b03163381146108875760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e657260448201526064016104d4565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b039092167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179055600180549091169055565b6000546001600160a01b0316331461095b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b8281146109aa5760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964206c656e67746800000000000000000000000000000000000060448201526064016104d4565b60005b83811015610aee578282828181106109c7576109c7611a58565b90506020020160208101906109dc919061180f565b600460008787858181106109f2576109f2611a58565b9050602002016020810190610a07919061180f565b6001600160a01b039081168252602082019290925260400160002080547fffffffffffffffffffffffff00000000000000000000000000000000000000001692909116919091179055828282818110610a6257610a62611a58565b9050602002016020810190610a77919061180f565b6001600160a01b0316858583818110610a9257610a92611a58565b9050602002016020810190610aa7919061180f565b6001600160a01b03167fd1c7fcab4d88416c4af1487be589ad0b28063f3740132e5c53e2b14c0b97bda360405160405180910390a380610ae681611a84565b9150506109ad565b5050505050565b6001600160a01b038116600090815260036020908152604091829020825160608101845290546001600160d81b038116808352600160d81b820460ff1693830193909352600160e01b900463ffffffff1692810183905260055491929160019190610b619084906113af565b15610b94576001600160a01b03808616600090815260046020526040902054610b8a91166106a6565b9094509250600091505b509193909250565b6000546001600160a01b03163314610bf65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b828114610c455760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964206c656e67746800000000000000000000000000000000000060448201526064016104d4565b60005b83811015610aee57828282818110610c6257610c62611a58565b9050602002016020810190610c779190611a9f565b60036000878785818110610c8d57610c8d611a58565b9050602002016020810190610ca2919061180f565b6001600160a01b031681526020810191909152604001600020805460ff92909216600160d81b027fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055828282818110610d0557610d05611a58565b9050602002016020810190610d1a9190611a9f565b60ff16858583818110610d2f57610d2f611a58565b9050602002016020810190610d44919061180f565b6001600160a01b03167f236e771abc981332874697c18edcefe526b1a17f2110c64f5d067a4638d9d8bd60405160405180910390a380610d8381611a84565b915050610c48565b6000546001600160a01b03163314610de55760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104d4565b80610e325760405162461bcd60e51b815260206004820152600960248201527f62616420696e707574000000000000000000000000000000000000000000000060448201526064016104d4565b600655565b336001600160a01b037f000000000000000000000000f2b7e347ca4ec2a0f139b1d3073d679911295a2a161480610e965750336001600160a01b037f000000000000000000000000b3072378821cdafac340bf18a0fbf15c72feb83b16145b80610ec95750336001600160a01b037f000000000000000000000000f2f3776b9f69a9302c897aec26b59027e01d36cc16145b610f155760405162461bcd60e51b815260206004820152600b60248201527f6e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016104d4565b60008213610f655760405162461bcd60e51b815260206004820152600960248201527f626164207072696365000000000000000000000000000000000000000000000060448201526064016104d4565b80421115610fcb57610f79816005546113af565b15610fc65760405162461bcd60e51b815260206004820152600e60248201527f7072696365206f7574646174656400000000000000000000000000000000000060448201526064016104d4565b611028565b600654610fd84283611ac2565b106110255760405162461bcd60e51b815260206004820152601460248201527f6675747572652074696d652072656a656374656400000000000000000000000060448201526064016104d4565b50425b604051806040016040528061103c846113c4565b6001600160d81b0316815260200161105383611421565b63ffffffff9081169091523360009081526002602090815260408083206001600160a01b038916845282528220845181549590920151909316600160d81b027fff000000000000000000000000000000000000000000000000000000000000009094166001600160d81b0390911617929092179055806110d285611477565b91509150806000146111bf576110e7826113c4565b6001600160a01b038616600090815260036020526040902080547fffffffffff000000000000000000000000000000000000000000000000000000166001600160d81b039290921691909117905561113e81611421565b6001600160a01b038616600081815260036020526040808220805463ffffffff95909516600160e01b027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90951694909417909355915133928592917f2d452398cb8bb9e5cdadeaabfe0a5d66bf83abdf317ed5633a64528f5bee74589190a4610aee565b60405133906001600160a01b038716907f08715290864ad7454226834e42c0a556570b46d883d377ff1489ff3d52b3176c90600090a35050505050565b600061120a82600554611210565b92915050565b6001600160a01b0382166000908152600360209081526040808320815160608101835290546001600160d81b038116808352600160d81b820460ff16948301859052600160e01b90910463ffffffff1692820183905290929091816112b75760405162461bcd60e51b815260206004820152601960248201527f756e6465726c79696e67446563696d616c206e6f74207365740000000000000060448201526064016104d4565b82158015906112c557508015155b6113115760405162461bcd60e51b815260206004820152601260248201527f6d61696e466565642064617461207a65726f000000000000000000000000000060448201526064016104d4565b61131b81876113af565b15611348576001600160a01b0380881660009081526004602052604090205461134491166106a6565b5092505b60006113596402540be40085611ad9565b90506012831161138f5761136e836012611ac2565b61137990600a611bdc565b6113839082611ad9565b9550505050505061120a565b61139a601284611ac2565b6113a590600a611bdc565b6113839082611bfe565b6000816113bc8442611ac2565b119392505050565b60006001600160d81b0382111561141d5760405162461bcd60e51b815260206004820152600760248201527f736166653231360000000000000000000000000000000000000000000000000060448201526064016104d4565b5090565b600063ffffffff82111561141d5760405162461bcd60e51b815260206004820152600660248201527f736166653332000000000000000000000000000000000000000000000000000060448201526064016104d4565b604080516001600160a01b037f000000000000000000000000f2b7e347ca4ec2a0f139b1d3073d679911295a2a811660009081526002602081815285832087851680855290825286842060a087018852546001600160d81b038082166060890190815263ffffffff600160d81b93849004811660808b01529089527f000000000000000000000000b3072378821cdafac340bf18a0fbf15c72feb83b8816875285855289872084885285528987208a51808c018c5290548084168252849004821681870152858a01527f000000000000000000000000f2f3776b9f69a9302c897aec26b59027e01d36cc9097168652938352878520918552908252868420875180890189529054938416815292049093169281019290925292820152600554829190600019835b60038160ff1610156116d157600060036115b9836001611c12565b6115c39190611c37565b90506115f2858360ff16600381106115dd576115dd611a58565b60200201516020015163ffffffff16856113af565b806116105750611610858260ff16600381106115dd576115dd611a58565b1561161b57506116bf565b600061166d868460ff166003811061163557611635611a58565b6020020151516001600160d81b03168760ff85166003811061165957611659611a58565b6020020151516001600160d81b03166116da565b9050838110156116bc576116b3868460ff166003811061168f5761168f611a58565b6020020151878460ff16600381106116a9576116a9611a58565b6020020151611703565b90985096509250825b50505b806116c981611c59565b91505061159e565b50505050915091565b60008183116116f2576116ed8383611ac2565b6116fc565b6116fc8284611ac2565b9392505050565b60008061172a84600001516001600160d81b031684600001516001600160d81b0316611753565b915061174a846020015163ffffffff16846020015163ffffffff1661176e565b90509250929050565b60006117626002848418611bfe565b6116fc90848416611c79565b600081831061177d57816116fc565b5090919050565b6001600160a01b038116811461179957600080fd5b50565b803580151581146117ac57600080fd5b919050565b6000806000606084860312156117c657600080fd5b83356117d181611784565b92506117df6020850161179c565b91506117ed6040850161179c565b90509250925092565b60006020828403121561180857600080fd5b5035919050565b60006020828403121561182157600080fd5b81356116fc81611784565b6000806040838503121561183f57600080fd5b823561184a81611784565b9150602083013561185a81611784565b809150509250929050565b60008083601f84011261187757600080fd5b50813567ffffffffffffffff81111561188f57600080fd5b6020830191508360208260051b85010111156118aa57600080fd5b9250929050565b600080602083850312156118c457600080fd5b823567ffffffffffffffff8111156118db57600080fd5b6118e785828601611865565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b8181101561192b5783518352928401929184019160010161190f565b50909695505050505050565b6000806000806040858703121561194d57600080fd5b843567ffffffffffffffff8082111561196557600080fd5b61197188838901611865565b9096509450602087013591508082111561198a57600080fd5b5061199787828801611865565b95989497509550505050565b6000806000606084860312156119b857600080fd5b83356119c381611784565b95602085013595506040909401359392505050565b805169ffffffffffffffffffff811681146117ac57600080fd5b600080600080600060a08688031215611a0a57600080fd5b611a13866119d8565b9450602086015193506040860151925060608601519150611a36608087016119d8565b90509295509295909350565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415611a9857611a98611a6e565b5060010190565b600060208284031215611ab157600080fd5b813560ff811681146116fc57600080fd5b600082821015611ad457611ad4611a6e565b500390565b6000816000190483118215151615611af357611af3611a6e565b500290565b600181815b80851115611b33578160001904821115611b1957611b19611a6e565b80851615611b2657918102915b93841c9390800290611afd565b509250929050565b600082611b4a5750600161120a565b81611b575750600061120a565b8160018114611b6d5760028114611b7757611b93565b600191505061120a565b60ff841115611b8857611b88611a6e565b50506001821b61120a565b5060208310610133831016604e8410600b8410161715611bb6575081810a61120a565b611bc08383611af8565b8060001904821115611bd457611bd4611a6e565b029392505050565b60006116fc8383611b3b565b634e487b7160e01b600052601260045260246000fd5b600082611c0d57611c0d611be8565b500490565b600060ff821660ff84168060ff03821115611c2f57611c2f611a6e565b019392505050565b600060ff831680611c4a57611c4a611be8565b8060ff84160691505092915050565b600060ff821660ff811415611c7057611c70611a6e565b60010192915050565b60008219821115611c8c57611c8c611a6e565b50019056fea26469706673582212209e1eb41d13d359a822d7c42dfca2291581e4f07d8087d24d352430aa1d403be364736f6c634300080b0033