Source Code
Overview
ETH Balance
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 21640831 | 41 days ago | 0 ETH | ||||
| 21640831 | 41 days ago | 0 ETH | ||||
| 21640831 | 41 days ago | 0 ETH | ||||
| 20641760 | 64 days ago | 0 ETH | ||||
| 20641760 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH | ||||
| 20641759 | 64 days ago | 0 ETH |
Loading...
Loading
Contract Name:
mErc20Host
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 2499 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
// interfaces
import {Steel} from "risc0/steel/Steel.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// contracts
import {ZkVerifier} from "src/verifier/ZkVerifier.sol";
import {mErc20Upgradable} from "src/mToken/mErc20Upgradable.sol";
import {mTokenProofDecoderLib} from "src/libraries/mTokenProofDecoderLib.sol";
import {ImErc20Host} from "src/interfaces/ImErc20Host.sol";
import {ImTokenOperationTypes} from "src/interfaces/ImToken.sol";
import {IRoles} from "src/interfaces/IRoles.sol";
contract mErc20Host is mErc20Upgradable, ZkVerifier, ImErc20Host, ImTokenOperationTypes {
using SafeERC20 for IERC20;
// ----------- STORAGE ------------
mapping(uint32 => mapping(address => uint256)) public accAmountInPerChain;
mapping(uint32 => mapping(address => uint256)) public accAmountOutPerChain;
mapping(address => mapping(address => bool)) public allowedCallers;
mapping(uint32 => bool) public allowedChains;
/**
* @notice Initializes the new money market
* @param underlying_ The address of the underlying asset
* @param operator_ The address of the Operator
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ ERC-20 name of this token
* @param symbol_ ERC-20 symbol of this token
* @param decimals_ ERC-20 decimal precision of this token
* @param admin_ Address of the administrator of this token
* @param zkVerifier_ The IRiscZeroVerifier address
*/
function initialize(
address underlying_,
address operator_,
address interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
address payable admin_,
address zkVerifier_,
address roles_
) external initializer {
// Initialize the base contract
proxyInitialize(
underlying_, operator_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_, admin_
);
// Initialize the ZkVerifier
ZkVerifier.initialize(zkVerifier_);
rolesOperator = IRoles(roles_);
// Set the proper admin now that initialization is done
admin = admin_;
}
// ----------- VIEW ------------
/**
* @inheritdoc ImErc20Host
*/
function isCallerAllowed(address sender, address caller) external view returns (bool) {
return allowedCallers[sender][caller];
}
/**
* @inheritdoc ImErc20Host
*/
function getProofData(address user, uint32 dstId) external view returns (bytes memory) {
return mTokenProofDecoderLib.encodeJournal(
user,
address(this),
accAmountInPerChain[dstId][user],
accAmountOutPerChain[dstId][user],
uint32(block.chainid),
dstId
);
}
// ----------- OWNER ------------
/**
* @notice Sets the _risc0Verifier address
* @param _risc0Verifier the new IRiscZeroVerifier address
*/
function setVerifier(address _risc0Verifier) external onlyAdmin {
_setVerifier(_risc0Verifier);
}
/**
* @notice Sets the image id
* @param _imageId the new image id
*/
function setImageId(bytes32 _imageId) external onlyAdmin {
_setImageId(_imageId);
}
function setTotalUnderlying(uint256 _amount) external onlyAdmin {
initialExchangeRateMantissa = _amount;
}
/**
* @notice Updates an allowed chain status
* @param _chainId the chain id
* @param _status the new status
*/
function updateAllowedChain(uint32 _chainId, bool _status) external {
if (msg.sender != admin && !rolesOperator.isAllowedFor(msg.sender, rolesOperator.CHAINS_MANAGER())) {
revert mErc20Host_CallerNotAllowed();
}
allowedChains[_chainId] = _status;
emit mErc20Host_ChainStatusUpdated(_chainId, _status);
}
/**
* @inheritdoc ImErc20Host
*/
function extractForRebalancing(uint256 amount) external {
if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.REBALANCER())) revert mErc20Host_NotRebalancer();
IERC20(underlying).safeTransfer(msg.sender, amount);
}
// ----------- PUBLIC ------------
/**
* @inheritdoc ImErc20Host
*/
function updateAllowedCallerStatus(address caller, bool status) external override {
allowedCallers[msg.sender][caller] = status;
emit AllowedCallerUpdated(msg.sender, caller, status);
}
/**
* @inheritdoc ImErc20Host
*/
function liquidateExternal(
bytes calldata journalData,
bytes calldata seal,
address[] calldata userToLiquidate,
uint256[] calldata liquidateAmount,
address[] calldata collateral,
address receiver
) external override {
// verify received data
if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.PROOF_BATCH_FORWARDER())) {
_verifyProof(journalData, seal);
}
bytes[] memory journals = abi.decode(journalData, (bytes[]));
uint256 length = journals.length;
require(length == liquidateAmount.length, mErc20Host_LengthMismatch());
require(length == userToLiquidate.length, mErc20Host_LengthMismatch());
require(length == collateral.length, mErc20Host_LengthMismatch());
for (uint256 i; i < length;) {
_liquidateExternal(journals[i], userToLiquidate[i], liquidateAmount[i], collateral[i], receiver);
unchecked {
++i;
}
}
}
/**
* @inheritdoc ImErc20Host
*/
function mintExternal(
bytes calldata journalData,
bytes calldata seal,
uint256[] calldata mintAmount,
address receiver
) external override {
if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.PROOF_BATCH_FORWARDER())) {
_verifyProof(journalData, seal);
}
bytes[] memory journals = abi.decode(journalData, (bytes[]));
uint256 length = journals.length;
require(length == mintAmount.length, mErc20Host_LengthMismatch());
for (uint256 i; i < length;) {
_mintExternal(journals[i], mintAmount[i], receiver);
unchecked {
++i;
}
}
}
/**
* @inheritdoc ImErc20Host
*/
function repayExternal(
bytes calldata journalData,
bytes calldata seal,
uint256[] calldata repayAmount,
address receiver
) external override {
if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.PROOF_BATCH_FORWARDER())) {
_verifyProof(journalData, seal);
}
bytes[] memory journals = abi.decode(journalData, (bytes[]));
uint256 length = journals.length;
require(length == repayAmount.length, mErc20Host_LengthMismatch());
for (uint256 i; i < length;) {
_repayExternal(journals[i], repayAmount[i], receiver);
unchecked {
++i;
}
}
}
/**
* @inheritdoc ImErc20Host
*/
function withdrawOnExtension(uint256 amount, uint32 dstChainId) external override {
require(amount > 0, mErc20Host_AmountNotValid());
// actions
accAmountOutPerChain[dstChainId][msg.sender] += amount;
_redeemUnderlying(msg.sender, amount, false);
emit mErc20Host_WithdrawOnExtensionChain(msg.sender, dstChainId, amount);
}
/**
* @inheritdoc ImErc20Host
*/
function borrowOnExtension(uint256 amount, uint32 dstChainId) external override {
require(amount > 0, mErc20Host_AmountNotValid());
// actions
accAmountOutPerChain[dstChainId][msg.sender] += amount;
_borrow(msg.sender, amount, false);
emit mErc20Host_BorrowOnExternsionChain(msg.sender, dstChainId, amount);
}
// ----------- PRIVATE ------------
function _verifyProof(bytes calldata journalData, bytes calldata seal) private {
require(journalData.length > 0, mErc20Host_JournalNotValid());
// verify it using the ZkVerifier contract
_verifyInput(journalData, seal);
}
function _checkSender(address msgSender, address srcSender) private view {
if (msgSender != srcSender) {
require(
allowedCallers[srcSender][msgSender] || msgSender == admin
|| rolesOperator.isAllowedFor(msgSender, rolesOperator.PROOF_FORWARDER())
|| rolesOperator.isAllowedFor(msgSender, rolesOperator.PROOF_BATCH_FORWARDER()),
mErc20Host_CallerNotAllowed()
);
}
}
function _liquidateExternal(
bytes memory singleJournal,
address userToLiquidate,
uint256 liquidateAmount,
address collateral,
address receiver
) internal {
(address _sender, address _market, uint256 _accAmountIn,, uint32 _chainId, uint32 _dstChainId) =
mTokenProofDecoderLib.decodeJournal(singleJournal);
receiver = _sender;
// base checks
{
_checkSender(msg.sender, _sender);
require(_dstChainId == uint32(block.chainid), mErc20Host_DstChainNotValid());
require(_market == address(this), mErc20Host_AddressNotValid());
require(allowedChains[_chainId], mErc20Host_ChainNotValid());
}
// operation checks
{
require(liquidateAmount > 0, mErc20Host_AmountNotValid());
require(liquidateAmount <= _accAmountIn - accAmountInPerChain[_chainId][_sender], mErc20Host_AmountTooBig());
require(userToLiquidate != msg.sender && userToLiquidate != _sender, mErc20Host_CallerNotAllowed());
}
collateral = collateral == address(0) ? address(this) : collateral;
// actions
accAmountInPerChain[_chainId][_sender] += liquidateAmount;
_liquidate(receiver, userToLiquidate, liquidateAmount, collateral, false);
emit mErc20Host_LiquidateExternal(
msg.sender, _sender, userToLiquidate, receiver, collateral, _chainId, liquidateAmount
);
}
function _mintExternal(bytes memory singleJournal, uint256 mintAmount, address receiver) internal {
(address _sender, address _market, uint256 _accAmountIn,, uint32 _chainId, uint32 _dstChainId) =
mTokenProofDecoderLib.decodeJournal(singleJournal);
receiver = _sender;
// base checks
{
_checkSender(msg.sender, _sender);
require(_dstChainId == uint32(block.chainid), mErc20Host_DstChainNotValid());
require(_market == address(this), mErc20Host_AddressNotValid());
require(allowedChains[_chainId], mErc20Host_ChainNotValid());
}
// operation checks
{
require(mintAmount > 0, mErc20Host_AmountNotValid());
require(mintAmount <= _accAmountIn - accAmountInPerChain[_chainId][_sender], mErc20Host_AmountTooBig());
}
// actions
accAmountInPerChain[_chainId][_sender] += mintAmount;
_mint(receiver, mintAmount, false);
emit mErc20Host_MintExternal(msg.sender, _sender, receiver, _chainId, mintAmount);
}
function _repayExternal(bytes memory singleJournal, uint256 repayAmount, address receiver) internal {
(address _sender, address _market, uint256 _accAmountIn,, uint32 _chainId, uint32 _dstChainId) =
mTokenProofDecoderLib.decodeJournal(singleJournal);
receiver = _sender;
// base checks
{
_checkSender(msg.sender, _sender);
require(_dstChainId == uint32(block.chainid), mErc20Host_DstChainNotValid());
require(_market == address(this), mErc20Host_AddressNotValid());
require(allowedChains[_chainId], mErc20Host_ChainNotValid());
}
// operation checks
{
require(repayAmount > 0, mErc20Host_AmountNotValid());
require(repayAmount <= _accAmountIn - accAmountInPerChain[_chainId][_sender], mErc20Host_AmountTooBig());
}
// actions
accAmountInPerChain[_chainId][_sender] += repayAmount;
_repayBehalf(receiver, repayAmount, false);
emit mErc20Host_RepayExternal(msg.sender, _sender, receiver, _chainId, repayAmount);
}
}// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;
/// @title Steel Library
/// @notice This library provides a collection of utilities to work with Steel commitments in Solidity.
library Steel {
/// @notice Represents a commitment to a specific block in the blockchain.
/// @dev The `id` combines the version and the actual identifier of the claim, such as the block number.
/// @dev The `digest` represents the data being committed to, e.g. the hash of the execution block.
/// @dev The `configID` is the cryptographic digest of the network configuration.
struct Commitment {
uint256 id;
bytes32 digest;
bytes32 configID;
}
/// @notice The version of the Commitment is incorrect.
error InvalidCommitmentVersion();
/// @notice The Commitment is too old and can no longer be validated.
error CommitmentTooOld();
/// @notice Validates if the provided Commitment matches the block hash of the given block number.
/// @param commitment The Commitment struct to validate.
/// @return True if the commitment's block hash matches the block hash of the block number, false otherwise.
function validateCommitment(Commitment memory commitment) internal view returns (bool) {
(uint240 claimID, uint16 version) = Encoding.decodeVersionedID(commitment.id);
if (version == 0) {
return validateBlockCommitment(claimID, commitment.digest);
} else if (version == 1) {
return validateBeaconCommitment(claimID, commitment.digest);
} else {
revert InvalidCommitmentVersion();
}
}
/// @notice Validates if the provided block commitment matches the block hash of the given block number.
/// @param blockNumber The block number to compare against.
/// @param blockHash The block hash to validate.
/// @return True if the block's block hash matches the block hash, false otherwise.
function validateBlockCommitment(uint256 blockNumber, bytes32 blockHash) internal view returns (bool) {
if (block.number - blockNumber > 256) {
revert CommitmentTooOld();
}
return blockHash == blockhash(blockNumber);
}
/// @notice Validates if the provided beacon commitment matches the block root of the given timestamp.
/// @param timestamp The timestamp to compare against.
/// @param blockRoot The block root to validate.
/// @return True if the block's block root matches the block root, false otherwise.
function validateBeaconCommitment(uint256 timestamp, bytes32 blockRoot) internal view returns (bool) {
if (block.timestamp - timestamp > 12 * 8191) {
revert CommitmentTooOld();
}
return blockRoot == Beacon.parentBlockRoot(timestamp);
}
}
/// @title Beacon Library
library Beacon {
/// @notice The address of the Beacon roots contract.
/// @dev https://eips.ethereum.org/EIPS/eip-4788
address internal constant BEACON_ROOTS_ADDRESS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;
/// @notice Find the root of the Beacon block corresponding to the parent of the execution block with the given timestamp.
/// @return root Returns the corresponding Beacon block root or null, if no such block exists.
function parentBlockRoot(uint256 timestamp) internal view returns (bytes32 root) {
(bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(abi.encode(timestamp));
if (success) {
return abi.decode(result, (bytes32));
}
}
}
/// @title Encoding Library
library Encoding {
/// @notice Encodes a version and ID into a single uint256 value.
/// @param id The base ID to be encoded, limited by 240 bits (or the maximum value of a uint240).
/// @param version The version number to be encoded, limited by 16 bits (or the maximum value of a uint16).
/// @return Returns a single uint256 value that contains both the `id` and the `version` encoded into it.
function encodeVersionedID(uint240 id, uint16 version) internal pure returns (uint256) {
uint256 encoded;
assembly {
encoded := or(shl(240, version), id)
}
return encoded;
}
/// @notice Decodes a version and ID from a single uint256 value.
/// @param id The single uint256 value to be decoded.
/// @return Returns two values: a uint240 for the original base ID and a uint16 for the version number encoded into it.
function decodeVersionedID(uint256 id) internal pure returns (uint240, uint16) {
uint240 decoded;
uint16 version;
assembly {
decoded := and(id, 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
version := shr(240, id)
}
return (decoded, version);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
// interfaces
import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
// contracts
import {Steel} from "risc0/steel/Steel.sol";
abstract contract ZkVerifier is Initializable {
// ----------- STORAGE ------------
IRiscZeroVerifier public verifier;
bytes32 public imageId;
error ZkVerifier_ImageNotValid();
error ZkVerifier_InputNotValid();
error ZkVerifier_VerifierNotSet();
event ImageSet(bytes32 _imageId);
event VerifierSet(address indexed oldVerifier, address indexed newVerifier);
// ----------- PUBLIC ------------
/**
* @notice Initializes a new ZkVerifier contract
* @param _verifier IRiscZeroVerifier contract implementation
*/
function initialize(address _verifier) public initializer {
require(_verifier != address(0), ZkVerifier_InputNotValid());
verifier = IRiscZeroVerifier(_verifier);
}
// ----------- INTERNAL ------------
/**
* @notice Sets the _risc0Verifier address
* @dev Admin check is needed on the external method
* @param _risc0Verifier the new IRiscZeroVerifier address
*/
function _setVerifier(address _risc0Verifier) internal {
require(_risc0Verifier != address(0), ZkVerifier_InputNotValid());
emit VerifierSet(address(verifier), _risc0Verifier);
verifier = IRiscZeroVerifier(_risc0Verifier);
}
/**
* @notice Sets the image id
* @dev Admin check is needed on the external method
* @param _imageId the new image id
*/
function _setImageId(bytes32 _imageId) internal {
require(_imageId != bytes32(0), ZkVerifier_ImageNotValid());
emit ImageSet(_imageId);
imageId = _imageId;
}
/**
* @notice Verifies an input
* @param journalEntry the risc0 journal entry
* @param seal the risc0 seal
*/
function _verifyInput(bytes calldata journalEntry, bytes calldata seal) internal virtual {
// generic checks
_checkAddresses();
// verify input
__verify(journalEntry, seal);
}
// ----------- PRIVATE ------------
function _checkAddresses() private view {
require(address(verifier) != address(0), ZkVerifier_VerifierNotSet());
}
function __verify(bytes calldata journalEntry, bytes calldata seal) private view {
verifier.verify(seal, imageId, sha256(journalEntry));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
import {mErc20} from "./mErc20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract mErc20Upgradable is mErc20, Initializable {
/**
* @notice Initialize the new money market
* @param underlying_ The address of the underlying asset
* @param operator_ The address of the Operator
* @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 proxyInitialize(
address underlying_,
address operator_,
address interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
address payable admin_
) public {
// Creator of the contract is admin during initialization
admin = payable(msg.sender);
// mErc20 initialize does the bulk of the work
super.initialize(
underlying_, operator_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_
);
// Set the proper admin now that initialization is done
admin = admin_;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {BytesLib} from "src/libraries/BytesLib.sol";
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
library mTokenProofDecoderLib {
uint256 public constant ENTRY_SIZE = 112; // single journal entry size
error mTokenProofDecoderLib_ChainNotFound();
function decodeJournal(bytes memory journalData)
internal
pure
returns (
address sender,
address market,
uint256 accAmountIn,
uint256 accAmountOut,
uint32 chainId,
uint32 dstChainId
)
{
// decode action data
// | Offset | Length | Data Type |
// |--------|---------|----------------------- |
// | 0 | 20 | address sender |
// | 20 | 40 | address market |
// | 40 | 32 | uint256 accAmountIn |
// | 72 | 32 | uint256 accAmountOut |
// | 104 | 4 | uint32 chainId |
sender = BytesLib.toAddress(BytesLib.slice(journalData, 0, 20), 0);
market = BytesLib.toAddress(BytesLib.slice(journalData, 20, 20), 0);
accAmountIn = BytesLib.toUint256(BytesLib.slice(journalData, 40, 32), 0);
accAmountOut = BytesLib.toUint256(BytesLib.slice(journalData, 72, 32), 0);
chainId = BytesLib.toUint32(BytesLib.slice(journalData, 104, 4), 0);
dstChainId = BytesLib.toUint32(BytesLib.slice(journalData, 108, 4), 0);
}
function encodeJournal(
address sender,
address market,
uint256 accAmountIn,
uint256 accAmountOut,
uint32 chainId,
uint32 dstChainId
) internal pure returns (bytes memory) {
return abi.encodePacked(sender, market, accAmountIn, accAmountOut, chainId, dstChainId);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
interface ImErc20Host {
// ----------- EVENTS -----------
/**
* @notice Emitted when a user updates allowed callers
*/
event AllowedCallerUpdated(address indexed sender, address indexed caller, bool status);
/**
* @notice Emitted when a chain id whitelist status is updated
*/
event mErc20Host_ChainStatusUpdated(uint32 indexed chainId, bool status);
/**
* @notice Emitted when a liquidate operation is executed
*/
event mErc20Host_LiquidateExternal(
address indexed msgSender,
address indexed srcSender,
address userToLiquidate,
address receiver,
address indexed collateral,
uint32 srcChainId,
uint256 amount
);
/**
* @notice Emitted when a mint operation is executed
*/
event mErc20Host_MintExternal(
address indexed msgSender, address indexed srcSender, address indexed receiver, uint32 chainId, uint256 amount
);
/**
* @notice Emitted when a borrow operation is executed
*/
event mErc20Host_BorrowExternal(
address indexed msgSender, address indexed srcSender, uint32 indexed chainId, uint256 amount
);
/**
* @notice Emitted when a repay operation is executed
*/
event mErc20Host_RepayExternal(
address indexed msgSender, address indexed srcSender, address indexed position, uint32 chainId, uint256 amount
);
/**
* @notice Emitted when a withdrawal is executed
*/
event mErc20Host_WithdrawExternal(
address indexed msgSender, address indexed srcSender, uint32 indexed chainId, uint256 amount
);
/**
* @notice Emitted when a borrow operation is triggered for an extension chain
*/
event mErc20Host_BorrowOnExternsionChain(address indexed sender, uint32 dstChainId, uint256 amount);
/**
* @notice Emitted when a withdraw operation is triggered for an extension chain
*/
event mErc20Host_WithdrawOnExtensionChain(address indexed sender, uint32 dstChainId, uint256 amount);
// ----------- ERRORS -----------
/**
* @notice Thrown when the chain id is not LINEA
*/
error mErc20Host_ProofGenerationInputNotValid();
/**
* @notice Thrown when the dst chain id is not current chain
*/
error mErc20Host_DstChainNotValid();
/**
* @notice Thrown when the chain id is not LINEA
*/
error mErc20Host_ChainNotValid();
/**
* @notice Thrown when the address is not valid
*/
error mErc20Host_AddressNotValid();
/**
* @notice Thrown when the amount provided is bigger than the available amount`
*/
error mErc20Host_AmountTooBig();
/**
* @notice Thrown when the amount specified is invalid (e.g., zero)
*/
error mErc20Host_AmountNotValid();
/**
* @notice Thrown when the journal data provided is invalid or corrupted
*/
error mErc20Host_JournalNotValid();
/**
* @notice Thrown when caller is not allowed
*/
error mErc20Host_CallerNotAllowed();
/**
* @notice Thrown when caller is not rebalancer
*/
error mErc20Host_NotRebalancer();
/**
* @notice Thrown when length of array is not valid
*/
error mErc20Host_LengthMismatch();
// ----------- VIEW -----------
/**
* @notice Returns if a caller is allowed for sender
*/
function isCallerAllowed(address sender, address caller) external view returns (bool);
/**
* @notice Returns the proof data journal
*/
function getProofData(address user, uint32 dstId) external view returns (bytes memory);
// ----------- PUBLIC -----------
/**
* @notice Extract amount to be used for rebalancing operation
* @param amount The amount to rebalance
*/
function extractForRebalancing(uint256 amount) external;
/**
* @notice Set caller status for `msg.sender`
* @param caller The caller address
* @param status The status to set for `caller`
*/
function updateAllowedCallerStatus(address caller, bool status) external;
/**
* @notice Mints tokens after external verification
* @param journalData The journal data for minting (array of encoded journals)
* @param seal The Zk proof seal
* @param userToLiquidate Array of positions to liquidate
* @param liquidateAmount Array of amounts to liquidate
* @param collateral Array of collaterals to seize
* @param receiver The collateral receiver
*/
function liquidateExternal(
bytes calldata journalData,
bytes calldata seal,
address[] calldata userToLiquidate,
uint256[] calldata liquidateAmount,
address[] calldata collateral,
address receiver
) external;
/**
* @notice Mints tokens after external verification
* @param journalData The journal data for minting (array of encoded journals)
* @param seal The Zk proof seal
* @param mintAmount Array of amounts to mint
* @param receiver The tokens receiver
*/
function mintExternal(
bytes calldata journalData,
bytes calldata seal,
uint256[] calldata mintAmount,
address receiver
) external;
/**
* @notice Repays tokens after external verification
* @param journalData The journal data for repayment (array of encoded journals)
* @param seal The Zk proof seal
* @param repayAmount Array of amounts to repay
* @param receiver The position to repay for
*/
function repayExternal(
bytes calldata journalData,
bytes calldata seal,
uint256[] calldata repayAmount,
address receiver
) external;
/**
* @notice Initiates a withdraw operation
* @param amount The amount to withdraw
* @param dstChainId The destination chain to recieve funds
*/
function withdrawOnExtension(uint256 amount, uint32 dstChainId) external;
/**
* @notice Initiates a withdraw operation
* @param amount The amount to withdraw
* @param dstChainId The destination chain to recieve funds
*/
function borrowOnExtension(uint256 amount, uint32 dstChainId) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
import {IRoles} from "./IRoles.sol";
interface ImTokenOperationTypes {
enum OperationType {
AmountIn,
AmountInHere,
AmountOut,
AmountOutHere,
Seize,
Transfer,
Mint,
Borrow,
Repay,
Redeem,
Liquidate
}
}
interface ImTokenDelegator {
/**
* @notice Non-standard token able to delegate
*/
function delegate(address delegatee) external;
}
interface ImTokenMinimal {
/**
* @notice EIP-20 token name for this token
*/
function name() external view returns (string memory);
/**
* @notice EIP-20 token symbol for this token
*/
function symbol() external view returns (string memory);
/**
* @notice EIP-20 token decimals for this token
*/
function decimals() external view returns (uint8);
/**
* @notice Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @notice Returns the value of tokens owned by `account`.
* @param account The account to check for
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Returns true
*/
function isMToken() external view returns (bool);
/**
* @dev Returns the underlying address
*/
function underlying() external view returns (address);
}
interface ImToken is ImTokenMinimal {
// ----------- STORAGE ------------
/**
* @notice Roles manager
*/
function rolesOperator() external view returns (IRoles);
/**
* @notice Administrator for this contract
*/
function admin() external view returns (address payable);
/**
* @notice Pending administrator for this contract
*/
function pendingAdmin() external view returns (address payable);
/**
* @notice Contract which oversees inter-mToken operations
*/
function operator() external view returns (address);
/**
* @notice Model which tells what the current interest rate should be
*/
function interestRateModel() external view returns (address);
/**
* @notice Fraction of interest currently set aside for reserves
*/
function reserveFactorMantissa() external view returns (uint256);
/**
* @notice Block number that interest was last accrued at
*/
function accrualBlockNumber() external view returns (uint256);
/**
* @notice Accumulator of the total earned interest rate since the opening of the market
*/
function borrowIndex() external view returns (uint256);
/**
* @notice Total amount of outstanding borrows of the underlying in this market
*/
function totalBorrows() external view returns (uint256);
/**
* @notice Total amount of reserves of the underlying held in this market
*/
function totalReserves() external view returns (uint256);
// ----------- ACTIONS ------------
/**
* @notice Transfers `amount` tokens to the `dst` address
* @param dst The address of the recipient
* @param amount The number of tokens to transfer
* @return Whether the transfer was successful or not
*/
function transfer(address dst, uint256 amount) external returns (bool);
/**
* @notice Transfers `amount` tokens from the `src` address to the `dst` address
* @param src The address from which tokens are transferred
* @param dst The address to which tokens are transferred
* @param amount The number of tokens to transfer
* @return Whether the transfer was successful or not
*/
function transferFrom(address src, address dst, uint256 amount) external returns (bool);
/**
* @notice Approves `spender` to spend `amount` tokens on behalf of the caller
* @param spender The address authorized to spend tokens
* @param amount The number of tokens to approve
* @return Whether the approval was successful or not
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @notice Returns the current allowance the `spender` has from the `owner`
* @param owner The address of the token holder
* @param spender The address authorized to spend the tokens
* @return The current remaining number of tokens `spender` can spend
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @notice Returns the balance of tokens held by `owner`
* @param owner The address to query the balance for
* @return The balance of tokens owned by `owner`
*/
function balanceOf(address owner) external view returns (uint256);
/**
* @notice Returns the underlying asset balance of the `owner`
* @param owner The address to query the balance of underlying assets for
* @return The balance of underlying assets owned by `owner`
*/
function balanceOfUnderlying(address owner) external returns (uint256);
/**
* @notice Returns the snapshot of account details for the given `account`
* @param account The address to query the account snapshot for
* @return (token balance, borrow balance, exchange rate)
*/
function getAccountSnapshot(address account) external view returns (uint256, uint256, uint256);
/**
* @notice Returns the current borrow rate per block
* @return The current borrow rate per block, scaled by 1e18
*/
function borrowRatePerBlock() external view returns (uint256);
/**
* @notice Returns the current supply rate per block
* @return The current supply rate per block, scaled by 1e18
*/
function supplyRatePerBlock() external view returns (uint256);
/**
* @notice Returns the total amount of borrows, accounting for interest
* @return The total amount of borrows
*/
function totalBorrowsCurrent() external returns (uint256);
/**
* @notice Returns the current borrow balance for `account`, accounting for interest
* @param account The address to query the borrow balance for
* @return The current borrow balance
*/
function borrowBalanceCurrent(address account) external returns (uint256);
/**
* @notice Returns the stored borrow balance for `account`, without accruing interest
* @param account The address to query the stored borrow balance for
* @return The stored borrow balance
*/
function borrowBalanceStored(address account) external view returns (uint256);
/**
* @notice Returns the current exchange rate, with interest accrued
* @return The current exchange rate
*/
function exchangeRateCurrent() external returns (uint256);
/**
* @notice Returns the stored exchange rate, without accruing interest
* @return The stored exchange rate
*/
function exchangeRateStored() external view returns (uint256);
/**
* @notice Returns the total amount of available cash in the contract
* @return The total amount of cash
*/
function getCash() external view returns (uint256);
/**
* @notice Accrues interest on the contract's outstanding loans
*/
function accrueInterest() external;
/**
* @notice Transfers collateral tokens (this market) to the liquidator.
* @dev Will fail unless called by another mToken during the process of liquidation.
* Its absolutely critical to use msg.sender as the borrowed mToken and not a parameter.
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of mTokens to seize
*/
function seize(address liquidator, address borrower, uint256 seizeTokens) external;
/**
* @notice Accrues interest and reduces reserves by transferring to admin
* @param reduceAmount Amount of reduction to reserves
*/
function reduceReserves(uint256 reduceAmount) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
interface IRoles {
error Roles_InputNotValid();
/**
* @notice Returns REBALANCER role
*/
function REBALANCER() external view returns (bytes32);
/**
* @notice Returns REBALANCER_EOA role
*/
function REBALANCER_EOA() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_PAUSE role
*/
function GUARDIAN_PAUSE() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_TRANSFER role
*/
function GUARDIAN_TRANSFER() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_SEIZE role
*/
function GUARDIAN_SEIZE() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_MINT role
*/
function GUARDIAN_MINT() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_BORROW role
*/
function GUARDIAN_BORROW() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_BRIDGE role
*/
function GUARDIAN_BRIDGE() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_BORROW_CAP role
*/
function GUARDIAN_BORROW_CAP() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_SUPPLY_CAP role
*/
function GUARDIAN_SUPPLY_CAP() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_RESERVE role
*/
function GUARDIAN_RESERVE() external view returns (bytes32);
/**
* @notice Returns PROOF_FORWARDER role
*/
function PROOF_FORWARDER() external view returns (bytes32);
/**
* @notice Returns PROOF_BATCH_FORWARDER role
*/
function PROOF_BATCH_FORWARDER() external view returns (bytes32);
/**
* @notice Returns PAUSE_MANAGER role
*/
function PAUSE_MANAGER() external view returns (bytes32);
/**
* @notice Returns CHAINS_MANAGER role
*/
function CHAINS_MANAGER() external view returns (bytes32);
/**
* @notice Returns allowance status for a contract and a role
* @param _contract the contract address
* @param _role the bytes32 role
*/
function isAllowedFor(address _contract, bytes32 _role) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;
import {reverseByteOrderUint32} from "./Util.sol";
/// @notice A receipt attesting to the execution of a guest program.
/// @dev A receipt contains two parts: a seal and a claim. The seal is a zero-knowledge proof
/// attesting to knowledge of a zkVM execution resulting in the claim. The claim is a set of public
/// outputs for the execution. Crucially, the claim includes the journal and the image ID. The
/// image ID identifies the program that was executed, and the journal is the public data written
/// by the program. Note that this struct only contains the claim digest, as can be obtained with
/// the `digest()` function on `ReceiptClaimLib`.
struct Receipt {
bytes seal;
bytes32 claimDigest;
}
/// @notice Public claims about a zkVM guest execution, such as the journal committed to by the guest.
/// @dev Also includes important information such as the exit code and the starting and ending system
/// state (i.e. the state of memory). `ReceiptClaim` is a "Merkle-ized struct" supporting
/// partial openings of the underlying fields from a hash commitment to the full structure.
struct ReceiptClaim {
/// @notice Digest of the SystemState just before execution has begun.
bytes32 preStateDigest;
/// @notice Digest of the SystemState just after execution has completed.
bytes32 postStateDigest;
/// @notice The exit code for the execution.
ExitCode exitCode;
/// @notice A digest of the input to the guest.
/// @dev This field is currently unused and must be set to the zero digest.
bytes32 input;
/// @notice Digest of the Output of the guest, including the journal
/// and assumptions set during execution.
bytes32 output;
}
library ReceiptClaimLib {
using OutputLib for Output;
using SystemStateLib for SystemState;
bytes32 constant TAG_DIGEST = sha256("risc0.ReceiptClaim");
// Define a constant to ensure hashing is done at compile time. Can't use the
// SystemStateLib.digest method here because the Solidity compiler complains.
bytes32 constant SYSTEM_STATE_ZERO_DIGEST = 0xa3acc27117418996340b84e5a90f3ef4c49d22c79e44aad822ec9c313e1eb8e2;
/// @notice Construct a ReceiptClaim from the given imageId and journalDigest.
/// Returned ReceiptClaim will represent a successful execution of the zkVM, running
/// the program committed by imageId and resulting in the journal specified by
/// journalDigest.
/// @param imageId The identifier for the guest program.
/// @param journalDigest The SHA-256 digest of the journal bytes.
/// @dev Input hash and postStateDigest are set to all-zeros (i.e. no committed input, or
/// final memory state), the exit code is (Halted, 0), and there are no assumptions
/// (i.e. the receipt is unconditional).
function ok(bytes32 imageId, bytes32 journalDigest) internal pure returns (ReceiptClaim memory) {
return ReceiptClaim(
imageId,
SYSTEM_STATE_ZERO_DIGEST,
ExitCode(SystemExitCode.Halted, 0),
bytes32(0),
Output(journalDigest, bytes32(0)).digest()
);
}
function digest(ReceiptClaim memory claim) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
TAG_DIGEST,
// down
claim.input,
claim.preStateDigest,
claim.postStateDigest,
claim.output,
// data
uint32(claim.exitCode.system) << 24,
uint32(claim.exitCode.user) << 24,
// down.length
uint16(4) << 8
)
);
}
}
/// @notice Commitment to the memory state and program counter (pc) of the zkVM.
/// @dev The "pre" and "post" fields of the ReceiptClaim are digests of the system state at the
/// start are stop of execution. Programs are loaded into the zkVM by creating a memory image
/// of the loaded program, and creating a system state for initializing the zkVM. This is
/// known as the "image ID".
struct SystemState {
/// @notice Program counter.
uint32 pc;
/// @notice Root hash of a merkle tree which confirms the integrity of the memory image.
bytes32 merkle_root;
}
library SystemStateLib {
bytes32 constant TAG_DIGEST = sha256("risc0.SystemState");
function digest(SystemState memory state) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
TAG_DIGEST,
// down
state.merkle_root,
// data
reverseByteOrderUint32(state.pc),
// down.length
uint16(1) << 8
)
);
}
}
/// @notice Exit condition indicated by the zkVM at the end of the guest execution.
/// @dev Exit codes have a "system" part and a "user" part. Semantically, the system part is set to
/// indicate the type of exit (e.g. halt, pause, or system split) and is directly controlled by the
/// zkVM. The user part is an exit code, similar to exit codes used in Linux, chosen by the guest
/// program to indicate additional information (e.g. 0 to indicate success or 1 to indicate an
/// error).
struct ExitCode {
SystemExitCode system;
uint8 user;
}
/// @notice Exit condition indicated by the zkVM at the end of the execution covered by this proof.
/// @dev
/// `Halted` indicates normal termination of a program with an interior exit code returned from the
/// guest program. A halted program cannot be resumed.
///
/// `Paused` indicates the execution ended in a paused state with an interior exit code set by the
/// guest program. A paused program can be resumed such that execution picks up where it left
/// of, with the same memory state.
///
/// `SystemSplit` indicates the execution ended on a host-initiated system split. System split is
/// mechanism by which the host can temporarily stop execution of the execution ended in a system
/// split has no output and no conclusions can be drawn about whether the program will eventually
/// halt. System split is used in continuations to split execution into individually provable segments.
enum SystemExitCode {
Halted,
Paused,
SystemSplit
}
/// @notice Output field in the `ReceiptClaim`, committing to a claimed journal and assumptions list.
struct Output {
/// @notice Digest of the journal committed to by the guest execution.
bytes32 journalDigest;
/// @notice Digest of the ordered list of `ReceiptClaim` digests corresponding to the
/// calls to `env::verify` and `env::verify_integrity`.
/// @dev Verifying the integrity of a `Receipt` corresponding to a `ReceiptClaim` with a
/// non-empty assumptions list does not guarantee unconditionally any of the claims over the
/// guest execution (i.e. if the assumptions list is non-empty, then the journal digest cannot
/// be trusted to correspond to a genuine execution). The claims can be checked by additional
/// verifying a `Receipt` for every digest in the assumptions list.
bytes32 assumptionsDigest;
}
library OutputLib {
bytes32 constant TAG_DIGEST = sha256("risc0.Output");
function digest(Output memory output) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
TAG_DIGEST,
// down
output.journalDigest,
output.assumptionsDigest,
// down.length
uint16(2) << 8
)
);
}
}
/// @notice Error raised when cryptographic verification of the zero-knowledge proof fails.
error VerificationFailed();
/// @notice Verifier interface for RISC Zero receipts of execution.
interface IRiscZeroVerifier {
/// @notice Verify that the given seal is a valid RISC Zero proof of execution with the
/// given image ID and journal digest. Reverts on failure.
/// @dev This method additionally ensures that the input hash is all-zeros (i.e. no
/// committed input), the exit code is (Halted, 0), and there are no assumptions (i.e. the
/// receipt is unconditional).
/// @param seal The encoded cryptographic proof (i.e. SNARK).
/// @param imageId The identifier for the guest program.
/// @param journalDigest The SHA-256 digest of the journal bytes.
function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external view;
/// @notice Verify that the given receipt is a valid RISC Zero receipt, ensuring the `seal` is
/// valid a cryptographic proof of the execution with the given `claim`. Reverts on failure.
/// @param receipt The receipt to be verified.
function verifyIntegrity(Receipt calldata receipt) external view;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
// interfaces
import {ImErc20} from "src/interfaces/ImErc20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ImTokenMinimal, ImTokenDelegator} from "src/interfaces/ImToken.sol";
// contracts
import {mToken} from "./mToken.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title Malda's mErc20 Contract
* @notice mTokens which wrap an EIP-20 underlying
*/
contract mErc20 is mToken, ImErc20 {
using SafeERC20 for IERC20;
// ----------- STORAGE ------------
/**
* @notice Underlying asset for this mToken
*/
address public underlying;
// ----------- ERRORS ------------
error mErc20_TokenNotValid();
/**
* @notice Initialize the new money market
* @param underlying_ The address of the underlying asset
* @param operator_ The address of the Operator
* @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_,
address operator_,
address interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_
) public {
// mToken initialize does the bulk of the work
super.initialize(operator_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_);
// Set underlying and sanity check it
underlying = underlying_;
ImTokenMinimal(underlying).totalSupply();
}
// ----------- OWNER ------------
/**
* @notice Admin call to delegate the votes of the MALDA-like underlying
* @param delegatee The address to delegate votes to
* @dev mTokens whose underlying are not should revert here
*/
function delegateMaldaLikeTo(address delegatee) external onlyAdmin {
ImTokenDelegator(underlying).delegate(delegatee);
}
/**
* @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(IERC20 token) external onlyAdmin {
require(address(token) != underlying, mErc20_TokenNotValid());
uint256 balance = token.balanceOf(address(this));
token.safeTransfer(admin, balance);
}
// ----------- MARKET PUBLIC ------------
/**
* @inheritdoc ImErc20
*/
function mint(uint256 mintAmount) external {
_mint(msg.sender, mintAmount, true);
}
/**
* @inheritdoc ImErc20
*/
function redeem(uint256 redeemTokens) external {
_redeem(msg.sender, redeemTokens, true);
}
/**
* @inheritdoc ImErc20
*/
function redeemUnderlying(uint256 redeemAmount) external {
_redeemUnderlying(msg.sender, redeemAmount, true);
}
/**
* @inheritdoc ImErc20
*/
function borrow(uint256 borrowAmount) external {
_borrow(msg.sender, borrowAmount, true);
}
/**
* @inheritdoc ImErc20
*/
function repay(uint256 repayAmount) external {
_repay(repayAmount, true);
}
/**
* @inheritdoc ImErc20
*/
function repayBehalf(address borrower, uint256 repayAmount) external {
_repayBehalf(borrower, repayAmount, true);
}
/**
* @inheritdoc ImErc20
*/
function liquidate(address borrower, uint256 repayAmount, address mTokenCollateral) external {
_liquidate(msg.sender, borrower, repayAmount, mTokenCollateral, true);
}
/**
* @inheritdoc ImErc20
*/
function addReserves(uint256 addAmount) external {
return _addReserves(addAmount);
}
// ----------- INTERNAL ------------
/**
* @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) {
return initialExchangeRateMantissa;
}
/**
* @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 override returns (uint256) {
uint256 balanceBefore = IERC20(underlying).balanceOf(address(this));
IERC20(underlying).safeTransferFrom(from, address(this), amount);
uint256 balanceAfter = IERC20(underlying).balanceOf(address(this));
return balanceAfter - balanceBefore;
}
/**
* @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 override {
IERC20(underlying).safeTransfer(to, amount);
}
}// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity =0.8.28; library BytesLib { function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore( 0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. ) ) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) } // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equal_nonAligned(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let endMinusWord := add(_preBytes, length) let mc := add(_preBytes, 0x20) let cc := add(_postBytes, 0x20) for { // the next line is the loop condition: // while(uint256(mc < endWord) + cb == 2) } eq(add(lt(mc, endMinusWord), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } // Only if still successful // For <1 word tail bytes if gt(success, 0) { // Get the remainder of length/32 // length % 32 = AND(length, 32 - 1) let numTailBytes := and(length, 0x1f) let mcRem := mload(mc) let ccRem := mload(cc) for { let i := 0 } // the next line is the loop condition: // while(uint256(i < numTailBytes) + cb == 2) eq(add(lt(i, numTailBytes), cb), 2) { i := add(i, 1) } { if iszero(eq(byte(i, mcRem), byte(i, ccRem))) { // unsuccess: success := 0 cb := 0 } } } } default { // unsuccess: success := 0 } } return success; } function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;
/// @notice reverse the byte order of the uint256 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint256(uint256 input) pure returns (uint256 v) {
v = input;
// swap bytes
v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8)
| ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
// swap 2-byte long pairs
v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16)
| ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
// swap 4-byte long pairs
v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32)
| ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
// swap 8-byte long pairs
v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64)
| ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
// swap 16-byte long pairs
v = (v >> 128) | (v << 128);
}
/// @notice reverse the byte order of the uint32 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint32(uint32 input) pure returns (uint32 v) {
v = input;
// swap bytes
v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);
// swap 2-byte long pairs
v = (v >> 16) | (v << 16);
}
/// @notice reverse the byte order of the uint16 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint16(uint16 input) pure returns (uint16 v) {
v = input;
// swap bytes
v = (v >> 8) | ((v & 0x00FF) << 8);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
interface ImErc20 {
/**
* @notice Sender supplies assets into the market and receives mTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param mintAmount The amount of the underlying asset to supply
*/
function mint(uint256 mintAmount) external;
/**
* @notice Sender redeems mTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of mTokens to redeem into underlying
*/
function redeem(uint256 redeemTokens) external;
/**
* @notice Sender redeems mTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemAmount The amount of underlying to redeem
*/
function redeemUnderlying(uint256 redeemAmount) external;
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
*/
function borrow(uint256 borrowAmount) external;
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
*/
function repay(uint256 repayAmount) external;
/**
* @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 repayBehalf(address borrower, uint256 repayAmount) external;
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this mToken to be liquidated
* @param repayAmount The amount of the underlying borrowed asset to repay
* @param mTokenCollateral The market in which to seize collateral from the borrower
*/
function liquidate(address borrower, uint256 repayAmount, address mTokenCollateral) external;
/**
* @notice The sender adds to reserves.
* @param addAmount The amount fo underlying token to add as reserves
*/
function addReserves(uint256 addAmount) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
// interfaces
import {IRoles} from "src/interfaces/IRoles.sol";
import {ImToken, ImTokenMinimal} from "src/interfaces/ImToken.sol";
import {IInterestRateModel} from "src/interfaces/IInterestRateModel.sol";
import {IOperator, IOperatorDefender} from "src/interfaces/IOperator.sol";
// contracts
import {mTokenConfiguration} from "./mTokenConfiguration.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
abstract contract mToken is mTokenConfiguration, ReentrancyGuard {
/**
* @notice Initialize the money market
* @param operator_ The address of the Operator
* @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(
address operator_,
address interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_
) public onlyAdmin {
require(accrualBlockNumber == 0 && borrowIndex == 0, mToken_AlreadyInitialized());
require(initialExchangeRateMantissa_ > 0, mToken_ExchangeRateNotValid());
// Set initial exchange rate
initialExchangeRateMantissa = initialExchangeRateMantissa_;
_setOperator(operator_);
accrualBlockNumber = _getBlockNumber();
borrowIndex = mantissaOne;
_setInterestRateModel(interestRateModel_);
name = name_;
symbol = symbol_;
decimals = decimals_;
}
function isMToken() external pure override returns (bool) {
return true;
}
// ----------- TOKENS VIEW ------------
/**
* @inheritdoc ImToken
*/
function allowance(address owner, address spender) external view override returns (uint256) {
return transferAllowances[owner][spender];
}
/**
* @inheritdoc ImTokenMinimal
*/
function balanceOf(address owner) external view override returns (uint256) {
return accountTokens[owner];
}
/**
* @inheritdoc ImToken
*/
function balanceOfUnderlying(address owner) external override returns (uint256) {
Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);
}
// ----------- MARKETS VIEW ------------
/**
* @inheritdoc ImToken
*/
function getAccountSnapshot(address account) external view override returns (uint256, uint256, uint256) {
return (accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());
}
/**
* @inheritdoc ImToken
*/
function borrowRatePerBlock() external view override returns (uint256) {
return IInterestRateModel(interestRateModel).getBorrowRate(_getCashPrior(), totalBorrows, totalReserves);
}
/**
* @inheritdoc ImToken
*/
function supplyRatePerBlock() external view override returns (uint256) {
return IInterestRateModel(interestRateModel).getSupplyRate(
_getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa
);
}
/**
* @inheritdoc ImToken
*/
function borrowBalanceStored(address account) external view override returns (uint256) {
return _borrowBalanceStored(account);
}
/**
* @inheritdoc ImToken
*/
function getCash() external view override returns (uint256) {
return _getCashPrior();
}
/**
* @inheritdoc ImToken
*/
function exchangeRateStored() external view override returns (uint256) {
return _exchangeRateStored();
}
// ----------- TOKENS PUBLIC ------------
/**
* @inheritdoc ImToken
*/
function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {
_transferTokens(msg.sender, msg.sender, dst, amount);
return true;
}
/**
* @inheritdoc ImToken
*/
function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {
_transferTokens(msg.sender, src, dst, amount);
return true;
}
/**
* @inheritdoc ImToken
*/
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;
}
// ----------- MARKETS PUBLIC ------------
/**
* @inheritdoc ImToken
*/
function totalBorrowsCurrent() external override nonReentrant returns (uint256) {
_accrueInterest();
return totalBorrows;
}
/**
* @inheritdoc ImToken
*/
function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {
_accrueInterest();
return _borrowBalanceStored(account);
}
/**
* @inheritdoc ImToken
*/
function exchangeRateCurrent() public override nonReentrant returns (uint256) {
_accrueInterest();
return _exchangeRateStored();
}
/**
* @inheritdoc ImToken
*/
function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {
_seize(msg.sender, liquidator, borrower, seizeTokens);
}
/**
* @inheritdoc ImToken
*/
function reduceReserves(uint256 reduceAmount) external override nonReentrant {
require(
msg.sender == admin || rolesOperator.isAllowedFor(msg.sender, rolesOperator.GUARDIAN_RESERVE()),
mToken_OnlyAdminOrRole()
);
_accrueInterest();
require(_getCashPrior() >= reduceAmount, mToken_ReserveCashNotAvailable());
require(reduceAmount <= totalReserves, mToken_ReserveCashNotAvailable());
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
// totalReserves - reduceAmount
uint256 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(payable(msg.sender), reduceAmount);
initialExchangeRateMantissa -= reduceAmount;
emit ReservesReduced(admin, reduceAmount, totalReservesNew);
}
// ----------- INTERNAL VIEW ------------
/**
* @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 _borrowBalanceStored(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;
}
// ----------- INTERNAL ------------
/**
* @notice Sender supplies assets into the market and receives mTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param user The user address
* @param mintAmount The amount of the underlying asset to supply
* @param doTransfer If an actual transfer should be performed
*/
function _mint(address user, uint256 mintAmount, bool doTransfer) internal nonReentrant {
_accrueInterest();
// emits the actual Mint event if successful and logs on errors, so we don't need to
__mint(user, mintAmount, doTransfer);
}
/**
* @notice Sender redeems mTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param user The user address
* @param redeemTokens The number of mTokens to redeem into underlying
* @param doTransfer If an actual transfer should be performed
*/
function _redeem(address user, uint256 redeemTokens, bool doTransfer) internal nonReentrant {
_accrueInterest();
// emits redeem-specific logs on errors, so we don't need to
__redeem(payable(user), redeemTokens, 0, doTransfer);
}
/**
* @notice Sender redeems mTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param user The user address
* @param redeemAmount The amount of underlying to receive from redeeming mTokens
* @param doTransfer If an actual transfer should be performed
*/
function _redeemUnderlying(address user, uint256 redeemAmount, bool doTransfer) internal nonReentrant {
_accrueInterest();
// emits redeem-specific logs on errors, so we don't need to
__redeem(payable(user), 0, redeemAmount, doTransfer);
}
/**
* @notice Sender borrows assets from the protocol to their own address
* @param user The user address
* @param borrowAmount The amount of the underlying asset to borrow
* @param doTransfer If an actual transfer should be performed
*/
function _borrow(address user, uint256 borrowAmount, bool doTransfer) internal nonReentrant {
_accrueInterest();
// emits borrow-specific logs on errors, so we don't need to
__borrow(payable(user), borrowAmount, doTransfer);
}
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
* @param doTransfer If an actual transfer should be performed
*/
function _repay(uint256 repayAmount, bool doTransfer) internal nonReentrant {
_accrueInterest();
// emits repay-borrow-specific logs on errors, so we don't need to
__repay(msg.sender, msg.sender, repayAmount, doTransfer);
}
/**
* @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
* @param doTransfer If an actual transfer should be performed
*/
function _repayBehalf(address borrower, uint256 repayAmount, bool doTransfer) internal nonReentrant {
_accrueInterest();
// emits repay-borrow-specific logs on errors, so we don't need to
__repay(msg.sender, borrower, repayAmount, doTransfer);
}
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The liquidator address
* @param borrower The borrower of this mToken to be liquidated
* @param mTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
* @param doTransfer If an actual transfer should be performed
*/
function _liquidate(
address liquidator,
address borrower,
uint256 repayAmount,
address mTokenCollateral,
bool doTransfer
) internal nonReentrant {
_accrueInterest();
ImToken(mTokenCollateral).accrueInterest();
// emits borrow-specific logs on errors, so we don't need to
__liquidate(liquidator, borrower, repayAmount, mTokenCollateral, doTransfer);
}
/**
* @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 mToken.
* Its absolutely critical to use msg.sender as the seizer mToken and not a parameter.
* @param seizerToken The contract seizing the collateral (i.e. borrowed mToken)
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of mTokens to seize
*/
function _seize(address seizerToken, address liquidator, address borrower, uint256 seizeTokens) internal {
IOperatorDefender(operator).beforeMTokenSeize(address(this), seizerToken, liquidator, borrower);
require(borrower != liquidator, mToken_InvalidInput());
/*
* 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: PROTOCOL_SEIZE_SHARE_MANTISSA}));
uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;
Exp memory exchangeRate = Exp({mantissa: _exchangeRateStored()});
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);
}
/**
* @notice Accrues interest and reduces reserves by transferring from msg.sender
* @param addAmount Amount of addition to reserves
*/
function _addReserves(uint256 addAmount) internal nonReentrant {
_accrueInterest();
// totalReserves + actualAddAmount
uint256 totalReservesNew;
uint256 actualAddAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the caller and the addAmount
* Note: The mToken must handle variations between ERC-20 and ETH underlying.
* On success, the mToken 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);
initialExchangeRateMantissa += actualAddAmount;
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);
}
// ----------- PRIVATE ------------
/**
* @notice The liquidator liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param liquidator The address repaying the borrow and seizing collateral
* @param borrower The borrower of this mToken to be liquidated
* @param mTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
* @param doTransfer If an actual transfer should be performed
*/
function __liquidate(
address liquidator,
address borrower,
uint256 repayAmount,
address mTokenCollateral,
bool doTransfer
) internal {
require(borrower != liquidator, mToken_InvalidInput());
require(repayAmount > 0 && repayAmount != type(uint256).max, mToken_InvalidInput());
IOperatorDefender(operator).beforeMTokenLiquidate(address(this), mTokenCollateral, borrower, repayAmount);
require(
ImToken(mTokenCollateral).accrualBlockNumber() == _getBlockNumber(), mToken_CollateralBlockNumberNotValid()
);
/* Fail if repayBorrow fails */
uint256 actualRepayAmount = __repay(liquidator, borrower, repayAmount, doTransfer);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We calculate the number of collateral tokens that will be seized */
uint256 seizeTokens =
IOperator(operator).liquidateCalculateSeizeTokens(address(this), mTokenCollateral, actualRepayAmount);
/* Revert if borrower collateral token balance < seizeTokens */
require(ImToken(mTokenCollateral).balanceOf(borrower) >= seizeTokens, mToken_LiquidateSeizeTooMuch());
// If this is also the collateral, run _seize to avoid re-entrancy, otherwise make an external call
if (address(mTokenCollateral) == address(this)) {
_seize(address(this), liquidator, borrower, seizeTokens);
} else {
ImToken(mTokenCollateral).seize(liquidator, borrower, seizeTokens);
}
/* We emit a LiquidateBorrow event */
emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(mTokenCollateral), seizeTokens);
}
/**
* @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
* @param doTransfer If an actual transfer should be performed
*/
function __repay(address payer, address borrower, uint256 repayAmount, bool doTransfer) private returns (uint256) {
IOperatorDefender(operator).beforeMTokenRepay(address(this), borrower);
/* We fetch the amount the borrower owes, with accumulated interest */
uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);
/* If repayAmount == type(uint256).max , 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 mToken must handle variations between ERC-20 and ETH underlying.
* On success, the mToken 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 = doTransfer ? _doTransferIn(payer, repayAmountFinal) : repayAmountFinal;
initialExchangeRateMantissa += actualRepayAmount;
/*
* 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 Users borrow assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
*/
function __borrow(address payable borrower, uint256 borrowAmount, bool doTransfer) private {
IOperatorDefender(operator).beforeMTokenBorrow(address(this), borrower, borrowAmount);
require(_getCashPrior() >= borrowAmount, mToken_BorrowCashNotAvailable());
/*
* We calculate the new borrower and total borrow balances, failing on overflow:
* accountBorrowNew = accountBorrow + borrowAmount
* totalBorrowsNew = totalBorrows + borrowAmount
*/
uint256 accountBorrowsPrev = _borrowBalanceStored(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;
if (doTransfer) {
/*
* We invoke _doTransferOut for the borrower and the borrowAmount.
* Note: The mToken must handle variations between ERC-20 and ETH underlying.
* On success, the mToken borrowAmount less of cash.
* _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
_doTransferOut(borrower, borrowAmount);
}
initialExchangeRateMantissa -= borrowAmount;
/* We emit a Borrow event */
emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);
}
function __redeem(address payable redeemer, uint256 redeemTokensIn, uint256 redeemAmountIn, bool doTransfer)
private
{
require(redeemTokensIn == 0 || redeemAmountIn == 0, mToken_InvalidInput());
/* exchangeRate = invoke Exchange Rate Stored() */
Exp memory exchangeRate = Exp({mantissa: _exchangeRateStored()});
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;
}
if (redeemTokens == 0 && redeemAmount == 0) revert mToken_RedeemEmpty();
/* Fail if redeem not allowed */
IOperatorDefender(operator).beforeMTokenRedeem(address(this), redeemer, redeemTokens);
require(_getCashPrior() >= redeemAmount, mToken_RedeemCashNotAvailable());
/////////////////////////
// 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 mToken must handle variations between ERC-20 and ETH underlying.
* On success, the mToken has redeemAmount less of cash.
* _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
if (doTransfer) _doTransferOut(redeemer, redeemAmount);
initialExchangeRateMantissa -= redeemAmount;
/* We emit a Transfer event, and a Redeem event */
emit Transfer(redeemer, address(this), redeemTokens);
emit Redeem(redeemer, redeemAmount, redeemTokens);
}
/**
* @notice User supplies assets into the market and receives mTokens 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
* @param doTransfer If an actual transfer should be performed
*/
function __mint(address minter, uint256 mintAmount, bool doTransfer) private {
IOperatorDefender(operator).beforeMTokenMint(address(this), minter);
Exp memory exchangeRate = Exp({mantissa: _exchangeRateStored()});
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call `_doTransferIn` for the minter and the mintAmount.
* Note: The mToken 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 mToken holds an additional `actualMintAmount`
* of cash.
*/
uint256 actualMintAmount = doTransfer ? _doTransferIn(minter, mintAmount) : mintAmount;
initialExchangeRateMantissa += actualMintAmount;
/*
* We get the current exchange rate and calculate the number of mTokens to be minted:
* mintTokens = actualMintAmount / exchangeRate
*/
uint256 mintTokens = div_(actualMintAmount, exchangeRate);
// avoid exchangeRate manipulation
if (totalSupply == 0) {
totalSupply = 1000;
accountTokens[address(0)] = 1000;
mintTokens -= 1000;
}
/*
* We calculate the new total supply of mTokens 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 */
IOperatorDefender(operator).afterMTokenMint(address(this));
}
/**
* @notice Transfer `tokens` tokens from `src` to `dst` by `spender`
* @dev Called by both `transfer` and `transferFrom` internally
* @param spender The address of the account performing the transfer
* @param src The address of the source account
* @param dst The address of the destination account
* @param tokens The number of tokens to transfer
*/
function _transferTokens(address spender, address src, address dst, uint256 tokens) private {
IOperatorDefender(operator).beforeMTokenTransfer(address(this), src, dst, tokens);
require(src != dst, mToken_TransferNotValid());
/* 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 srcTokensNew = accountTokens[src] - tokens;
uint256 dstTokensNew = accountTokens[dst] + tokens;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
accountTokens[src] = srcTokensNew;
accountTokens[dst] = dstTokensNew;
/* Eat some of the allowance (if necessary) */
if (startingAllowance != type(uint256).max) {
transferAllowances[src][spender] = allowanceNew;
}
/* We emit a Transfer event */
emit Transfer(src, dst, tokens);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
/**
* @title IInterestRateModel
* @notice Interface for the interest rate contracts
*/
interface IInterestRateModel {
/// @notice Emitted when interest rate parameters are updated
/// @param baseRatePerBlock The base rate per block
/// @param multiplierPerBlock The multiplier per block for the interest rate slope
/// @param jumpMultiplierPerBlock The multiplier after hitting the kink
/// @param kink The utilization point where the jump multiplier is applied
event NewInterestParams(
uint256 baseRatePerBlock, uint256 multiplierPerBlock, uint256 jumpMultiplierPerBlock, uint256 kink
);
/**
* @notice Should return true
*/
function isInterestRateModel() external view returns (bool);
/**
* @notice The approximate number of blocks per year that is assumed by the interest rate model
* @return The number of blocks per year
*/
function blocksPerYear() external view returns (uint256);
/**
* @notice The multiplier of utilization rate that gives the slope of the interest rate
* @return The multiplier per block
*/
function multiplierPerBlock() external view returns (uint256);
/**
* @notice The base interest rate which is the y-intercept when utilization rate is 0
* @return The base rate per block
*/
function baseRatePerBlock() external view returns (uint256);
/**
* @notice The multiplierPerBlock after hitting a specified utilization point
* @return The jump multiplier per block
*/
function jumpMultiplierPerBlock() external view returns (uint256);
/**
* @notice The utilization point at which the jump multiplier is applied
* @return The utilization point (kink)
*/
function kink() external view returns (uint256);
/**
* @notice A name for user-friendliness, e.g. WBTC
* @return The name of the interest rate model
*/
function name() external view returns (string memory);
/**
* @notice Calculates the utilization rate of the market
* @param cash The total cash in the market
* @param borrows The total borrows in the market
* @param reserves The total reserves in the market
* @return The utilization rate as a mantissa between [0, 1e18]
*/
function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves) external pure returns (uint256);
/**
* @notice Returns the current borrow rate per block for the market
* @param cash The total cash in the market
* @param borrows The total borrows in the market
* @param reserves The total reserves in the market
* @return The current borrow rate per block, scaled by 1e18
*/
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external view returns (uint256);
/**
* @notice Returns the current supply rate per block for the market
* @param cash The total cash in the market
* @param borrows The total borrows in the market
* @param reserves The total reserves in the market
* @param reserveFactorMantissa The current reserve factor for the market
* @return The current supply rate per block, scaled by 1e18
*/
function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa)
external
view
returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
import {IRoles} from "./IRoles.sol";
import {ImTokenOperationTypes} from "./ImToken.sol";
interface IOperatorData {
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 MALDA
bool isMalded;
}
}
interface IOperatorDefender {
/**
* @notice Checks if the account should be allowed to transfer tokens in the given market
* @param mToken The market to verify the transfer against
* @param src The account which sources the tokens
* @param dst The account which receives the tokens
* @param transferTokens The number of mTokens to transfer
*/
function beforeMTokenTransfer(address mToken, address src, address dst, uint256 transferTokens) external;
/**
* @notice Checks if the account should be allowed to mint tokens in the given market
* @param mToken The market to verify the mint against
* @param minter The account which would get the minted tokens
*/
function beforeMTokenMint(address mToken, address minter) external;
/**
* @notice Validates mint and reverts on rejection. May emit logs.
* @param mToken Asset being minted
*/
function afterMTokenMint(address mToken) external view;
/**
* @notice Checks if the account should be allowed to redeem tokens in the given market
* @param mToken The market to verify the redeem against
* @param redeemer The account which would redeem the tokens
* @param redeemTokens The number of mTokens to exchange for the underlying asset in the market
*/
function beforeMTokenRedeem(address mToken, address redeemer, uint256 redeemTokens) external;
/**
* @notice Checks if the account should be allowed to borrow the underlying asset of the given market
* @param mToken The market to verify the borrow against
* @param borrower The account which would borrow the asset
* @param borrowAmount The amount of underlying the account would borrow
*/
function beforeMTokenBorrow(address mToken, address borrower, uint256 borrowAmount) external;
/**
* @notice Checks if the account should be allowed to repay a borrow in the given market
* @param mToken The market to verify the repay against
* @param borrower The account which would borrowed the asset
*/
function beforeMTokenRepay(address mToken, address borrower) external;
/**
* @notice Checks if the liquidation should be allowed to occur
* @param mTokenBorrowed Asset which was borrowed by the borrower
* @param mTokenCollateral Asset which was used as collateral and will be seized
* @param borrower The address of the borrower
* @param repayAmount The amount of underlying being repaid
*/
function beforeMTokenLiquidate(
address mTokenBorrowed,
address mTokenCollateral,
address borrower,
uint256 repayAmount
) external view;
/**
* @notice Checks if the seizing of assets should be allowed to occur
* @param mTokenCollateral Asset which was used as collateral and will be seized
* @param mTokenBorrowed Asset which was borrowed by the borrower
* @param liquidator The address repaying the borrow and seizing the collateral
* @param borrower The address of the borrower
*/
function beforeMTokenSeize(address mTokenCollateral, address mTokenBorrowed, address liquidator, address borrower)
external;
}
interface IOperator {
// ----------- VIEW ------------
/**
* @notice Should return true
*/
function isOperator() external view returns (bool);
/**
* @notice Returns if operation is paused
* @param mToken The mToken to check
* @param _type the operation type
*/
function isPaused(address mToken, ImTokenOperationTypes.OperationType _type) external view returns (bool);
/**
* @notice Roles manager
*/
function rolesOperator() external view returns (IRoles);
/**
* @notice Oracle which gives the price of any given asset
*/
function oracleOperator() external view returns (address);
/**
* @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow
*/
function closeFactorMantissa() external view returns (uint256);
/**
* @notice Multiplier representing the discount on collateral that a liquidator receives
*/
function liquidationIncentiveMantissa() external view returns (uint256);
/**
* @notice Returns the assets an account has entered
* @param _user The address of the account to pull assets for
* @return mTokens A dynamic list with the assets the account has entered
*/
function getAssetsIn(address _user) external view returns (address[] memory mTokens);
/**
* @notice A list of all markets
*/
function getAllMarkets() external view returns (address[] memory mTokens);
/**
* @notice Borrow caps enforced by borrowAllowed for each mToken address. Defaults to zero which corresponds to unlimited borrowing.
*/
function borrowCaps(address _mToken) external view returns (uint256);
/**
* @notice Supply caps enforced by supplyAllowed for each mToken address. Defaults to zero which corresponds to unlimited supplying.
*/
function supplyCaps(address _mToken) external view returns (uint256);
/**
* @notice Reward Distributor to markets supply and borrow (including protocol token)
*/
function rewardDistributor() external view returns (address);
/**
* @notice Returns whether the given account is entered in the given asset
* @param account The address of the account to check
* @param mToken The mToken to check
* @return True if the account is in the asset, otherwise false.
*/
function checkMembership(address account, address mToken) external view returns (bool);
/**
* @notice Determine the current account liquidity wrt collateral requirements
* @return account liquidity in excess of collateral requirements,
* account shortfall below collateral requirements)
*/
function getAccountLiquidity(address account) external view returns (uint256, uint256);
/**
* @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
* @param mTokenModify The market to hypothetically redeem/borrow in
* @param account The account to determine liquidity for
* @param redeemTokens The number of tokens to hypothetically redeem
* @param borrowAmount The amount of underlying to hypothetically borrow
* @return hypothetical account liquidity in excess of collateral requirements,
* hypothetical account shortfall below collateral requirements)
*/
function getHypotheticalAccountLiquidity(
address account,
address mTokenModify,
uint256 redeemTokens,
uint256 borrowAmount
) external view returns (uint256, uint256);
/**
* @notice Calculate number of tokens of collateral asset to seize given an underlying amount
* @dev Used in liquidation (called in mTokenBorrowed.liquidate)
* @param mTokenBorrowed The address of the borrowed mToken
* @param mTokenCollateral The address of the collateral mToken
* @param actualRepayAmount The amount of mTokenBorrowed underlying to convert into mTokenCollateral tokens
* @return number of mTokenCollateral tokens to be seized in a liquidation
*/
function liquidateCalculateSeizeTokens(address mTokenBorrowed, address mTokenCollateral, uint256 actualRepayAmount)
external
view
returns (uint256);
/**
* @notice Returns true if the given mToken market has been deprecated
* @dev All borrows in a deprecated mToken market can be immediately liquidated
* @param mToken The market to check if deprecated
*/
function isDeprecated(address mToken) external view returns (bool);
// ----------- ACTIONS ------------
/**
* @notice Set pause for a specific operation
* @param mToken The market token address
* @param _type The pause operation type
* @param state The pause operation status
*/
function setPaused(address mToken, ImTokenOperationTypes.OperationType _type, bool state) external;
/**
* @notice Add assets to be included in account liquidity calculation
* @param _mTokens The list of addresses of the mToken markets to be enabled
*/
function enterMarkets(address[] calldata _mTokens) external;
/**
* @notice Removes asset from sender's account liquidity calculation
* @dev Sender must not have an outstanding borrow balance in the asset,
* or be providing necessary collateral for an outstanding borrow.
* @param _mToken The address of the asset to be removed
*/
function exitMarket(address _mToken) external;
/**
* @notice Claim all the MALDA accrued by holder in all markets
* @param holder The address to claim MALDA for
*/
function claimMalda(address holder) external;
/**
* @notice Claim all the MALDA accrued by holder in the specified markets
* @param holder The address to claim MALDA for
* @param mTokens The list of markets to claim MALDA in
*/
function claimMalda(address holder, address[] memory mTokens) external;
/**
* @notice Claim all MALDA accrued by the holders
* @param holders The addresses to claim MALDA for
* @param mTokens The list of markets to claim MALDA in
* @param borrowers Whether or not to claim MALDA earned by borrowing
* @param suppliers Whether or not to claim MALDA earned by supplying
*/
function claimMalda(address[] memory holders, address[] memory mTokens, bool borrowers, bool suppliers) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
// interfaces
import {IRoles} from "src/interfaces/IRoles.sol";
import {IOperator} from "src/interfaces/IOperator.sol";
import {IInterestRateModel} from "src/interfaces/IInterestRateModel.sol";
import {mTokenStorage} from "./mTokenStorage.sol";
abstract contract mTokenConfiguration is mTokenStorage {
// ----------- MODIFIERS ------------
modifier onlyAdmin() {
require(msg.sender == admin, mToken_OnlyAdmin());
_;
}
// ----------- OWNER ------------
/**
* @notice Sets a new Operator for the market
* @dev Admin function to set a new operator
*/
function setOperator(address _operator) external onlyAdmin {
_setOperator(_operator);
}
/**
* @notice Sets a new Operator for the market
* @dev Admin function to set a new operator
*/
function setRolesOperator(address _roles) external onlyAdmin {
require(_roles != address(0), mToken_InvalidInput());
emit NewRolesOperator(address(rolesOperator), _roles);
rolesOperator = IRoles(_roles);
}
/**
* @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh
* @dev Admin function to accrue interest and update the interest rate model
* @param newInterestRateModel the new interest rate model to use
*/
function setInterestRateModel(address newInterestRateModel) external onlyAdmin {
_accrueInterest();
// emits interest-rate-model-update-specific logs on errors, so we don't need to.
return _setInterestRateModel(newInterestRateModel);
}
function setBorrowRateMaxMantissa(uint256 maxMantissa) external onlyAdmin {
uint256 _oldVal = borrowRateMaxMantissa;
borrowRateMaxMantissa = maxMantissa;
// validate new mantissa
_accrueInterest();
emit NewBorrowRateMaxMantissa(_oldVal, maxMantissa);
}
/**
* @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
* @dev Admin function to accrue interest and set a new reserve factor
*/
function setReserveFactor(uint256 newReserveFactorMantissa) external onlyAdmin {
_accrueInterest();
require(newReserveFactorMantissa <= RESERVE_FACTOR_MAX_MANTISSA, mToken_ReserveFactorTooHigh());
emit NewReserveFactor(reserveFactorMantissa, newReserveFactorMantissa);
reserveFactorMantissa = newReserveFactorMantissa;
}
/**
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @param newPendingAdmin New pending admin.
*/
function setPendingAdmin(address payable newPendingAdmin) external onlyAdmin {
// Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
emit NewPendingAdmin(pendingAdmin, newPendingAdmin);
// Store pendingAdmin with value newPendingAdmin
pendingAdmin = newPendingAdmin;
}
/**
* @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
* @dev Admin function for pending admin to accept role and update admin
*/
function acceptAdmin() external {
// Check caller is pendingAdmin
require(msg.sender == pendingAdmin, mToken_OnlyAdmin());
// 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);
}
// ----------- INTERNAL ------------
/**
* @notice updates the interest rate model (*requires fresh interest accrual)
* @dev Admin function to update the interest rate model
* @param newInterestRateModel the new interest rate model to use
*/
function _setInterestRateModel(address newInterestRateModel) internal onlyAdmin {
// Ensure invoke newInterestRateModel.isInterestRateModel() returns true
require(IInterestRateModel(newInterestRateModel).isInterestRateModel(), mToken_MarketMethodNotValid());
emit NewMarketInterestRateModel(interestRateModel, newInterestRateModel);
interestRateModel = newInterestRateModel;
}
function _setOperator(address _operator) internal {
require(IOperator(_operator).isOperator(), mToken_MarketMethodNotValid());
emit NewOperator(operator, _operator);
operator = _operator;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
// interfaces
import {IRoles} from "src/interfaces/IRoles.sol";
import {ImToken, ImTokenMinimal} from "src/interfaces/ImToken.sol";
import {IInterestRateModel} from "src/interfaces/IInterestRateModel.sol";
// contracts
import {ExponentialNoError} from "src/utils/ExponentialNoError.sol";
abstract contract mTokenStorage is ImToken, ExponentialNoError {
// ----------- ACCESS STORAGE ------------
/**
* @inheritdoc ImToken
*/
address payable public admin;
/**
* @inheritdoc ImToken
*/
address payable public pendingAdmin;
/**
* @inheritdoc ImToken
*/
address public operator;
/**
* @inheritdoc ImToken
*/
IRoles public rolesOperator;
// ----------- TOKENS STORAGE ------------
/**
* @inheritdoc ImTokenMinimal
*/
string public name;
/**
* @inheritdoc ImTokenMinimal
*/
string public symbol;
/**
* @inheritdoc ImTokenMinimal
*/
uint8 public decimals;
// ----------- MARKET STORAGE ------------
/**
* @inheritdoc ImToken
*/
address public interestRateModel;
/**
* @inheritdoc ImToken
*/
uint256 public reserveFactorMantissa;
/**
* @inheritdoc ImToken
*/
uint256 public accrualBlockNumber;
/**
* @inheritdoc ImToken
*/
uint256 public borrowIndex;
/**
* @inheritdoc ImToken
*/
uint256 public totalBorrows;
/**
* @inheritdoc ImToken
*/
uint256 public totalReserves;
/**
* @inheritdoc ImTokenMinimal
*/
uint256 public totalSupply;
/**
* @notice Maximum borrow rate that can ever be applied (.0005% / block)
*/
uint256 public borrowRateMaxMantissa = 0.00004e16;
/**
* @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;
// 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 Initial exchange rate used when minting the first mTokens (used when totalSupply = 0)
*/
uint256 internal initialExchangeRateMantissa;
/**
* @notice Maximum fraction of interest that can be set aside for reserves
*/
uint256 internal constant RESERVE_FACTOR_MAX_MANTISSA = 1e18;
/**
* @notice Share of seized collateral that is added to reserves
*/
uint256 internal constant PROTOCOL_SEIZE_SHARE_MANTISSA = 2.8e16; //2.8%
// ----------- ERRORS ------------
error mToken_OnlyAdmin();
error mToken_RedeemEmpty();
error mToken_InvalidInput();
error mToken_OnlyAdminOrRole();
error mToken_TransferNotValid();
error mToken_BorrowRateTooHigh();
error mToken_AlreadyInitialized();
error mToken_ReserveFactorTooHigh();
error mToken_BlockNumberNotValid();
error mToken_ExchangeRateNotValid();
error mToken_MarketMethodNotValid();
error mToken_LiquidateSeizeTooMuch();
error mToken_RedeemCashNotAvailable();
error mToken_BorrowCashNotAvailable();
error mToken_ReserveCashNotAvailable();
error mToken_RedeemTransferOutNotPossible();
error mToken_CollateralBlockNumberNotValid();
// ----------- ACCESS EVENTS ------------
/**
* @notice Event emitted when rolesOperator is changed
*/
event NewRolesOperator(address indexed oldRoles, address indexed newRoles);
/**
* @notice Event emitted when pendingAdmin is changed
*/
event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);
/**
* @notice Event emitted when pendingAdmin is accepted, which means admin is updated
*/
event NewAdmin(address indexed oldAdmin, address indexed newAdmin);
/**
* @notice Event emitted when Operator is changed
*/
event NewOperator(address indexed oldOperator, address indexed newOperator);
// ----------- TOKENS EVENTS ------------
/**
* @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);
// ----------- MARKETS 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 indexed minter, uint256 mintAmount, uint256 mintTokens);
/**
* @notice Event emitted when tokens are redeemed
*/
event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens);
/**
* @notice Event emitted when underlying is borrowed
*/
event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);
/**
* @notice Event emitted when a borrow is repaid
*/
event RepayBorrow(
address indexed payer,
address indexed borrower,
uint256 repayAmount,
uint256 accountBorrows,
uint256 totalBorrows
);
/**
* @notice Event emitted when a borrow is liquidated
*/
event LiquidateBorrow(
address indexed liquidator,
address indexed borrower,
uint256 repayAmount,
address indexed mTokenCollateral,
uint256 seizeTokens
);
/**
* @notice Event emitted when interestRateModel is changed
*/
event NewMarketInterestRateModel(address indexed oldInterestRateModel, address indexed 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 indexed benefactor, uint256 addAmount, uint256 newTotalReserves);
/**
* @notice Event emitted when the reserves are reduced
*/
event ReservesReduced(address indexed admin, uint256 reduceAmount, uint256 newTotalReserves);
/**
* @notice Event emitted when the borrow max mantissa is updated
*/
event NewBorrowRateMaxMantissa(uint256 oldVal, uint256 maxMantissa);
// ----------- VIRTUAL ------------
/**
* @inheritdoc ImToken
*/
function accrueInterest() external virtual {
_accrueInterest();
}
/**
* @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.timestamp;
}
/**
* @notice Calculates the exchange rate from the underlying to the MToken
* @dev This function does not accrue interest before calculating the exchange rate
* Can generate issues if inflated by an attacker when market is created
* Solution: use 0 collateral factor initially
* @return calculated exchange rate scaled by 1e18
*/
function _exchangeRateStored() 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 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;
// ----------- NON-VIRTUAL ------------
function _accrueInterest() internal {
/* Remember the initial block number */
uint256 currentBlockNumber = _getBlockNumber();
uint256 accrualBlockNumberPrior = accrualBlockNumber;
/* Short-circuit accumulating 0 interest */
if (accrualBlockNumberPrior == currentBlockNumber) return;
/* 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 =
IInterestRateModel(interestRateModel).getBorrowRate(cashPrior, borrowsPrior, reservesPrior);
require(borrowRateMantissa <= borrowRateMaxMantissa, mToken_BorrowRateTooHigh());
/* 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);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
/**
* @title Exponential module for storing fixed-precision decimals
* @author Compound
* @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
* Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
* `Exp({mantissa: 5100000000000000000})`.
*/
abstract 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)});
}
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"risc0/=lib/risc0-ethereum/contracts/src/",
"forge-std/=lib/forge-std/src/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"risc0-ethereum/=lib/risc0-ethereum/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
],
"optimizer": {
"enabled": true,
"runs": 2499
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": false,
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZkVerifier_ImageNotValid","type":"error"},{"inputs":[],"name":"ZkVerifier_InputNotValid","type":"error"},{"inputs":[],"name":"ZkVerifier_VerifierNotSet","type":"error"},{"inputs":[],"name":"mErc20Host_AddressNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_AmountNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_AmountTooBig","type":"error"},{"inputs":[],"name":"mErc20Host_CallerNotAllowed","type":"error"},{"inputs":[],"name":"mErc20Host_ChainNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_DstChainNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_JournalNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_LengthMismatch","type":"error"},{"inputs":[],"name":"mErc20Host_NotRebalancer","type":"error"},{"inputs":[],"name":"mErc20Host_ProofGenerationInputNotValid","type":"error"},{"inputs":[],"name":"mErc20_TokenNotValid","type":"error"},{"inputs":[],"name":"mToken_AlreadyInitialized","type":"error"},{"inputs":[],"name":"mToken_BlockNumberNotValid","type":"error"},{"inputs":[],"name":"mToken_BorrowCashNotAvailable","type":"error"},{"inputs":[],"name":"mToken_BorrowRateTooHigh","type":"error"},{"inputs":[],"name":"mToken_CollateralBlockNumberNotValid","type":"error"},{"inputs":[],"name":"mToken_ExchangeRateNotValid","type":"error"},{"inputs":[],"name":"mToken_InvalidInput","type":"error"},{"inputs":[],"name":"mToken_LiquidateSeizeTooMuch","type":"error"},{"inputs":[],"name":"mToken_MarketMethodNotValid","type":"error"},{"inputs":[],"name":"mToken_OnlyAdmin","type":"error"},{"inputs":[],"name":"mToken_OnlyAdminOrRole","type":"error"},{"inputs":[],"name":"mToken_RedeemCashNotAvailable","type":"error"},{"inputs":[],"name":"mToken_RedeemEmpty","type":"error"},{"inputs":[],"name":"mToken_RedeemTransferOutNotPossible","type":"error"},{"inputs":[],"name":"mToken_ReserveCashNotAvailable","type":"error"},{"inputs":[],"name":"mToken_ReserveFactorTooHigh","type":"error"},{"inputs":[],"name":"mToken_TransferNotValid","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":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AllowedCallerUpdated","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":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_imageId","type":"bytes32"}],"name":"ImageSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"mTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"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":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldVal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxMantissa","type":"uint256"}],"name":"NewBorrowRateMaxMantissa","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldInterestRateModel","type":"address"},{"indexed":true,"internalType":"address","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOperator","type":"address"},{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"NewOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","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":true,"internalType":"address","name":"oldRoles","type":"address"},{"indexed":true,"internalType":"address","name":"newRoles","type":"address"}],"name":"NewRolesOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"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":true,"internalType":"address","name":"payer","type":"address"},{"indexed":true,"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":true,"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":true,"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":true,"internalType":"address","name":"oldVerifier","type":"address"},{"indexed":true,"internalType":"address","name":"newVerifier","type":"address"}],"name":"VerifierSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_BorrowExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint32","name":"dstChainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_BorrowOnExternsionChain","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"mErc20Host_ChainStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":false,"internalType":"address","name":"userToLiquidate","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint32","name":"srcChainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_LiquidateExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_MintExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_RepayExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_WithdrawExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint32","name":"dstChainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_WithdrawOnExtensionChain","type":"event"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"address","name":"","type":"address"}],"name":"accAmountInPerChain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"address","name":"","type":"address"}],"name":"accAmountOutPerChain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"addReserves","outputs":[],"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":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowedCallers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"allowedChains","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[],"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":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"dstChainId","type":"uint32"}],"name":"borrowOnExtension","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowRateMaxMantissa","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":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegateMaldaLikeTo","outputs":[],"stateMutability":"nonpayable","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":"uint256","name":"amount","type":"uint256"}],"name":"extractForRebalancing","outputs":[],"stateMutability":"nonpayable","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"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"dstId","type":"uint32"}],"name":"getProofData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imageId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"address","name":"operator_","type":"address"},{"internalType":"address","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":"address","name":"operator_","type":"address"},{"internalType":"address","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":"address payable","name":"admin_","type":"address"},{"internalType":"address","name":"zkVerifier_","type":"address"},{"internalType":"address","name":"roles_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator_","type":"address"},{"internalType":"address","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":"_verifier","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"caller","type":"address"}],"name":"isCallerAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"address","name":"mTokenCollateral","type":"address"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"journalData","type":"bytes"},{"internalType":"bytes","name":"seal","type":"bytes"},{"internalType":"address[]","name":"userToLiquidate","type":"address[]"},{"internalType":"uint256[]","name":"liquidateAmount","type":"uint256[]"},{"internalType":"address[]","name":"collateral","type":"address[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"liquidateExternal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"journalData","type":"bytes"},{"internalType":"bytes","name":"seal","type":"bytes"},{"internalType":"uint256[]","name":"mintAmount","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mintExternal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"address","name":"operator_","type":"address"},{"internalType":"address","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":"address payable","name":"admin_","type":"address"}],"name":"proxyInitialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"reduceReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBehalf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"journalData","type":"bytes"},{"internalType":"bytes","name":"seal","type":"bytes"},{"internalType":"uint256[]","name":"repayAmount","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"repayExternal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rolesOperator","outputs":[{"internalType":"contract IRoles","name":"","type":"address"}],"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":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxMantissa","type":"uint256"}],"name":"setBorrowRateMaxMantissa","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_imageId","type":"bytes32"}],"name":"setImageId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newInterestRateModel","type":"address"}],"name":"setInterestRateModel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"setPendingAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"setReserveFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_roles","type":"address"}],"name":"setRolesOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setTotalUnderlying","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_risc0Verifier","type":"address"}],"name":"setVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","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":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"updateAllowedCallerStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_chainId","type":"uint32"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"updateAllowedChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IRiscZeroVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"dstChainId","type":"uint32"}],"name":"withdrawOnExtension","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6080604052645d21dba000600d55348015601857600080fd5b506001601255615d538061002d6000396000f3fe608060405234801561001057600080fd5b50600436106105145760003560e01c80637f7b13d4116102a1578063b2a02ff11161016b578063dd62ed3e116100e3578063f851a44011610097578063f8f9da281161007c578063f8f9da2814610ad6578063fe62355f14610ade578063ffcaadfe14610b1a57600080fd5b8063f851a44014610ab0578063f89416ee14610ac357600080fd5b8063ee27a2f2116100c8578063ee27a2f214610a86578063ef3f7dd514610a8f578063f3fdb15a14610a9857600080fd5b8063dd62ed3e14610a3a578063e67218cd14610a7357600080fd5b8063c37f68e21161013a578063c5ebeaec1161011f578063c5ebeaec14610a01578063d87f7cad14610a14578063db006a7514610a2757600080fd5b8063c37f68e2146109c0578063c4d66de8146109ee57600080fd5b8063b2a02ff11461097f578063b3ab15fb14610992578063b57a4a72146109a5578063bd6d894d146109b857600080fd5b806399c4383711610219578063a66b92ab116101cd578063a9059cbb116101b2578063a9059cbb1461095b578063aa5af0fd1461096e578063ae9d70b01461097757600080fd5b8063a66b92ab14610940578063a6afed951461095357600080fd5b80639d9339b3116101fe5780639d9339b314610907578063a0712d681461091a578063a4777a7a1461092d57600080fd5b806399c43837146108e157806399d8c1b4146108f457600080fd5b80638f840ddd1161027057806391a0d4381161025557806391a0d438146108b357806395d89b41146108c657806395dd9193146108ce57600080fd5b80638f840ddd14610897578063901129c2146108a057600080fd5b80637f7b13d41461083b578063852a12e31461084e5780638bcd4016146108615780638da735271461087457600080fd5b80633936c445116103e25780635437988d1161035a5780636c540baf1161030e57806370a08231116102f357806370a08231146107f757806373acee98146108205780637821a5141461082857600080fd5b80636c540baf146107db5780636f307dc3146107e457600080fd5b80635bdcecb71161033f5780635bdcecb714610793578063600bb376146107a6578063699cd5e2146107d457600080fd5b80635437988d1461076d578063570ca7351461078057600080fd5b80634914c008116103b15780634f2be4ce116103965780634f2be4ce1461071c5780634fbdc6ae1461072f5780634fecab701461075a57600080fd5b80634914c008146106f65780634dd18bf51461070957600080fd5b80633936c445146106a75780633af9e669146106d25780633b1d21a2146106e557806347bd3718146106ed57600080fd5b8063182df0f51161049057806326782247116104445780632b7ac3f3116104295780632b7ac3f314610662578063313ce56714610675578063371fd8e61461069457600080fd5b80632678224714610624578063277cd86a1461064f57600080fd5b80631be19560116104755780631be19560146105eb5780631c446983146105fe57806323b872dd1461061157600080fd5b8063182df0f5146105d05780631a31d465146105d857600080fd5b8063095ea7b3116104e7578063173b9904116104cc578063173b99041461059d57806317bfdfbc146105b457806318160ddd146105c757600080fd5b8063095ea7b3146105725780630e18b6811461059557600080fd5b806306fdde031461051957806307d923e91461053757806307e279591461054a57806308fee2631461055f575b600080fd5b610521610b2d565b60405161052e919061512e565b60405180910390f35b610521610545366004615175565b610bbb565b61055d6105583660046151aa565b610c7e565b005b61055d61056d366004615251565b610ee1565b61058561058036600461530b565b611063565b604051901515815260200161052e565b61055d6110d1565b6105a660075481565b60405190815260200161052e565b6105a66105c2366004615337565b6111a0565b6105a6600c5481565b6105a66111cc565b61055d6105e6366004615428565b6111db565b61055d6105f9366004615337565b611296565b61055d61060c3660046151aa565b611395565b61058561061f3660046154e3565b61144b565b600154610637906001600160a01b031681565b6040516001600160a01b03909116815260200161052e565b61055d61065d366004615524565b611475565b601454610637906001600160a01b031681565b6006546106829060ff1681565b60405160ff909116815260200161052e565b61055d6106a23660046151aa565b61151e565b6105a66106b5366004615547565b601760209081526000928352604080842090915290825290205481565b6105a66106e0366004615337565b611529565b6105a6611568565b6105a6600a5481565b61055d61070436600461557e565b611573565b61055d610717366004615337565b611586565b61055d61072a3660046155ce565b61161a565b6105a661073d366004615547565b601660209081526000928352604080842090915290825290205481565b600354610637906001600160a01b031681565b61055d61077b366004615337565b611686565b600254610637906001600160a01b031681565b61055d6107a136600461530b565b6116ba565b6105856107b43660046155fc565b601860209081526000928352604080842090915290825290205460ff1681565b6001610585565b6105a660085481565b601354610637906001600160a01b031681565b6105a6610805366004615337565b6001600160a01b03166000908152600f602052604090205490565b6105a66116c6565b61055d6108363660046151aa565b6116e9565b61055d61084936600461561a565b6116f2565b61055d61085c3660046151aa565b611885565b61055d61086f366004615337565b611891565b610585610882366004615703565b60196020526000908152604090205460ff1681565b6105a6600b5481565b61055d6108ae3660046151aa565b6118cd565b61055d6108c13660046151aa565b611901565b610521611931565b6105a66108dc366004615337565b61193e565b61055d6108ef36600461571e565b611949565b61055d61090236600461573a565b611ad1565b61055d610915366004615251565b611bd8565b61055d6109283660046151aa565b611d4e565b61055d61093b3660046157e1565b611d5a565b61055d61094e366004615524565b611f6e565b61055d61200f565b61058561096936600461530b565b612019565b6105a660095481565b6105a661203c565b61055d61098d3660046154e3565b6120db565b61055d6109a0366004615337565b6120f9565b61055d6109b33660046158fa565b61212d565b6105a6612191565b6109d36109ce366004615337565b6121b7565b6040805193845260208401929092529082015260600161052e565b61055d6109fc366004615337565b6121f2565b61055d610a0f3660046151aa565b61238e565b61055d610a22366004615337565b61239a565b61055d610a353660046151aa565b612440565b6105a6610a483660046155fc565b6001600160a01b03918216600090815260106020908152604080832093909416825291909152205490565b61055d610a813660046151aa565b61244c565b6105a6600d5481565b6105a660155481565b6006546106379061010090046001600160a01b031681565b600054610637906001600160a01b031681565b61055d610ad1366004615337565b6124c4565b6105a661257f565b610585610aec3660046155fc565b6001600160a01b03918216600090815260186020908152604080832093909416825291909152205460ff1690565b61055d610b283660046151aa565b6125d9565b60048054610b3a906159ca565b80601f0160208091040260200160405190810160405280929190818152602001828054610b66906159ca565b8015610bb35780601f10610b8857610100808354040283529160200191610bb3565b820191906000526020600020905b815481529060010190602001808311610b9657829003601f168201915b505050505081565b63ffffffff811660008181526016602090815260408083206001600160a01b03871680855290835281842054948452601783528184209084528252918290205482517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088811b82169483019490945230841b166034820152604881019490945260688401526001600160e01b03194660e090811b8216608886015285901b16608c840152815180840360700181526090909301909152905b90505b92915050565b610c86612722565b6000546001600160a01b0316331480610d975750600354604080517fd71c72e000000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c913391849163d71c72e09160048083019260209291908290030181865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d979190615a1d565b610dcd576040517f90b0f0f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd5612765565b80610ddf60115490565b1015610e17576040517f72d33f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54811115610e53576040517f72d33f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081600b54610e639190615a50565b600b8190559050610e74338361294a565b8160116000828254610e869190615a50565b909155505060005460408051848152602081018490526001600160a01b03909216917f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e910160405180910390a250610ede6001601255565b50565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015610f34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f589190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610fa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc59190615a1d565b610fd557610fd587878787612961565b6000610fe387890189615a63565b8051909150838114611008576040516302e262f160e01b815260040160405180910390fd5b60005b818110156110575761104f83828151811061102857611028615b49565b602002602001015187878481811061104257611042615b49565b90506020020135866129aa565b60010161100b565b50505050505050505050565b3360008181526010602090815260408083206001600160a01b03871680855292528083208590555191929182907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906110bf9087815260200190565b60405180910390a35060019392505050565b6001546001600160a01b031633146110fc5760405163303676d360e01b815260040160405180910390fd5b60008054600180546001600160a01b0380821673ffffffffffffffffffffffffffffffffffffffff19808616821787559092169092556040519190921692829184917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc91a36001546040516001600160a01b03918216918316907fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a990600090a35050565b60006111aa612722565b6111b2612765565b6111bb82612b7a565b90506111c76001601255565b919050565b60006111d6612bce565b905090565b6111e9868686868686611ad1565b6013805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038916908117909155604080517f18160ddd00000000000000000000000000000000000000000000000000000000815290516318160ddd916004808201926020929091908290030181865afa158015611268573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128c9190615a04565b5050505050505050565b6000546001600160a01b031633146112c15760405163303676d360e01b815260040160405180910390fd5b6013546001600160a01b0390811690821603611309576040517f9e14909700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113749190615a04565b600054909150611391906001600160a01b03848116911683612c37565b5050565b6000546001600160a01b031633146113c05760405163303676d360e01b815260040160405180910390fd5b6113c8612765565b670de0b6b3a764000081111561140a576040517fea1eeb4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075460408051918252602082018390527faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460910160405180910390a1600755565b6000611455612722565b61146133858585612cab565b50600161146e6001601255565b9392505050565b600082116114965760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff81166000908152601760209081526040808320338452909152812080548492906114c6908490615b5f565b909155506114d8905033836000612edc565b6040805163ffffffff831681526020810184905233917fffb02ed8426abe4f31f431f9f4c6075fcf5916fb1ba83041a0979073b27a3d7191015b60405180910390a25050565b610ede816001612ef9565b600080604051806020016040528061153f612191565b90526001600160a01b0384166000908152600f602052604090205490915061146e908290612f20565b60006111d660115490565b611581338484846001612f38565b505050565b6000546001600160a01b031633146115b15760405163303676d360e01b815260040160405180910390fd5b6001546040516001600160a01b038084169216907fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a990600090a36001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b3360008181526018602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fb2cc4dde7f9044ba1999f7843e2f9cd1e4ce506f8cc2e16de26ce982bf113fa6910160405180910390a35050565b6000546001600160a01b031633146116b15760405163303676d360e01b815260040160405180910390fd5b610ede81612fb2565b6113918282600161305b565b60006116d0612722565b6116d8612765565b50600a546116e66001601255565b90565b610ede81613082565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561173d5750825b905060008267ffffffffffffffff16600114801561175a5750303b155b905081158015611768575080155b1561179f576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156117d357845468ff00000000000000001916680100000000000000001785555b6117e38f8f8f8f8f8f8f8f61212d565b6117ec876121f2565b600380546001600160a01b0380891673ffffffffffffffffffffffffffffffffffffffff199283161790925560008054928b1692909116919091179055831561187457845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050505050565b610ede33826001612edc565b6000546001600160a01b031633146118bc5760405163303676d360e01b815260040160405180910390fd5b6118c4612765565b610ede81613115565b6000546001600160a01b031633146118f85760405163303676d360e01b815260040160405180910390fd5b610ede81613256565b6000546001600160a01b0316331461192c5760405163303676d360e01b815260040160405180910390fd5b601155565b60058054610b3a906159ca565b6000610c7882612b7a565b6000546001600160a01b03163314801590611a5e5750600354604080517fe048241300000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c913391849163e04824139160048083019260209291908290030181865afa1580156119cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ef9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5c9190615a1d565b155b15611a7c5760405163271bd90b60e01b815260040160405180910390fd5b63ffffffff8216600081815260196020908152604091829020805460ff191685151590811790915591519182527f3615c8513bc6436318086c0663e19b0334871c8c175a33b9340c3ecab97137cc9101611512565b6000546001600160a01b03163314611afc5760405163303676d360e01b815260040160405180910390fd5b600854158015611b0c5750600954155b611b42576040517fc99314d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008411611b7c576040517fa08b42d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011849055611b8a866132c5565b42600855670de0b6b3a7640000600955611ba385613115565b6004611baf8482615bb9565b506005611bbc8382615bb9565b506006805460ff191660ff929092169190911790555050505050565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015611c2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611c98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbc9190615a1d565b611ccc57611ccc87878787612961565b6000611cda87890189615a63565b8051909150838114611cff576040516302e262f160e01b815260040160405180910390fd5b60005b8181101561105757611d46838281518110611d1f57611d1f615b49565b6020026020010151878784818110611d3957611d39615b49565b90506020020135866133c6565b600101611d02565b610ede33826001613588565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015611dad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd19190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611e1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e3e9190615a1d565b611e4e57611e4e8b8b8b8b612961565b6000611e5c8b8d018d615a63565b8051909150858114611e81576040516302e262f160e01b815260040160405180910390fd5b808814611ea1576040516302e262f160e01b815260040160405180910390fd5b808414611ec1576040516302e262f160e01b815260040160405180910390fd5b60005b81811015611f5e57611f56838281518110611ee157611ee1615b49565b60200260200101518b8b84818110611efb57611efb615b49565b9050602002016020810190611f109190615337565b8a8a85818110611f2257611f22615b49565b90506020020135898986818110611f3b57611f3b615b49565b9050602002016020810190611f509190615337565b886135a3565b600101611ec4565b5050505050505050505050505050565b60008211611f8f5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff8116600090815260176020908152604080832033845290915281208054849290611fbf908490615b5f565b90915550611fd19050338360006137e9565b6040805163ffffffff831681526020810184905233917febeabb72629991c0657c42b5db17113f1eb371cca1f8f30ee73c9f66232c2a629101611512565b612017612765565b565b6000612023612722565b61202f33338585612cab565b506001610c786001601255565b6006546000906001600160a01b036101009091041663b816881661205f60115490565b600a54600b546007546040516001600160e01b031960e087901b16815260048101949094526024840192909252604483015260648201526084015b602060405180830381865afa1580156120b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d69190615a04565b6120e3612722565b6120ef33848484613804565b6115816001601255565b6000546001600160a01b031633146121245760405163303676d360e01b815260040160405180910390fd5b610ede816132c5565b6000805473ffffffffffffffffffffffffffffffffffffffff19163317905561215b888888888888886111db565b6000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905550505050505050565b600061219b612722565b6121a3612765565b6121ab612bce565b90506116e66001601255565b6001600160a01b0381166000908152600f6020526040812054819081906121dd85612b7a565b6121e5612bce565b9250925092509193909250565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561223d5750825b905060008267ffffffffffffffff16600114801561225a5750303b155b905081158015612268575080155b1561229f576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156122d357845468ff00000000000000001916680100000000000000001785555b6001600160a01b038616612313576040517f2756006b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038816179055831561238657845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b610ede338260016137e9565b6000546001600160a01b031633146123c55760405163303676d360e01b815260040160405180910390fd5b6013546040517f5c19a95c0000000000000000000000000000000000000000000000000000000081526001600160a01b03838116600483015290911690635c19a95c90602401600060405180830381600087803b15801561242557600080fd5b505af1158015612439573d6000803e3d6000fd5b5050505050565b610ede33826001613a7b565b6000546001600160a01b031633146124775760405163303676d360e01b815260040160405180910390fd5b600d805490829055612487612765565b60408051828152602081018490527f3ae66e204f1f2ae1bf6ea9ddb0331246f344118a48794bc9351cc730a0486658910160405180910390a15050565b6000546001600160a01b031633146124ef5760405163303676d360e01b815260040160405180910390fd5b6001600160a01b0381166125165760405163bca3ab8960e01b815260040160405180910390fd5b6003546040516001600160a01b038084169216907f9859cd0a756b5f08366068b791448fb837581d3b8afc097914d88edbc7bff2a390600090a36003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6006546000906001600160a01b03610100909104166315f240536125a260115490565b600a54600b546040516001600160e01b031960e086901b16815260048101939093526024830191909152604482015260640161209a565b600354604080517f9e106dc700000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c9133918491639e106dc79160048083019260209291908290030181865afa158015612645573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126699190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156126b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d69190615a1d565b61270b576040517ea1222300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601354610ede906001600160a01b03163383612c37565b60026012540361275e576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002601255565b6008544290818103612775575050565b600061278060115490565b600a54600b546009546006546040517f15f2405300000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905294955092939192909160009161010090046001600160a01b0316906315f2405390606401602060405180830381865afa158015612808573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061282c9190615a04565b9050600d5481111561286a576040517f8179ac1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006128768789615a50565b9050600061289260405180602001604052808581525083613a98565b905060006128a08288612f20565b905060006128ae8883615b5f565b905060006128cd6040518060200160405280600754815250848a613ac9565b905060006128dc85898a613ac9565b60088e90556009819055600a849055600b839055604080518d815260208101879052908101829052606081018590529091507f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049060800160405180910390a150505050505050505050505050565b601354611391906001600160a01b03168383612c37565b82612998576040517fc39550bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6129a484848484613aea565b50505050565b60008060008060006129bb88613afe565b95509550509450945094508495506129d33386613b90565b4663ffffffff168163ffffffff16146129ff57604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b0384163014612a285760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff16612a5e576040516357e05e4960e01b815260040160405180910390fd5b60008711612a7f5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b0389168452909152902054612ab39084615a50565b871115612ad3576040516361ee36eb60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b038916845290915281208054899290612b0c908490615b5f565b90915550612b1e90508688600061305b565b6040805163ffffffff84168152602081018990526001600160a01b03808916929088169133917fe2cce63addd46173601de3603e8e65733e0aeb61b438aba8c8b139fae31a673291015b60405180910390a45050505050505050565b6001600160a01b0381166000908152600e6020526040812080548203612ba35750600092915050565b6009548154600091612bb491615c78565b9050816001015481612bc69190615c8f565b949350505050565b600c54600090808203612be357505060115490565b6000612bee60115490565b90506000600b54600a5483612c039190615b5f565b612c0d9190615a50565b9050600083612c24670de0b6b3a764000084615c78565b612c2e9190615c8f565b95945050505050565b6040516001600160a01b0383811660248301526044820183905261158191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613df4565b6002546040517f17bf120e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038581166024830152848116604483015260648201849052909116906317bf120e90608401600060405180830381600087803b158015612d2057600080fd5b505af1158015612d34573d6000803e3d6000fd5b50505050816001600160a01b0316836001600160a01b031603612d83576040517f98a79b5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000836001600160a01b0316856001600160a01b031603612da75750600019612dcf565b506001600160a01b038084166000908152601060209081526040808320938816835292905220545b6000612ddb8383615a50565b6001600160a01b0386166000908152600f602052604081205491925090612e03908590615a50565b6001600160a01b0386166000908152600f602052604081205491925090612e2b908690615b5f565b6001600160a01b038089166000908152600f602052604080822086905591891681522081905590506000198414612e85576001600160a01b038088166000908152601060209081526040808320938c168352929052208390555b856001600160a01b0316876001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef87604051612eca91815260200190565b60405180910390a35050505050505050565b612ee4612722565b612eec612765565b6120ef8360008484613e75565b612f01612722565b612f09612765565b612f15333384846140ef565b506113916001601255565b600080612f2d8484613a98565b9050612bc681614261565b612f40612722565b612f48612765565b816001600160a01b031663a6afed956040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612f8357600080fd5b505af1158015612f97573d6000803e3d6000fd5b50505050612fa88585858585614279565b6124396001601255565b6001600160a01b038116612ff2576040517f2756006b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014546040516001600160a01b038084169216907fab81081e8fd2866d32d2dd90fdfa53fa9e0aa3d7eed73c684b6dcc4e2c0369fd90600090a36014805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b613063612722565b61306b612765565b613077338484846140ef565b506115816001601255565b61308a612722565b613092612765565b60008061309f3384614662565b905080601160008282546130b39190615b5f565b9091555050600b546130c6908290615b5f565b600b819055604080518381526020810183905291935033917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a25050610ede6001601255565b6000546001600160a01b031633146131405760405163303676d360e01b815260040160405180910390fd5b806001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561317e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a29190615a1d565b6131d8576040517f3e1fc32700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040516001600160a01b0380841692610100900416907fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f92690600090a3600680546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b8061328d576040517ffa79c3a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518181527fe02ca3e718b01d09207861e781e45e3c93060b331a03eed1ab268d12e793583f9060200160405180910390a1601555565b806001600160a01b0316634456eda26040518163ffffffff1660e01b8152600401602060405180830381865afa158015613303573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133279190615a1d565b61335d576040517f3e1fc32700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546040516001600160a01b038084169216907ff1e04d73c4304b5ff164f9d10c7473e2a1593b740674a6107975e2a7001c1e5c90600090a36002805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b60008060008060006133d788613afe565b95509550509450945094508495506133ef3386613b90565b4663ffffffff168163ffffffff161461341b57604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b03841630146134445760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff1661347a576040516357e05e4960e01b815260040160405180910390fd5b6000871161349b5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b03891684529091529020546134cf9084615a50565b8711156134ef576040516361ee36eb60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b038916845290915281208054899290613528908490615b5f565b9091555061353a905086886000613588565b6040805163ffffffff84168152602081018990526001600160a01b03808916929088169133917fadedbbedfe5f9c3d3ea9e37eda061744d9ca5a97fbe143765b6164fcee7108569101612b68565b613590612722565b613598612765565b6120ef838383614767565b60008060008060006135b48a613afe565b95509550509450945094508495506135cc3386613b90565b4663ffffffff168163ffffffff16146135f857604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b03841630146136215760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff16613657576040516357e05e4960e01b815260040160405180910390fd5b600088116136785760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b03891684529091529020546136ac9084615a50565b8811156136cc576040516361ee36eb60e01b815260040160405180910390fd5b6001600160a01b03891633148015906136f75750846001600160a01b0316896001600160a01b031614155b6137145760405163271bd90b60e01b815260040160405180910390fd5b6001600160a01b03871615613729578661372b565b305b63ffffffff831660009081526016602090815260408083206001600160a01b038a168452909152812080549299508a92909190613769908490615b5f565b9091555061377d9050868a8a8a6000612f38565b604080516001600160a01b038b81168252888116602083015263ffffffff851682840152606082018b905291518983169288169133917fc643e0e4d5d1082f6bfc662466c6ee07f745f7725754c136e448452d1f1878679181900360800190a450505050505050505050565b6137f1612722565b6137f9612765565b6120ef8383836149c6565b6002546040517f6765dff90000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0386811660248301528581166044830152848116606483015290911690636765dff990608401600060405180830381600087803b15801561387a57600080fd5b505af115801561388e573d6000803e3d6000fd5b50505050826001600160a01b0316826001600160a01b0316036138c45760405163bca3ab8960e01b815260040160405180910390fd5b60006138e5826040518060200160405280666379da05b60000815250614b5e565b905060006138f38284615a50565b90506000604051806020016040528061390a612bce565b90529050600061391a8285612f20565b9050600081600b5461392c9190615b5f565b600b819055600c54909150613942908690615a50565b600c556001600160a01b0387166000908152600f6020526040902054613969908790615a50565b6001600160a01b038089166000908152600f602052604080822093909355908a1681522054613999908590615b5f565b6001600160a01b03808a166000818152600f602052604090819020939093559151908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906139ed9088815260200190565b60405180910390a360405185815230906001600160a01b038916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3604080518381526020810183905230917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a2505050505050505050565b613a83612722565b613a8b612765565b6120ef8383600084613e75565b6040805160208101909152600081526040518060200160405280613ac0856000015185614b81565b90529392505050565b600080613ad68585613a98565b9050612c2e613ae482614261565b84614b8d565b613af2614b99565b6129a484848484614bdb565b600080600080600080613b1e613b178860006014614c96565b6000614dbe565b9550613b2f613b1788601480614c96565b9450613b48613b418860286020614c96565b6000614e34565b9350613b5a613b418860486020614c96565b9250613b73613b6c8860686004614c96565b6000614e9a565b9150613b85613b6c88606c6004614c96565b905091939550919395565b806001600160a01b0316826001600160a01b031614611391576001600160a01b0380821660009081526018602090815260408083209386168352929052205460ff1680613bea57506000546001600160a01b038381169116145b80613ced5750600354604080517fa872019500000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c918591849163a87201959160048083019260209291908290030181865afa158015613c5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c809190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015613cc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ced9190615a1d565b80613dd757506003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c918591849163a1bd302d9160048083019260209291908290030181865afa158015613d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d6a9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015613db3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd79190615a1d565b6113915760405163271bd90b60e01b815260040160405180910390fd5b6000613e096001600160a01b03841683614f00565b90508051600014158015613e2e575080806020019051810190613e2c9190615a1d565b155b15611581576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024015b60405180910390fd5b821580613e80575081155b613e9d5760405163bca3ab8960e01b815260040160405180910390fd5b60006040518060200160405280613eb2612bce565b905290506000808515613ed357859150613ecc8387612f20565b9050613ee3565b613edd8584614f0e565b91508490505b81158015613eef575080155b15613f26576040517fbbf3e56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546040517f1e32bd9b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0389811660248301526044820185905290911690631e32bd9b90606401600060405180830381600087803b158015613f9357600080fd5b505af1158015613fa7573d6000803e3d6000fd5b5050505080613fb560115490565b1015613fed576040517f14ec588c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600c54613ffb9190615a50565b600c556001600160a01b0387166000908152600f6020526040902054614022908390615a50565b6001600160a01b0388166000908152600f6020526040902055831561404b5761404b878261294a565b806011600082825461405d9190615a50565b909155505060405182815230906001600160a01b038916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a360408051828152602081018490526001600160a01b038916917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929910160405180910390a250505050505050565b6002546040517fc321fbcc0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038581166024830152600092169063c321fbcc90604401600060405180830381600087803b15801561415657600080fd5b505af115801561416a573d6000803e3d6000fd5b50505050600061417985612b7a565b90506000600019851461418c578461418e565b815b905060008461419d57816141a7565b6141a78883614662565b905080601160008282546141bb9190615b5f565b90915550600090506141cd8285615a50565b9050600082600a546141df9190615a50565b6001600160a01b038a81166000818152600e6020908152604091829020878155600954600190910155600a859055815188815290810187905290810184905292935091908c16907f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19060600160405180910390a3509098975050505050505050565b8051600090610c7890670de0b6b3a764000090615c8f565b846001600160a01b0316846001600160a01b0316036142ab5760405163bca3ab8960e01b815260040160405180910390fd5b6000831180156142bd57506000198314155b6142da5760405163bca3ab8960e01b815260040160405180910390fd5b6002546040517fb50ce7620000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0384811660248301528681166044830152606482018690529091169063b50ce7629060840160006040518083038186803b15801561434d57600080fd5b505afa158015614361573d6000803e3d6000fd5b5050505061436c4290565b826001600160a01b0316636c540baf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156143aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143ce9190615a04565b14614405576040517fdf70808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614413868686856140ef565b6002546040517fc488847b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038681166024830152604482018490529293506000929091169063c488847b90606401602060405180830381865afa158015614489573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144ad9190615a04565b6040516370a0823160e01b81526001600160a01b03888116600483015291925082918616906370a0823190602401602060405180830381865afa1580156144f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061451c9190615a04565b1015614554576040517f7286c37500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b306001600160a01b038516036145755761457030888884613804565b6145f9565b6040517fb2a02ff10000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015287811660248301526044820183905285169063b2a02ff190606401600060405180830381600087803b1580156145e057600080fd5b505af11580156145f4573d6000803e3d6000fd5b505050505b836001600160a01b0316866001600160a01b0316886001600160a01b03167f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb528585604051614651929190918252602082015260400190565b60405180910390a450505050505050565b6013546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156146af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146d39190615a04565b6013549091506146ee906001600160a01b0316853086614f2c565b6013546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015614737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061475b9190615a04565b9050612c2e8282615a50565b6002546040517fc0f1ee090000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0385811660248301529091169063c0f1ee0990604401600060405180830381600087803b1580156147cd57600080fd5b505af11580156147e1573d6000803e3d6000fd5b50505050600060405180602001604052806147fa612bce565b9052905060008261480b5783614815565b6148158585614662565b905080601160008282546148299190615b5f565b909155506000905061483b8284614f0e565b9050600c54600003614889576103e8600c81905560008052600f6020527ff4803e074bd026baaf6ed2e288c9515f68c72fb7216eebdd7cae1718a53ec3758190556148869082615a50565b90505b80600c546148979190615b5f565b600c556001600160a01b0386166000908152600f60205260409020546148be908290615b5f565b6001600160a01b0387166000818152600f602090815260409182902093909355805185815292830184905290917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a26040518181526001600160a01b0387169030907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a36002546040517f0d926fc80000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0390911690630d926fc89060240160006040518083038186803b1580156149b257600080fd5b505afa158015611057573d6000803e3d6000fd5b6002546040517f50795f8a0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03858116602483015260448201859052909116906350795f8a90606401600060405180830381600087803b158015614a3357600080fd5b505af1158015614a47573d6000803e3d6000fd5b5050505081614a5560115490565b1015614a8d576040517fcd85b93e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614a9884612b7a565b90506000614aa68483615b5f565b9050600084600a54614ab89190615b5f565b6001600160a01b0387166000908152600e60205260409020838155600954600190910155600a81905590508315614af357614af3868661294a565b8460116000828254614b059190615a50565b909155505060408051868152602081018490529081018290526001600160a01b038716907f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809060600160405180910390a2505050505050565b6000670de0b6b3a7640000614b77848460000151614b81565b610c759190615c8f565b6000610c758284615c78565b6000610c758284615b5f565b6014546001600160a01b0316612017576040517ff242df5300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014546015546040516001600160a01b039092169163ab750e75918591859190600290614c0b908b908b90615cb1565b602060405180830381855afa158015614c28573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614c4b9190615a04565b6040518563ffffffff1660e01b8152600401614c6a9493929190615cc1565b60006040518083038186803b158015614c8257600080fd5b505afa15801561128c573d6000803e3d6000fd5b606081614ca481601f615b5f565b1015614cf25760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401613e6c565b614cfc8284615b5f565b84511015614d4c5760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401613e6c565b606082158015614d6b5760405191506000825260208201604052614db5565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015614da4578051835260209283019201614d8c565b5050858452601f01601f1916604052505b50949350505050565b6000614dcb826014615b5f565b83511015614e1b5760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e647300000000000000000000006044820152606401613e6c565b5001602001516c01000000000000000000000000900490565b6000614e41826020615b5f565b83511015614e915760405162461bcd60e51b815260206004820152601560248201527f746f55696e743235365f6f75744f66426f756e647300000000000000000000006044820152606401613e6c565b50016020015190565b6000614ea7826004615b5f565b83511015614ef75760405162461bcd60e51b815260206004820152601460248201527f746f55696e7433325f6f75744f66426f756e64730000000000000000000000006044820152606401613e6c565b50016004015190565b6060610c7583836000614f65565b6000610c75614f2584670de0b6b3a7640000614b81565b835161501b565b6040516001600160a01b0384811660248301528381166044830152606482018390526129a49186918216906323b872dd90608401612c64565b606081471015614fa3576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401613e6c565b600080856001600160a01b03168486604051614fbf9190615d01565b60006040518083038185875af1925050503d8060008114614ffc576040519150601f19603f3d011682016040523d82523d6000602084013e615001565b606091505b5091509150615011868383615027565b9695505050505050565b6000610c758284615c8f565b60608261503c576150378261509c565b61146e565b815115801561505357506001600160a01b0384163b155b15615095576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401613e6c565b508061146e565b8051156150ac5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156150f95781810151838201526020016150e1565b50506000910152565b6000815180845261511a8160208601602086016150de565b601f01601f19169290920160200192915050565b602081526000610c756020830184615102565b6001600160a01b0381168114610ede57600080fd5b80356111c781615141565b803563ffffffff811681146111c757600080fd5b6000806040838503121561518857600080fd5b823561519381615141565b91506151a160208401615161565b90509250929050565b6000602082840312156151bc57600080fd5b5035919050565b60008083601f8401126151d557600080fd5b50813567ffffffffffffffff8111156151ed57600080fd5b60208301915083602082850101111561520557600080fd5b9250929050565b60008083601f84011261521e57600080fd5b50813567ffffffffffffffff81111561523657600080fd5b6020830191508360208260051b850101111561520557600080fd5b60008060008060008060006080888a03121561526c57600080fd5b873567ffffffffffffffff81111561528357600080fd5b61528f8a828b016151c3565b909850965050602088013567ffffffffffffffff8111156152af57600080fd5b6152bb8a828b016151c3565b909650945050604088013567ffffffffffffffff8111156152db57600080fd5b6152e78a828b0161520c565b90945092505060608801356152fb81615141565b8091505092959891949750929550565b6000806040838503121561531e57600080fd5b823561532981615141565b946020939093013593505050565b60006020828403121561534957600080fd5b813561146e81615141565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561539357615393615354565b604052919050565b60008067ffffffffffffffff8411156153b6576153b6615354565b50601f8301601f19166020016153cb8161536a565b9150508281528383830111156153e057600080fd5b828260208301376000602084830101529392505050565b600082601f83011261540857600080fd5b610c758383356020850161539b565b803560ff811681146111c757600080fd5b600080600080600080600060e0888a03121561544357600080fd5b873561544e81615141565b9650602088013561545e81615141565b9550604088013561546e81615141565b945060608801359350608088013567ffffffffffffffff81111561549157600080fd5b61549d8a828b016153f7565b93505060a088013567ffffffffffffffff8111156154ba57600080fd5b6154c68a828b016153f7565b9250506154d560c08901615417565b905092959891949750929550565b6000806000606084860312156154f857600080fd5b833561550381615141565b9250602084013561551381615141565b929592945050506040919091013590565b6000806040838503121561553757600080fd5b823591506151a160208401615161565b6000806040838503121561555a57600080fd5b61556383615161565b9150602083013561557381615141565b809150509250929050565b60008060006060848603121561559357600080fd5b833561559e81615141565b92506020840135915060408401356155b581615141565b809150509250925092565b8015158114610ede57600080fd5b600080604083850312156155e157600080fd5b82356155ec81615141565b91506020830135615573816155c0565b6000806040838503121561560f57600080fd5b823561556381615141565b6000806000806000806000806000806101408b8d03121561563a57600080fd5b6156438b615156565b995061565160208c01615156565b985061565f60408c01615156565b975060608b0135965060808b013567ffffffffffffffff81111561568257600080fd5b61568e8d828e016153f7565b96505060a08b013567ffffffffffffffff8111156156ab57600080fd5b6156b78d828e016153f7565b9550506156c660c08c01615417565b93506156d460e08c01615156565b92506156e36101008c01615156565b91506156f26101208c01615156565b90509295989b9194979a5092959850565b60006020828403121561571557600080fd5b610c7582615161565b6000806040838503121561573157600080fd5b6155ec83615161565b60008060008060008060c0878903121561575357600080fd5b863561575e81615141565b9550602087013561576e81615141565b945060408701359350606087013567ffffffffffffffff81111561579157600080fd5b61579d89828a016153f7565b935050608087013567ffffffffffffffff8111156157ba57600080fd5b6157c689828a016153f7565b9250506157d560a08801615417565b90509295509295509295565b600080600080600080600080600080600060c08c8e03121561580257600080fd5b8b3567ffffffffffffffff81111561581957600080fd5b6158258e828f016151c3565b909c509a505060208c013567ffffffffffffffff81111561584557600080fd5b6158518e828f016151c3565b909a5098505060408c013567ffffffffffffffff81111561587157600080fd5b61587d8e828f0161520c565b90985096505060608c013567ffffffffffffffff81111561589d57600080fd5b6158a98e828f0161520c565b90965094505060808c013567ffffffffffffffff8111156158c957600080fd5b6158d58e828f0161520c565b90945092506158e8905060a08d01615156565b90509295989b509295989b9093969950565b600080600080600080600080610100898b03121561591757600080fd5b883561592281615141565b9750602089013561593281615141565b9650604089013561594281615141565b955060608901359450608089013567ffffffffffffffff81111561596557600080fd5b6159718b828c016153f7565b94505060a089013567ffffffffffffffff81111561598e57600080fd5b61599a8b828c016153f7565b9350506159a960c08a01615417565b915060e08901356159b981615141565b809150509295985092959890939650565b600181811c908216806159de57607f821691505b6020821081036159fe57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215615a1657600080fd5b5051919050565b600060208284031215615a2f57600080fd5b815161146e816155c0565b634e487b7160e01b600052601160045260246000fd5b81810381811115610c7857610c78615a3a565b600060208284031215615a7557600080fd5b813567ffffffffffffffff811115615a8c57600080fd5b8201601f81018413615a9d57600080fd5b803567ffffffffffffffff811115615ab757615ab7615354565b8060051b615ac76020820161536a565b91825260208184018101929081019087841115615ae357600080fd5b6020850192505b83831015615b3e57823567ffffffffffffffff811115615b0957600080fd5b8501603f81018913615b1a57600080fd5b615b2c8960208301356040840161539b565b83525060209283019290910190615aea565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b80820180821115610c7857610c78615a3a565b601f82111561158157806000526020600020601f840160051c81016020851015615b995750805b601f840160051c820191505b818110156124395760008155600101615ba5565b815167ffffffffffffffff811115615bd357615bd3615354565b615be781615be184546159ca565b84615b72565b6020601f821160018114615c1b5760008315615c035750848201515b600019600385901b1c1916600184901b178455612439565b600084815260208120601f198516915b82811015615c4b5787850151825560209485019460019092019101615c2b565b5084821015615c695786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b8082028115828204841417610c7857610c78615a3a565b600082615cac57634e487b7160e01b600052601260045260246000fd5b500490565b8183823760009101908152919050565b606081528360608201528385608083013760006080858301015260006080601f19601f870116830101905083602083015282604083015295945050505050565b60008251615d138184602087016150de565b919091019291505056fea2646970667358221220724ffecd1dda03b6a8f003dfa58cd82a8ac6d25418fd46d9382ac52b8b1e24df64736f6c634300081c0033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106105145760003560e01c80637f7b13d4116102a1578063b2a02ff11161016b578063dd62ed3e116100e3578063f851a44011610097578063f8f9da281161007c578063f8f9da2814610ad6578063fe62355f14610ade578063ffcaadfe14610b1a57600080fd5b8063f851a44014610ab0578063f89416ee14610ac357600080fd5b8063ee27a2f2116100c8578063ee27a2f214610a86578063ef3f7dd514610a8f578063f3fdb15a14610a9857600080fd5b8063dd62ed3e14610a3a578063e67218cd14610a7357600080fd5b8063c37f68e21161013a578063c5ebeaec1161011f578063c5ebeaec14610a01578063d87f7cad14610a14578063db006a7514610a2757600080fd5b8063c37f68e2146109c0578063c4d66de8146109ee57600080fd5b8063b2a02ff11461097f578063b3ab15fb14610992578063b57a4a72146109a5578063bd6d894d146109b857600080fd5b806399c4383711610219578063a66b92ab116101cd578063a9059cbb116101b2578063a9059cbb1461095b578063aa5af0fd1461096e578063ae9d70b01461097757600080fd5b8063a66b92ab14610940578063a6afed951461095357600080fd5b80639d9339b3116101fe5780639d9339b314610907578063a0712d681461091a578063a4777a7a1461092d57600080fd5b806399c43837146108e157806399d8c1b4146108f457600080fd5b80638f840ddd1161027057806391a0d4381161025557806391a0d438146108b357806395d89b41146108c657806395dd9193146108ce57600080fd5b80638f840ddd14610897578063901129c2146108a057600080fd5b80637f7b13d41461083b578063852a12e31461084e5780638bcd4016146108615780638da735271461087457600080fd5b80633936c445116103e25780635437988d1161035a5780636c540baf1161030e57806370a08231116102f357806370a08231146107f757806373acee98146108205780637821a5141461082857600080fd5b80636c540baf146107db5780636f307dc3146107e457600080fd5b80635bdcecb71161033f5780635bdcecb714610793578063600bb376146107a6578063699cd5e2146107d457600080fd5b80635437988d1461076d578063570ca7351461078057600080fd5b80634914c008116103b15780634f2be4ce116103965780634f2be4ce1461071c5780634fbdc6ae1461072f5780634fecab701461075a57600080fd5b80634914c008146106f65780634dd18bf51461070957600080fd5b80633936c445146106a75780633af9e669146106d25780633b1d21a2146106e557806347bd3718146106ed57600080fd5b8063182df0f51161049057806326782247116104445780632b7ac3f3116104295780632b7ac3f314610662578063313ce56714610675578063371fd8e61461069457600080fd5b80632678224714610624578063277cd86a1461064f57600080fd5b80631be19560116104755780631be19560146105eb5780631c446983146105fe57806323b872dd1461061157600080fd5b8063182df0f5146105d05780631a31d465146105d857600080fd5b8063095ea7b3116104e7578063173b9904116104cc578063173b99041461059d57806317bfdfbc146105b457806318160ddd146105c757600080fd5b8063095ea7b3146105725780630e18b6811461059557600080fd5b806306fdde031461051957806307d923e91461053757806307e279591461054a57806308fee2631461055f575b600080fd5b610521610b2d565b60405161052e919061512e565b60405180910390f35b610521610545366004615175565b610bbb565b61055d6105583660046151aa565b610c7e565b005b61055d61056d366004615251565b610ee1565b61058561058036600461530b565b611063565b604051901515815260200161052e565b61055d6110d1565b6105a660075481565b60405190815260200161052e565b6105a66105c2366004615337565b6111a0565b6105a6600c5481565b6105a66111cc565b61055d6105e6366004615428565b6111db565b61055d6105f9366004615337565b611296565b61055d61060c3660046151aa565b611395565b61058561061f3660046154e3565b61144b565b600154610637906001600160a01b031681565b6040516001600160a01b03909116815260200161052e565b61055d61065d366004615524565b611475565b601454610637906001600160a01b031681565b6006546106829060ff1681565b60405160ff909116815260200161052e565b61055d6106a23660046151aa565b61151e565b6105a66106b5366004615547565b601760209081526000928352604080842090915290825290205481565b6105a66106e0366004615337565b611529565b6105a6611568565b6105a6600a5481565b61055d61070436600461557e565b611573565b61055d610717366004615337565b611586565b61055d61072a3660046155ce565b61161a565b6105a661073d366004615547565b601660209081526000928352604080842090915290825290205481565b600354610637906001600160a01b031681565b61055d61077b366004615337565b611686565b600254610637906001600160a01b031681565b61055d6107a136600461530b565b6116ba565b6105856107b43660046155fc565b601860209081526000928352604080842090915290825290205460ff1681565b6001610585565b6105a660085481565b601354610637906001600160a01b031681565b6105a6610805366004615337565b6001600160a01b03166000908152600f602052604090205490565b6105a66116c6565b61055d6108363660046151aa565b6116e9565b61055d61084936600461561a565b6116f2565b61055d61085c3660046151aa565b611885565b61055d61086f366004615337565b611891565b610585610882366004615703565b60196020526000908152604090205460ff1681565b6105a6600b5481565b61055d6108ae3660046151aa565b6118cd565b61055d6108c13660046151aa565b611901565b610521611931565b6105a66108dc366004615337565b61193e565b61055d6108ef36600461571e565b611949565b61055d61090236600461573a565b611ad1565b61055d610915366004615251565b611bd8565b61055d6109283660046151aa565b611d4e565b61055d61093b3660046157e1565b611d5a565b61055d61094e366004615524565b611f6e565b61055d61200f565b61058561096936600461530b565b612019565b6105a660095481565b6105a661203c565b61055d61098d3660046154e3565b6120db565b61055d6109a0366004615337565b6120f9565b61055d6109b33660046158fa565b61212d565b6105a6612191565b6109d36109ce366004615337565b6121b7565b6040805193845260208401929092529082015260600161052e565b61055d6109fc366004615337565b6121f2565b61055d610a0f3660046151aa565b61238e565b61055d610a22366004615337565b61239a565b61055d610a353660046151aa565b612440565b6105a6610a483660046155fc565b6001600160a01b03918216600090815260106020908152604080832093909416825291909152205490565b61055d610a813660046151aa565b61244c565b6105a6600d5481565b6105a660155481565b6006546106379061010090046001600160a01b031681565b600054610637906001600160a01b031681565b61055d610ad1366004615337565b6124c4565b6105a661257f565b610585610aec3660046155fc565b6001600160a01b03918216600090815260186020908152604080832093909416825291909152205460ff1690565b61055d610b283660046151aa565b6125d9565b60048054610b3a906159ca565b80601f0160208091040260200160405190810160405280929190818152602001828054610b66906159ca565b8015610bb35780601f10610b8857610100808354040283529160200191610bb3565b820191906000526020600020905b815481529060010190602001808311610b9657829003601f168201915b505050505081565b63ffffffff811660008181526016602090815260408083206001600160a01b03871680855290835281842054948452601783528184209084528252918290205482517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088811b82169483019490945230841b166034820152604881019490945260688401526001600160e01b03194660e090811b8216608886015285901b16608c840152815180840360700181526090909301909152905b90505b92915050565b610c86612722565b6000546001600160a01b0316331480610d975750600354604080517fd71c72e000000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c913391849163d71c72e09160048083019260209291908290030181865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d979190615a1d565b610dcd576040517f90b0f0f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd5612765565b80610ddf60115490565b1015610e17576040517f72d33f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54811115610e53576040517f72d33f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081600b54610e639190615a50565b600b8190559050610e74338361294a565b8160116000828254610e869190615a50565b909155505060005460408051848152602081018490526001600160a01b03909216917f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e910160405180910390a250610ede6001601255565b50565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015610f34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f589190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610fa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc59190615a1d565b610fd557610fd587878787612961565b6000610fe387890189615a63565b8051909150838114611008576040516302e262f160e01b815260040160405180910390fd5b60005b818110156110575761104f83828151811061102857611028615b49565b602002602001015187878481811061104257611042615b49565b90506020020135866129aa565b60010161100b565b50505050505050505050565b3360008181526010602090815260408083206001600160a01b03871680855292528083208590555191929182907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906110bf9087815260200190565b60405180910390a35060019392505050565b6001546001600160a01b031633146110fc5760405163303676d360e01b815260040160405180910390fd5b60008054600180546001600160a01b0380821673ffffffffffffffffffffffffffffffffffffffff19808616821787559092169092556040519190921692829184917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc91a36001546040516001600160a01b03918216918316907fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a990600090a35050565b60006111aa612722565b6111b2612765565b6111bb82612b7a565b90506111c76001601255565b919050565b60006111d6612bce565b905090565b6111e9868686868686611ad1565b6013805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038916908117909155604080517f18160ddd00000000000000000000000000000000000000000000000000000000815290516318160ddd916004808201926020929091908290030181865afa158015611268573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128c9190615a04565b5050505050505050565b6000546001600160a01b031633146112c15760405163303676d360e01b815260040160405180910390fd5b6013546001600160a01b0390811690821603611309576040517f9e14909700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113749190615a04565b600054909150611391906001600160a01b03848116911683612c37565b5050565b6000546001600160a01b031633146113c05760405163303676d360e01b815260040160405180910390fd5b6113c8612765565b670de0b6b3a764000081111561140a576040517fea1eeb4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075460408051918252602082018390527faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460910160405180910390a1600755565b6000611455612722565b61146133858585612cab565b50600161146e6001601255565b9392505050565b600082116114965760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff81166000908152601760209081526040808320338452909152812080548492906114c6908490615b5f565b909155506114d8905033836000612edc565b6040805163ffffffff831681526020810184905233917fffb02ed8426abe4f31f431f9f4c6075fcf5916fb1ba83041a0979073b27a3d7191015b60405180910390a25050565b610ede816001612ef9565b600080604051806020016040528061153f612191565b90526001600160a01b0384166000908152600f602052604090205490915061146e908290612f20565b60006111d660115490565b611581338484846001612f38565b505050565b6000546001600160a01b031633146115b15760405163303676d360e01b815260040160405180910390fd5b6001546040516001600160a01b038084169216907fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a990600090a36001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b3360008181526018602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fb2cc4dde7f9044ba1999f7843e2f9cd1e4ce506f8cc2e16de26ce982bf113fa6910160405180910390a35050565b6000546001600160a01b031633146116b15760405163303676d360e01b815260040160405180910390fd5b610ede81612fb2565b6113918282600161305b565b60006116d0612722565b6116d8612765565b50600a546116e66001601255565b90565b610ede81613082565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561173d5750825b905060008267ffffffffffffffff16600114801561175a5750303b155b905081158015611768575080155b1561179f576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156117d357845468ff00000000000000001916680100000000000000001785555b6117e38f8f8f8f8f8f8f8f61212d565b6117ec876121f2565b600380546001600160a01b0380891673ffffffffffffffffffffffffffffffffffffffff199283161790925560008054928b1692909116919091179055831561187457845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050505050565b610ede33826001612edc565b6000546001600160a01b031633146118bc5760405163303676d360e01b815260040160405180910390fd5b6118c4612765565b610ede81613115565b6000546001600160a01b031633146118f85760405163303676d360e01b815260040160405180910390fd5b610ede81613256565b6000546001600160a01b0316331461192c5760405163303676d360e01b815260040160405180910390fd5b601155565b60058054610b3a906159ca565b6000610c7882612b7a565b6000546001600160a01b03163314801590611a5e5750600354604080517fe048241300000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c913391849163e04824139160048083019260209291908290030181865afa1580156119cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ef9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5c9190615a1d565b155b15611a7c5760405163271bd90b60e01b815260040160405180910390fd5b63ffffffff8216600081815260196020908152604091829020805460ff191685151590811790915591519182527f3615c8513bc6436318086c0663e19b0334871c8c175a33b9340c3ecab97137cc9101611512565b6000546001600160a01b03163314611afc5760405163303676d360e01b815260040160405180910390fd5b600854158015611b0c5750600954155b611b42576040517fc99314d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008411611b7c576040517fa08b42d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011849055611b8a866132c5565b42600855670de0b6b3a7640000600955611ba385613115565b6004611baf8482615bb9565b506005611bbc8382615bb9565b506006805460ff191660ff929092169190911790555050505050565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015611c2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611c98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbc9190615a1d565b611ccc57611ccc87878787612961565b6000611cda87890189615a63565b8051909150838114611cff576040516302e262f160e01b815260040160405180910390fd5b60005b8181101561105757611d46838281518110611d1f57611d1f615b49565b6020026020010151878784818110611d3957611d39615b49565b90506020020135866133c6565b600101611d02565b610ede33826001613588565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015611dad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd19190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611e1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e3e9190615a1d565b611e4e57611e4e8b8b8b8b612961565b6000611e5c8b8d018d615a63565b8051909150858114611e81576040516302e262f160e01b815260040160405180910390fd5b808814611ea1576040516302e262f160e01b815260040160405180910390fd5b808414611ec1576040516302e262f160e01b815260040160405180910390fd5b60005b81811015611f5e57611f56838281518110611ee157611ee1615b49565b60200260200101518b8b84818110611efb57611efb615b49565b9050602002016020810190611f109190615337565b8a8a85818110611f2257611f22615b49565b90506020020135898986818110611f3b57611f3b615b49565b9050602002016020810190611f509190615337565b886135a3565b600101611ec4565b5050505050505050505050505050565b60008211611f8f5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff8116600090815260176020908152604080832033845290915281208054849290611fbf908490615b5f565b90915550611fd19050338360006137e9565b6040805163ffffffff831681526020810184905233917febeabb72629991c0657c42b5db17113f1eb371cca1f8f30ee73c9f66232c2a629101611512565b612017612765565b565b6000612023612722565b61202f33338585612cab565b506001610c786001601255565b6006546000906001600160a01b036101009091041663b816881661205f60115490565b600a54600b546007546040516001600160e01b031960e087901b16815260048101949094526024840192909252604483015260648201526084015b602060405180830381865afa1580156120b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d69190615a04565b6120e3612722565b6120ef33848484613804565b6115816001601255565b6000546001600160a01b031633146121245760405163303676d360e01b815260040160405180910390fd5b610ede816132c5565b6000805473ffffffffffffffffffffffffffffffffffffffff19163317905561215b888888888888886111db565b6000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905550505050505050565b600061219b612722565b6121a3612765565b6121ab612bce565b90506116e66001601255565b6001600160a01b0381166000908152600f6020526040812054819081906121dd85612b7a565b6121e5612bce565b9250925092509193909250565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561223d5750825b905060008267ffffffffffffffff16600114801561225a5750303b155b905081158015612268575080155b1561229f576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156122d357845468ff00000000000000001916680100000000000000001785555b6001600160a01b038616612313576040517f2756006b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038816179055831561238657845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b610ede338260016137e9565b6000546001600160a01b031633146123c55760405163303676d360e01b815260040160405180910390fd5b6013546040517f5c19a95c0000000000000000000000000000000000000000000000000000000081526001600160a01b03838116600483015290911690635c19a95c90602401600060405180830381600087803b15801561242557600080fd5b505af1158015612439573d6000803e3d6000fd5b5050505050565b610ede33826001613a7b565b6000546001600160a01b031633146124775760405163303676d360e01b815260040160405180910390fd5b600d805490829055612487612765565b60408051828152602081018490527f3ae66e204f1f2ae1bf6ea9ddb0331246f344118a48794bc9351cc730a0486658910160405180910390a15050565b6000546001600160a01b031633146124ef5760405163303676d360e01b815260040160405180910390fd5b6001600160a01b0381166125165760405163bca3ab8960e01b815260040160405180910390fd5b6003546040516001600160a01b038084169216907f9859cd0a756b5f08366068b791448fb837581d3b8afc097914d88edbc7bff2a390600090a36003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6006546000906001600160a01b03610100909104166315f240536125a260115490565b600a54600b546040516001600160e01b031960e086901b16815260048101939093526024830191909152604482015260640161209a565b600354604080517f9e106dc700000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c9133918491639e106dc79160048083019260209291908290030181865afa158015612645573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126699190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156126b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d69190615a1d565b61270b576040517ea1222300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601354610ede906001600160a01b03163383612c37565b60026012540361275e576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002601255565b6008544290818103612775575050565b600061278060115490565b600a54600b546009546006546040517f15f2405300000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905294955092939192909160009161010090046001600160a01b0316906315f2405390606401602060405180830381865afa158015612808573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061282c9190615a04565b9050600d5481111561286a576040517f8179ac1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006128768789615a50565b9050600061289260405180602001604052808581525083613a98565b905060006128a08288612f20565b905060006128ae8883615b5f565b905060006128cd6040518060200160405280600754815250848a613ac9565b905060006128dc85898a613ac9565b60088e90556009819055600a849055600b839055604080518d815260208101879052908101829052606081018590529091507f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049060800160405180910390a150505050505050505050505050565b601354611391906001600160a01b03168383612c37565b82612998576040517fc39550bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6129a484848484613aea565b50505050565b60008060008060006129bb88613afe565b95509550509450945094508495506129d33386613b90565b4663ffffffff168163ffffffff16146129ff57604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b0384163014612a285760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff16612a5e576040516357e05e4960e01b815260040160405180910390fd5b60008711612a7f5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b0389168452909152902054612ab39084615a50565b871115612ad3576040516361ee36eb60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b038916845290915281208054899290612b0c908490615b5f565b90915550612b1e90508688600061305b565b6040805163ffffffff84168152602081018990526001600160a01b03808916929088169133917fe2cce63addd46173601de3603e8e65733e0aeb61b438aba8c8b139fae31a673291015b60405180910390a45050505050505050565b6001600160a01b0381166000908152600e6020526040812080548203612ba35750600092915050565b6009548154600091612bb491615c78565b9050816001015481612bc69190615c8f565b949350505050565b600c54600090808203612be357505060115490565b6000612bee60115490565b90506000600b54600a5483612c039190615b5f565b612c0d9190615a50565b9050600083612c24670de0b6b3a764000084615c78565b612c2e9190615c8f565b95945050505050565b6040516001600160a01b0383811660248301526044820183905261158191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613df4565b6002546040517f17bf120e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038581166024830152848116604483015260648201849052909116906317bf120e90608401600060405180830381600087803b158015612d2057600080fd5b505af1158015612d34573d6000803e3d6000fd5b50505050816001600160a01b0316836001600160a01b031603612d83576040517f98a79b5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000836001600160a01b0316856001600160a01b031603612da75750600019612dcf565b506001600160a01b038084166000908152601060209081526040808320938816835292905220545b6000612ddb8383615a50565b6001600160a01b0386166000908152600f602052604081205491925090612e03908590615a50565b6001600160a01b0386166000908152600f602052604081205491925090612e2b908690615b5f565b6001600160a01b038089166000908152600f602052604080822086905591891681522081905590506000198414612e85576001600160a01b038088166000908152601060209081526040808320938c168352929052208390555b856001600160a01b0316876001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef87604051612eca91815260200190565b60405180910390a35050505050505050565b612ee4612722565b612eec612765565b6120ef8360008484613e75565b612f01612722565b612f09612765565b612f15333384846140ef565b506113916001601255565b600080612f2d8484613a98565b9050612bc681614261565b612f40612722565b612f48612765565b816001600160a01b031663a6afed956040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612f8357600080fd5b505af1158015612f97573d6000803e3d6000fd5b50505050612fa88585858585614279565b6124396001601255565b6001600160a01b038116612ff2576040517f2756006b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014546040516001600160a01b038084169216907fab81081e8fd2866d32d2dd90fdfa53fa9e0aa3d7eed73c684b6dcc4e2c0369fd90600090a36014805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b613063612722565b61306b612765565b613077338484846140ef565b506115816001601255565b61308a612722565b613092612765565b60008061309f3384614662565b905080601160008282546130b39190615b5f565b9091555050600b546130c6908290615b5f565b600b819055604080518381526020810183905291935033917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a25050610ede6001601255565b6000546001600160a01b031633146131405760405163303676d360e01b815260040160405180910390fd5b806001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561317e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a29190615a1d565b6131d8576040517f3e1fc32700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040516001600160a01b0380841692610100900416907fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f92690600090a3600680546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b8061328d576040517ffa79c3a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518181527fe02ca3e718b01d09207861e781e45e3c93060b331a03eed1ab268d12e793583f9060200160405180910390a1601555565b806001600160a01b0316634456eda26040518163ffffffff1660e01b8152600401602060405180830381865afa158015613303573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133279190615a1d565b61335d576040517f3e1fc32700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546040516001600160a01b038084169216907ff1e04d73c4304b5ff164f9d10c7473e2a1593b740674a6107975e2a7001c1e5c90600090a36002805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b60008060008060006133d788613afe565b95509550509450945094508495506133ef3386613b90565b4663ffffffff168163ffffffff161461341b57604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b03841630146134445760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff1661347a576040516357e05e4960e01b815260040160405180910390fd5b6000871161349b5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b03891684529091529020546134cf9084615a50565b8711156134ef576040516361ee36eb60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b038916845290915281208054899290613528908490615b5f565b9091555061353a905086886000613588565b6040805163ffffffff84168152602081018990526001600160a01b03808916929088169133917fadedbbedfe5f9c3d3ea9e37eda061744d9ca5a97fbe143765b6164fcee7108569101612b68565b613590612722565b613598612765565b6120ef838383614767565b60008060008060006135b48a613afe565b95509550509450945094508495506135cc3386613b90565b4663ffffffff168163ffffffff16146135f857604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b03841630146136215760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff16613657576040516357e05e4960e01b815260040160405180910390fd5b600088116136785760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b03891684529091529020546136ac9084615a50565b8811156136cc576040516361ee36eb60e01b815260040160405180910390fd5b6001600160a01b03891633148015906136f75750846001600160a01b0316896001600160a01b031614155b6137145760405163271bd90b60e01b815260040160405180910390fd5b6001600160a01b03871615613729578661372b565b305b63ffffffff831660009081526016602090815260408083206001600160a01b038a168452909152812080549299508a92909190613769908490615b5f565b9091555061377d9050868a8a8a6000612f38565b604080516001600160a01b038b81168252888116602083015263ffffffff851682840152606082018b905291518983169288169133917fc643e0e4d5d1082f6bfc662466c6ee07f745f7725754c136e448452d1f1878679181900360800190a450505050505050505050565b6137f1612722565b6137f9612765565b6120ef8383836149c6565b6002546040517f6765dff90000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0386811660248301528581166044830152848116606483015290911690636765dff990608401600060405180830381600087803b15801561387a57600080fd5b505af115801561388e573d6000803e3d6000fd5b50505050826001600160a01b0316826001600160a01b0316036138c45760405163bca3ab8960e01b815260040160405180910390fd5b60006138e5826040518060200160405280666379da05b60000815250614b5e565b905060006138f38284615a50565b90506000604051806020016040528061390a612bce565b90529050600061391a8285612f20565b9050600081600b5461392c9190615b5f565b600b819055600c54909150613942908690615a50565b600c556001600160a01b0387166000908152600f6020526040902054613969908790615a50565b6001600160a01b038089166000908152600f602052604080822093909355908a1681522054613999908590615b5f565b6001600160a01b03808a166000818152600f602052604090819020939093559151908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906139ed9088815260200190565b60405180910390a360405185815230906001600160a01b038916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3604080518381526020810183905230917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a2505050505050505050565b613a83612722565b613a8b612765565b6120ef8383600084613e75565b6040805160208101909152600081526040518060200160405280613ac0856000015185614b81565b90529392505050565b600080613ad68585613a98565b9050612c2e613ae482614261565b84614b8d565b613af2614b99565b6129a484848484614bdb565b600080600080600080613b1e613b178860006014614c96565b6000614dbe565b9550613b2f613b1788601480614c96565b9450613b48613b418860286020614c96565b6000614e34565b9350613b5a613b418860486020614c96565b9250613b73613b6c8860686004614c96565b6000614e9a565b9150613b85613b6c88606c6004614c96565b905091939550919395565b806001600160a01b0316826001600160a01b031614611391576001600160a01b0380821660009081526018602090815260408083209386168352929052205460ff1680613bea57506000546001600160a01b038381169116145b80613ced5750600354604080517fa872019500000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c918591849163a87201959160048083019260209291908290030181865afa158015613c5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c809190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015613cc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ced9190615a1d565b80613dd757506003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c918591849163a1bd302d9160048083019260209291908290030181865afa158015613d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d6a9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015613db3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd79190615a1d565b6113915760405163271bd90b60e01b815260040160405180910390fd5b6000613e096001600160a01b03841683614f00565b90508051600014158015613e2e575080806020019051810190613e2c9190615a1d565b155b15611581576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024015b60405180910390fd5b821580613e80575081155b613e9d5760405163bca3ab8960e01b815260040160405180910390fd5b60006040518060200160405280613eb2612bce565b905290506000808515613ed357859150613ecc8387612f20565b9050613ee3565b613edd8584614f0e565b91508490505b81158015613eef575080155b15613f26576040517fbbf3e56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546040517f1e32bd9b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0389811660248301526044820185905290911690631e32bd9b90606401600060405180830381600087803b158015613f9357600080fd5b505af1158015613fa7573d6000803e3d6000fd5b5050505080613fb560115490565b1015613fed576040517f14ec588c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600c54613ffb9190615a50565b600c556001600160a01b0387166000908152600f6020526040902054614022908390615a50565b6001600160a01b0388166000908152600f6020526040902055831561404b5761404b878261294a565b806011600082825461405d9190615a50565b909155505060405182815230906001600160a01b038916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a360408051828152602081018490526001600160a01b038916917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929910160405180910390a250505050505050565b6002546040517fc321fbcc0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038581166024830152600092169063c321fbcc90604401600060405180830381600087803b15801561415657600080fd5b505af115801561416a573d6000803e3d6000fd5b50505050600061417985612b7a565b90506000600019851461418c578461418e565b815b905060008461419d57816141a7565b6141a78883614662565b905080601160008282546141bb9190615b5f565b90915550600090506141cd8285615a50565b9050600082600a546141df9190615a50565b6001600160a01b038a81166000818152600e6020908152604091829020878155600954600190910155600a859055815188815290810187905290810184905292935091908c16907f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19060600160405180910390a3509098975050505050505050565b8051600090610c7890670de0b6b3a764000090615c8f565b846001600160a01b0316846001600160a01b0316036142ab5760405163bca3ab8960e01b815260040160405180910390fd5b6000831180156142bd57506000198314155b6142da5760405163bca3ab8960e01b815260040160405180910390fd5b6002546040517fb50ce7620000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0384811660248301528681166044830152606482018690529091169063b50ce7629060840160006040518083038186803b15801561434d57600080fd5b505afa158015614361573d6000803e3d6000fd5b5050505061436c4290565b826001600160a01b0316636c540baf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156143aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143ce9190615a04565b14614405576040517fdf70808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614413868686856140ef565b6002546040517fc488847b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038681166024830152604482018490529293506000929091169063c488847b90606401602060405180830381865afa158015614489573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144ad9190615a04565b6040516370a0823160e01b81526001600160a01b03888116600483015291925082918616906370a0823190602401602060405180830381865afa1580156144f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061451c9190615a04565b1015614554576040517f7286c37500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b306001600160a01b038516036145755761457030888884613804565b6145f9565b6040517fb2a02ff10000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015287811660248301526044820183905285169063b2a02ff190606401600060405180830381600087803b1580156145e057600080fd5b505af11580156145f4573d6000803e3d6000fd5b505050505b836001600160a01b0316866001600160a01b0316886001600160a01b03167f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb528585604051614651929190918252602082015260400190565b60405180910390a450505050505050565b6013546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156146af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146d39190615a04565b6013549091506146ee906001600160a01b0316853086614f2c565b6013546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015614737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061475b9190615a04565b9050612c2e8282615a50565b6002546040517fc0f1ee090000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0385811660248301529091169063c0f1ee0990604401600060405180830381600087803b1580156147cd57600080fd5b505af11580156147e1573d6000803e3d6000fd5b50505050600060405180602001604052806147fa612bce565b9052905060008261480b5783614815565b6148158585614662565b905080601160008282546148299190615b5f565b909155506000905061483b8284614f0e565b9050600c54600003614889576103e8600c81905560008052600f6020527ff4803e074bd026baaf6ed2e288c9515f68c72fb7216eebdd7cae1718a53ec3758190556148869082615a50565b90505b80600c546148979190615b5f565b600c556001600160a01b0386166000908152600f60205260409020546148be908290615b5f565b6001600160a01b0387166000818152600f602090815260409182902093909355805185815292830184905290917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a26040518181526001600160a01b0387169030907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a36002546040517f0d926fc80000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0390911690630d926fc89060240160006040518083038186803b1580156149b257600080fd5b505afa158015611057573d6000803e3d6000fd5b6002546040517f50795f8a0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03858116602483015260448201859052909116906350795f8a90606401600060405180830381600087803b158015614a3357600080fd5b505af1158015614a47573d6000803e3d6000fd5b5050505081614a5560115490565b1015614a8d576040517fcd85b93e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614a9884612b7a565b90506000614aa68483615b5f565b9050600084600a54614ab89190615b5f565b6001600160a01b0387166000908152600e60205260409020838155600954600190910155600a81905590508315614af357614af3868661294a565b8460116000828254614b059190615a50565b909155505060408051868152602081018490529081018290526001600160a01b038716907f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809060600160405180910390a2505050505050565b6000670de0b6b3a7640000614b77848460000151614b81565b610c759190615c8f565b6000610c758284615c78565b6000610c758284615b5f565b6014546001600160a01b0316612017576040517ff242df5300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014546015546040516001600160a01b039092169163ab750e75918591859190600290614c0b908b908b90615cb1565b602060405180830381855afa158015614c28573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614c4b9190615a04565b6040518563ffffffff1660e01b8152600401614c6a9493929190615cc1565b60006040518083038186803b158015614c8257600080fd5b505afa15801561128c573d6000803e3d6000fd5b606081614ca481601f615b5f565b1015614cf25760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401613e6c565b614cfc8284615b5f565b84511015614d4c5760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401613e6c565b606082158015614d6b5760405191506000825260208201604052614db5565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015614da4578051835260209283019201614d8c565b5050858452601f01601f1916604052505b50949350505050565b6000614dcb826014615b5f565b83511015614e1b5760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e647300000000000000000000006044820152606401613e6c565b5001602001516c01000000000000000000000000900490565b6000614e41826020615b5f565b83511015614e915760405162461bcd60e51b815260206004820152601560248201527f746f55696e743235365f6f75744f66426f756e647300000000000000000000006044820152606401613e6c565b50016020015190565b6000614ea7826004615b5f565b83511015614ef75760405162461bcd60e51b815260206004820152601460248201527f746f55696e7433325f6f75744f66426f756e64730000000000000000000000006044820152606401613e6c565b50016004015190565b6060610c7583836000614f65565b6000610c75614f2584670de0b6b3a7640000614b81565b835161501b565b6040516001600160a01b0384811660248301528381166044830152606482018390526129a49186918216906323b872dd90608401612c64565b606081471015614fa3576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401613e6c565b600080856001600160a01b03168486604051614fbf9190615d01565b60006040518083038185875af1925050503d8060008114614ffc576040519150601f19603f3d011682016040523d82523d6000602084013e615001565b606091505b5091509150615011868383615027565b9695505050505050565b6000610c758284615c8f565b60608261503c576150378261509c565b61146e565b815115801561505357506001600160a01b0384163b155b15615095576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401613e6c565b508061146e565b8051156150ac5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156150f95781810151838201526020016150e1565b50506000910152565b6000815180845261511a8160208601602086016150de565b601f01601f19169290920160200192915050565b602081526000610c756020830184615102565b6001600160a01b0381168114610ede57600080fd5b80356111c781615141565b803563ffffffff811681146111c757600080fd5b6000806040838503121561518857600080fd5b823561519381615141565b91506151a160208401615161565b90509250929050565b6000602082840312156151bc57600080fd5b5035919050565b60008083601f8401126151d557600080fd5b50813567ffffffffffffffff8111156151ed57600080fd5b60208301915083602082850101111561520557600080fd5b9250929050565b60008083601f84011261521e57600080fd5b50813567ffffffffffffffff81111561523657600080fd5b6020830191508360208260051b850101111561520557600080fd5b60008060008060008060006080888a03121561526c57600080fd5b873567ffffffffffffffff81111561528357600080fd5b61528f8a828b016151c3565b909850965050602088013567ffffffffffffffff8111156152af57600080fd5b6152bb8a828b016151c3565b909650945050604088013567ffffffffffffffff8111156152db57600080fd5b6152e78a828b0161520c565b90945092505060608801356152fb81615141565b8091505092959891949750929550565b6000806040838503121561531e57600080fd5b823561532981615141565b946020939093013593505050565b60006020828403121561534957600080fd5b813561146e81615141565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561539357615393615354565b604052919050565b60008067ffffffffffffffff8411156153b6576153b6615354565b50601f8301601f19166020016153cb8161536a565b9150508281528383830111156153e057600080fd5b828260208301376000602084830101529392505050565b600082601f83011261540857600080fd5b610c758383356020850161539b565b803560ff811681146111c757600080fd5b600080600080600080600060e0888a03121561544357600080fd5b873561544e81615141565b9650602088013561545e81615141565b9550604088013561546e81615141565b945060608801359350608088013567ffffffffffffffff81111561549157600080fd5b61549d8a828b016153f7565b93505060a088013567ffffffffffffffff8111156154ba57600080fd5b6154c68a828b016153f7565b9250506154d560c08901615417565b905092959891949750929550565b6000806000606084860312156154f857600080fd5b833561550381615141565b9250602084013561551381615141565b929592945050506040919091013590565b6000806040838503121561553757600080fd5b823591506151a160208401615161565b6000806040838503121561555a57600080fd5b61556383615161565b9150602083013561557381615141565b809150509250929050565b60008060006060848603121561559357600080fd5b833561559e81615141565b92506020840135915060408401356155b581615141565b809150509250925092565b8015158114610ede57600080fd5b600080604083850312156155e157600080fd5b82356155ec81615141565b91506020830135615573816155c0565b6000806040838503121561560f57600080fd5b823561556381615141565b6000806000806000806000806000806101408b8d03121561563a57600080fd5b6156438b615156565b995061565160208c01615156565b985061565f60408c01615156565b975060608b0135965060808b013567ffffffffffffffff81111561568257600080fd5b61568e8d828e016153f7565b96505060a08b013567ffffffffffffffff8111156156ab57600080fd5b6156b78d828e016153f7565b9550506156c660c08c01615417565b93506156d460e08c01615156565b92506156e36101008c01615156565b91506156f26101208c01615156565b90509295989b9194979a5092959850565b60006020828403121561571557600080fd5b610c7582615161565b6000806040838503121561573157600080fd5b6155ec83615161565b60008060008060008060c0878903121561575357600080fd5b863561575e81615141565b9550602087013561576e81615141565b945060408701359350606087013567ffffffffffffffff81111561579157600080fd5b61579d89828a016153f7565b935050608087013567ffffffffffffffff8111156157ba57600080fd5b6157c689828a016153f7565b9250506157d560a08801615417565b90509295509295509295565b600080600080600080600080600080600060c08c8e03121561580257600080fd5b8b3567ffffffffffffffff81111561581957600080fd5b6158258e828f016151c3565b909c509a505060208c013567ffffffffffffffff81111561584557600080fd5b6158518e828f016151c3565b909a5098505060408c013567ffffffffffffffff81111561587157600080fd5b61587d8e828f0161520c565b90985096505060608c013567ffffffffffffffff81111561589d57600080fd5b6158a98e828f0161520c565b90965094505060808c013567ffffffffffffffff8111156158c957600080fd5b6158d58e828f0161520c565b90945092506158e8905060a08d01615156565b90509295989b509295989b9093969950565b600080600080600080600080610100898b03121561591757600080fd5b883561592281615141565b9750602089013561593281615141565b9650604089013561594281615141565b955060608901359450608089013567ffffffffffffffff81111561596557600080fd5b6159718b828c016153f7565b94505060a089013567ffffffffffffffff81111561598e57600080fd5b61599a8b828c016153f7565b9350506159a960c08a01615417565b915060e08901356159b981615141565b809150509295985092959890939650565b600181811c908216806159de57607f821691505b6020821081036159fe57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215615a1657600080fd5b5051919050565b600060208284031215615a2f57600080fd5b815161146e816155c0565b634e487b7160e01b600052601160045260246000fd5b81810381811115610c7857610c78615a3a565b600060208284031215615a7557600080fd5b813567ffffffffffffffff811115615a8c57600080fd5b8201601f81018413615a9d57600080fd5b803567ffffffffffffffff811115615ab757615ab7615354565b8060051b615ac76020820161536a565b91825260208184018101929081019087841115615ae357600080fd5b6020850192505b83831015615b3e57823567ffffffffffffffff811115615b0957600080fd5b8501603f81018913615b1a57600080fd5b615b2c8960208301356040840161539b565b83525060209283019290910190615aea565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b80820180821115610c7857610c78615a3a565b601f82111561158157806000526020600020601f840160051c81016020851015615b995750805b601f840160051c820191505b818110156124395760008155600101615ba5565b815167ffffffffffffffff811115615bd357615bd3615354565b615be781615be184546159ca565b84615b72565b6020601f821160018114615c1b5760008315615c035750848201515b600019600385901b1c1916600184901b178455612439565b600084815260208120601f198516915b82811015615c4b5787850151825560209485019460019092019101615c2b565b5084821015615c695786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b8082028115828204841417610c7857610c78615a3a565b600082615cac57634e487b7160e01b600052601260045260246000fd5b500490565b8183823760009101908152919050565b606081528360608201528385608083013760006080858301015260006080601f19601f870116830101905083602083015282604083015295945050505050565b60008251615d138184602087016150de565b919091019291505056fea2646970667358221220724ffecd1dda03b6a8f003dfa58cd82a8ac6d25418fd46d9382ac52b8b1e24df64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.