Source Code
Overview
MON Balance
MON Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
MagmaBoostedDelegate
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.20;
import "../PErc20Delegate.sol";
import "../PeridottrollerInterface.sol";
import "../InterestRateModel.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
interface IMagma {
// Deposits (synchronous)
function depositWMON(
uint256 assets,
address receiver,
uint256 referralId
) external returns (uint256 shares);
function depositMON(
address receiver,
uint256 referralId
) external payable returns (uint256 shares);
// Redemptions (asynchronous - two step)
function requestRedeem(
uint256 shares,
address controller,
address owner
) external returns (uint256 requestId);
function redeemMON(
uint256 requestId,
address controller,
address receiver
) external returns (uint256 assets);
function redeem(
uint256 requestId,
address controller,
address receiver
) external returns (uint256 assets);
// ERC-4626 View functions
function balanceOf(address account) external view returns (uint256 shares);
function convertToAssets(
uint256 shares
) external view returns (uint256 assets);
function convertToShares(
uint256 assets
) external view returns (uint256 shares);
function totalAssets() external view returns (uint256);
// Request tracking
function ownerRequestId(
address owner
) external view returns (uint256 requestId);
}
/**
* @title MagmaBoostedDelegate
* @notice Compound-style delegate for a Magma-boosted market.
* @dev Underlying is WMON. Idle WMON is deposited into Magma to earn gMON (Monad LST) yield.
* Due to Magma's asynchronous redemption design (Monad unbonding period), this implementation
* maintains a buffer of gMON shares for immediate withdrawals. If buffer is insufficient,
* withdrawals may fail until admin rebalances or async redemptions complete.
*/
contract MagmaBoostedDelegate is PErc20Delegate {
using SafeERC20 for IERC20;
uint256 internal constant MANTISSA_ONE = 1e18;
/// @notice Magma contract (gMON LST).
IMagma public magmaVault;
/// @notice Fraction of total managed assets to keep as liquid gMON buffer (scaled by 1e18).
/// @dev Higher buffer = more immediate withdrawal capacity, but less capital efficiency.
uint256 public vaultBufferMantissa;
/// @notice When true, deposits are paused and funds should be withdrawn from Magma.
bool public vaultPaused;
/// @notice Pending redemption request ID from Magma (0 if none).
uint256 public pendingRedemptionId;
/// @notice Referral ID for Magma points (default: 0).
uint256 public referralId;
event VaultBufferUpdated(uint256 previousMantissa, uint256 newMantissa);
event VaultPausedUpdated(bool paused);
event VaultDeposited(uint256 assets, uint256 shares);
event RedemptionRequested(uint256 requestId, uint256 shares);
event RedemptionCompleted(uint256 requestId, uint256 assets);
event ReferralIdUpdated(uint256 newReferralId);
/**
* @notice Called when becoming the implementation for a delegator.
*/
function _becomeImplementation(bytes memory data) public override {
require(msg.sender == admin, "only admin");
if (data.length > 0) {
(address vault_, uint256 buffer_) = abi.decode(
data,
(address, uint256)
);
if (vault_ != address(0)) {
magmaVault = IMagma(vault_);
vaultBufferMantissa = buffer_;
}
}
}
/**
* @notice Admin function to set the Magma vault.
*/
function _setMagmaVault(address vault_, uint256 buffer_) external {
require(msg.sender == admin, "only admin");
require(vault_ != address(0), "zero vault");
magmaVault = IMagma(vault_);
vaultBufferMantissa = buffer_;
emit VaultBufferUpdated(0, buffer_);
}
/**
* @notice Initialize delegate storage.
*/
function initialize(
address underlying_,
PeridottrollerInterface peridottroller_,
InterestRateModel interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
IMagma magmaVault_,
uint256 vaultBufferMantissa_
) public {
require(accrualBlockNumber == 0 && borrowIndex == 0, "already init");
admin = payable(msg.sender);
magmaVault = magmaVault_;
vaultBufferMantissa = vaultBufferMantissa_;
super.initialize(
underlying_,
peridottroller_,
interestRateModel_,
initialExchangeRateMantissa_,
name_,
symbol_,
decimals_
);
}
// Admin setters
function _setVaultBuffer(uint256 newMantissa) external {
require(msg.sender == admin, "only admin");
require(newMantissa <= MANTISSA_ONE, "invalid buffer");
uint256 prev = vaultBufferMantissa;
vaultBufferMantissa = newMantissa;
emit VaultBufferUpdated(prev, newMantissa);
_rebalanceVault();
}
function _setVaultPaused(bool pause) external {
require(msg.sender == admin, "only admin");
if (pause == vaultPaused) return;
vaultPaused = pause;
emit VaultPausedUpdated(pause);
// Note: When paused, we can't immediately pull from Magma due to async redemptions
// Admin must manually request redemptions via _requestRedemption
}
function _setReferralId(uint256 newReferralId) external {
require(msg.sender == admin, "only admin");
referralId = newReferralId;
emit ReferralIdUpdated(newReferralId);
}
/**
* @notice Admin function to request redemption from Magma (step 1 of async withdrawal).
* @param shares Amount of gMON shares to redeem.
*/
function _requestRedemption(uint256 shares) external {
require(msg.sender == admin, "only admin");
require(pendingRedemptionId == 0, "redemption pending");
uint256 requestId = magmaVault.requestRedeem(
shares,
address(this),
address(this)
);
pendingRedemptionId = requestId;
emit RedemptionRequested(requestId, shares);
}
/**
* @notice Admin function to complete redemption from Magma (step 2 of async withdrawal).
* @dev Can only be called after WITHDRAWAL_DELAY epochs have passed.
*/
function _completeRedemption() external {
require(msg.sender == admin, "only admin");
uint256 requestId = pendingRedemptionId;
require(requestId != 0, "no pending redemption");
// Complete redemption - this returns WMON to this contract
uint256 assets = magmaVault.redeem(
requestId,
address(this),
address(this)
);
pendingRedemptionId = 0;
emit RedemptionCompleted(requestId, assets);
_rebalanceVault();
}
// Overridden cash/view hooks
function getCashPrior() internal view override returns (uint256) {
return super.getCashPrior() + _vaultWithdrawable();
}
function doTransferIn(
address from,
uint256 amount
) internal override returns (uint256) {
uint256 actual = super.doTransferIn(from, amount);
_rebalanceVault();
return actual;
}
function doTransferOut(
address payable to,
uint256 amount
) internal override {
_ensureLocalLiquidity(amount);
super.doTransferOut(to, amount);
_rebalanceVault();
}
// Vault helpers
function _rebalanceVault() internal {
if (vaultPaused) return;
uint256 localCash = super.getCashPrior();
uint256 vaultAssets = _vaultAssets();
uint256 total = localCash + vaultAssets;
if (total == 0) return;
uint256 target = (total * vaultBufferMantissa) / MANTISSA_ONE;
if (localCash > target) {
uint256 toDeposit = localCash - target;
_depositToVault(toDeposit);
}
// Note: We cannot pull from vault immediately due to async redemptions
// Admin must manually request and complete redemptions if buffer is low
}
function _ensureLocalLiquidity(uint256 amount) internal view {
uint256 localCash = super.getCashPrior();
require(
localCash >= amount,
"insufficient buffer - admin must complete redemption"
);
}
function _depositToVault(uint256 amount) internal {
if (amount == 0 || vaultPaused) return;
IERC20(underlying).forceApprove(address(magmaVault), amount);
uint256 shares = magmaVault.depositWMON(
amount,
address(this),
referralId
);
emit VaultDeposited(amount, shares);
}
function _vaultAssets() internal view returns (uint256) {
uint256 shares = magmaVault.balanceOf(address(this));
if (shares == 0) return 0;
return magmaVault.convertToAssets(shares);
}
function _vaultWithdrawable() internal view returns (uint256) {
// Since redemptions are async, we can only count what's immediately available
// which is the gMON balance converted to assets (optimistic)
return _vaultAssets();
}
/**
* @notice View function to check if a pending redemption can be completed.
*/
function canCompleteRedemption() external view returns (bool) {
uint256 requestId = pendingRedemptionId;
if (requestId == 0) return false;
// Note: Magma doesn't expose a "isClaimable" function
// Admin should track epochs and call _completeRedemption after WITHDRAWAL_DELAY
return true;
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
import "./PErc20.sol";
/**
* @title Peridot's PErc20Delegate Contract
* @notice PTokens which wrap an EIP-20 underlying and are delegated to
* @author Peridot
*/
contract PErc20Delegate is PErc20, CDelegateInterface {
/**
* @notice Construct an empty delegate
*/
constructor() {}
/**
* @notice Called by the delegator on a delegate to initialize it for duty
* @param data The encoded bytes data for any initialization
*/
function _becomeImplementation(bytes memory data) public virtual override {
// Shh -- currently unused
data;
// Shh -- we don't ever want this hook to be marked pure
if (false) {
implementation = address(0);
}
require(msg.sender == admin, "only the admin may call _becomeImplementation");
}
/**
* @notice Called by the delegator on a delegate to forfeit its responsibility
*/
function _resignImplementation() public virtual override {
// Shh -- we don't ever want this hook to be marked pure
if (false) {
implementation = address(0);
}
require(msg.sender == admin, "only the admin may call _resignImplementation");
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
import "./PToken.sol";
abstract contract PeridottrollerInterface {
/// @notice Indicator that this is a Peridottroller contract (for inspection)
bool public constant isPeridottroller = true;
/**
* Assets You Are In **
*/
function enterMarkets(
address[] calldata pTokens
) external virtual returns (uint256[] memory);
function exitMarket(address pToken) external virtual returns (uint256);
/**
* Policy Hooks **
*/
function mintAllowed(
address pToken,
address minter,
uint256 mintAmount
) external virtual returns (uint256);
function mintVerify(
address pToken,
address minter,
uint256 mintAmount,
uint256 mintTokens
) external virtual;
function redeemAllowed(
address pToken,
address redeemer,
uint256 redeemTokens
) external virtual returns (uint256);
function redeemVerify(
address pToken,
address redeemer,
uint256 redeemAmount,
uint256 redeemTokens
) external virtual;
function borrowAllowed(
address pToken,
address borrower,
uint256 borrowAmount
) external virtual returns (uint256);
function borrowVerify(
address pToken,
address borrower,
uint256 borrowAmount
) external virtual;
function repayBorrowAllowed(
address pToken,
address payer,
address borrower,
uint256 repayAmount
) external virtual returns (uint256);
function repayBorrowVerify(
address pToken,
address payer,
address borrower,
uint256 repayAmount,
uint256 borrowerIndex
) external virtual;
function liquidateBorrowAllowed(
address pTokenBorrowed,
address pTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount
) external virtual returns (uint256);
function liquidateBorrowVerify(
address pTokenBorrowed,
address pTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount,
uint256 seizeTokens
) external virtual;
function seizeAllowed(
address pTokenCollateral,
address pTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) external virtual returns (uint256);
function seizeVerify(
address pTokenCollateral,
address pTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) external virtual;
function transferAllowed(
address pToken,
address src,
address dst,
uint256 transferTokens
) external virtual returns (uint256);
function transferVerify(
address pToken,
address src,
address dst,
uint256 transferTokens
) external virtual;
/**
* Liquidity/Liquidation Calculations **
*/
function liquidateCalculateSeizeTokens(
address pTokenBorrowed,
address pTokenCollateral,
uint256 repayAmount
) external view virtual returns (uint256, uint256);
function getAccountLiquidity(
address account
) external view virtual returns (uint256, uint256, uint256);
function getAllMarkets() external view virtual returns (PToken[] memory);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
/**
* @title Peridot's InterestRateModel Interface
* @author Peridot
*/
abstract contract InterestRateModel {
/// @notice Indicator that this is an InterestRateModel contract (for inspection)
bool public constant isInterestRateModel = true;
/**
* @notice Calculates the current borrow interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @return The borrow rate per block (as a percentage, and scaled by 1e18)
*/
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external view virtual returns (uint256);
/**
* @notice Calculates the current supply interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @param reserveFactorMantissa The current reserve factor the market has
* @return The supply rate per block (as a percentage, and scaled by 1e18)
*/
function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa)
external
view
virtual
returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
import "./PToken.sol";
interface PeridotLike {
function delegate(address delegatee) external;
}
/**
* @title Peridot's PErc20 Contract
* @notice PTokens which wrap an EIP-20 underlying
* @author Peridot
*/
contract PErc20 is PToken, PErc20Interface {
/**
* @notice Initialize the new money market
* @param underlying_ The address of the underlying asset
* @param peridottroller_ The address of the Peridottroller
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ ERC-20 name of this token
* @param symbol_ ERC-20 symbol of this token
* @param decimals_ ERC-20 decimal precision of this token
*/
function initialize(
address underlying_,
PeridottrollerInterface peridottroller_,
InterestRateModel interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_
) public {
// PToken initialize does the bulk of the work
super.initialize(peridottroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_);
// Set underlying and sanity check it
underlying = underlying_;
EIP20Interface(underlying).totalSupply();
}
/**
* User Interface **
*/
/**
* @notice Sender supplies assets into the market and receives pTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param mintAmount The amount of the underlying asset to supply
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function mint(uint256 mintAmount) external override returns (uint256) {
mintInternal(mintAmount);
return NO_ERROR;
}
/**
* @notice Sender redeems pTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of pTokens to redeem into underlying
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeem(uint256 redeemTokens) external override returns (uint256) {
redeemInternal(redeemTokens);
return NO_ERROR;
}
/**
* @notice Sender redeems pTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemAmount The amount of underlying to redeem
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeemUnderlying(uint256 redeemAmount) external override returns (uint256) {
redeemUnderlyingInternal(redeemAmount);
return NO_ERROR;
}
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function borrow(uint256 borrowAmount) external override returns (uint256) {
borrowInternal(borrowAmount);
return NO_ERROR;
}
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function repayBorrow(uint256 repayAmount) external override returns (uint256) {
repayBorrowInternal(repayAmount);
return NO_ERROR;
}
/**
* @notice Sender repays a borrow belonging to borrower
* @param borrower the account with the debt being payed off
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function repayBorrowBehalf(address borrower, uint256 repayAmount) external override returns (uint256) {
repayBorrowBehalfInternal(borrower, repayAmount);
return NO_ERROR;
}
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this pToken to be liquidated
* @param repayAmount The amount of the underlying borrowed asset to repay
* @param pTokenCollateral The market in which to seize collateral from the borrower
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function liquidateBorrow(address borrower, uint256 repayAmount, PTokenInterface pTokenCollateral)
external
override
returns (uint256)
{
liquidateBorrowInternal(borrower, repayAmount, pTokenCollateral);
return NO_ERROR;
}
/**
* @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)
* @param token The address of the ERC-20 token to sweep
*/
function sweepToken(EIP20NonStandardInterface token) external override {
require(msg.sender == admin, "PErc20::sweepToken: only admin can sweep tokens");
require(address(token) != underlying, "PErc20::sweepToken: can not sweep underlying token");
uint256 balance = token.balanceOf(address(this));
token.transfer(admin, balance);
}
/**
* @notice The sender adds to reserves.
* @param addAmount The amount fo underlying token to add as reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _addReserves(uint256 addAmount) external override returns (uint256) {
return _addReservesInternal(addAmount);
}
/**
* Flash Loan Support **
*/
/**
* @notice Returns the underlying token address for flash loans
* @return The address of the underlying ERC20 token
*/
function getUnderlyingAddress() internal view override returns (address) {
return underlying;
}
/**
* Safe Token **
*/
/**
* @notice Gets balance of this contract in terms of the underlying
* @dev This excludes the value of the current message, if any
* @return The quantity of underlying tokens owned by this contract
*/
function getCashPrior() internal view virtual override returns (uint256) {
EIP20Interface token = EIP20Interface(underlying);
return token.balanceOf(address(this));
}
/**
* @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case.
* This will revert due to insufficient balance or insufficient allowance.
* This function returns the actual amount received,
* which may be less than `amount` if there is a fee attached to the transfer.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) {
// Read from storage once
address underlying_ = underlying;
EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying_);
uint256 balanceBefore = EIP20Interface(underlying_).balanceOf(address(this));
token.transferFrom(from, address(this), amount);
bool success;
assembly {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a peridotliant ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of override external call
}
default {
// This is an excessively non-peridotliant ERC-20, revert.
revert(0, 0)
}
}
require(success, "TOKEN_TRANSFER_IN_FAILED");
// Calculate the amount that was *actually* transferred
uint256 balanceAfter = EIP20Interface(underlying_).balanceOf(address(this));
return balanceAfter - balanceBefore; // underflow already checked above, just subtract
}
/**
* @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory
* error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
* insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
* it is >= amount, this should not revert in normal conditions.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferOut(address payable to, uint256 amount) internal virtual override {
EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
token.transfer(to, amount);
bool success;
assembly {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a peridotliant ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of override external call
}
default {
// This is an excessively non-peridotliant ERC-20, revert.
revert(0, 0)
}
}
require(success, "TOKEN_TRANSFER_OUT_FAILED");
}
/**
* @notice Admin call to delegate the votes of the PERIDOT-like underlying
* @param peridotLikeDelegatee The address to delegate votes to
* @dev PTokens whose underlying are not PeridotLike should revert here
*/
function _delegatePeridotLikeTo(address peridotLikeDelegatee) external {
require(msg.sender == admin, "only the admin may set the peridot-like delegate");
PeridotLike(underlying).delegate(peridotLikeDelegatee);
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
import "./PeridottrollerInterface.sol";
import "./PTokenInterfaces.sol";
import "./ErrorReporter.sol";
import "./EIP20Interface.sol";
import "./InterestRateModel.sol";
import "./ExponentialNoError.sol";
import "./PeridottrollerStorage.sol";
/**
* @title Peridot's PToken Contract
* @notice Abstract base for PTokens
* @author Peridot
*/
abstract contract PToken is
PTokenInterface,
ExponentialNoError,
TokenErrorReporter,
IERC3156FlashLender
{
/**
* @notice Initialize the money market
* @param peridottroller_ The address of the Peridottroller
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ EIP-20 name of this token
* @param symbol_ EIP-20 symbol of this token
* @param decimals_ EIP-20 decimal precision of this token
*/
function initialize(
PeridottrollerInterface peridottroller_,
InterestRateModel interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_
) public {
require(msg.sender == admin, "only admin may initialize the market");
require(
accrualBlockNumber == 0 && borrowIndex == 0,
"market may only be initialized once"
);
// Set initial exchange rate
initialExchangeRateMantissa = initialExchangeRateMantissa_;
require(
initialExchangeRateMantissa > 0,
"initial exchange rate must be greater than zero."
);
// Set the peridottroller
uint256 err = _setPeridottroller(peridottroller_);
require(err == NO_ERROR, "setting peridottroller failed");
// Initialize block number and borrow index (block number mocks depend on peridottroller being set)
accrualBlockNumber = getBlockNumber();
borrowIndex = mantissaOne;
// Set the interest rate model (depends on block number / borrow index)
err = _setInterestRateModelFresh(interestRateModel_);
require(err == NO_ERROR, "setting interest rate model failed");
name = name_;
symbol = symbol_;
decimals = decimals_;
// Set default flash loan parameters
// 0.05% fee, up to 30% of available cash, enabled by default
flashLoanFeeBps = 5;
maxFlashLoanRatio = 3000; // 30%
flashLoansPaused = false;
// The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
_notEntered = true;
}
/**
* @notice Transfer `tokens` tokens from `src` to `dst` by `spender`
* @dev Called by both `transfer` and `transferFrom` internally
* @param spender The address of the account performing the transfer
* @param src The address of the source account
* @param dst The address of the destination account
* @param tokens The number of tokens to transfer
* @return 0 if the transfer succeeded, else revert
*/
function transferTokens(
address spender,
address src,
address dst,
uint256 tokens
) internal returns (uint256) {
/* Fail if transfer not allowed */
uint256 allowed = peridottroller.transferAllowed(
address(this),
src,
dst,
tokens
);
if (allowed != 0) {
revert TransferPeridottrollerRejection(allowed);
}
/* Do not allow self-transfers */
if (src == dst) {
revert TransferNotAllowed();
}
/* Get the allowance, infinite for the account owner */
uint256 startingAllowance = 0;
if (spender == src) {
startingAllowance = type(uint256).max;
} else {
startingAllowance = transferAllowances[src][spender];
}
/* Do the calculations, checking for {under,over}flow */
uint256 allowanceNew = startingAllowance - tokens;
uint256 srpTokensNew = accountTokens[src] - tokens;
uint256 dstTokensNew = accountTokens[dst] + tokens;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
accountTokens[src] = srpTokensNew;
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
// peridottroller.transferVerify(address(this), src, dst, tokens);
return NO_ERROR;
}
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transfer(
address dst,
uint256 amount
) external override nonReentrant returns (bool) {
return transferTokens(msg.sender, msg.sender, dst, amount) == NO_ERROR;
}
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transferFrom(
address src,
address dst,
uint256 amount
) external override nonReentrant returns (bool) {
return transferTokens(msg.sender, src, dst, amount) == NO_ERROR;
}
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved (uint256.max means infinite)
* @return Whether or not the approval succeeded
*/
function approve(
address spender,
uint256 amount
) external override returns (bool) {
address src = msg.sender;
transferAllowances[src][spender] = amount;
emit Approval(src, spender, amount);
return true;
}
/**
* @notice 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 (uint256) {
Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);
}
/**
* @notice Get a snapshot of the account's balances, and the cached exchange rate
* @dev This is used by peridottroller to more efficiently perform liquidity checks.
* @param account Address of the account to snapshot
* @return (possible error, token balance, borrow balance, exchange rate mantissa)
*/
function getAccountSnapshot(
address account
) external view override returns (uint256, uint256, uint256, uint256) {
return (
NO_ERROR,
accountTokens[account],
borrowBalanceStoredInternal(account),
exchangeRateStoredInternal()
);
}
/**
* @dev Function to simply retrieve block number
* This exists mainly for inheriting test contracts to stub this result.
*/
function getBlockNumber() internal view virtual returns (uint256) {
return block.number;
}
/**
* @notice Returns the current per-block borrow interest rate for this pToken
* @return The borrow interest rate per block, scaled by 1e18
*/
function borrowRatePerBlock() external view override returns (uint256) {
return
interestRateModel.getBorrowRate(
getCashPrior(),
totalBorrows,
totalReserves
);
}
/**
* @notice Returns the current per-block supply interest rate for this pToken
* @return The supply interest rate per block, scaled by 1e18
*/
function supplyRatePerBlock() external view override returns (uint256) {
return
interestRateModel.getSupplyRate(
getCashPrior(),
totalBorrows,
totalReserves,
reserveFactorMantissa
);
}
/**
* @notice Returns the current total borrows plus accrued interest
* @return The total borrows with interest
*/
function totalBorrowsCurrent()
external
override
nonReentrant
returns (uint256)
{
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 (uint256) {
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 (uint256) {
return borrowBalanceStoredInternal(account);
}
/**
* @notice Return the borrow balance of account based on stored data
* @param account The address whose balance should be calculated
* @return (error code, the calculated balance or 0 if error code is non-zero)
*/
function borrowBalanceStoredInternal(
address account
) internal view returns (uint256) {
/* Get borrowBalance and borrowIndex */
BorrowSnapshot storage borrowSnapshot = accountBorrows[account];
/* If borrowBalance = 0 then borrowIndex is likely also 0.
* Rather than failing the calculation with a division by 0, we immediately return 0 in this case.
*/
if (borrowSnapshot.principal == 0) {
return 0;
}
/* Calculate new borrow balance using the interest index:
* recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
*/
uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;
return principalTimesIndex / borrowSnapshot.interestIndex;
}
/**
* @notice Accrue interest then return the up-to-date exchange rate
* @return Calculated exchange rate scaled by 1e18
*/
function exchangeRateCurrent()
public
override
nonReentrant
returns (uint256)
{
accrueInterest();
return exchangeRateStored();
}
/**
* @notice Calculates the exchange rate from the underlying to the PToken
* @dev This function does not accrue interest before calculating the exchange rate
* @return Calculated exchange rate scaled by 1e18
*/
function exchangeRateStored() public view override returns (uint256) {
return exchangeRateStoredInternal();
}
/**
* @notice Calculates the exchange rate from the underlying to the PToken
* @dev This function does not accrue interest before calculating the exchange rate
* @return calculated exchange rate scaled by 1e18
*/
function exchangeRateStoredInternal()
internal
view
virtual
returns (uint256)
{
uint256 _totalSupply = totalSupply;
if (_totalSupply == 0) {
/*
* If there are no tokens minted:
* exchangeRate = initialExchangeRate
*/
return initialExchangeRateMantissa;
} else {
/*
* Otherwise:
* exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
*/
uint256 totalCash = getCashPrior();
uint256 cashPlusBorrowsMinusReserves = totalCash +
totalBorrows -
totalReserves;
uint256 exchangeRate = (cashPlusBorrowsMinusReserves * expScale) /
_totalSupply;
return exchangeRate;
}
}
/**
* @notice Get cash balance of this pToken in the underlying asset
* @return The quantity of underlying asset owned by this contract
*/
function getCash() external view override returns (uint256) {
return getCashPrior();
}
/**
* @notice Applies accrued interest to total borrows and reserves
* @dev This calculates interest accrued from the last checkpointed block
* up to the current block and writes new checkpoint to storage.
*/
function accrueInterest() public virtual override returns (uint256) {
/* Remember the initial block number */
uint256 currentBlockNumber = getBlockNumber();
uint256 accrualBlockNumberPrior = accrualBlockNumber;
/* Short-circuit accumulating 0 interest */
if (accrualBlockNumberPrior == currentBlockNumber) {
return NO_ERROR;
}
/* Read the previous values out of storage */
uint256 cashPrior = getCashPrior();
uint256 borrowsPrior = totalBorrows;
uint256 reservesPrior = totalReserves;
uint256 borrowIndexPrior = borrowIndex;
/* Calculate the current borrow interest rate */
uint256 borrowRateMantissa = interestRateModel.getBorrowRate(
cashPrior,
borrowsPrior,
reservesPrior
);
require(
borrowRateMantissa <= borrowRateMaxMantissa,
"borrow rate is absurdly high"
);
/* Calculate the number of blocks elapsed since the last accrual */
uint256 blockDelta = currentBlockNumber - accrualBlockNumberPrior;
/*
* Calculate the interest accumulated into borrows and reserves and the new index:
* simpleInterestFactor = borrowRate * blockDelta
* interestAccumulated = simpleInterestFactor * totalBorrows
* totalBorrowsNew = interestAccumulated + totalBorrows
* totalReservesNew = interestAccumulated * reserveFactor + totalReserves
* borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
*/
Exp memory simpleInterestFactor = mul_(
Exp({mantissa: borrowRateMantissa}),
blockDelta
);
uint256 interestAccumulated = mul_ScalarTruncate(
simpleInterestFactor,
borrowsPrior
);
uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;
uint256 totalReservesNew = mul_ScalarTruncateAddUInt(
Exp({mantissa: reserveFactorMantissa}),
interestAccumulated,
reservesPrior
);
uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(
simpleInterestFactor,
borrowIndexPrior,
borrowIndexPrior
);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
accrualBlockNumber = currentBlockNumber;
borrowIndex = borrowIndexNew;
totalBorrows = totalBorrowsNew;
totalReserves = totalReservesNew;
/* We emit an AccrueInterest event */
emit AccrueInterest(
cashPrior,
interestAccumulated,
borrowIndexNew,
totalBorrowsNew
);
return NO_ERROR;
}
/**
* @notice Sender supplies assets into the market and receives pTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param mintAmount The amount of the underlying asset to supply
*/
function mintInternal(uint256 mintAmount) internal nonReentrant {
accrueInterest();
// mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
mintFresh(msg.sender, mintAmount);
}
/**
* @notice User supplies assets into the market and receives pTokens 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
*/
function mintFresh(address minter, uint256 mintAmount) internal {
/* Fail if mint not allowed */
uint256 allowed = peridottroller.mintAllowed(
address(this),
minter,
mintAmount
);
if (allowed != 0) {
revert MintPeridottrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert MintFreshnessCheck();
}
Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call `doTransferIn` for the minter and the mintAmount.
* Note: The pToken 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 pToken holds an additional `actualMintAmount`
* of cash.
*/
uint256 actualMintAmount = doTransferIn(minter, mintAmount);
/*
* We get the current exchange rate and calculate the number of pTokens to be minted:
* mintTokens = actualMintAmount / exchangeRate
*/
uint256 mintTokens = div_(actualMintAmount, exchangeRate);
/*
* We calculate the new total supply of pTokens and minter token balance, checking for overflow:
* totalSupplyNew = totalSupply + mintTokens
* accountTokensNew = accountTokens[minter] + mintTokens
* And write them into storage
*/
totalSupply = totalSupply + mintTokens;
accountTokens[minter] = accountTokens[minter] + mintTokens;
/* We emit a Mint event, and a Transfer event */
emit Mint(minter, actualMintAmount, mintTokens);
emit Transfer(address(this), minter, mintTokens);
/* We call the defense hook */
// unused function
// peridottroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);
}
/**
* @notice Sender redeems pTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of pTokens to redeem into underlying
*/
function redeemInternal(uint256 redeemTokens) internal nonReentrant {
accrueInterest();
// redeemFresh emits redeem-specific logs on errors, so we don't need to
redeemFresh(payable(msg.sender), redeemTokens, 0);
}
/**
* @notice Sender redeems pTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemAmount The amount of underlying to receive from redeeming pTokens
*/
function redeemUnderlyingInternal(
uint256 redeemAmount
) internal nonReentrant {
accrueInterest();
// redeemFresh emits redeem-specific logs on errors, so we don't need to
redeemFresh(payable(msg.sender), 0, redeemAmount);
}
/**
* @notice User redeems pTokens 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 pTokens 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 pTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)
*/
function redeemFresh(
address payable redeemer,
uint256 redeemTokensIn,
uint256 redeemAmountIn
) internal {
require(
redeemTokensIn == 0 || redeemAmountIn == 0,
"one of redeemTokensIn or redeemAmountIn must be zero"
);
/* exchangeRate = invoke Exchange Rate Stored() */
Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
uint256 redeemTokens;
uint256 redeemAmount;
/* 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
*/
redeemTokens = redeemTokensIn;
redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn);
} else {
/*
* We get the current exchange rate and calculate the amount to be redeemed:
* redeemTokens = redeemAmountIn / exchangeRate
* redeemAmount = redeemAmountIn
*/
redeemTokens = div_(redeemAmountIn, exchangeRate);
redeemAmount = redeemAmountIn;
}
/* Fail if redeem not allowed */
uint256 allowed = peridottroller.redeemAllowed(
address(this),
redeemer,
redeemTokens
);
if (allowed != 0) {
revert RedeemPeridottrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert RedeemFreshnessCheck();
}
/* Fail gracefully if protocol has insufficient cash */
if (getCashPrior() < redeemAmount) {
revert RedeemTransferOutNotPossible();
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We write the previously calculated values into storage.
* Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.
*/
totalSupply = totalSupply - redeemTokens;
accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens;
/*
* We invoke doTransferOut for the redeemer and the redeemAmount.
* Note: The pToken must handle variations between ERC-20 and ETH underlying.
* On success, the pToken has redeemAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(redeemer, redeemAmount);
/* We emit a Transfer event, and a Redeem event */
emit Transfer(redeemer, address(this), redeemTokens);
emit Redeem(redeemer, redeemAmount, redeemTokens);
/* We call the defense hook */
peridottroller.redeemVerify(
address(this),
redeemer,
redeemAmount,
redeemTokens
);
}
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
*/
function borrowInternal(uint256 borrowAmount) internal nonReentrant {
accrueInterest();
// borrowFresh emits borrow-specific logs on errors, so we don't need to
borrowFresh(payable(msg.sender), borrowAmount);
}
/**
* @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,
uint256 borrowAmount
) internal {
/* Fail if borrow not allowed */
uint256 allowed = peridottroller.borrowAllowed(
address(this),
borrower,
borrowAmount
);
if (allowed != 0) {
revert BorrowPeridottrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert BorrowFreshnessCheck();
}
/* Fail gracefully if protocol has insufficient underlying cash */
if (getCashPrior() < borrowAmount) {
revert BorrowCashNotAvailable();
}
/*
* We calculate the new borrower and total borrow balances, failing on overflow:
* accountBorrowNew = accountBorrow + borrowAmount
* totalBorrowsNew = totalBorrows + borrowAmount
*/
uint256 accountBorrowsPrev = borrowBalanceStoredInternal(borrower);
uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;
uint256 totalBorrowsNew = totalBorrows + borrowAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We write the previously calculated values into storage.
* Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.
`*/
accountBorrows[borrower].principal = accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = totalBorrowsNew;
/*
* We invoke doTransferOut for the borrower and the borrowAmount.
* Note: The pToken must handle variations between ERC-20 and ETH underlying.
* On success, the pToken borrowAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(borrower, borrowAmount);
/* We emit a Borrow event */
emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);
}
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
*/
function repayBorrowInternal(uint256 repayAmount) internal nonReentrant {
accrueInterest();
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
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, or -1 for the full outstanding amount
*/
function repayBorrowBehalfInternal(
address borrower,
uint256 repayAmount
) internal nonReentrant {
accrueInterest();
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
repayBorrowFresh(msg.sender, borrower, repayAmount);
}
/**
* @notice Borrows are repaid by another user (possibly the borrower).
* @param payer the account paying off the borrow
* @param borrower the account with the debt being payed off
* @param repayAmount the amount of underlying tokens being returned, or -1 for the full outstanding amount
* @return (uint) the actual repayment amount.
*/
function repayBorrowFresh(
address payer,
address borrower,
uint256 repayAmount
) internal returns (uint256) {
/* Fail if repayBorrow not allowed */
uint256 allowed = peridottroller.repayBorrowAllowed(
address(this),
payer,
borrower,
repayAmount
);
if (allowed != 0) {
revert RepayBorrowPeridottrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert RepayBorrowFreshnessCheck();
}
/* We fetch the amount the borrower owes, with accumulated interest */
uint256 accountBorrowsPrev = borrowBalanceStoredInternal(borrower);
/* If repayAmount == -1, repayAmount = accountBorrows */
uint256 repayAmountFinal = repayAmount == type(uint256).max
? accountBorrowsPrev
: repayAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the payer and the repayAmount
* Note: The pToken must handle variations between ERC-20 and ETH underlying.
* On success, the pToken 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.
*/
uint256 actualRepayAmount = doTransferIn(payer, repayAmountFinal);
/*
* We calculate the new borrower and total borrow balances, failing on underflow:
* accountBorrowsNew = accountBorrows - actualRepayAmount
* totalBorrowsNew = totalBorrows - actualRepayAmount
*/
uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;
uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;
/* We write the previously calculated values into storage */
accountBorrows[borrower].principal = accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = totalBorrowsNew;
/* We emit a RepayBorrow event */
emit RepayBorrow(
payer,
borrower,
actualRepayAmount,
accountBorrowsNew,
totalBorrowsNew
);
return actualRepayAmount;
}
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this pToken to be liquidated
* @param pTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
*/
function liquidateBorrowInternal(
address borrower,
uint256 repayAmount,
PTokenInterface pTokenCollateral
) internal nonReentrant {
accrueInterest();
uint256 error = pTokenCollateral.accrueInterest();
if (error != NO_ERROR) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
revert LiquidateAccrueCollateralInterestFailed(error);
}
// liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to
liquidateBorrowFresh(
msg.sender,
borrower,
repayAmount,
pTokenCollateral
);
}
/**
* @notice The liquidator liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this pToken to be liquidated
* @param liquidator The address repaying the borrow and seizing collateral
* @param pTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
*/
function liquidateBorrowFresh(
address liquidator,
address borrower,
uint256 repayAmount,
PTokenInterface pTokenCollateral
) internal {
/* Fail if liquidate not allowed */
uint256 allowed = peridottroller.liquidateBorrowAllowed(
address(this),
address(pTokenCollateral),
liquidator,
borrower,
repayAmount
);
if (allowed != 0) {
revert LiquidatePeridottrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert LiquidateFreshnessCheck();
}
/* Verify pTokenCollateral market's block number equals current block number */
if (pTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
revert LiquidateCollateralFreshnessCheck();
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
revert LiquidateLiquidatorIsBorrower();
}
/* Fail if repayAmount = 0 */
if (repayAmount == 0) {
revert LiquidateCloseAmountIsZero();
}
/* Fail if repayAmount = -1 */
if (repayAmount == type(uint256).max) {
revert LiquidateCloseAmountIsUintMax();
}
/* Fail if repayBorrow fails */
uint256 actualRepayAmount = repayBorrowFresh(
liquidator,
borrower,
repayAmount
);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We calculate the number of collateral tokens that will be seized */
(uint256 amountSeizeError, uint256 seizeTokens) = peridottroller
.liquidateCalculateSeizeTokens(
address(this),
address(pTokenCollateral),
actualRepayAmount
);
require(
amountSeizeError == NO_ERROR,
"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"
);
/* Revert if borrower collateral token balance < seizeTokens */
require(
pTokenCollateral.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(pTokenCollateral) == address(this)) {
seizeInternal(address(this), liquidator, borrower, seizeTokens);
} else {
require(
pTokenCollateral.seize(liquidator, borrower, seizeTokens) ==
NO_ERROR,
"token seizure failed"
);
}
/* We emit a LiquidateBorrow event */
emit LiquidateBorrow(
liquidator,
borrower,
actualRepayAmount,
address(pTokenCollateral),
seizeTokens
);
}
/**
* @notice Transfers collateral tokens (this market) to the liquidator.
* @dev Will fail unless called by another pToken during the process of liquidation.
* Its absolutely critical to use msg.sender as the borrowed pToken and not a parameter.
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of pTokens to seize
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function seize(
address liquidator,
address borrower,
uint256 seizeTokens
) external override nonReentrant returns (uint256) {
seizeInternal(msg.sender, liquidator, borrower, seizeTokens);
return NO_ERROR;
}
/**
* @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 PToken.
* Its absolutely critical to use msg.sender as the seizer pToken and not a parameter.
* @param seizerToken The contract seizing the collateral (i.e. borrowed pToken)
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of pTokens to seize
*/
function seizeInternal(
address seizerToken,
address liquidator,
address borrower,
uint256 seizeTokens
) internal {
/* Fail if seize not allowed */
uint256 allowed = peridottroller.seizeAllowed(
address(this),
seizerToken,
liquidator,
borrower,
seizeTokens
);
if (allowed != 0) {
revert LiquidateSeizePeridottrollerRejection(allowed);
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
revert LiquidateSeizeLiquidatorIsBorrower();
}
/*
* We calculate the new borrower and liquidator token balances, failing on underflow/overflow:
* borrowerTokensNew = accountTokens[borrower] - seizeTokens
* liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
*/
uint256 protocolSeizeTokens = mul_(
seizeTokens,
Exp({mantissa: protocolSeizeShareMantissa})
);
uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;
Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
uint256 protocolSeizeAmount = mul_ScalarTruncate(
exchangeRate,
protocolSeizeTokens
);
uint256 totalReservesNew = totalReserves + protocolSeizeAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the calculated values into storage */
totalReserves = totalReservesNew;
totalSupply = totalSupply - protocolSeizeTokens;
accountTokens[borrower] = accountTokens[borrower] - seizeTokens;
accountTokens[liquidator] =
accountTokens[liquidator] +
liquidatorSeizeTokens;
/* Emit a Transfer event */
emit Transfer(borrower, liquidator, liquidatorSeizeTokens);
emit Transfer(borrower, address(this), protocolSeizeTokens);
emit ReservesAdded(
address(this),
protocolSeizeAmount,
totalReservesNew
);
}
/**
* Admin Functions **
*/
/**
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @param newPendingAdmin New pending admin.
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setPendingAdmin(
address payable newPendingAdmin
) external override returns (uint256) {
// Check caller = admin
if (msg.sender != admin) {
revert SetPendingAdminOwnerCheck();
}
// Save current value, if any, for inclusion in log
address oldPendingAdmin = pendingAdmin;
// Store pendingAdmin with value newPendingAdmin
pendingAdmin = newPendingAdmin;
// Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
return NO_ERROR;
}
/**
* @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
* @dev Admin function for pending admin to accept role and update admin
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _acceptAdmin() external override returns (uint256) {
// Check caller is pendingAdmin and pendingAdmin ≠ address(0)
if (msg.sender != pendingAdmin || msg.sender == address(0)) {
revert AcceptAdminPendingAdminCheck();
}
// Save current values for inclusion in log
address oldAdmin = admin;
address oldPendingAdmin = pendingAdmin;
// Store admin with value pendingAdmin
admin = pendingAdmin;
// Clear the pending value
pendingAdmin = payable(address(0));
emit NewAdmin(oldAdmin, admin);
emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
return NO_ERROR;
}
/**
* @notice Sets a new peridottroller for the market
* @dev Admin function to set a new peridottroller
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setPeridottroller(
PeridottrollerInterface newPeridottroller
) public override returns (uint256) {
// Check caller is admin
if (msg.sender != admin) {
revert SetPeridottrollerOwnerCheck();
}
PeridottrollerInterface oldPeridottroller = peridottroller;
// Ensure invoke peridottroller.isPeridottroller() returns true
require(
newPeridottroller.isPeridottroller(),
"marker method returned false"
);
// Set market's peridottroller to newPeridottroller
peridottroller = newPeridottroller;
// Emit NewPeridottroller(oldPeridottroller, newPeridottroller)
emit NewPeridottroller(oldPeridottroller, newPeridottroller);
return NO_ERROR;
}
/**
* @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
* @dev Admin function to accrue interest and set a new reserve factor
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setReserveFactor(
uint256 newReserveFactorMantissa
) external override nonReentrant returns (uint256) {
accrueInterest();
// _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to.
return _setReserveFactorFresh(newReserveFactorMantissa);
}
/**
* @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)
* @dev Admin function to set a new reserve factor
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setReserveFactorFresh(
uint256 newReserveFactorMantissa
) internal returns (uint256) {
// Check caller is admin
if (msg.sender != admin) {
revert SetReserveFactorAdminCheck();
}
// Verify market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert SetReserveFactorFreshCheck();
}
// Check newReserveFactor ≤ maxReserveFactor
if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
revert SetReserveFactorBoundsCheck();
}
uint256 oldReserveFactorMantissa = reserveFactorMantissa;
reserveFactorMantissa = newReserveFactorMantissa;
emit NewReserveFactor(
oldReserveFactorMantissa,
newReserveFactorMantissa
);
return NO_ERROR;
}
/**
* @notice Accrues interest and reduces reserves by transferring from msg.sender
* @param addAmount Amount of addition to reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _addReservesInternal(
uint256 addAmount
) internal nonReentrant returns (uint256) {
accrueInterest();
// _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
_addReservesFresh(addAmount);
return NO_ERROR;
}
/**
* @notice Add reserves by transferring from caller
* @dev Requires fresh interest accrual
* @param addAmount Amount of addition to reserves
* @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees
*/
function _addReservesFresh(
uint256 addAmount
) internal returns (uint256, uint256) {
// totalReserves + actualAddAmount
uint256 totalReservesNew;
uint256 actualAddAmount;
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert AddReservesFactorFreshCheck(actualAddAmount);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the caller and the addAmount
* Note: The pToken must handle variations between ERC-20 and ETH underlying.
* On success, the pToken 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;
// Store reserves[n+1] = reserves[n] + actualAddAmount
totalReserves = totalReservesNew;
/* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */
emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);
/* Return (NO_ERROR, actualAddAmount) */
return (NO_ERROR, actualAddAmount);
}
/**
* @notice Accrues interest and reduces reserves by transferring to admin
* @param reduceAmount Amount of reduction to reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _reduceReserves(
uint256 reduceAmount
) external override nonReentrant returns (uint256) {
accrueInterest();
// _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to.
return _reduceReservesFresh(reduceAmount);
}
/**
* @notice Reduces reserves by transferring to admin
* @dev Requires fresh interest accrual
* @param reduceAmount Amount of reduction to reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _reduceReservesFresh(
uint256 reduceAmount
) internal returns (uint256) {
// totalReserves - reduceAmount
uint256 totalReservesNew;
// Check caller is admin
if (msg.sender != admin) {
revert ReduceReservesAdminCheck();
}
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert ReduceReservesFreshCheck();
}
// Fail gracefully if protocol has insufficient underlying cash
if (getCashPrior() < reduceAmount) {
revert ReduceReservesCashNotAvailable();
}
// Check reduceAmount ≤ reserves[n] (totalReserves)
if (reduceAmount > totalReserves) {
revert ReduceReservesCashValidation();
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
totalReservesNew = totalReserves - reduceAmount;
// Store reserves[n+1] = reserves[n] - reduceAmount
totalReserves = totalReservesNew;
// doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
doTransferOut(admin, reduceAmount);
emit ReservesReduced(admin, reduceAmount, totalReservesNew);
return NO_ERROR;
}
/**
* @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh
* @dev Admin function to accrue interest and update the interest rate model
* @param newInterestRateModel the new interest rate model to use
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setInterestRateModel(
InterestRateModel newInterestRateModel
) public override returns (uint256) {
accrueInterest();
// _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to.
return _setInterestRateModelFresh(newInterestRateModel);
}
/**
* @notice updates the interest rate model (*requires fresh interest accrual)
* @dev Admin function to update the interest rate model
* @param newInterestRateModel the new interest rate model to use
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setInterestRateModelFresh(
InterestRateModel newInterestRateModel
) internal returns (uint256) {
// Used to store old model for use in the event that is emitted on success
InterestRateModel oldInterestRateModel;
// Check caller is admin
if (msg.sender != admin) {
revert SetInterestRateModelOwnerCheck();
}
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert SetInterestRateModelFreshCheck();
}
// Track the market's current interest rate model
oldInterestRateModel = interestRateModel;
// Ensure invoke newInterestRateModel.isInterestRateModel() returns true
require(
newInterestRateModel.isInterestRateModel(),
"marker method returned false"
);
// Set the interest rate model to newInterestRateModel
interestRateModel = newInterestRateModel;
// Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)
emit NewMarketInterestRateModel(
oldInterestRateModel,
newInterestRateModel
);
return NO_ERROR;
}
/**
* Flash Loan Functions **
*/
/**
* @notice Returns the maximum amount available for flash loan for a given token
* @param token The address of the token to flash loan
* @return The maximum amount available for flash loan
*/
function maxFlashLoan(
address token
)
external
view
override(PTokenInterface, IERC3156FlashLender)
returns (uint256)
{
// Only allow flash loans of the underlying token
if (token != getUnderlyingAddress()) {
return 0;
}
// If flash loans are paused, return 0
if (flashLoansPaused) {
return 0;
}
uint256 cash = getCashPrior();
uint256 maxAmount = (cash * maxFlashLoanRatio) / 10000;
return maxAmount;
}
/**
* @notice Returns the fee for a flash loan of the given amount
* @param token The address of the token to flash loan
* @param amount The amount to flash loan
* @return The fee for the flash loan
*/
function flashFee(
address token,
uint256 amount
)
external
view
override(PTokenInterface, IERC3156FlashLender)
returns (uint256)
{
// Only allow flash loans of the underlying token
require(
token == getUnderlyingAddress(),
"FlashLoan: token not supported"
);
return (amount * flashLoanFeeBps) / 10000;
}
/**
* @notice Executes a flash loan
* @param receiver The contract that will receive the flash loan
* @param token The address of the token to flash loan
* @param amount The amount to flash loan
* @param data Arbitrary data to pass to the receiver
* @return True if the flash loan was successful
*/
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
)
external
override(PTokenInterface, IERC3156FlashLender)
nonReentrant
returns (bool)
{
// Only allow flash loans of the underlying token
require(
token == getUnderlyingAddress(),
"FlashLoan: token not supported"
);
// Check if flash loans are paused
require(!flashLoansPaused, "FlashLoan: flash loans are paused");
// Check if amount is within limits
require(
amount <= this.maxFlashLoan(token),
"FlashLoan: amount exceeds maximum"
);
// Calculate minimum required fee
uint256 minFee = (amount * flashLoanFeeBps) / 10000;
// Get cash balance before
uint256 balanceBefore = getCashPrior();
// Transfer tokens to receiver
doTransferOut(payable(address(receiver)), amount);
// Execute the flash loan callback
require(
receiver.onFlashLoan(msg.sender, token, amount, minFee, data) ==
keccak256("ERC3156FlashBorrower.onFlashLoan"),
"FlashLoan: invalid callback return"
);
// Get cash balance after
uint256 balanceAfter = getCashPrior();
// Compute the actual fee paid as the delta of cash
require(balanceAfter >= balanceBefore, "FlashLoan: loan not repaid");
uint256 actualFee = balanceAfter - balanceBefore;
// Require borrower paid at least the minimum fee
require(actualFee >= minFee, "FlashLoan: loan not repaid");
// Update reserves with the actual fee to avoid inflating available cash
if (actualFee > 0) {
totalReserves = totalReserves + actualFee;
}
// Emit flash loan event with the actual fee
emit FlashLoan(address(receiver), token, amount, actualFee);
return true;
}
/**
* @notice Admin function to set flash loan fee
* @param newFeeBps New fee in basis points (1 = 0.01%)
*/
function _setFlashLoanFee(uint256 newFeeBps) external returns (uint256) {
// Check caller is admin
require(msg.sender == admin, "Only admin can set flash loan fee");
// Fee cannot exceed 1% (100 basis points)
require(newFeeBps <= 100, "Flash loan fee too high");
uint256 oldFeeBps = flashLoanFeeBps;
flashLoanFeeBps = newFeeBps;
emit NewFlashLoanFee(oldFeeBps, newFeeBps);
return NO_ERROR;
}
/**
* @notice Admin function to set maximum flash loan ratio
* @param newMaxRatio New maximum ratio in basis points (10000 = 100%)
*/
function _setMaxFlashLoanRatio(
uint256 newMaxRatio
) external returns (uint256) {
// Check caller is admin
require(msg.sender == admin, "Only admin can set max flash loan ratio");
// Ratio cannot exceed 100% (10000 basis points)
require(newMaxRatio <= 10000, "Flash loan ratio too high");
uint256 oldMaxRatio = maxFlashLoanRatio;
maxFlashLoanRatio = newMaxRatio;
emit NewMaxFlashLoanRatio(oldMaxRatio, newMaxRatio);
return NO_ERROR;
}
/**
* @notice Admin function to pause/unpause flash loans
* @param state True to pause, false to unpause
*/
function _setFlashLoansPaused(bool state) external returns (bool) {
// Check caller is admin or pause guardian
if (
!(msg.sender == admin ||
msg.sender ==
PeridottrollerV2Storage(address(peridottroller))
.pauseGuardian())
) {
return false;
}
flashLoansPaused = state;
emit FlashLoansPaused(state);
return true;
}
/**
* @notice Get the underlying token address (to be implemented in derived contracts)
* @return The address of the underlying token
*/
function getUnderlyingAddress() internal view virtual returns (address);
/**
* Safe Token **
*/
/**
* @notice Gets balance of this contract in terms of the underlying
* @dev This excludes the value of the current message, if any
* @return The quantity of underlying owned by this contract
*/
function getCashPrior() internal view virtual returns (uint256);
/**
* @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
* This may revert due to insufficient balance or insufficient allowance.
*/
function doTransferIn(
address from,
uint256 amount
) internal virtual returns (uint256);
/**
* @dev Performs a transfer out, ideally returning an explanatory error code upon failure rather than reverting.
* If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
* If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
*/
function doTransferOut(address payable to, uint256 amount) internal virtual;
/**
* Reentrancy Guard **
*/
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
*/
modifier nonReentrant() {
require(_notEntered, "re-entered");
_notEntered = false;
_;
_notEntered = true; // get a gas-refund post-Istanbul
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
import "./PeridottrollerInterface.sol";
import "./InterestRateModel.sol";
import "./EIP20NonStandardInterface.sol";
import "./ErrorReporter.sol";
// Add ERC-3156 imports
interface IERC3156FlashBorrower {
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
interface IERC3156FlashLender {
function maxFlashLoan(address token) external view returns (uint256);
function flashFee(
address token,
uint256 amount
) external view returns (uint256);
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
}
contract PTokenStorage {
/**
* @dev Guard variable for re-entrancy checks
*/
bool internal _notEntered;
/**
* @notice EIP-20 token name for this token
*/
string public name;
/**
* @notice EIP-20 token symbol for this token
*/
string public symbol;
/**
* @notice EIP-20 token decimals for this token
*/
uint8 public decimals;
// Maximum borrow rate that can ever be applied (.0005% / block)
uint256 internal constant borrowRateMaxMantissa = 0.0005e16;
// Maximum fraction of interest that can be set aside for reserves
uint256 internal constant reserveFactorMaxMantissa = 1e18;
/**
* @notice Administrator for this contract
*/
address payable public admin;
/**
* @notice Pending administrator for this contract
*/
address payable public pendingAdmin;
/**
* @notice Contract which oversees inter-pToken operations
*/
PeridottrollerInterface public peridottroller;
/**
* @notice Model which tells what the current interest rate should be
*/
InterestRateModel public interestRateModel;
// Initial exchange rate used when minting the first PTokens (used when totalSupply = 0)
uint256 internal initialExchangeRateMantissa;
/**
* @notice Fraction of interest currently set aside for reserves
*/
uint256 public reserveFactorMantissa;
/**
* @notice Block number that interest was last accrued at
*/
uint256 public accrualBlockNumber;
/**
* @notice Accumulator of the total earned interest rate since the opening of the market
*/
uint256 public borrowIndex;
/**
* @notice Total amount of outstanding borrows of the underlying in this market
*/
uint256 public totalBorrows;
/**
* @notice Total amount of reserves of the underlying held in this market
*/
uint256 public totalReserves;
/**
* @notice Total number of tokens in circulation
*/
uint256 public totalSupply;
// Official record of token balances for each account
mapping(address => uint256) internal accountTokens;
// Approved token transfer amounts on behalf of others
mapping(address => mapping(address => uint256)) internal transferAllowances;
/**
* @notice Container for borrow balance information
* @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
* @member interestIndex Global borrowIndex as of the most recent balance-changing action
*/
struct BorrowSnapshot {
uint256 principal;
uint256 interestIndex;
}
// Mapping of account addresses to outstanding borrow balances
mapping(address => BorrowSnapshot) internal accountBorrows;
/**
* @notice Share of seized collateral that is added to reserves
*/
uint256 public constant protocolSeizeShareMantissa = 2.8e16; //2.8%
/**
* Flash Loan Storage **
*/
/**
* @notice Flash loan fee rate (in basis points). 1 = 0.01%
*/
uint256 public flashLoanFeeBps;
/**
* @notice Maximum flash loan amount as a percentage of total cash (in basis points)
* 10000 = 100% of available cash
*/
uint256 public maxFlashLoanRatio;
/**
* @notice Whether flash loans are paused
*/
bool public flashLoansPaused;
}
abstract contract PTokenInterface is PTokenStorage {
/**
* @notice Indicator that this is a PToken contract (for inspection)
*/
bool public constant isPToken = true;
/**
* Market Events **
*/
/**
* @notice Event emitted when interest is accrued
*/
event AccrueInterest(
uint256 cashPrior,
uint256 interestAccumulated,
uint256 borrowIndex,
uint256 totalBorrows
);
/**
* @notice Event emitted when tokens are minted
*/
event Mint(address minter, uint256 mintAmount, uint256 mintTokens);
/**
* @notice Event emitted when tokens are redeemed
*/
event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens);
/**
* @notice Event emitted when underlying is borrowed
*/
event Borrow(
address borrower,
uint256 borrowAmount,
uint256 accountBorrows,
uint256 totalBorrows
);
/**
* @notice Event emitted when a borrow is repaid
*/
event RepayBorrow(
address payer,
address borrower,
uint256 repayAmount,
uint256 accountBorrows,
uint256 totalBorrows
);
/**
* @notice Event emitted when a borrow is liquidated
*/
event LiquidateBorrow(
address liquidator,
address borrower,
uint256 repayAmount,
address pTokenCollateral,
uint256 seizeTokens
);
/**
* Admin Events **
*/
/**
* @notice Event emitted when pendingAdmin is changed
*/
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
/**
* @notice Event emitted when pendingAdmin is accepted, which means admin is updated
*/
event NewAdmin(address oldAdmin, address newAdmin);
/**
* @notice Event emitted when peridottroller is changed
*/
event NewPeridottroller(
PeridottrollerInterface oldPeridottroller,
PeridottrollerInterface newPeridottroller
);
/**
* @notice Event emitted when interestRateModel is changed
*/
event NewMarketInterestRateModel(
InterestRateModel oldInterestRateModel,
InterestRateModel newInterestRateModel
);
/**
* @notice Event emitted when the reserve factor is changed
*/
event NewReserveFactor(
uint256 oldReserveFactorMantissa,
uint256 newReserveFactorMantissa
);
/**
* @notice Event emitted when the reserves are added
*/
event ReservesAdded(
address benefactor,
uint256 addAmount,
uint256 newTotalReserves
);
/**
* @notice Event emitted when the reserves are reduced
*/
event ReservesReduced(
address admin,
uint256 reduceAmount,
uint256 newTotalReserves
);
/**
* @notice EIP20 Transfer event
*/
event Transfer(address indexed from, address indexed to, uint256 amount);
/**
* @notice EIP20 Approval event
*/
event Approval(
address indexed owner,
address indexed spender,
uint256 amount
);
/**
* Flash Loan Events **
*/
/**
* @notice Event emitted when a flash loan is executed
*/
event FlashLoan(
address indexed receiver,
address indexed token,
uint256 amount,
uint256 fee
);
/**
* @notice Event emitted when flash loan fee is changed
*/
event NewFlashLoanFee(uint256 oldFeeBps, uint256 newFeeBps);
/**
* @notice Event emitted when maximum flash loan ratio is changed
*/
event NewMaxFlashLoanRatio(uint256 oldMaxRatio, uint256 newMaxRatio);
/**
* @notice Event emitted when flash loans are paused/unpaused
*/
event FlashLoansPaused(bool paused);
/**
* User Interface **
*/
function transfer(
address dst,
uint256 amount
) external virtual returns (bool);
function transferFrom(
address src,
address dst,
uint256 amount
) external virtual returns (bool);
function approve(
address spender,
uint256 amount
) external virtual returns (bool);
function allowance(
address owner,
address spender
) external view virtual returns (uint256);
function balanceOf(address owner) external view virtual returns (uint256);
function balanceOfUnderlying(
address owner
) external virtual returns (uint256);
function getAccountSnapshot(
address account
) external view virtual returns (uint256, uint256, uint256, uint256);
function borrowRatePerBlock() external view virtual returns (uint256);
function supplyRatePerBlock() external view virtual returns (uint256);
function totalBorrowsCurrent() external virtual returns (uint256);
function borrowBalanceCurrent(
address account
) external virtual returns (uint256);
function borrowBalanceStored(
address account
) external view virtual returns (uint256);
function exchangeRateCurrent() external virtual returns (uint256);
function exchangeRateStored() external view virtual returns (uint256);
function getCash() external view virtual returns (uint256);
function accrueInterest() external virtual returns (uint256);
function seize(
address liquidator,
address borrower,
uint256 seizeTokens
) external virtual returns (uint256);
/**
* Admin Functions **
*/
function _setPendingAdmin(
address payable newPendingAdmin
) external virtual returns (uint256);
function _acceptAdmin() external virtual returns (uint256);
function _setPeridottroller(
PeridottrollerInterface newPeridottroller
) external virtual returns (uint256);
function _setReserveFactor(
uint256 newReserveFactorMantissa
) external virtual returns (uint256);
function _reduceReserves(
uint256 reduceAmount
) external virtual returns (uint256);
function _setInterestRateModel(
InterestRateModel newInterestRateModel
) external virtual returns (uint256);
/**
* Flash Loan Functions **
*/
function maxFlashLoan(
address token
) external view virtual returns (uint256);
function flashFee(
address token,
uint256 amount
) external view virtual returns (uint256);
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external virtual returns (bool);
}
contract PErc20Storage {
/**
* @notice Underlying asset for this PToken
*/
address public underlying;
}
abstract contract PErc20Interface is PErc20Storage {
/**
* User Interface **
*/
function mint(uint256 mintAmount) external virtual returns (uint256);
function redeem(uint256 redeemTokens) external virtual returns (uint256);
function redeemUnderlying(
uint256 redeemAmount
) external virtual returns (uint256);
function borrow(uint256 borrowAmount) external virtual returns (uint256);
function repayBorrow(
uint256 repayAmount
) external virtual returns (uint256);
function repayBorrowBehalf(
address borrower,
uint256 repayAmount
) external virtual returns (uint256);
function liquidateBorrow(
address borrower,
uint256 repayAmount,
PTokenInterface pTokenCollateral
) external virtual returns (uint256);
function sweepToken(EIP20NonStandardInterface token) external virtual;
/**
* Admin Functions **
*/
function _addReserves(uint256 addAmount) external virtual returns (uint256);
}
contract CDelegationStorage {
/**
* @notice Implementation address for this contract
*/
address public implementation;
}
abstract contract CDelegatorInterface is CDelegationStorage {
/**
* @notice Emitted when implementation is changed
*/
event NewImplementation(
address oldImplementation,
address newImplementation
);
/**
* @notice Called by the admin to update the implementation of the delegator
* @param implementation_ The address of the new implementation for delegation
* @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
* @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
*/
function _setImplementation(
address implementation_,
bool allowResign,
bytes memory becomeImplementationData
) external virtual;
}
abstract contract CDelegateInterface is CDelegationStorage {
/**
* @notice Called by the delegator on a delegate to initialize it for duty
* @dev Should revert if any issues arise which make it unfit for delegation
* @param data The encoded bytes data for any initialization
*/
function _becomeImplementation(bytes memory data) external virtual;
/**
* @notice Called by the delegator on a delegate to forfeit its responsibility
*/
function _resignImplementation() external virtual;
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
contract PeridottrollerErrorReporter {
enum Error {
NO_ERROR,
UNAUTHORIZED,
COMPTROLLER_MISMATCH,
INSUFFICIENT_SHORTFALL,
INSUFFICIENT_LIQUIDITY,
INVALID_CLOSE_FACTOR,
INVALID_COLLATERAL_FACTOR,
INVALID_LIQUIDATION_INCENTIVE,
MARKET_NOT_ENTERED, // no longer possible
MARKET_NOT_LISTED,
MARKET_ALREADY_LISTED,
MATH_ERROR,
NONZERO_BORROW_BALANCE,
PRICE_ERROR,
REJECTION,
SNAPSHOT_ERROR,
TOO_MANY_ASSETS,
TOO_MUCH_REPAY
}
enum FailureInfo {
ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
EXIT_MARKET_BALANCE_OWED,
EXIT_MARKET_REJECTION,
SET_CLOSE_FACTOR_OWNER_CHECK,
SET_CLOSE_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_OWNER_CHECK,
SET_COLLATERAL_FACTOR_NO_EXISTS,
SET_COLLATERAL_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
SET_IMPLEMENTATION_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_VALIDATION,
SET_MAX_ASSETS_OWNER_CHECK,
SET_PENDING_ADMIN_OWNER_CHECK,
SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
SET_PRICE_ORACLE_OWNER_CHECK,
SUPPORT_MARKET_EXISTS,
SUPPORT_MARKET_OWNER_CHECK,
SET_PAUSE_GUARDIAN_OWNER_CHECK,
SET_FLASH_LOAN_FEE_OWNER_CHECK,
SET_FLASH_LOAN_FEE_VALIDATION,
SET_MAX_FLASH_LOAN_RATIO_OWNER_CHECK,
SET_MAX_FLASH_LOAN_RATIO_VALIDATION
}
/**
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
* contract-specific code that enables us to report opaque error codes from upgradeable contracts.
*
*/
event Failure(uint256 error, uint256 info, uint256 detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/
function fail(Error err, FailureInfo info) internal returns (uint256) {
emit Failure(uint256(err), uint256(info), 0);
return uint256(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/
function failOpaque(Error err, FailureInfo info, uint256 opaqueError) internal returns (uint256) {
emit Failure(uint256(err), uint256(info), opaqueError);
return uint256(err);
}
}
contract TokenErrorReporter {
uint256 public constant NO_ERROR = 0; // support legacy return codes
error TransferPeridottrollerRejection(uint256 errorCode);
error TransferNotAllowed();
error TransferNotEnough();
error TransferTooMuch();
error MintPeridottrollerRejection(uint256 errorCode);
error MintFreshnessCheck();
error RedeemPeridottrollerRejection(uint256 errorCode);
error RedeemFreshnessCheck();
error RedeemTransferOutNotPossible();
error BorrowPeridottrollerRejection(uint256 errorCode);
error BorrowFreshnessCheck();
error BorrowCashNotAvailable();
error RepayBorrowPeridottrollerRejection(uint256 errorCode);
error RepayBorrowFreshnessCheck();
error LiquidatePeridottrollerRejection(uint256 errorCode);
error LiquidateFreshnessCheck();
error LiquidateCollateralFreshnessCheck();
error LiquidateAccrueBorrowInterestFailed(uint256 errorCode);
error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);
error LiquidateLiquidatorIsBorrower();
error LiquidateCloseAmountIsZero();
error LiquidateCloseAmountIsUintMax();
error LiquidateRepayBorrowFreshFailed(uint256 errorCode);
error LiquidateSeizePeridottrollerRejection(uint256 errorCode);
error LiquidateSeizeLiquidatorIsBorrower();
error AcceptAdminPendingAdminCheck();
error SetPeridottrollerOwnerCheck();
error SetPendingAdminOwnerCheck();
error SetReserveFactorAdminCheck();
error SetReserveFactorFreshCheck();
error SetReserveFactorBoundsCheck();
error AddReservesFactorFreshCheck(uint256 actualAddAmount);
error ReduceReservesAdminCheck();
error ReduceReservesFreshCheck();
error ReduceReservesCashNotAvailable();
error ReduceReservesCashValidation();
error SetInterestRateModelOwnerCheck();
error SetInterestRateModelFreshCheck();
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
/**
* @title ERC 20 Token Standard Interface
* https://eips.ethereum.org/EIPS/eip-20
*/
interface EIP20Interface {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance The balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return success Whether or not the transfer succeeded
*/
function transfer(address dst, uint256 amount) external returns (bool success);
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return success Whether or not the transfer succeeded
*/
function transferFrom(address src, address dst, uint256 amount) external returns (bool success);
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved (-1 means infinite)
* @return success Whether or not the approval succeeded
*/
function approve(address spender, uint256 amount) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent (-1 means infinite)
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
/**
* @title Exponential module for storing fixed-precision decimals
* @author Peridot
* @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 {
uint256 constant expScale = 1e18;
uint256 constant doubleScale = 1e36;
uint256 constant halfExpScale = expScale / 2;
uint256 constant mantissaOne = expScale;
struct Exp {
uint256 mantissa;
}
struct Double {
uint256 mantissa;
}
/**
* @dev Truncates the given exp to a whole number value.
* For example, truncate(Exp{mantissa: 15 * expScale}) = 15
*/
function truncate(Exp memory exp) internal pure returns (uint256) {
// Note: We are not using careful math here as we're performing a division that cannot fail
return exp.mantissa / expScale;
}
/**
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
*/
function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {
Exp memory product = mul_(a, scalar);
return truncate(product);
}
/**
* @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
*/
function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {
Exp memory product = mul_(a, scalar);
return add_(truncate(product), addend);
}
/**
* @dev Checks if first Exp is less than second Exp.
*/
function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {
return left.mantissa < right.mantissa;
}
/**
* @dev Checks if left Exp <= right Exp.
*/
function lessThanOrEqualExp(Exp memory left, Exp memory right) internal pure returns (bool) {
return left.mantissa <= right.mantissa;
}
/**
* @dev Checks if left Exp > right Exp.
*/
function greaterThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {
return left.mantissa > right.mantissa;
}
/**
* @dev returns true if Exp is exactly zero
*/
function isZeroExp(Exp memory value) internal pure returns (bool) {
return value.mantissa == 0;
}
function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {
require(n < 2 ** 224, errorMessage);
return uint224(n);
}
function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {
require(n < 2 ** 32, errorMessage);
return uint32(n);
}
function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
}
function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {
return mul_(a, b.mantissa) / expScale;
}
function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
}
function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint256 a, Double memory b) internal pure returns (uint256) {
return mul_(a, b.mantissa) / doubleScale;
}
function mul_(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
}
function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
return Exp({mantissa: div_(a.mantissa, b)});
}
function div_(uint256 a, Exp memory b) internal pure returns (uint256) {
return div_(mul_(a, expScale), b.mantissa);
}
function div_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
}
function div_(Double memory a, uint256 b) internal pure returns (Double memory) {
return Double({mantissa: div_(a.mantissa, b)});
}
function div_(uint256 a, Double memory b) internal pure returns (uint256) {
return div_(mul_(a, doubleScale), b.mantissa);
}
function div_(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {
return Double({mantissa: div_(mul_(a, doubleScale), b)});
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
import "./PToken.sol";
import "./PriceOracle.sol";
contract UnitrollerAdminStorage {
/**
* @notice Administrator for this contract
*/
address public admin;
/**
* @notice Pending administrator for this contract
*/
address public pendingAdmin;
/**
* @notice Active brains of Unitroller
*/
address public peridottrollerImplementation;
/**
* @notice Pending brains of Unitroller
*/
address public pendingPeridottrollerImplementation;
}
contract PeridottrollerV1Storage is UnitrollerAdminStorage {
/**
* @notice Oracle which gives the price of any given asset
*/
PriceOracle public oracle;
/**
* @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow
*/
uint256 public closeFactorMantissa;
/**
* @notice Multiplier representing the discount on collateral that a liquidator receives
*/
uint256 public liquidationIncentiveMantissa;
/**
* @notice Max number of assets a single account can participate in (borrow or use as collateral)
*/
uint256 public maxAssets;
/**
* @notice Per-account mapping of "assets you are in", capped by maxAssets
*/
mapping(address => PToken[]) public accountAssets;
}
contract PeridottrollerV2Storage is PeridottrollerV1Storage {
struct Market {
// Whether or not this market is listed
bool isListed;
// Multiplier representing the most one can borrow against their collateral in this market.
// For instance, 0.9 to allow borrowing 90% of collateral value.
// Must be between 0 and 1, and stored as a mantissa.
uint256 collateralFactorMantissa;
// Per-market mapping of "accounts in this asset"
mapping(address => bool) accountMembership;
// Whether or not this market receives PERIDOT
bool isPeridoted;
}
/**
* @notice Official mapping of pTokens -> Market metadata
* @dev Used e.g. to determine if a market is supported
*/
mapping(address => Market) public markets;
/**
* @notice The Pause Guardian can pause certain actions as a safety mechanism.
* Actions which allow users to remove their own assets cannot be paused.
* Liquidation / seizing / transfer can only be paused globally, not by market.
*/
address public pauseGuardian;
bool public _mintGuardianPaused;
bool public _borrowGuardianPaused;
bool public transferGuardianPaused;
bool public seizeGuardianPaused;
mapping(address => bool) public mintGuardianPaused;
mapping(address => bool) public borrowGuardianPaused;
}
contract PeridottrollerV3Storage is PeridottrollerV2Storage {
struct PeridotMarketState {
// The market's last updated peridotBorrowIndex or peridotSupplyIndex
uint224 index;
// The block number the index was last updated at
uint32 block;
}
/// @notice A list of all markets
PToken[] public allMarkets;
/// @notice The rate at which the flywheel distributes PERIDOT, per block
uint256 public peridotRate;
/// @notice The portion of peridotRate that each market currently receives
mapping(address => uint256) public peridotSpeeds;
/// @notice The PERIDOT market supply state for each market
mapping(address => PeridotMarketState) public peridotSupplyState;
/// @notice The PERIDOT market borrow state for each market
mapping(address => PeridotMarketState) public peridotBorrowState;
/// @notice The PERIDOT borrow index for each market for each supplier as of the last time they accrued PERIDOT
mapping(address => mapping(address => uint256)) public peridotSupplierIndex;
/// @notice The PERIDOT borrow index for each market for each borrower as of the last time they accrued PERIDOT
mapping(address => mapping(address => uint256)) public peridotBorrowerIndex;
/// @notice The PERIDOT accrued but not yet transferred to each user
mapping(address => uint256) public peridotAccrued;
}
contract PeridottrollerV4Storage is PeridottrollerV3Storage {
// @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market.
address public borrowCapGuardian;
// @notice Borrow caps enforced by borrowAllowed for each pToken address. Defaults to zero which corresponds to unlimited borrowing.
mapping(address => uint256) public borrowCaps;
}
contract PeridottrollerV5Storage is PeridottrollerV4Storage {
/// @notice The portion of PERIDOT that each contributor receives per block
mapping(address => uint256) public peridotContributorSpeeds;
/// @notice Last block at which a contributor's PERIDOT rewards have been allocated
mapping(address => uint256) public lastContributorBlock;
}
contract PeridottrollerV6Storage is PeridottrollerV5Storage {
/// @notice The rate at which peridot is distributed to the corresponding borrow market (per block)
mapping(address => uint256) public peridotBorrowSpeeds;
/// @notice The rate at which peridot is distributed to the corresponding supply market (per block)
mapping(address => uint256) public peridotSupplySpeeds;
}
contract PeridottrollerV7Storage is PeridottrollerV6Storage {
/// @notice Flag indicating whether the function to fix PERIDOT accruals has been executed (RE: proposal 62 bug)
bool public proposal65FixExecuted;
/// @notice Accounting storage mapping account addresses to how much PERIDOT they owe the protocol.
mapping(address => uint256) public peridotReceivable;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
/**
* @title EIP20NonStandardInterface
* @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
* See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
interface EIP20NonStandardInterface {
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance The balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transfer(address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transferFrom(address src, address dst, uint256 amount) external;
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved
* @return success Whether or not the approval succeeded
*/
function approve(address spender, uint256 amount) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
import "./PToken.sol";
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 pToken asset
* @param pToken The pToken to get the underlying price of
* @return The underlying asset price mantissa (scaled by 1e18).
* Zero means the price is unavailable.
*/
function getUnderlyingPrice(
PToken pToken
) external view virtual returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@chainlink/contracts-ccip/contracts/=node_modules/@chainlink/contracts-ccip/contracts/",
"@chainlink/contracts/=node_modules/@chainlink/contracts/",
"solidity-bytes-utils/=lib/solidity-bytes-utils/",
"@arbitrum/=node_modules/@arbitrum/",
"@axelar-network/=node_modules/@axelar-network/",
"@eth-optimism/=node_modules/@eth-optimism/",
"@offchainlabs/=node_modules/@offchainlabs/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@pythnetwork/=node_modules/@pythnetwork/",
"@scroll-tech/=node_modules/@scroll-tech/",
"@storknetwork/=node_modules/@storknetwork/",
"@thirdweb-dev/=node_modules/@thirdweb-dev/",
"@zksync/=node_modules/@zksync/",
"account-abstraction/=lib/account-abstraction/contracts/",
"createx/=lib/pyth-crosschain/lazer/contracts/evm/lib/createx/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"erc721a-upgradeable/=node_modules/erc721a-upgradeable/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"hardhat/=node_modules/hardhat/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/pyth-crosschain/target_chains/ethereum/sdk/stylus/pyth-mock-solidity/lib/openzeppelin-foundry-upgrades/src/",
"pyth-crosschain/=lib/pyth-crosschain/",
"solady/=node_modules/solady/",
"solidity-stringutils/=lib/pyth-crosschain/target_chains/ethereum/sdk/stylus/pyth-mock-solidity/lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
"sstore2/=node_modules/@thirdweb-dev/dynamic-contracts/lib/sstore2/",
"wormhole-ntt/=lib/wormhole-ntt/src/",
"wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": true,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"AcceptAdminPendingAdminCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualAddAmount","type":"uint256"}],"name":"AddReservesFactorFreshCheck","type":"error"},{"inputs":[],"name":"BorrowCashNotAvailable","type":"error"},{"inputs":[],"name":"BorrowFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"BorrowPeridottrollerRejection","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateAccrueBorrowInterestFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateAccrueCollateralInterestFailed","type":"error"},{"inputs":[],"name":"LiquidateCloseAmountIsUintMax","type":"error"},{"inputs":[],"name":"LiquidateCloseAmountIsZero","type":"error"},{"inputs":[],"name":"LiquidateCollateralFreshnessCheck","type":"error"},{"inputs":[],"name":"LiquidateFreshnessCheck","type":"error"},{"inputs":[],"name":"LiquidateLiquidatorIsBorrower","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidatePeridottrollerRejection","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateRepayBorrowFreshFailed","type":"error"},{"inputs":[],"name":"LiquidateSeizeLiquidatorIsBorrower","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateSeizePeridottrollerRejection","type":"error"},{"inputs":[],"name":"MintFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"MintPeridottrollerRejection","type":"error"},{"inputs":[],"name":"RedeemFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"RedeemPeridottrollerRejection","type":"error"},{"inputs":[],"name":"RedeemTransferOutNotPossible","type":"error"},{"inputs":[],"name":"ReduceReservesAdminCheck","type":"error"},{"inputs":[],"name":"ReduceReservesCashNotAvailable","type":"error"},{"inputs":[],"name":"ReduceReservesCashValidation","type":"error"},{"inputs":[],"name":"ReduceReservesFreshCheck","type":"error"},{"inputs":[],"name":"RepayBorrowFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"RepayBorrowPeridottrollerRejection","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SetInterestRateModelFreshCheck","type":"error"},{"inputs":[],"name":"SetInterestRateModelOwnerCheck","type":"error"},{"inputs":[],"name":"SetPendingAdminOwnerCheck","type":"error"},{"inputs":[],"name":"SetPeridottrollerOwnerCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorAdminCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorBoundsCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorFreshCheck","type":"error"},{"inputs":[],"name":"TransferNotAllowed","type":"error"},{"inputs":[],"name":"TransferNotEnough","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"TransferPeridottrollerRejection","type":"error"},{"inputs":[],"name":"TransferTooMuch","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"FlashLoansPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"pTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFeeBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFeeBps","type":"uint256"}],"name":"NewFlashLoanFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMaxRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxRatio","type":"uint256"}],"name":"NewMaxFlashLoanRatio","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract PeridottrollerInterface","name":"oldPeridottroller","type":"address"},{"indexed":false,"internalType":"contract PeridottrollerInterface","name":"newPeridottroller","type":"address"}],"name":"NewPeridottroller","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"RedemptionCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"RedemptionRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newReferralId","type":"uint256"}],"name":"ReferralIdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMantissa","type":"uint256"}],"name":"VaultBufferUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"VaultDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"VaultPausedUpdated","type":"event"},{"inputs":[],"name":"NO_ERROR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"_becomeImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_completeRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peridotLikeDelegatee","type":"address"}],"name":"_delegatePeridotLikeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"_requestRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_resignImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFeeBps","type":"uint256"}],"name":"_setFlashLoanFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state","type":"bool"}],"name":"_setFlashLoansPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault_","type":"address"},{"internalType":"uint256","name":"buffer_","type":"uint256"}],"name":"_setMagmaVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxRatio","type":"uint256"}],"name":"_setMaxFlashLoanRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract PeridottrollerInterface","name":"newPeridottroller","type":"address"}],"name":"_setPeridottroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReferralId","type":"uint256"}],"name":"_setReferralId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMantissa","type":"uint256"}],"name":"_setVaultBuffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"pause","type":"bool"}],"name":"_setVaultPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canCompleteRedemption","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flashLoanFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flashLoansPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract PeridottrollerInterface","name":"peridottroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract PeridottrollerInterface","name":"peridottroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract PeridottrollerInterface","name":"peridottroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"contract IMagma","name":"magmaVault_","type":"address"},{"internalType":"uint256","name":"vaultBufferMantissa_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract PTokenInterface","name":"pTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"magmaVault","outputs":[{"internalType":"contract IMagma","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxFlashLoanRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingRedemptionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"peridottroller","outputs":[{"internalType":"contract PeridottrollerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolSeizeShareMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"referralId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract EIP20NonStandardInterface","name":"token","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultBufferMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60808060405234601557614d11908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806306fdde03146104e457806308cf1d50146104df578063095ea7b3146104da5780630e752702146104d55780630ed14e63146104d0578063153ab505146104cb578063166aa708146104c6578063173b9904146104c157806317bfdfbc146104bc57806318160ddd146104b7578063182df0f5146104b25780631a31d465146104ad5780631be19560146104a857806323b872dd146104a35780632608f8181461049e578063267822471461049957806327d699f914610494578063313ce5671461048f578063358e0b001461048a5780633af9e669146104855780633b1d21a2146104805780633e9410101461047b57806343e9aa641461047657806347bd37181461047157806356e677281461046c5780635a890c0e146104675780635c60da1b146104625780635cffe9de1461045d578063601a0bf114610458578063613255ab146104535780636752e7021461044e57806369ab3250146104495780636c540baf146104445780636f307dc31461043f57806370a082311461043a57806373acee981461043557806373d025d614610430578063852a12e31461042b5780638827dbe4146104265780638f840ddd1461042157806395d89b411461041c57806395dd91931461041757806399d8c1b4146104125780639b503bae1461040d578063a0712d6814610408578063a07eaa0614610403578063a252d8be146103fe578063a456365d146103f9578063a6afed95146103f4578063a9059cbb146103ef578063aa5af0fd146103ea578063ae4d65c7146103e5578063ae9d70b0146103e0578063b2a02ff1146103db578063b71d1a0c146103d6578063bd6d894d146103d1578063be7de3f8146103cc578063c0c7347e146103c7578063c37f68e2146103c2578063c3b68700146103bd578063c423fe29146103b8578063c53d59d9146103b3578063c5ebeaec146103ae578063d5c6d3bd146103a9578063d9d98ce4146103a4578063db006a751461039f578063dcef7fbd1461039a578063dd62ed3e14610395578063e9c714f214610390578063ef94c6451461038b578063f2b3abbd14610386578063f3fdb15a14610381578063f5e3c4621461037c578063f6f1024914610377578063f851a44014610372578063f8f9da281461036d5763fca7820b14610368575f80fd5b612a53565b6129f1565b6129c5565b61299d565b612599565b612571565b612541565b612466565b612345565b6122e6565b6122bf565b612215565b6121bb565b612194565b612018565b611f65565b611f3d565b611e6a565b611e08565b611deb565b611dce565b611db4565b611d0c565b611ccb565b611c26565b611bca565b611bad565b611b56565b611b3c565b611b1f565b611a9a565b611a02565b61185d565b61183b565b6117b9565b611792565b6116ed565b6116d0565b6116ab565b61146a565b611448565b611403565b6113c8565b61139c565b61137f565b611365565b611344565b61131d565b6111f4565b611167565b61113f565b611124565b611067565b61104a565b610f50565b610e8a565b610e70565b610e07565b610d22565b610d02565b610cde565b610cb6565b610c57565b610c15565b610abb565b610a2f565b61097c565b61095f565b610908565b6108eb565b610832565b6107b0565b610793565b610742565b6106b6565b610688565b6105ae565b5f9103126104f357565b5f80fd5b90600182811c92168015610525575b602083101461051157565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610506565b634e487b7160e01b5f52604160045260245ffd5b602081019081106001600160401b0382111761055e57604052565b61052f565b90601f801991011681019081106001600160401b0382111761055e57604052565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b346104f3575f3660031901126104f3576040515f6001546105ce816104f7565b80845290600181169081156106645750600114610606575b610602836105f681850382610563565b60405191829182610584565b0390f35b60015f9081527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6939250905b80821061064a575090915081016020016105f66105e6565b919260018160209254838588010152019101909291610632565b60ff191660208086019190915291151560051b840190910191506105f690506105e6565b346104f3575f3660031901126104f3576020601654604051908152f35b6001600160a01b038116036104f357565b346104f35760403660031901126104f3576004356106d3816106a5565b60243590335f52600f602052816106fd8260405f209060018060a01b03165f5260205260405f2090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a360405160018152602090f35b346104f35760203660031901126104f35761077d6004355f5461076760ff8216612b5e565b60ff19165f5561077561383f565b503333614439565b50600160ff195f5416175f5560206040515f8152f35b346104f3575f3660031901126104f3576020601254604051908152f35b346104f3575f3660031901126104f35760035460081c6001600160a01b031633036107d757005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f72657369676e4960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346104f35760203660031901126104f35760043561085e60018060a01b0360035460081c163314612b25565b670de0b6b3a764000081116108b5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef9060165461089b82601655565b604080519182526020820192909252a16108b3613d26565b005b60405162461bcd60e51b815260206004820152600e60248201526d34b73b30b634b210313ab33332b960911b6044820152606490fd5b346104f3575f3660031901126104f3576020600854604051908152f35b346104f35760203660031901126104f357602061094c60043561092a816106a5565b5f5461093860ff8216612b5e565b60ff19165f5561094661383f565b506140ef565b600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f3576020600d54604051908152f35b346104f3575f3660031901126104f3576020610996613d83565b604051908152f35b604435906109ab826106a5565b565b604051906109ab602083610563565b9291926001600160401b03821161055e57604051916109e5601f8201601f191660200184610563565b8294818452818301116104f3578281602093845f960137010152565b9080601f830112156104f357816020610a1c933591016109bc565b90565b60c4359060ff821682036104f357565b346104f35760e03660031901126104f357600435610a4c816106a5565b60243590610a59826106a5565b60443591610a66836106a5565b6064356084356001600160401b0381116104f357610a88903690600401610a01565b9060a435946001600160401b0386116104f357610aac6108b3963690600401610a01565b93610ab5610a1f565b95612bb1565b346104f35760203660031901126104f357600435610ad8816106a5565b60035460081c6001600160a01b031690610afc6001600160a01b0383163314612c36565b6013546001600160a01b039182169291610b3091610b289160089190911c165b6001600160a01b031690565b831415612c9a565b6040516370a0823160e01b8152306004820152602081602481865afa908115610bb5575f91610bba575b50823b156104f35760405163a9059cbb60e01b81526001600160a01b0390921660048301526024820152905f908290818381604481015b03925af18015610bb557610ba157005b80610baf5f6108b393610563565b806104e9565b612ba6565b610bdc915060203d602011610be2575b610bd48183610563565b810190612b97565b5f610b5a565b503d610bca565b60609060031901126104f357600435610c01816106a5565b90602435610c0e816106a5565b9060443590565b346104f3576020610c43610c2836610be9565b915f54610c3760ff8216612b5e565b60ff19165f5533613ddf565b15600160ff195f5416175f55604051908152f35b346104f35760403660031901126104f357610c9e600435610c77816106a5565b602435905f54610c8960ff8216612b5e565b60ff19165f55610c9761383f565b5033614439565b505f805460ff19166001178155604051908152602090f35b346104f3575f3660031901126104f3576004546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f3576020610cf8612d01565b6040519015158152f35b346104f3575f3660031901126104f357602060ff60035416604051908152f35b346104f35760403660031901126104f357600435610d3f816106a5565b60243590610d5b60018060a01b0360035460081c163314612b25565b6001600160a01b03168015610dd5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef91610db0610dd09260018060a01b03166001600160601b0360a01b6015541617601555565b610db981601655565b604051918291829190602060408401935f81520152565b0390a1005b60405162461bcd60e51b815260206004820152600a6024820152691e995c9bc81d985d5b1d60b21b6044820152606490fd5b346104f35760203660031901126104f3576020670de0b6b3a7640000610e66600435610e32816106a5565b610e3a6139b5565b9060405191610e4883610543565b82526001600160a01b03165f908152600e85526040902054906141fa565b5104604051908152f35b346104f3575f3660031901126104f3576020610996613fbc565b346104f35760203660031901126104f3576004355f54610eac60ff8216612b5e565b60ff19165f55610eba61383f565b506009544303610f3d57610ece90336149da565b600c54818101809111610f3857600c8190556040805133815260208101939093528201527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59080606081015b0390a1610f2d600160ff195f5416175f55565b6040515f8152602090f35b61304e565b6338acf79960e01b5f525f60045260245ffd5b346104f35760203660031901126104f357600435600354610f8790610f809060081c6001600160a01b0316610b1c565b3314612b25565b610f9360185415612d12565b601554610fa8906001600160a01b0316610b1c565b604051633ea0e43760e11b815260048101839052306024820181905260448201529190602090839060649082905f905af18015610bb5577fd5ba0459265c0cfa94aa9d715cc0b5c1305e25f72d371f73e627359065e83fdc925f9161102b575b5061101281601855565b6040805191825260208201929092529081908101610dd0565b611044915060203d602011610be257610bd48183610563565b5f611008565b346104f3575f3660031901126104f3576020600b54604051908152f35b346104f35760203660031901126104f3576004356001600160401b0381116104f357366023820112156104f3576110a89036906024816004013591016109bc565b6110c060018060a01b0360035460081c163314612b25565b8051806110c957005b81604091810103126104f35760406020820151916110e6836106a5565b01516001600160a01b0390911690816110fb57005b61111f6108b39260018060a01b03166001600160601b0360a01b6015541617601555565b601655565b346104f3575f3660031901126104f357602060405160018152f35b346104f3575f3660031901126104f3576014546040516001600160a01b039091168152602090f35b346104f35760803660031901126104f357600435611184816106a5565b60243590611191826106a5565b604435606435916001600160401b0383116104f357366023840112156104f3578260040135916001600160401b0383116104f35736602484860101116104f3576106029460246111e2950192612d53565b60405190151581529081906020820190565b346104f35760203660031901126104f3576004355f5461121660ff8216612b5e565b60ff19165f5561122461383f565b5060035461123d9060081c6001600160a01b0316610b1c565b330361130e5760095443036112ff5780611255613fbc565b106112f057600c54908181116112e157611290817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e93613186565b61129981600c55565b6003546112b390839060081c6001600160a01b0316613fd8565b6003546040805160089290921c6001600160a01b031682526020820193909352918201528060608101610f1a565b6378d2980560e11b5f5260045ffd5b633345e99960e01b5f5260045ffd5b630dff50cb60e41b5f5260045ffd5b630f7e5e6d60e41b5f5260045ffd5b346104f35760203660031901126104f357602061099660043561133f816106a5565b6131a0565b346104f3575f3660031901126104f3576020604051666379da05b600008152f35b346104f3575f3660031901126104f35760206040515f8152f35b346104f3575f3660031901126104f3576020600954604051908152f35b346104f3575f3660031901126104f35760135460405160089190911c6001600160a01b03168152602090f35b346104f35760203660031901126104f3576004356113e5816106a5565b60018060a01b03165f52600e602052602060405f2054604051908152f35b346104f3575f3660031901126104f3575f5461142160ff8216612b5e565b60ff19165f5561142f61383f565b506020600b54600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f357602060ff601754166040519015158152f35b346104f35760203660031901126104f3576004355f5461148c60ff8216612b5e565b60ff19165f5561149a61383f565b506114a5600161482d565b6114c16114b0613d83565b6114b86109ad565b90815282614beb565b6005549091906114d9906001600160a01b0316610b1c565b6020604051809263eabe7d9160e01b8252815f816114fc89333060048501614896565b03925af1908115610bb5575f91611682575b5080611670575060095443036116615780611527613fbc565b106116525761154061153b83600d54613186565b600d55565b335f908152600e6020526040902061155b9083905b54613186565b335f908152600e60205260409020556115748133613fd8565b604051828152309033905f516020614cbc5f395f51905f5290602090a360408051338152602081018390529081018390527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a92990606090a16005546115e0906001600160a01b0316610b1c565b91823b156104f3576040516351dff98960e01b815230600482015233602482015260448101929092526064820152905f908290608490829084905af18015610bb55761163e575b5f805460ff19166001178155604051908152602090f35b80610baf5f61164c93610563565b5f611627565b6391240a1b60e01b5f5260045ffd5b6397b5cfcd60e01b5f5260045ffd5b635bd5f7ab60e11b5f5260045260245ffd5b61169b915060203d602011610be257610bd48183610563565b5f61150e565b801515036104f357565b346104f35760203660031901126104f3576108b36004356116cb816116a1565b6131e1565b346104f3575f3660031901126104f3576020600c54604051908152f35b346104f3575f3660031901126104f3576040515f60025461170d816104f7565b8084529060018116908115610664575060011461173457610602836105f681850382610563565b60025f9081527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace939250905b808210611778575090915081016020016105f66105e6565b919260018160209254838588010152019101909291611760565b346104f35760203660031901126104f35760206109966004356117b4816106a5565b6140ef565b346104f35760c03660031901126104f3576004356117d6816106a5565b6024356117e2816106a5565b6044356064356001600160401b0381116104f357611804903690600401610a01565b6084356001600160401b0381116104f357611823903690600401610a01565b9160a4359360ff851685036104f3576108b3956135b4565b346104f3575f3660031901126104f357602060ff601354166040519015158152f35b346104f35760203660031901126104f3576004355f5461187f60ff8216612b5e565b60ff19165f5561188d61383f565b506005546118a3906001600160a01b0316610b1c565b60206040518092634ef4c3e160e01b8252815f816118c688333060048501614896565b03925af1908115610bb5575f916119d6575b50806119c4575060095443036119b5577f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f611930611929611917613d83565b936119206109ad565b948552336149da565b9283614beb565b809261194161153b83600d54613193565b335f908152600e6020526040902061195c9083905b54613193565b335f908152600e602052604090205560408051338152602081019290925281019190915280606081010390a1604051908152339030905f516020614cbc5f395f51905f5290602090a3610f2d600160ff195f5416175f55565b6338d8859760e01b5f5260045ffd5b6303171a8760e61b5f5260045260245ffd5b6119ef915060203d602011610be257610bd48183610563565b5f6118d8565b60e435906109ab826106a5565b346104f3576101203660031901126104f357600435611a20816106a5565b60243590611a2d826106a5565b611a3561099e565b91606435926084356001600160401b0381116104f357611a59903690600401610a01565b9360a435946001600160401b0386116104f357611a7d6108b3963690600401610a01565b91611a86610a1f565b93611a8f6119f5565b9561010435976136ec565b346104f35760203660031901126104f357600435611ab7816106a5565b600354611ad19060081c6001600160a01b0316331461378e565b601354611aeb90610b1c9060081c6001600160a01b031681565b803b156104f3576040516317066a5760e21b81526001600160a01b0383166004820152905f90829081838160248101610b91565b346104f3575f3660031901126104f3576020601954604051908152f35b346104f3575f3660031901126104f357602061099661383f565b346104f35760403660031901126104f357611b95600435611b76816106a5565b602435905f54611b8860ff8216612b5e565b60ff19165f553380613ddf565b5f805460ff1916600117905560405190158152602090f35b346104f3575f3660031901126104f3576020600a54604051908152f35b346104f35760203660031901126104f3577f66f1d115ba8b08c31c22d86374bc36cea7c2771ef78949fa59328010115b4b9c6020600435611c1960018060a01b0360035460081c163314612b25565b80601955604051908152a1005b346104f3575f3660031901126104f3576006546001600160a01b03166020611c4c613fbc565b600b54600c54600854604051635c0b440b60e11b815260048101949094526024840192909252604483015260648201529182908180608481015b03915afa8015610bb557610602915f91611cac575b506040519081529081906020820190565b611cc5915060203d602011610be257610bd48183610563565b5f611c9b565b346104f357611cf7611cdc36610be9565b915f54611ceb60ff8216612b5e565b60ff19165f5533614225565b600160ff195f5416175f5560206040515f8152f35b346104f35760203660031901126104f357600435611d29816106a5565b60035460081c6001600160a01b03163303611da557600480546001600160a01b039283166001600160a01b0319821681179092556040805193909116835260208301919091527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99190819081015b0390a16040515f8152602090f35b635cb56c2b60e01b5f5260045ffd5b346104f3575f3660031901126104f35760206109966139b5565b346104f3575f3660031901126104f3576020601854604051908152f35b346104f3575f3660031901126104f3576020601154604051908152f35b346104f35760203660031901126104f3576080600435611e27816106a5565b6001600160a01b0381165f908152600e602052604090205490611e49906140ef565b611e51613d83565b90604051925f8452602084015260408301526060820152f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611ee85780611ec36127107f84e3f33c20d693ca1e98ee0ab87bf817997a96baa0819ed94c5b96f4a6663f139311156139e8565b601254611ecf82601255565b6040805191825260208201929092529081908101611d97565b60405162461bcd60e51b815260206004820152602760248201527f4f6e6c792061646d696e2063616e20736574206d617820666c617368206c6f616044820152666e20726174696f60c81b6064820152608490fd5b346104f3575f3660031901126104f3576005546040516001600160a01b039091168152602090f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611fc95780611fbd60647f2cd87d6d243502fbf87bd7dd851659e05ac6a614994d7f04592e4f173f939783931115613a34565b601154611ecf82601155565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c792061646d696e2063616e2073657420666c617368206c6f616e2066656044820152606560f81b6064820152608490fd5b346104f35760203660031901126104f3576004355f5461203a60ff8216612b5e565b60ff19165f5561204861383f565b5060055461205e906001600160a01b0316610b1c565b6020604051809263368f515360e21b8252815f8161208188333060048501614896565b03925af1908115610bb5575f91612175575b50806121635750600954430361215457806120ac613fbc565b1061214557806120e57f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80926120e0336140ef565b613193565b90610f1a6120f582600b54613193565b335f908152601060205260409020848155600a54600190910155600b81905561211e8333613fd8565b60408051338152602081019490945283019390935260608201929092529081906080820190565b6348c2588160e01b5f5260045ffd5b630e8d8c6160e21b5f5260045ffd5b635684deff60e11b5f5260045260245ffd5b61218e915060203d602011610be257610bd48183610563565b5f612093565b346104f35760203660031901126104f35760206109966004356121b6816106a5565b613ae1565b346104f35760403660031901126104f3576127106122086004356121de816106a5565b601354602435916121ff9160081c6001600160a01b03908116911614612f56565b60115490613062565b6040519190048152602090f35b346104f35760203660031901126104f3576004355f5461223760ff8216612b5e565b60ff19165f5561224561383f565b50801590811591826122b7575b61225b9061482d565b612263613d83565b9161226c6109ad565b928352156122a6576122828161229192936141fa565b670de0b6b3a764000090510490565b6005546114d9906001600160a01b0316610b1c565b506122b1905f614beb565b5f612291565b506001612252565b346104f35760203660031901126104f3576020610cf86004356122e1816116a1565b613bf8565b346104f35760403660031901126104f357602061233c600435612308816106a5565b60243590612315826106a5565b60018060a01b03165f52600f835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b346104f3575f3660031901126104f3576004546001600160a01b03168033811480159061245e575b61244f576003547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9927ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc916123f6906123d19060081c6001600160a01b0316610b1c565b91600354610100600160a81b03191660089190911b610100600160a81b031617600355565b600480546001600160a01b0319169055600354604080516001600160a01b03938416815260089290921c9092166020820152a1600454604080516001600160a01b03938416815292909116602083015281908101611d97565b631ba24f2960e21b5f5260045ffd5b50331561236d565b346104f3575f3660031901126104f35760035461249290610f809060081c6001600160a01b0316610b1c565b6018546124a0811515613ce2565b6015546124b5906001600160a01b0316610b1c565b604051635d043b2960e11b815260048101839052306024820181905260448201529190602090839060649082905f905af1908115610bb5577fc220b62a9bc2190b38c6c59a97aeca3788b99c20f6ba14f5f02b144a1d79dd43925f92612520575b5061089b5f601855565b61253a91925060203d602011610be257610bd48183610563565b905f612516565b346104f35760203660031901126104f3576020610996600435612563816106a5565b61256b61383f565b5061412f565b346104f3575f3660031901126104f3576006546040516001600160a01b039091168152602090f35b346104f35760603660031901126104f3576004356125b6816106a5565b602435906044356125c6816106a5565b5f546125d460ff8216612b5e565b60ff19165f556125e261383f565b5060405163a6afed9560e01b81526001600160a01b039190911692906020816004815f885af1908115610bb5575f9161297e575b508061296c5750600554612632906001600160a01b0316610b1c565b604051632fe3f38f60e11b81523060048201526001600160a01b038581166024830152336044830152841660648201526084810183905290602090829060a49082905f905af1908115610bb5575f9161294d575b508061293b5750600954430361292c57604051636c540baf60e01b8152602081600481875afa908115610bb5575f9161290d575b5043036128fe576001600160a01b03821633146128ef5780156128e0575f1981146128d1576126ea908233614439565b600554909190612702906001600160a01b0316610b1c565b6040805163c488847b60e01b815294918590818061272588873060048501614896565b03915afa938415610bb5575f905f9561289d575b5061274490156148e3565b6040516370a0823160e01b81526001600160a01b0383166004820152602081602481855afa908115610bb5576127859186915f9161287e575b50101561494b565b3081036127f55783610f1a916127be7f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb5296853330614225565b604080513381526001600160a01b0395861660208201529081019590955290921660608401526080830191909152819060a0820190565b60405163b2a02ff160e01b8152936020858061281684873360048501614896565b03815f865af1918215610bb55761285a610f1a937f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52975f9161285f575b5015614997565b6127be565b612878915060203d602011610be257610bd48183610563565b5f612853565b612897915060203d602011610be257610bd48183610563565b5f61277d565b61274495506128c4915060403d6040116128ca575b6128bc8183610563565b8101906148cd565b94612739565b503d6128b2565b635982c5bb60e11b5f5260045ffd5b63d29da7ef60e01b5f5260045ffd5b631bd1a62160e21b5f5260045ffd5b631046f38d60e31b5f5260045ffd5b612926915060203d602011610be257610bd48183610563565b5f6126ba565b6380965b1b60e01b5f5260045ffd5b632d33e4f360e21b5f5260045260245ffd5b612966915060203d602011610be257610bd48183610563565b5f612686565b633eea49b760e11b5f5260045260245ffd5b612997915060203d602011610be257610bd48183610563565b5f612616565b346104f3575f3660031901126104f3576015546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f35760035460405160089190911c6001600160a01b03168152602090f35b346104f3575f3660031901126104f357600654611c86906020906001600160a01b0316612a1c613fbc565b600b54600c546040516315f2405360e01b815260048101939093526024830191909152604482015292839190829081906064820190565b346104f35760203660031901126104f3576004355f54612a7560ff8216612b5e565b60ff19165f55612a8361383f565b5060035460081c6001600160a01b03163303612b16576009544303612b0757670de0b6b3a76400008111612af8577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600854612adf82600855565b6040805191825260208201929092529081908101610f1a565b63717220f360e11b5f5260045ffd5b637dfca6b760e11b5f5260045ffd5b631205b57b60e11b5f5260045ffd5b15612b2c57565b60405162461bcd60e51b815260206004820152600a60248201526937b7363c9030b236b4b760b11b6044820152606490fd5b15612b6557565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b908160209103126104f3575190565b6040513d5f823e3d90fd5b9294612be994602096612bc794600499946135b4565b601354610100600160a81b03191660089190911b610100600160a81b03161790565b60138190556040516318160ddd60e01b8152928391829060081c6001600160a01b03165afa8015610bb557612c1b5750565b612c339060203d602011610be257610bd48183610563565b50565b15612c3d57565b60405162461bcd60e51b815260206004820152602f60248201527f5045726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b15612ca157565b60405162461bcd60e51b815260206004820152603260248201527f5045726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b60185415612d0e57600190565b5f90565b15612d1957565b60405162461bcd60e51b8152602060048201526012602482015271726564656d7074696f6e2070656e64696e6760701b6044820152606490fd5b915f54612d6260ff8216612b5e565b60ff19165f55601354612d809060081c6001600160a01b0316610b1c565b6001600160a01b0383169590612d97908714612f56565b612db2612dad612da960135460ff1690565b1590565b612fa2565b60405163613255ab60e01b81526001600160a01b0384166004820152602081602481305afa8015610bb557612e4d94612df985926020945f91612f39575b50831115612ff8565b612e10612e0860115484613062565b612710900490565b958694612e1b613fbc565b6001600160a01b0390991699612e31858c613fd8565b6040516323e30c8b60e01b815296879586953360048801613093565b03815f895af1908115610bb5577f0d7d75e01ab95780d3cd1c8ec0dd6c2ce19e3a20427eec8bf53283b6fb8e95f094612eb67f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9612ed894612ecf945f91612f1a575b50146130e3565b612ebe613fbc565b612eca8282101561313a565b613186565b9283101561313a565b81612f01575b604080519182526020820192909252a36001906109ab600160ff195f5416175f55565b612f15612f1083600c54613193565b600c55565b612ede565b612f33915060203d602011610be257610bd48183610563565b5f612eaf565b612f509150853d8711610be257610bd48183610563565b5f612df0565b15612f5d57565b60405162461bcd60e51b815260206004820152601e60248201527f466c6173684c6f616e3a20746f6b656e206e6f7420737570706f7274656400006044820152606490fd5b15612fa957565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20666c617368206c6f616e73206172652070617573656044820152601960fa1b6064820152608490fd5b15612fff57565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20616d6f756e742065786365656473206d6178696d756044820152606d60f81b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b81810292918115918404141715610f3857565b811561307f570490565b634e487b7160e01b5f52601260045260245ffd5b6001600160a01b039182168152911660208201526040810191909152606081019190915260a060808201819052810183905260c09290918190848401375f828201840152601f01601f1916010190565b156130ea57565b60405162461bcd60e51b815260206004820152602260248201527f466c6173684c6f616e3a20696e76616c69642063616c6c6261636b2072657475604482015261393760f11b6064820152608490fd5b1561314157565b60405162461bcd60e51b815260206004820152601a60248201527f466c6173684c6f616e3a206c6f616e206e6f74207265706169640000000000006044820152606490fd5b91908203918211610f3857565b91908201809211610f3857565b601354906001600160a01b03600883901c81169116036131dc5760ff16612d0e576127106131d86131cf613fbc565b60125490613062565b0490565b505f90565b6131f960018060a01b0360035460081c163314612b25565b60175490151560ff8216151581146132475760ff1990911660ff8216176017556040519081527fe99ce3808ef0a5bbecc8824375b67638e945a147d2836eaf2a2c2b8f482bb54e90602090a1565b5050565b1561325257565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b156132aa57565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b1561330f57565b60405162461bcd60e51b815260206004820152601d60248201527f73657474696e672070657269646f7474726f6c6c6572206661696c65640000006044820152606490fd5b1561335b57565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b601f82116133b857505050565b5f5260205f20906020601f840160051c830193106133f0575b601f0160051c01905b8181106133e5575050565b5f81556001016133da565b90915081906133d1565b9081516001600160401b03811161055e576134218161341a6001546104f7565b60016133ab565b602092601f821160011461346157613451929382915f92613456575b50508160011b915f199060031b1c19161790565b600155565b015190505f8061343d565b60015f52601f198216937fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f5b8681106134c557508360019596106134ad575b505050811b01600155565b01515f1960f88460031b161c191690555f80806134a2565b9192602060018192868501518155019401920161348f565b9081516001600160401b03811161055e57613504816134fd6002546104f7565b60026133ab565b602092601f821160011461353857613533929382915f926134565750508160011b915f199060031b1c19161790565b600255565b60025f52601f198216937f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f5b86811061359c5750836001959610613584575b505050811b01600255565b01515f1960f88460031b161c191690555f8080613579565b91926020600181928685015181550194019201613566565b60035490959492939060081c6001600160a01b03166001600160a01b0316330361369b5761364361363d6136489361361e61361861365d9a61360b61364d9a6009541580613691575b6136069061324b565b600755565b6121b660075415156132a3565b15613308565b61362743600955565b613638670de0b6b3a7640000600a55565b61412f565b15613354565b6133fa565b6134dd565b60ff1660ff196003541617600355565b6136676005601155565b613672610bb8601255565b61368160ff1960135416601355565b6109ab600160ff195f5416175f55565b50600a54156135fd565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b90919293949596976009541580613784575b15613750576003546109ab9961374891610100600160a81b0319163360081b610100600160a81b03161760035560018060a01b03166001600160601b0360a01b6015541617601555565b601655612bb1565b60405162461bcd60e51b815260206004820152600c60248201526b185b1c9958591e481a5b9a5d60a21b6044820152606490fd5b50600a54156136fe565b1561379557565b60405162461bcd60e51b815260206004820152603060248201527f6f6e6c79207468652061646d696e206d6179207365742074686520706572696460448201526f6f742d6c696b652064656c656761746560801b6064820152608490fd5b156137fa57565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b6009544381146131dc57613851613fbc565b600b5490600c5492600a5492613871610b1c60065460018060a01b031690565b6040516315f2405360e01b81526004810185905260248101839052604481018790529590602090879060649082905afa958615610bb55761394a866120e06122826139389961394461391b7f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049d6139899b613966995f92613990575b5061390a9061390465048c273950008411156137f3565b43613186565b906139136109ad565b9081526141fa565b976120e061228261392f6122828d8d6141fa565b9e8f9c8d613193565b9b6008546139136109ad565b966141fa565b9161395443600955565b61395d83600a55565b612f1084600b55565b604051948594859094939260609260808301968352602083015260408201520152565b0390a15f90565b61390a9192506139ae9060203d602011610be257610bd48183610563565b91906138ed565b5f546139c360ff8216612b5e565b60ff19165f556139d161383f565b506139da613d83565b90600160ff195f5416175f55565b156139ef57565b60405162461bcd60e51b815260206004820152601960248201527f466c617368206c6f616e20726174696f20746f6f2068696768000000000000006044820152606490fd5b15613a3b57565b60405162461bcd60e51b815260206004820152601760248201527f466c617368206c6f616e2066656520746f6f20686967680000000000000000006044820152606490fd5b908160209103126104f35751610a1c816116a1565b15613a9c57565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b600354613af99060081c6001600160a01b0316610b1c565b3303613bd4576005546001600160a01b031660405163fa707af960e01b815291906020836004816001600160a01b0386165afa928315610bb5577ff911792cf103798503b6dab0992e023d45661865683c550a919b8c9ad07dbf7f93613b66915f91613ba5575b50613a95565b600580546001600160a01b0319166001600160a01b0384161790555b604080516001600160a01b03928316815292909116602083015281908101613989565b613bc7915060203d602011613bcd575b613bbf8183610563565b810190613a80565b5f613b60565b503d613bb5565b634c2fa00360e01b5f5260045ffd5b908160209103126104f35751610a1c816106a5565b600354613c109060081c6001600160a01b0316610b1c565b33148015613c61575b156131dc57613c59816111e27f7958d1d41a26870ec9486cb4dc33830b446f038d00661e064b50719bdde7f42c9360ff8019601354169115151617601355565b0390a1600190565b5060046020613c80610b1c610b1c610b1c60055460018060a01b031690565b604051631251eb1160e11b815292839182905afa908115610bb5575f91613cb3575b506001600160a01b03163314613c19565b613cd5915060203d602011613cdb575b613ccd8183610563565b810190613be3565b5f613ca2565b503d613cc3565b15613ce957565b60405162461bcd60e51b81526020600482015260156024820152743737903832b73234b733903932b232b6b83a34b7b760591b6044820152606490fd5b60ff601754166109ab57613d386145da565b613d40614633565b8101808211610f3857801561324757613d65670de0b6b3a76400009160165490613062565b04808211613d71575050565b8103908111610f38576109ab906146cd565b600d5480613d92575060075490565b613d9a613fbc565b90600b548201809211610f3857600c549182810392818411610f3857670de0b6b3a76400008402938404670de0b6b3a76400001491141715610f3857610a1c91613075565b600554613df4906001600160a01b0316610b1c565b6040516317b9b84b60e31b81523060048201526001600160a01b038481166024830152851660448201526064810186905290602090829060849082905f905af1908115610bb5575f91613f9d575b5080613f8b57506001600160a01b038381169490831693848614613f7c575f516020614cbc5f395f51905f5293613f12936001600160a01b0381168703613f54575f195b613e908582613186565b93613ef8613eb2876115558760018060a01b03165f52600e60205260405f2090565b91613ed1886119568360018060a01b03165f52600e60205260405f2090565b6001600160a01b038781165f908152600e6020526040808220969096559216825292902090565b5519613f19575b5050604051918252509081906020820190565b0390a35f90565b613f36613f4b9260018060a01b03165f52600f60205260405f2090565b9060018060a01b03165f5260205260405f2090565b555f8080613eff565b6001600160a01b0382165f908152600f60205260409020613f76908290613f36565b54613e86565b638cd22d1960e01b5f5260045ffd5b630c93cb5b60e01b5f5260045260245ffd5b613fb6915060203d602011610be257610bd48183610563565b5f613e42565b613fc46145da565b613fcc614633565b8101809111610f385790565b81613fe16145da565b1061408d5760135461400090610b1c9060081c6001600160a01b031681565b91823b156104f35760405163a9059cbb60e01b81526001600160a01b039290921660048301526024820152905f908290604490829084905af18015610bb557614079575b503d801561406e57602014614057575f80fd5b60205f803e6140665f516147e1565b6109ab613d26565b506140665f196147e1565b80610baf5f61408793610563565b5f614044565b60405162461bcd60e51b815260206004820152603460248201527f696e73756666696369656e7420627566666572202d2061646d696e206d7573746044820152731031b7b6b83632ba32903932b232b6b83a34b7b760611b6064820152608490fd5b6001600160a01b03165f9081526010602052604090208054908115614129576001614120610a1c93600a5490613062565b91015490613075565b50505f90565b6003546141479060081c6001600160a01b0316610b1c565b33036141eb5760095443036141dc576006546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa928315610bb5577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936141bc915f91613ba55750613a95565b600680546001600160a01b0319166001600160a01b038416179055613b82565b630be2a5cb60e11b5f5260045ffd5b63407fded560e01b5f5260045ffd5b90614213915f60405161420c81610543565b5251613062565b6040519061422082610543565b815290565b92602083829395865f614242610b1c60055460018060a01b031690565b60405163d02f735160e01b81523060048201526001600160a01b03948516602482015292841660448401529390921660648201526084810194909452839160a49183915af1908115610bb5575f9161441a575b508061440857506001600160a01b03838116939083168085146143f9577fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc594815f516020614cbc5f395f51905f526143bb614342976143056142f56109ad565b666379da05b600008152896148b8565b96614310888a613186565b9161438861436d61432e6122828c614326613d83565b6139136109ad565b9b61433b8d600c54613193565b9d8e600c55565b61435161153b8d600d54613186565b6001600160a01b0384165f908152600e60205260409020611555565b6001600160a01b039092165f908152600e6020526040902090565b556143aa61436d836119568460018060a01b03165f52600e60205260405f2090565b556040519081529081906020820190565b0390a360405191825230915f516020614cbc5f395f51905f5290602090a360408051308152602081019290925281019190915280606081015b0390a1565b633a94626760e11b5f5260045ffd5b6305a647b960e31b5f5260045260245ffd5b614433915060203d602011610be257610bd48183610563565b5f614295565b600554909190614451906001600160a01b0316610b1c565b604051631200453160e11b81523060048201526001600160a01b038481166024830152831660448201526064810185905290602090829060849082905f905af1908115610bb5575f916145bb575b50806145a95750600954430361459a577f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1916144da826140ef565b935f19810361458b57506145856144fc6144f5865b846149da565b8096613186565b61450886600b54613186565b90806145248660018060a01b03165f52601060205260405f2090565b55600a546001600160a01b0386165f9081526010602052604090206001015561454c82600b55565b604080516001600160a01b03958616815295909416602086015292840186905260608401929092526080830191909152819060a0820190565b0390a190565b6144fc6144f5614585926144ef565b63c9021e2f60e01b5f5260045ffd5b6335a6a70b60e21b5f5260045260245ffd5b6145d4915060203d602011610be257610bd48183610563565b5f61449f565b6013546040516370a0823160e01b8152306004820152906020908290602490829060081c6001600160a01b03165afa908115610bb5575f9161461a575090565b610a1c915060203d602011610be257610bd48183610563565b601554614648906001600160a01b0316610b1c565b6040516370a0823160e01b815230600482015290602082602481845afa918215610bb5575f926146ac575b508115614129576040516303d1689d60e11b81526004810192909252602090829060249082905afa908115610bb5575f9161461a575090565b6146c691925060203d602011610be257610bd48183610563565b905f614673565b801580156147d5575b612c33576013545f91906147169082906146fb9060081c6001600160a01b0316610b1c565b601554614710906001600160a01b0316610b1c565b90614b16565b60155460209061476690614732906001600160a01b0316610b1c565b6019546040516364ee50b160e01b815260048101869052306024820152604481019190915294859283919082906064820190565b03925af1908115610bb5577f6c89b3dfee140ff66a50284d3adbd08d9d66970bf92bfbbb60e8ed1dda9dec04925f926147b4575b5060408051918252602082019290925290819081016143f4565b6147ce91925060203d602011610be257610bd48183610563565b905f61479a565b5060175460ff166146d6565b156147e857565b60405162461bcd60e51b815260206004820152601960248201527f544f4b454e5f5452414e534645525f4f55545f4641494c4544000000000000006044820152606490fd5b1561483457565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6001600160a01b03918216815291166020820152604081019190915260600190565b6131d890670de0b6b3a7640000925190613062565b91908260409103126104f3576020825192015190565b156148ea57565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b1561495257565b60405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606490fd5b1561499e57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b6013546040516370a0823160e01b815230600482015260089190911c6001600160a01b0316929091602083602481875afa928315610bb5575f93614af5575b50833b156104f3576040516323b872dd60e01b8152915f9183918291614a459190309060048501614896565b038183875af18015610bb557614ae1575b503d8015614ad657602014614a69575f80fd5b60205f803e614a785f51614c17565b6040516370a0823160e01b815230600482015291602090839060249082905afa8015610bb557614aaf925f91614ab7575b50613186565b610a1c613d26565b614ad0915060203d602011610be257610bd48183610563565b5f614aa9565b50614a785f19614c17565b80610baf5f614aef93610563565b5f614a56565b614b0f91935060203d602011610be257610bd48183610563565b915f614a19565b60405163095ea7b360e01b60208281019182526001600160a01b0385166024840152604480840196909652948252929390925f90614b55606486610563565b84519082855af15f51903d81614bbf575b501590505b614b7457505050565b60405163095ea7b360e01b60208201526001600160a01b039390931660248401525f60448085019190915283526109ab92614bba90614bb4606482610563565b82614c63565b614c63565b15159050614bdf5750614b6b6001600160a01b0382163b15155b5f614b66565b6001614b6b9114614bd9565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610f3857610a1c915190613075565b15614c1e57565b60405162461bcd60e51b815260206004820152601860248201527f544f4b454e5f5452414e534645525f494e5f4641494c454400000000000000006044820152606490fd5b905f602091828151910182855af115612ba6575f513d614cb257506001600160a01b0381163b155b614c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614c8b56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122041851f2064eb0319c3b4be00ac29637f6834fd1a22c297eeb8f839c535abd4db64736f6c634300081e0033
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806306fdde03146104e457806308cf1d50146104df578063095ea7b3146104da5780630e752702146104d55780630ed14e63146104d0578063153ab505146104cb578063166aa708146104c6578063173b9904146104c157806317bfdfbc146104bc57806318160ddd146104b7578063182df0f5146104b25780631a31d465146104ad5780631be19560146104a857806323b872dd146104a35780632608f8181461049e578063267822471461049957806327d699f914610494578063313ce5671461048f578063358e0b001461048a5780633af9e669146104855780633b1d21a2146104805780633e9410101461047b57806343e9aa641461047657806347bd37181461047157806356e677281461046c5780635a890c0e146104675780635c60da1b146104625780635cffe9de1461045d578063601a0bf114610458578063613255ab146104535780636752e7021461044e57806369ab3250146104495780636c540baf146104445780636f307dc31461043f57806370a082311461043a57806373acee981461043557806373d025d614610430578063852a12e31461042b5780638827dbe4146104265780638f840ddd1461042157806395d89b411461041c57806395dd91931461041757806399d8c1b4146104125780639b503bae1461040d578063a0712d6814610408578063a07eaa0614610403578063a252d8be146103fe578063a456365d146103f9578063a6afed95146103f4578063a9059cbb146103ef578063aa5af0fd146103ea578063ae4d65c7146103e5578063ae9d70b0146103e0578063b2a02ff1146103db578063b71d1a0c146103d6578063bd6d894d146103d1578063be7de3f8146103cc578063c0c7347e146103c7578063c37f68e2146103c2578063c3b68700146103bd578063c423fe29146103b8578063c53d59d9146103b3578063c5ebeaec146103ae578063d5c6d3bd146103a9578063d9d98ce4146103a4578063db006a751461039f578063dcef7fbd1461039a578063dd62ed3e14610395578063e9c714f214610390578063ef94c6451461038b578063f2b3abbd14610386578063f3fdb15a14610381578063f5e3c4621461037c578063f6f1024914610377578063f851a44014610372578063f8f9da281461036d5763fca7820b14610368575f80fd5b612a53565b6129f1565b6129c5565b61299d565b612599565b612571565b612541565b612466565b612345565b6122e6565b6122bf565b612215565b6121bb565b612194565b612018565b611f65565b611f3d565b611e6a565b611e08565b611deb565b611dce565b611db4565b611d0c565b611ccb565b611c26565b611bca565b611bad565b611b56565b611b3c565b611b1f565b611a9a565b611a02565b61185d565b61183b565b6117b9565b611792565b6116ed565b6116d0565b6116ab565b61146a565b611448565b611403565b6113c8565b61139c565b61137f565b611365565b611344565b61131d565b6111f4565b611167565b61113f565b611124565b611067565b61104a565b610f50565b610e8a565b610e70565b610e07565b610d22565b610d02565b610cde565b610cb6565b610c57565b610c15565b610abb565b610a2f565b61097c565b61095f565b610908565b6108eb565b610832565b6107b0565b610793565b610742565b6106b6565b610688565b6105ae565b5f9103126104f357565b5f80fd5b90600182811c92168015610525575b602083101461051157565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610506565b634e487b7160e01b5f52604160045260245ffd5b602081019081106001600160401b0382111761055e57604052565b61052f565b90601f801991011681019081106001600160401b0382111761055e57604052565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b346104f3575f3660031901126104f3576040515f6001546105ce816104f7565b80845290600181169081156106645750600114610606575b610602836105f681850382610563565b60405191829182610584565b0390f35b60015f9081527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6939250905b80821061064a575090915081016020016105f66105e6565b919260018160209254838588010152019101909291610632565b60ff191660208086019190915291151560051b840190910191506105f690506105e6565b346104f3575f3660031901126104f3576020601654604051908152f35b6001600160a01b038116036104f357565b346104f35760403660031901126104f3576004356106d3816106a5565b60243590335f52600f602052816106fd8260405f209060018060a01b03165f5260205260405f2090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a360405160018152602090f35b346104f35760203660031901126104f35761077d6004355f5461076760ff8216612b5e565b60ff19165f5561077561383f565b503333614439565b50600160ff195f5416175f5560206040515f8152f35b346104f3575f3660031901126104f3576020601254604051908152f35b346104f3575f3660031901126104f35760035460081c6001600160a01b031633036107d757005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f72657369676e4960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346104f35760203660031901126104f35760043561085e60018060a01b0360035460081c163314612b25565b670de0b6b3a764000081116108b5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef9060165461089b82601655565b604080519182526020820192909252a16108b3613d26565b005b60405162461bcd60e51b815260206004820152600e60248201526d34b73b30b634b210313ab33332b960911b6044820152606490fd5b346104f3575f3660031901126104f3576020600854604051908152f35b346104f35760203660031901126104f357602061094c60043561092a816106a5565b5f5461093860ff8216612b5e565b60ff19165f5561094661383f565b506140ef565b600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f3576020600d54604051908152f35b346104f3575f3660031901126104f3576020610996613d83565b604051908152f35b604435906109ab826106a5565b565b604051906109ab602083610563565b9291926001600160401b03821161055e57604051916109e5601f8201601f191660200184610563565b8294818452818301116104f3578281602093845f960137010152565b9080601f830112156104f357816020610a1c933591016109bc565b90565b60c4359060ff821682036104f357565b346104f35760e03660031901126104f357600435610a4c816106a5565b60243590610a59826106a5565b60443591610a66836106a5565b6064356084356001600160401b0381116104f357610a88903690600401610a01565b9060a435946001600160401b0386116104f357610aac6108b3963690600401610a01565b93610ab5610a1f565b95612bb1565b346104f35760203660031901126104f357600435610ad8816106a5565b60035460081c6001600160a01b031690610afc6001600160a01b0383163314612c36565b6013546001600160a01b039182169291610b3091610b289160089190911c165b6001600160a01b031690565b831415612c9a565b6040516370a0823160e01b8152306004820152602081602481865afa908115610bb5575f91610bba575b50823b156104f35760405163a9059cbb60e01b81526001600160a01b0390921660048301526024820152905f908290818381604481015b03925af18015610bb557610ba157005b80610baf5f6108b393610563565b806104e9565b612ba6565b610bdc915060203d602011610be2575b610bd48183610563565b810190612b97565b5f610b5a565b503d610bca565b60609060031901126104f357600435610c01816106a5565b90602435610c0e816106a5565b9060443590565b346104f3576020610c43610c2836610be9565b915f54610c3760ff8216612b5e565b60ff19165f5533613ddf565b15600160ff195f5416175f55604051908152f35b346104f35760403660031901126104f357610c9e600435610c77816106a5565b602435905f54610c8960ff8216612b5e565b60ff19165f55610c9761383f565b5033614439565b505f805460ff19166001178155604051908152602090f35b346104f3575f3660031901126104f3576004546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f3576020610cf8612d01565b6040519015158152f35b346104f3575f3660031901126104f357602060ff60035416604051908152f35b346104f35760403660031901126104f357600435610d3f816106a5565b60243590610d5b60018060a01b0360035460081c163314612b25565b6001600160a01b03168015610dd5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef91610db0610dd09260018060a01b03166001600160601b0360a01b6015541617601555565b610db981601655565b604051918291829190602060408401935f81520152565b0390a1005b60405162461bcd60e51b815260206004820152600a6024820152691e995c9bc81d985d5b1d60b21b6044820152606490fd5b346104f35760203660031901126104f3576020670de0b6b3a7640000610e66600435610e32816106a5565b610e3a6139b5565b9060405191610e4883610543565b82526001600160a01b03165f908152600e85526040902054906141fa565b5104604051908152f35b346104f3575f3660031901126104f3576020610996613fbc565b346104f35760203660031901126104f3576004355f54610eac60ff8216612b5e565b60ff19165f55610eba61383f565b506009544303610f3d57610ece90336149da565b600c54818101809111610f3857600c8190556040805133815260208101939093528201527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59080606081015b0390a1610f2d600160ff195f5416175f55565b6040515f8152602090f35b61304e565b6338acf79960e01b5f525f60045260245ffd5b346104f35760203660031901126104f357600435600354610f8790610f809060081c6001600160a01b0316610b1c565b3314612b25565b610f9360185415612d12565b601554610fa8906001600160a01b0316610b1c565b604051633ea0e43760e11b815260048101839052306024820181905260448201529190602090839060649082905f905af18015610bb5577fd5ba0459265c0cfa94aa9d715cc0b5c1305e25f72d371f73e627359065e83fdc925f9161102b575b5061101281601855565b6040805191825260208201929092529081908101610dd0565b611044915060203d602011610be257610bd48183610563565b5f611008565b346104f3575f3660031901126104f3576020600b54604051908152f35b346104f35760203660031901126104f3576004356001600160401b0381116104f357366023820112156104f3576110a89036906024816004013591016109bc565b6110c060018060a01b0360035460081c163314612b25565b8051806110c957005b81604091810103126104f35760406020820151916110e6836106a5565b01516001600160a01b0390911690816110fb57005b61111f6108b39260018060a01b03166001600160601b0360a01b6015541617601555565b601655565b346104f3575f3660031901126104f357602060405160018152f35b346104f3575f3660031901126104f3576014546040516001600160a01b039091168152602090f35b346104f35760803660031901126104f357600435611184816106a5565b60243590611191826106a5565b604435606435916001600160401b0383116104f357366023840112156104f3578260040135916001600160401b0383116104f35736602484860101116104f3576106029460246111e2950192612d53565b60405190151581529081906020820190565b346104f35760203660031901126104f3576004355f5461121660ff8216612b5e565b60ff19165f5561122461383f565b5060035461123d9060081c6001600160a01b0316610b1c565b330361130e5760095443036112ff5780611255613fbc565b106112f057600c54908181116112e157611290817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e93613186565b61129981600c55565b6003546112b390839060081c6001600160a01b0316613fd8565b6003546040805160089290921c6001600160a01b031682526020820193909352918201528060608101610f1a565b6378d2980560e11b5f5260045ffd5b633345e99960e01b5f5260045ffd5b630dff50cb60e41b5f5260045ffd5b630f7e5e6d60e41b5f5260045ffd5b346104f35760203660031901126104f357602061099660043561133f816106a5565b6131a0565b346104f3575f3660031901126104f3576020604051666379da05b600008152f35b346104f3575f3660031901126104f35760206040515f8152f35b346104f3575f3660031901126104f3576020600954604051908152f35b346104f3575f3660031901126104f35760135460405160089190911c6001600160a01b03168152602090f35b346104f35760203660031901126104f3576004356113e5816106a5565b60018060a01b03165f52600e602052602060405f2054604051908152f35b346104f3575f3660031901126104f3575f5461142160ff8216612b5e565b60ff19165f5561142f61383f565b506020600b54600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f357602060ff601754166040519015158152f35b346104f35760203660031901126104f3576004355f5461148c60ff8216612b5e565b60ff19165f5561149a61383f565b506114a5600161482d565b6114c16114b0613d83565b6114b86109ad565b90815282614beb565b6005549091906114d9906001600160a01b0316610b1c565b6020604051809263eabe7d9160e01b8252815f816114fc89333060048501614896565b03925af1908115610bb5575f91611682575b5080611670575060095443036116615780611527613fbc565b106116525761154061153b83600d54613186565b600d55565b335f908152600e6020526040902061155b9083905b54613186565b335f908152600e60205260409020556115748133613fd8565b604051828152309033905f516020614cbc5f395f51905f5290602090a360408051338152602081018390529081018390527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a92990606090a16005546115e0906001600160a01b0316610b1c565b91823b156104f3576040516351dff98960e01b815230600482015233602482015260448101929092526064820152905f908290608490829084905af18015610bb55761163e575b5f805460ff19166001178155604051908152602090f35b80610baf5f61164c93610563565b5f611627565b6391240a1b60e01b5f5260045ffd5b6397b5cfcd60e01b5f5260045ffd5b635bd5f7ab60e11b5f5260045260245ffd5b61169b915060203d602011610be257610bd48183610563565b5f61150e565b801515036104f357565b346104f35760203660031901126104f3576108b36004356116cb816116a1565b6131e1565b346104f3575f3660031901126104f3576020600c54604051908152f35b346104f3575f3660031901126104f3576040515f60025461170d816104f7565b8084529060018116908115610664575060011461173457610602836105f681850382610563565b60025f9081527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace939250905b808210611778575090915081016020016105f66105e6565b919260018160209254838588010152019101909291611760565b346104f35760203660031901126104f35760206109966004356117b4816106a5565b6140ef565b346104f35760c03660031901126104f3576004356117d6816106a5565b6024356117e2816106a5565b6044356064356001600160401b0381116104f357611804903690600401610a01565b6084356001600160401b0381116104f357611823903690600401610a01565b9160a4359360ff851685036104f3576108b3956135b4565b346104f3575f3660031901126104f357602060ff601354166040519015158152f35b346104f35760203660031901126104f3576004355f5461187f60ff8216612b5e565b60ff19165f5561188d61383f565b506005546118a3906001600160a01b0316610b1c565b60206040518092634ef4c3e160e01b8252815f816118c688333060048501614896565b03925af1908115610bb5575f916119d6575b50806119c4575060095443036119b5577f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f611930611929611917613d83565b936119206109ad565b948552336149da565b9283614beb565b809261194161153b83600d54613193565b335f908152600e6020526040902061195c9083905b54613193565b335f908152600e602052604090205560408051338152602081019290925281019190915280606081010390a1604051908152339030905f516020614cbc5f395f51905f5290602090a3610f2d600160ff195f5416175f55565b6338d8859760e01b5f5260045ffd5b6303171a8760e61b5f5260045260245ffd5b6119ef915060203d602011610be257610bd48183610563565b5f6118d8565b60e435906109ab826106a5565b346104f3576101203660031901126104f357600435611a20816106a5565b60243590611a2d826106a5565b611a3561099e565b91606435926084356001600160401b0381116104f357611a59903690600401610a01565b9360a435946001600160401b0386116104f357611a7d6108b3963690600401610a01565b91611a86610a1f565b93611a8f6119f5565b9561010435976136ec565b346104f35760203660031901126104f357600435611ab7816106a5565b600354611ad19060081c6001600160a01b0316331461378e565b601354611aeb90610b1c9060081c6001600160a01b031681565b803b156104f3576040516317066a5760e21b81526001600160a01b0383166004820152905f90829081838160248101610b91565b346104f3575f3660031901126104f3576020601954604051908152f35b346104f3575f3660031901126104f357602061099661383f565b346104f35760403660031901126104f357611b95600435611b76816106a5565b602435905f54611b8860ff8216612b5e565b60ff19165f553380613ddf565b5f805460ff1916600117905560405190158152602090f35b346104f3575f3660031901126104f3576020600a54604051908152f35b346104f35760203660031901126104f3577f66f1d115ba8b08c31c22d86374bc36cea7c2771ef78949fa59328010115b4b9c6020600435611c1960018060a01b0360035460081c163314612b25565b80601955604051908152a1005b346104f3575f3660031901126104f3576006546001600160a01b03166020611c4c613fbc565b600b54600c54600854604051635c0b440b60e11b815260048101949094526024840192909252604483015260648201529182908180608481015b03915afa8015610bb557610602915f91611cac575b506040519081529081906020820190565b611cc5915060203d602011610be257610bd48183610563565b5f611c9b565b346104f357611cf7611cdc36610be9565b915f54611ceb60ff8216612b5e565b60ff19165f5533614225565b600160ff195f5416175f5560206040515f8152f35b346104f35760203660031901126104f357600435611d29816106a5565b60035460081c6001600160a01b03163303611da557600480546001600160a01b039283166001600160a01b0319821681179092556040805193909116835260208301919091527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99190819081015b0390a16040515f8152602090f35b635cb56c2b60e01b5f5260045ffd5b346104f3575f3660031901126104f35760206109966139b5565b346104f3575f3660031901126104f3576020601854604051908152f35b346104f3575f3660031901126104f3576020601154604051908152f35b346104f35760203660031901126104f3576080600435611e27816106a5565b6001600160a01b0381165f908152600e602052604090205490611e49906140ef565b611e51613d83565b90604051925f8452602084015260408301526060820152f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611ee85780611ec36127107f84e3f33c20d693ca1e98ee0ab87bf817997a96baa0819ed94c5b96f4a6663f139311156139e8565b601254611ecf82601255565b6040805191825260208201929092529081908101611d97565b60405162461bcd60e51b815260206004820152602760248201527f4f6e6c792061646d696e2063616e20736574206d617820666c617368206c6f616044820152666e20726174696f60c81b6064820152608490fd5b346104f3575f3660031901126104f3576005546040516001600160a01b039091168152602090f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611fc95780611fbd60647f2cd87d6d243502fbf87bd7dd851659e05ac6a614994d7f04592e4f173f939783931115613a34565b601154611ecf82601155565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c792061646d696e2063616e2073657420666c617368206c6f616e2066656044820152606560f81b6064820152608490fd5b346104f35760203660031901126104f3576004355f5461203a60ff8216612b5e565b60ff19165f5561204861383f565b5060055461205e906001600160a01b0316610b1c565b6020604051809263368f515360e21b8252815f8161208188333060048501614896565b03925af1908115610bb5575f91612175575b50806121635750600954430361215457806120ac613fbc565b1061214557806120e57f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80926120e0336140ef565b613193565b90610f1a6120f582600b54613193565b335f908152601060205260409020848155600a54600190910155600b81905561211e8333613fd8565b60408051338152602081019490945283019390935260608201929092529081906080820190565b6348c2588160e01b5f5260045ffd5b630e8d8c6160e21b5f5260045ffd5b635684deff60e11b5f5260045260245ffd5b61218e915060203d602011610be257610bd48183610563565b5f612093565b346104f35760203660031901126104f35760206109966004356121b6816106a5565b613ae1565b346104f35760403660031901126104f3576127106122086004356121de816106a5565b601354602435916121ff9160081c6001600160a01b03908116911614612f56565b60115490613062565b6040519190048152602090f35b346104f35760203660031901126104f3576004355f5461223760ff8216612b5e565b60ff19165f5561224561383f565b50801590811591826122b7575b61225b9061482d565b612263613d83565b9161226c6109ad565b928352156122a6576122828161229192936141fa565b670de0b6b3a764000090510490565b6005546114d9906001600160a01b0316610b1c565b506122b1905f614beb565b5f612291565b506001612252565b346104f35760203660031901126104f3576020610cf86004356122e1816116a1565b613bf8565b346104f35760403660031901126104f357602061233c600435612308816106a5565b60243590612315826106a5565b60018060a01b03165f52600f835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b346104f3575f3660031901126104f3576004546001600160a01b03168033811480159061245e575b61244f576003547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9927ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc916123f6906123d19060081c6001600160a01b0316610b1c565b91600354610100600160a81b03191660089190911b610100600160a81b031617600355565b600480546001600160a01b0319169055600354604080516001600160a01b03938416815260089290921c9092166020820152a1600454604080516001600160a01b03938416815292909116602083015281908101611d97565b631ba24f2960e21b5f5260045ffd5b50331561236d565b346104f3575f3660031901126104f35760035461249290610f809060081c6001600160a01b0316610b1c565b6018546124a0811515613ce2565b6015546124b5906001600160a01b0316610b1c565b604051635d043b2960e11b815260048101839052306024820181905260448201529190602090839060649082905f905af1908115610bb5577fc220b62a9bc2190b38c6c59a97aeca3788b99c20f6ba14f5f02b144a1d79dd43925f92612520575b5061089b5f601855565b61253a91925060203d602011610be257610bd48183610563565b905f612516565b346104f35760203660031901126104f3576020610996600435612563816106a5565b61256b61383f565b5061412f565b346104f3575f3660031901126104f3576006546040516001600160a01b039091168152602090f35b346104f35760603660031901126104f3576004356125b6816106a5565b602435906044356125c6816106a5565b5f546125d460ff8216612b5e565b60ff19165f556125e261383f565b5060405163a6afed9560e01b81526001600160a01b039190911692906020816004815f885af1908115610bb5575f9161297e575b508061296c5750600554612632906001600160a01b0316610b1c565b604051632fe3f38f60e11b81523060048201526001600160a01b038581166024830152336044830152841660648201526084810183905290602090829060a49082905f905af1908115610bb5575f9161294d575b508061293b5750600954430361292c57604051636c540baf60e01b8152602081600481875afa908115610bb5575f9161290d575b5043036128fe576001600160a01b03821633146128ef5780156128e0575f1981146128d1576126ea908233614439565b600554909190612702906001600160a01b0316610b1c565b6040805163c488847b60e01b815294918590818061272588873060048501614896565b03915afa938415610bb5575f905f9561289d575b5061274490156148e3565b6040516370a0823160e01b81526001600160a01b0383166004820152602081602481855afa908115610bb5576127859186915f9161287e575b50101561494b565b3081036127f55783610f1a916127be7f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb5296853330614225565b604080513381526001600160a01b0395861660208201529081019590955290921660608401526080830191909152819060a0820190565b60405163b2a02ff160e01b8152936020858061281684873360048501614896565b03815f865af1918215610bb55761285a610f1a937f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52975f9161285f575b5015614997565b6127be565b612878915060203d602011610be257610bd48183610563565b5f612853565b612897915060203d602011610be257610bd48183610563565b5f61277d565b61274495506128c4915060403d6040116128ca575b6128bc8183610563565b8101906148cd565b94612739565b503d6128b2565b635982c5bb60e11b5f5260045ffd5b63d29da7ef60e01b5f5260045ffd5b631bd1a62160e21b5f5260045ffd5b631046f38d60e31b5f5260045ffd5b612926915060203d602011610be257610bd48183610563565b5f6126ba565b6380965b1b60e01b5f5260045ffd5b632d33e4f360e21b5f5260045260245ffd5b612966915060203d602011610be257610bd48183610563565b5f612686565b633eea49b760e11b5f5260045260245ffd5b612997915060203d602011610be257610bd48183610563565b5f612616565b346104f3575f3660031901126104f3576015546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f35760035460405160089190911c6001600160a01b03168152602090f35b346104f3575f3660031901126104f357600654611c86906020906001600160a01b0316612a1c613fbc565b600b54600c546040516315f2405360e01b815260048101939093526024830191909152604482015292839190829081906064820190565b346104f35760203660031901126104f3576004355f54612a7560ff8216612b5e565b60ff19165f55612a8361383f565b5060035460081c6001600160a01b03163303612b16576009544303612b0757670de0b6b3a76400008111612af8577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600854612adf82600855565b6040805191825260208201929092529081908101610f1a565b63717220f360e11b5f5260045ffd5b637dfca6b760e11b5f5260045ffd5b631205b57b60e11b5f5260045ffd5b15612b2c57565b60405162461bcd60e51b815260206004820152600a60248201526937b7363c9030b236b4b760b11b6044820152606490fd5b15612b6557565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b908160209103126104f3575190565b6040513d5f823e3d90fd5b9294612be994602096612bc794600499946135b4565b601354610100600160a81b03191660089190911b610100600160a81b03161790565b60138190556040516318160ddd60e01b8152928391829060081c6001600160a01b03165afa8015610bb557612c1b5750565b612c339060203d602011610be257610bd48183610563565b50565b15612c3d57565b60405162461bcd60e51b815260206004820152602f60248201527f5045726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b15612ca157565b60405162461bcd60e51b815260206004820152603260248201527f5045726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b60185415612d0e57600190565b5f90565b15612d1957565b60405162461bcd60e51b8152602060048201526012602482015271726564656d7074696f6e2070656e64696e6760701b6044820152606490fd5b915f54612d6260ff8216612b5e565b60ff19165f55601354612d809060081c6001600160a01b0316610b1c565b6001600160a01b0383169590612d97908714612f56565b612db2612dad612da960135460ff1690565b1590565b612fa2565b60405163613255ab60e01b81526001600160a01b0384166004820152602081602481305afa8015610bb557612e4d94612df985926020945f91612f39575b50831115612ff8565b612e10612e0860115484613062565b612710900490565b958694612e1b613fbc565b6001600160a01b0390991699612e31858c613fd8565b6040516323e30c8b60e01b815296879586953360048801613093565b03815f895af1908115610bb5577f0d7d75e01ab95780d3cd1c8ec0dd6c2ce19e3a20427eec8bf53283b6fb8e95f094612eb67f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9612ed894612ecf945f91612f1a575b50146130e3565b612ebe613fbc565b612eca8282101561313a565b613186565b9283101561313a565b81612f01575b604080519182526020820192909252a36001906109ab600160ff195f5416175f55565b612f15612f1083600c54613193565b600c55565b612ede565b612f33915060203d602011610be257610bd48183610563565b5f612eaf565b612f509150853d8711610be257610bd48183610563565b5f612df0565b15612f5d57565b60405162461bcd60e51b815260206004820152601e60248201527f466c6173684c6f616e3a20746f6b656e206e6f7420737570706f7274656400006044820152606490fd5b15612fa957565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20666c617368206c6f616e73206172652070617573656044820152601960fa1b6064820152608490fd5b15612fff57565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20616d6f756e742065786365656473206d6178696d756044820152606d60f81b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b81810292918115918404141715610f3857565b811561307f570490565b634e487b7160e01b5f52601260045260245ffd5b6001600160a01b039182168152911660208201526040810191909152606081019190915260a060808201819052810183905260c09290918190848401375f828201840152601f01601f1916010190565b156130ea57565b60405162461bcd60e51b815260206004820152602260248201527f466c6173684c6f616e3a20696e76616c69642063616c6c6261636b2072657475604482015261393760f11b6064820152608490fd5b1561314157565b60405162461bcd60e51b815260206004820152601a60248201527f466c6173684c6f616e3a206c6f616e206e6f74207265706169640000000000006044820152606490fd5b91908203918211610f3857565b91908201809211610f3857565b601354906001600160a01b03600883901c81169116036131dc5760ff16612d0e576127106131d86131cf613fbc565b60125490613062565b0490565b505f90565b6131f960018060a01b0360035460081c163314612b25565b60175490151560ff8216151581146132475760ff1990911660ff8216176017556040519081527fe99ce3808ef0a5bbecc8824375b67638e945a147d2836eaf2a2c2b8f482bb54e90602090a1565b5050565b1561325257565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b156132aa57565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b1561330f57565b60405162461bcd60e51b815260206004820152601d60248201527f73657474696e672070657269646f7474726f6c6c6572206661696c65640000006044820152606490fd5b1561335b57565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b601f82116133b857505050565b5f5260205f20906020601f840160051c830193106133f0575b601f0160051c01905b8181106133e5575050565b5f81556001016133da565b90915081906133d1565b9081516001600160401b03811161055e576134218161341a6001546104f7565b60016133ab565b602092601f821160011461346157613451929382915f92613456575b50508160011b915f199060031b1c19161790565b600155565b015190505f8061343d565b60015f52601f198216937fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f5b8681106134c557508360019596106134ad575b505050811b01600155565b01515f1960f88460031b161c191690555f80806134a2565b9192602060018192868501518155019401920161348f565b9081516001600160401b03811161055e57613504816134fd6002546104f7565b60026133ab565b602092601f821160011461353857613533929382915f926134565750508160011b915f199060031b1c19161790565b600255565b60025f52601f198216937f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f5b86811061359c5750836001959610613584575b505050811b01600255565b01515f1960f88460031b161c191690555f8080613579565b91926020600181928685015181550194019201613566565b60035490959492939060081c6001600160a01b03166001600160a01b0316330361369b5761364361363d6136489361361e61361861365d9a61360b61364d9a6009541580613691575b6136069061324b565b600755565b6121b660075415156132a3565b15613308565b61362743600955565b613638670de0b6b3a7640000600a55565b61412f565b15613354565b6133fa565b6134dd565b60ff1660ff196003541617600355565b6136676005601155565b613672610bb8601255565b61368160ff1960135416601355565b6109ab600160ff195f5416175f55565b50600a54156135fd565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b90919293949596976009541580613784575b15613750576003546109ab9961374891610100600160a81b0319163360081b610100600160a81b03161760035560018060a01b03166001600160601b0360a01b6015541617601555565b601655612bb1565b60405162461bcd60e51b815260206004820152600c60248201526b185b1c9958591e481a5b9a5d60a21b6044820152606490fd5b50600a54156136fe565b1561379557565b60405162461bcd60e51b815260206004820152603060248201527f6f6e6c79207468652061646d696e206d6179207365742074686520706572696460448201526f6f742d6c696b652064656c656761746560801b6064820152608490fd5b156137fa57565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b6009544381146131dc57613851613fbc565b600b5490600c5492600a5492613871610b1c60065460018060a01b031690565b6040516315f2405360e01b81526004810185905260248101839052604481018790529590602090879060649082905afa958615610bb55761394a866120e06122826139389961394461391b7f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049d6139899b613966995f92613990575b5061390a9061390465048c273950008411156137f3565b43613186565b906139136109ad565b9081526141fa565b976120e061228261392f6122828d8d6141fa565b9e8f9c8d613193565b9b6008546139136109ad565b966141fa565b9161395443600955565b61395d83600a55565b612f1084600b55565b604051948594859094939260609260808301968352602083015260408201520152565b0390a15f90565b61390a9192506139ae9060203d602011610be257610bd48183610563565b91906138ed565b5f546139c360ff8216612b5e565b60ff19165f556139d161383f565b506139da613d83565b90600160ff195f5416175f55565b156139ef57565b60405162461bcd60e51b815260206004820152601960248201527f466c617368206c6f616e20726174696f20746f6f2068696768000000000000006044820152606490fd5b15613a3b57565b60405162461bcd60e51b815260206004820152601760248201527f466c617368206c6f616e2066656520746f6f20686967680000000000000000006044820152606490fd5b908160209103126104f35751610a1c816116a1565b15613a9c57565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b600354613af99060081c6001600160a01b0316610b1c565b3303613bd4576005546001600160a01b031660405163fa707af960e01b815291906020836004816001600160a01b0386165afa928315610bb5577ff911792cf103798503b6dab0992e023d45661865683c550a919b8c9ad07dbf7f93613b66915f91613ba5575b50613a95565b600580546001600160a01b0319166001600160a01b0384161790555b604080516001600160a01b03928316815292909116602083015281908101613989565b613bc7915060203d602011613bcd575b613bbf8183610563565b810190613a80565b5f613b60565b503d613bb5565b634c2fa00360e01b5f5260045ffd5b908160209103126104f35751610a1c816106a5565b600354613c109060081c6001600160a01b0316610b1c565b33148015613c61575b156131dc57613c59816111e27f7958d1d41a26870ec9486cb4dc33830b446f038d00661e064b50719bdde7f42c9360ff8019601354169115151617601355565b0390a1600190565b5060046020613c80610b1c610b1c610b1c60055460018060a01b031690565b604051631251eb1160e11b815292839182905afa908115610bb5575f91613cb3575b506001600160a01b03163314613c19565b613cd5915060203d602011613cdb575b613ccd8183610563565b810190613be3565b5f613ca2565b503d613cc3565b15613ce957565b60405162461bcd60e51b81526020600482015260156024820152743737903832b73234b733903932b232b6b83a34b7b760591b6044820152606490fd5b60ff601754166109ab57613d386145da565b613d40614633565b8101808211610f3857801561324757613d65670de0b6b3a76400009160165490613062565b04808211613d71575050565b8103908111610f38576109ab906146cd565b600d5480613d92575060075490565b613d9a613fbc565b90600b548201809211610f3857600c549182810392818411610f3857670de0b6b3a76400008402938404670de0b6b3a76400001491141715610f3857610a1c91613075565b600554613df4906001600160a01b0316610b1c565b6040516317b9b84b60e31b81523060048201526001600160a01b038481166024830152851660448201526064810186905290602090829060849082905f905af1908115610bb5575f91613f9d575b5080613f8b57506001600160a01b038381169490831693848614613f7c575f516020614cbc5f395f51905f5293613f12936001600160a01b0381168703613f54575f195b613e908582613186565b93613ef8613eb2876115558760018060a01b03165f52600e60205260405f2090565b91613ed1886119568360018060a01b03165f52600e60205260405f2090565b6001600160a01b038781165f908152600e6020526040808220969096559216825292902090565b5519613f19575b5050604051918252509081906020820190565b0390a35f90565b613f36613f4b9260018060a01b03165f52600f60205260405f2090565b9060018060a01b03165f5260205260405f2090565b555f8080613eff565b6001600160a01b0382165f908152600f60205260409020613f76908290613f36565b54613e86565b638cd22d1960e01b5f5260045ffd5b630c93cb5b60e01b5f5260045260245ffd5b613fb6915060203d602011610be257610bd48183610563565b5f613e42565b613fc46145da565b613fcc614633565b8101809111610f385790565b81613fe16145da565b1061408d5760135461400090610b1c9060081c6001600160a01b031681565b91823b156104f35760405163a9059cbb60e01b81526001600160a01b039290921660048301526024820152905f908290604490829084905af18015610bb557614079575b503d801561406e57602014614057575f80fd5b60205f803e6140665f516147e1565b6109ab613d26565b506140665f196147e1565b80610baf5f61408793610563565b5f614044565b60405162461bcd60e51b815260206004820152603460248201527f696e73756666696369656e7420627566666572202d2061646d696e206d7573746044820152731031b7b6b83632ba32903932b232b6b83a34b7b760611b6064820152608490fd5b6001600160a01b03165f9081526010602052604090208054908115614129576001614120610a1c93600a5490613062565b91015490613075565b50505f90565b6003546141479060081c6001600160a01b0316610b1c565b33036141eb5760095443036141dc576006546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa928315610bb5577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936141bc915f91613ba55750613a95565b600680546001600160a01b0319166001600160a01b038416179055613b82565b630be2a5cb60e11b5f5260045ffd5b63407fded560e01b5f5260045ffd5b90614213915f60405161420c81610543565b5251613062565b6040519061422082610543565b815290565b92602083829395865f614242610b1c60055460018060a01b031690565b60405163d02f735160e01b81523060048201526001600160a01b03948516602482015292841660448401529390921660648201526084810194909452839160a49183915af1908115610bb5575f9161441a575b508061440857506001600160a01b03838116939083168085146143f9577fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc594815f516020614cbc5f395f51905f526143bb614342976143056142f56109ad565b666379da05b600008152896148b8565b96614310888a613186565b9161438861436d61432e6122828c614326613d83565b6139136109ad565b9b61433b8d600c54613193565b9d8e600c55565b61435161153b8d600d54613186565b6001600160a01b0384165f908152600e60205260409020611555565b6001600160a01b039092165f908152600e6020526040902090565b556143aa61436d836119568460018060a01b03165f52600e60205260405f2090565b556040519081529081906020820190565b0390a360405191825230915f516020614cbc5f395f51905f5290602090a360408051308152602081019290925281019190915280606081015b0390a1565b633a94626760e11b5f5260045ffd5b6305a647b960e31b5f5260045260245ffd5b614433915060203d602011610be257610bd48183610563565b5f614295565b600554909190614451906001600160a01b0316610b1c565b604051631200453160e11b81523060048201526001600160a01b038481166024830152831660448201526064810185905290602090829060849082905f905af1908115610bb5575f916145bb575b50806145a95750600954430361459a577f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1916144da826140ef565b935f19810361458b57506145856144fc6144f5865b846149da565b8096613186565b61450886600b54613186565b90806145248660018060a01b03165f52601060205260405f2090565b55600a546001600160a01b0386165f9081526010602052604090206001015561454c82600b55565b604080516001600160a01b03958616815295909416602086015292840186905260608401929092526080830191909152819060a0820190565b0390a190565b6144fc6144f5614585926144ef565b63c9021e2f60e01b5f5260045ffd5b6335a6a70b60e21b5f5260045260245ffd5b6145d4915060203d602011610be257610bd48183610563565b5f61449f565b6013546040516370a0823160e01b8152306004820152906020908290602490829060081c6001600160a01b03165afa908115610bb5575f9161461a575090565b610a1c915060203d602011610be257610bd48183610563565b601554614648906001600160a01b0316610b1c565b6040516370a0823160e01b815230600482015290602082602481845afa918215610bb5575f926146ac575b508115614129576040516303d1689d60e11b81526004810192909252602090829060249082905afa908115610bb5575f9161461a575090565b6146c691925060203d602011610be257610bd48183610563565b905f614673565b801580156147d5575b612c33576013545f91906147169082906146fb9060081c6001600160a01b0316610b1c565b601554614710906001600160a01b0316610b1c565b90614b16565b60155460209061476690614732906001600160a01b0316610b1c565b6019546040516364ee50b160e01b815260048101869052306024820152604481019190915294859283919082906064820190565b03925af1908115610bb5577f6c89b3dfee140ff66a50284d3adbd08d9d66970bf92bfbbb60e8ed1dda9dec04925f926147b4575b5060408051918252602082019290925290819081016143f4565b6147ce91925060203d602011610be257610bd48183610563565b905f61479a565b5060175460ff166146d6565b156147e857565b60405162461bcd60e51b815260206004820152601960248201527f544f4b454e5f5452414e534645525f4f55545f4641494c4544000000000000006044820152606490fd5b1561483457565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6001600160a01b03918216815291166020820152604081019190915260600190565b6131d890670de0b6b3a7640000925190613062565b91908260409103126104f3576020825192015190565b156148ea57565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b1561495257565b60405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606490fd5b1561499e57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b6013546040516370a0823160e01b815230600482015260089190911c6001600160a01b0316929091602083602481875afa928315610bb5575f93614af5575b50833b156104f3576040516323b872dd60e01b8152915f9183918291614a459190309060048501614896565b038183875af18015610bb557614ae1575b503d8015614ad657602014614a69575f80fd5b60205f803e614a785f51614c17565b6040516370a0823160e01b815230600482015291602090839060249082905afa8015610bb557614aaf925f91614ab7575b50613186565b610a1c613d26565b614ad0915060203d602011610be257610bd48183610563565b5f614aa9565b50614a785f19614c17565b80610baf5f614aef93610563565b5f614a56565b614b0f91935060203d602011610be257610bd48183610563565b915f614a19565b60405163095ea7b360e01b60208281019182526001600160a01b0385166024840152604480840196909652948252929390925f90614b55606486610563565b84519082855af15f51903d81614bbf575b501590505b614b7457505050565b60405163095ea7b360e01b60208201526001600160a01b039390931660248401525f60448085019190915283526109ab92614bba90614bb4606482610563565b82614c63565b614c63565b15159050614bdf5750614b6b6001600160a01b0382163b15155b5f614b66565b6001614b6b9114614bd9565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610f3857610a1c915190613075565b15614c1e57565b60405162461bcd60e51b815260206004820152601860248201527f544f4b454e5f5452414e534645525f494e5f4641494c454400000000000000006044820152606490fd5b905f602091828151910182855af115612ba6575f513d614cb257506001600160a01b0381163b155b614c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614c8b56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122041851f2064eb0319c3b4be00ac29637f6834fd1a22c297eeb8f839c535abd4db64736f6c634300081e0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MON
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.