Source Code
Overview
ETH Balance
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 28263079 | 50 secs ago | 0 ETH | ||||
| 28263079 | 50 secs ago | 0 ETH | ||||
| 28263079 | 50 secs ago | 0 ETH | ||||
| 28263079 | 50 secs ago | 0 ETH | ||||
| 28263079 | 50 secs ago | 0 ETH | ||||
| 28263079 | 50 secs ago | 0 ETH | ||||
| 28263051 | 1 min ago | 0 ETH | ||||
| 28263051 | 1 min ago | 0 ETH | ||||
| 28263051 | 1 min ago | 0 ETH | ||||
| 28263051 | 1 min ago | 0 ETH | ||||
| 28263051 | 1 min ago | 0 ETH | ||||
| 28263049 | 1 min ago | 0 ETH | ||||
| 28263049 | 1 min ago | 0 ETH | ||||
| 28263049 | 1 min ago | 0 ETH | ||||
| 28263049 | 1 min ago | 0 ETH | ||||
| 28263049 | 1 min ago | 0 ETH | ||||
| 28263049 | 1 min ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH | ||||
| 28263045 | 2 mins ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
MUSD
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 999999 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { PausableUpgradeable } from "../lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol";
import { MYieldToOne } from "../lib/evm-m-extensions/src/projects/yieldToOne/MYieldToOne.sol";
import { IMUSD } from "./IMUSD.sol";
/**
███╗ ███╗███████╗████████╗ █████╗ ███╗ ███╗ █████╗ ███████╗██╗ ██╗ ██╗ ██╗███████╗██████╗
████╗ ████║██╔════╝╚══██╔══╝██╔══██╗████╗ ████║██╔══██╗██╔════╝██║ ██╔╝ ██║ ██║██╔════╝██╔══██╗
██╔████╔██║█████╗ ██║ ███████║██╔████╔██║███████║███████╗█████╔╝ ██║ ██║███████╗██║ ██║
██║╚██╔╝██║██╔══╝ ██║ ██╔══██║██║╚██╔╝██║██╔══██║╚════██║██╔═██╗ ██║ ██║╚════██║██║ ██║
██║ ╚═╝ ██║███████╗ ██║ ██║ ██║██║ ╚═╝ ██║██║ ██║███████║██║ ██╗ ╚██████╔╝███████║██████╔╝
╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═════╝
*/
/**
* @title MUSD
* @notice M extension for the MUSD token.
* @author M0 Labs
*/
contract MUSD is IMUSD, MYieldToOne, PausableUpgradeable {
/* ============ Variables ============ */
/// @inheritdoc IMUSD
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
/// @inheritdoc IMUSD
bytes32 public constant FORCED_TRANSFER_MANAGER_ROLE = keccak256("FORCED_TRANSFER_MANAGER_ROLE");
/* ============ Constructor ============ */
/**
* @custom:oz-upgrades-unsafe-allow constructor
* @notice Constructs MUSD Implementation contract
* @dev `_disableInitializers()` is called in the inherited MExtension's constructor.
* @param mToken The address of the MToken
* @param swapFacility The address of the SwapFacility
*/
constructor(address mToken, address swapFacility) MYieldToOne(mToken, swapFacility) {}
/* ============ Initializer ============ */
/**
* @dev Initializes the MUSD token.
* @param yieldRecipient The address of a yield destination.
* @param admin The address of an admin.
* @param freezeManager The address of a freeze manager.
* @param yieldRecipientManager The address of a yield recipient setter.
* @param pauser The address of a pauser.
*/
function initialize(
address yieldRecipient,
address admin,
address freezeManager,
address yieldRecipientManager,
address pauser,
address forcedTransferManager
) external initializer {
if (pauser == address(0)) revert ZeroPauser();
if (forcedTransferManager == address(0)) revert ZeroForcedTransferManager();
__MYieldToOne_init("MetaMask USD", "mUSD", yieldRecipient, admin, freezeManager, yieldRecipientManager);
__Pausable_init();
_grantRole(PAUSER_ROLE, pauser);
_grantRole(FORCED_TRANSFER_MANAGER_ROLE, forcedTransferManager);
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IMUSD
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
/// @inheritdoc IMUSD
function unpause() external onlyRole(PAUSER_ROLE) {
_unpause();
}
/// @inheritdoc IMUSD
function forceTransfer(
address frozenAccount,
address recipient,
uint256 amount
) external onlyRole(FORCED_TRANSFER_MANAGER_ROLE) {
_forceTransfer(frozenAccount, recipient, amount);
}
/// @inheritdoc IMUSD
function forceTransfers(
address[] calldata frozenAccounts,
address[] calldata recipients,
uint256[] calldata amounts
) external onlyRole(FORCED_TRANSFER_MANAGER_ROLE) {
if (frozenAccounts.length != recipients.length || frozenAccounts.length != amounts.length) {
revert ArrayLengthMismatch();
}
for (uint256 i; i < frozenAccounts.length; ++i) {
_forceTransfer(frozenAccounts[i], recipients[i], amounts[i]);
}
}
/* ============ Hooks For Internal Interactive Functions ============ */
/**
* @dev Hook called before wrapping M into mUSD.
* @param account The account from which M is deposited.
* @param recipient The account receiving the minted mUSD.
* @param amount The amount of tokens to wrap.
*/
function _beforeWrap(address account, address recipient, uint256 amount) internal view override {
_requireNotPaused();
super._beforeWrap(account, recipient, amount);
}
/**
* @dev Hook called before unwrapping mUSD.
* @param account The account from which mUSD is burned.
* @param amount The amount of tokens to unwrap.
*/
function _beforeUnwrap(address account, uint256 amount) internal view override {
_requireNotPaused();
super._beforeUnwrap(account, amount);
}
/**
* @dev Hook called before transferring mUSD.
* @param sender The address from which the tokens are being transferred.
* @param recipient The address to which the tokens are being transferred.
* @param amount The amount of tokens to transfer.
*/
function _beforeTransfer(address sender, address recipient, uint256 amount) internal view override {
_requireNotPaused();
super._beforeTransfer(sender, recipient, amount);
}
/**
* @dev Hook called before claiming yield.
* @dev MUST only be callable by the `YIELD_RECIPIENT_MANAGER_ROLE`.
* @dev Addresses with the `YIELD_RECIPIENT_MANAGER_ROLE`
* are still able to claim yield when the contract is paused.
*/
function _beforeClaimYield() internal view override onlyRole(YIELD_RECIPIENT_MANAGER_ROLE) {}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Internal ERC20 force transfer function to seize funds from a frozen account.
* @param frozenAccount The frozen account from which tokens are seized.
* @param recipient The recipient's address.
* @param amount The amount to be transferred.
* @dev Force transfer is only allowed for frozen accounts.
* @dev No `_beforeTransfer` checks apply to forced transfers; ignore checks for paused and frozen states.
* @dev Since this function can only be called by the `FORCED_TRANSFER_MANAGER_ROLE`,
* we do not check if the recipient is frozen.
*/
function _forceTransfer(address frozenAccount, address recipient, uint256 amount) internal {
_revertIfInvalidRecipient(recipient);
_revertIfNotFrozen(frozenAccount);
emit Transfer(frozenAccount, recipient, amount);
emit ForcedTransfer(frozenAccount, recipient, msg.sender, amount);
if (amount == 0) return;
_revertIfInsufficientBalance(frozenAccount, amount);
_update(frozenAccount, recipient, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
function __Pausable_init() internal onlyInitializing {
}
function __Pausable_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IERC20 } from "../../../lib/common/src/interfaces/IERC20.sol";
import { IMYieldToOne } from "./IMYieldToOne.sol";
import { Freezable } from "../../components/Freezable.sol";
import { MExtension } from "../../MExtension.sol";
abstract contract MYieldToOneStorageLayout {
/// @custom:storage-location erc7201:M0.storage.MYieldToOne
struct MYieldToOneStorageStruct {
uint256 totalSupply;
address yieldRecipient;
mapping(address account => uint256 balance) balanceOf;
}
// keccak256(abi.encode(uint256(keccak256("M0.storage.MYieldToOne")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _M_YIELD_TO_ONE_STORAGE_LOCATION =
0xee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af100;
function _getMYieldToOneStorageLocation() internal pure returns (MYieldToOneStorageStruct storage $) {
assembly {
$.slot := _M_YIELD_TO_ONE_STORAGE_LOCATION
}
}
}
/**
* @title MYieldToOne
* @notice Upgradeable ERC20 Token contract for wrapping M into a non-rebasing token
* with yield claimable by a single recipient.
* @author M0 Labs
*/
contract MYieldToOne is IMYieldToOne, MYieldToOneStorageLayout, MExtension, Freezable {
/* ============ Variables ============ */
/// @inheritdoc IMYieldToOne
bytes32 public constant YIELD_RECIPIENT_MANAGER_ROLE = keccak256("YIELD_RECIPIENT_MANAGER_ROLE");
/* ============ Constructor ============ */
/**
* @custom:oz-upgrades-unsafe-allow constructor
* @notice Constructs MYieldToOne Implementation contract
* @dev Sets immutable storage.
* @param mToken The address of $M token.
* @param swapFacility The address of Swap Facility.
*/
constructor(address mToken, address swapFacility) MExtension(mToken, swapFacility) {}
/* ============ Initializer ============ */
/**
* @dev Initializes the M extension token with yield claimable by a single recipient.
* @param name The name of the token (e.g. "M Yield to One").
* @param symbol The symbol of the token (e.g. "MYO").
* @param yieldRecipient_ The address of a yield destination.
* @param admin The address of an admin.
* @param freezeManager The address of a freeze manager.
* @param yieldRecipientManager The address of a yield recipient setter.
*/
function initialize(
string memory name,
string memory symbol,
address yieldRecipient_,
address admin,
address freezeManager,
address yieldRecipientManager
) public virtual initializer {
__MYieldToOne_init(name, symbol, yieldRecipient_, admin, freezeManager, yieldRecipientManager);
}
/**
* @notice Initializes the MYieldToOne token.
* @param name The name of the token (e.g. "M Yield to One").
* @param symbol The symbol of the token (e.g. "MYO").
* @param yieldRecipient_ The address of a yield destination.
* @param admin The address of an admin.
* @param freezeManager The address of a freeze manager.
* @param yieldRecipientManager The address of a yield recipient setter.
*/
function __MYieldToOne_init(
string memory name,
string memory symbol,
address yieldRecipient_,
address admin,
address freezeManager,
address yieldRecipientManager
) internal onlyInitializing {
if (yieldRecipientManager == address(0)) revert ZeroYieldRecipientManager();
if (admin == address(0)) revert ZeroAdmin();
__MExtension_init(name, symbol);
__Freezable_init(freezeManager);
_setYieldRecipient(yieldRecipient_);
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(YIELD_RECIPIENT_MANAGER_ROLE, yieldRecipientManager);
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IMYieldToOne
function claimYield() public returns (uint256) {
_beforeClaimYield();
uint256 yield_ = yield();
if (yield_ == 0) return 0;
emit YieldClaimed(yield_);
_mint(yieldRecipient(), yield_);
return yield_;
}
/// @inheritdoc IMYieldToOne
function setYieldRecipient(address account) external onlyRole(YIELD_RECIPIENT_MANAGER_ROLE) {
// Claim yield for the previous yield recipient.
claimYield();
_setYieldRecipient(account);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IERC20
function balanceOf(address account) public view override returns (uint256) {
return _getMYieldToOneStorageLocation().balanceOf[account];
}
/// @inheritdoc IERC20
function totalSupply() public view returns (uint256) {
return _getMYieldToOneStorageLocation().totalSupply;
}
/// @inheritdoc IMYieldToOne
function yield() public view returns (uint256) {
unchecked {
uint256 balance_ = _mBalanceOf(address(this));
uint256 totalSupply_ = totalSupply();
return balance_ > totalSupply_ ? balance_ - totalSupply_ : 0;
}
}
/// @inheritdoc IMYieldToOne
function yieldRecipient() public view returns (address) {
return _getMYieldToOneStorageLocation().yieldRecipient;
}
/* ============ Hooks For Internal Interactive Functions ============ */
/**
* @dev Hooks called before approval of M extension spend.
* @param account The account from which M is deposited.
* @param spender The account spending M Extension token.
*/
function _beforeApprove(address account, address spender, uint256 /* amount */) internal view virtual override {
FreezableStorageStruct storage $ = _getFreezableStorageLocation();
_revertIfFrozen($, account);
_revertIfFrozen($, spender);
}
/**
* @dev Hooks called before wrapping M into M Extension token.
* @param account The account from which M is deposited.
* @param recipient The account receiving the minted M Extension token.
*/
function _beforeWrap(address account, address recipient, uint256 /* amount */) internal view virtual override {
FreezableStorageStruct storage $ = _getFreezableStorageLocation();
_revertIfFrozen($, account);
_revertIfFrozen($, recipient);
}
/**
* @dev Hook called before unwrapping M Extension token.
* @param account The account from which M Extension token is burned.
*/
function _beforeUnwrap(address account, uint256 /* amount */) internal view virtual override {
_revertIfFrozen(_getFreezableStorageLocation(), account);
}
/**
* @dev Hook called before transferring M Extension token.
* @param sender The address from which the tokens are being transferred.
* @param recipient The address to which the tokens are being transferred.
*/
function _beforeTransfer(address sender, address recipient, uint256 /* amount */) internal view virtual override {
FreezableStorageStruct storage $ = _getFreezableStorageLocation();
_revertIfFrozen($, msg.sender);
_revertIfFrozen($, sender);
_revertIfFrozen($, recipient);
}
/**
* @dev Hook called before claiming yield from the M Extension token. To be overridden in derived extensions.
*/
function _beforeClaimYield() internal view virtual {}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Mints `amount` tokens to `recipient`.
* @param recipient The address whose account balance will be incremented.
* @param amount The present amount of tokens to mint.`
*/
function _mint(address recipient, uint256 amount) internal override {
MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();
// NOTE: Can be `unchecked` because the max amount of $M is never greater than `type(uint240).max`.
unchecked {
$.balanceOf[recipient] += amount;
$.totalSupply += amount;
}
emit Transfer(address(0), recipient, amount);
}
/**
* @dev Burns `amount` tokens from `account`.
* @param account The address whose account balance will be decremented.
* @param amount The present amount of tokens to burn.
*/
function _burn(address account, uint256 amount) internal override {
MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();
// NOTE: Can be `unchecked` because `_revertIfInsufficientBalance` is used in MExtension.
unchecked {
$.balanceOf[account] -= amount;
$.totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
}
/**
* @dev Internal balance update function called on transfer.
* @param sender The sender's address.
* @param recipient The recipient's address.
* @param amount The amount to be transferred.
*/
function _update(address sender, address recipient, uint256 amount) internal override {
MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();
// NOTE: Can be `unchecked` because `_revertIfInsufficientBalance` for `sender` is used in MExtension.
unchecked {
$.balanceOf[sender] -= amount;
$.balanceOf[recipient] += amount;
}
}
/**
* @dev Sets the yield recipient.
* @param yieldRecipient_ The address of the new yield recipient.
*/
function _setYieldRecipient(address yieldRecipient_) internal {
if (yieldRecipient_ == address(0)) revert ZeroYieldRecipient();
MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();
if (yieldRecipient_ == $.yieldRecipient) return;
$.yieldRecipient = yieldRecipient_;
emit YieldRecipientSet(yieldRecipient_);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title MUSD Interface
* @author M0 Labs
*
*/
interface IMUSD {
/* ============ Events ============ */
/**
* @notice Emitted when tokens are forcefully transferred from a frozen account.
* @param frozenAccount The address of the frozen account.
* @param recipient The address of the recipient.
* @param forcedTransferManager The address of the force transfer manager that triggered the event.
* @param amount The amount of tokens transferred.
*/
event ForcedTransfer(
address indexed frozenAccount,
address indexed recipient,
address indexed forcedTransferManager,
uint256 amount
);
/* ============ Custom Errors ============ */
/// @notice Emitted in constructor if Pauser is 0x0.
error ZeroPauser();
/// @notice Emitted in constructor if Force Transfer Manager is 0x0.
error ZeroForcedTransferManager();
/// @notice Emitted when the length of the input arrays do not match in `forceTransfer` method.
error ArrayLengthMismatch();
/* ============ Interactive Functions ============ */
/**
* @notice Pauses the contract.
* @dev Can only be called by an account with the PAUSER_ROLE.
* @dev When paused, wrap/unwrap and transfer of tokens are disabled.
* Approval is still enabled to allow users to change their allowances.
* Addresses with the FORCED_TRANSFER_MANAGER_ROLE can still transfer tokens from frozen accounts.
* Addresses with the FREEZE_MANAGER_ROLE can still freeze and unfreeze accounts.
* Addresses with the YIELD_RECIPIENT_MANAGER_ROLE can still claim yield.
*/
function pause() external;
/**
* @notice Unpauses the contract.
* @dev Can only be called by an account with the PAUSER_ROLE.
*/
function unpause() external;
/**
* @notice Forcefully transfers tokens from a frozen account to a recipient.
* @dev Can only be called by an account with the FORCED_TRANSFER_MANAGER_ROLE.
* @param frozenAccount The address of the frozen account.
* @param recipient The address of the recipient.
* @param amount The amount of tokens to transfer.
*/
function forceTransfer(address frozenAccount, address recipient, uint256 amount) external;
/**
* @notice Forcefully transfers tokens from frozen accounts to recipients.
* @dev Can only be called by an account with the FORCED_TRANSFER_MANAGER_ROLE.
* @param frozenAccounts The addresses of the frozen accounts.
* @param recipients The addresses of the recipients.
* @param amounts The amounts of tokens to transfer.
*/
function forceTransfers(
address[] calldata frozenAccounts,
address[] calldata recipients,
uint256[] calldata amounts
) external;
/* ============ View/Pure Functions ============ */
/// @notice The role that can pause and unpause the contract.
function PAUSER_ROLE() external view returns (bytes32);
/// @notice The role that can force transfer tokens from frozen accounts.
function FORCED_TRANSFER_MANAGER_ROLE() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 reinitialization) 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 Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
/**
* @title ERC20 Token Standard.
* @author M^0 Labs
* @dev The interface as defined by EIP-20: https://eips.ethereum.org/EIPS/eip-20
*/
interface IERC20 {
/* ============ Events ============ */
/**
* @notice Emitted when `spender` has been approved for `amount` of the token balance of `account`.
* @param account The address of the account.
* @param spender The address of the spender being approved for the allowance.
* @param amount The amount of the allowance being approved.
*/
event Approval(address indexed account, address indexed spender, uint256 amount);
/**
* @notice Emitted when `amount` tokens is transferred from `sender` to `recipient`.
* @param sender The address of the sender who's token balance is decremented.
* @param recipient The address of the recipient who's token balance is incremented.
* @param amount The amount of tokens being transferred.
*/
event Transfer(address indexed sender, address indexed recipient, uint256 amount);
/* ============ Interactive Functions ============ */
/**
* @notice Allows a calling account to approve `spender` to spend up to `amount` of its token balance.
* @dev MUST emit an `Approval` event.
* @param spender The address of the account being allowed to spend up to the allowed amount.
* @param amount The amount of the allowance being approved.
* @return Whether or not the approval was successful.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @notice Allows a calling account to transfer `amount` tokens to `recipient`.
* @param recipient The address of the recipient who's token balance will be incremented.
* @param amount The amount of tokens being transferred.
* @return Whether or not the transfer was successful.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @notice Allows a calling account to transfer `amount` tokens from `sender`, with allowance, to a `recipient`.
* @param sender The address of the sender who's token balance will be decremented.
* @param recipient The address of the recipient who's token balance will be incremented.
* @param amount The amount of tokens being transferred.
* @return Whether or not the transfer was successful.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the allowance `spender` is allowed to spend on behalf of `account`.
* @param account The address of the account who's token balance `spender` is allowed to spend.
* @param spender The address of an account allowed to spend on behalf of `account`.
* @return The amount `spender` can spend on behalf of `account`.
*/
function allowance(address account, address spender) external view returns (uint256);
/**
* @notice Returns the token balance of `account`.
* @param account The address of some account.
* @return The token balance of `account`.
*/
function balanceOf(address account) external view returns (uint256);
/// @notice Returns the number of decimals UIs should assume all amounts have.
function decimals() external view returns (uint8);
/// @notice Returns the name of the contract/token.
function name() external view returns (string memory);
/// @notice Returns the symbol of the token.
function symbol() external view returns (string memory);
/// @notice Returns the current total supply of the token.
function totalSupply() external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title M Extension where all yield is claimable by a single recipient.
* @author M0 Labs
*/
interface IMYieldToOne {
/* ============ Events ============ */
/**
* @notice Emitted when this contract's excess M is claimed.
* @param yield The amount of M yield claimed.
*/
event YieldClaimed(uint256 yield);
/**
* @notice Emitted when the yield recipient is set.
* @param yieldRecipient The address of the new yield recipient.
*/
event YieldRecipientSet(address indexed yieldRecipient);
/* ============ Custom Errors ============ */
/// @notice Emitted in constructor if Yield Recipient is 0x0.
error ZeroYieldRecipient();
/// @notice Emitted in constructor if Yield Recipient Manager is 0x0.
error ZeroYieldRecipientManager();
/// @notice Emitted in constructor if Admin is 0x0.
error ZeroAdmin();
/* ============ Interactive Functions ============ */
/// @notice Claims accrued yield to yield recipient.
function claimYield() external returns (uint256);
/**
* @notice Sets the yield recipient.
* @dev MUST only be callable by the YIELD_RECIPIENT_MANAGER_ROLE.
* @dev SHOULD revert if `yieldRecipient` is 0x0.
* @dev SHOULD return early if the `yieldRecipient` is already the actual yield recipient.
* @param yieldRecipient The address of the new yield recipient.
*/
function setYieldRecipient(address yieldRecipient) external;
/* ============ View/Pure Functions ============ */
/// @notice The role that can manage the yield recipient.
function YIELD_RECIPIENT_MANAGER_ROLE() external view returns (bytes32);
/// @notice The amount of accrued yield.
function yield() external view returns (uint256);
/// @notice The address of the yield recipient.
function yieldRecipient() external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {
AccessControlUpgradeable
} from "../../lib/common/lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol";
import { IFreezable } from "./IFreezable.sol";
abstract contract FreezableStorageLayout {
/// @custom:storage-location erc7201:M0.storage.Freezable
struct FreezableStorageStruct {
mapping(address account => bool isFrozen) isFrozen;
}
// keccak256(abi.encode(uint256(keccak256("M0.storage.Freezable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _FREEZABLE_STORAGE_LOCATION =
0x2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00;
function _getFreezableStorageLocation() internal pure returns (FreezableStorageStruct storage $) {
assembly {
$.slot := _FREEZABLE_STORAGE_LOCATION
}
}
}
/**
* @title Freezable
* @notice Upgradeable contract that allows for the freezing of accounts.
* @dev This contract is used to prevent certain accounts from interacting with the contract.
* @author M0 Labs
*/
abstract contract Freezable is IFreezable, FreezableStorageLayout, AccessControlUpgradeable {
/* ============ Variables ============ */
/// @inheritdoc IFreezable
bytes32 public constant FREEZE_MANAGER_ROLE = keccak256("FREEZE_MANAGER_ROLE");
/* ============ Initializer ============ */
/**
* @notice Initializes the contract with the given freeze manager.
* @param freezeManager The address of a freeze manager.
*/
function __Freezable_init(address freezeManager) internal onlyInitializing {
if (freezeManager == address(0)) revert ZeroFreezeManager();
_grantRole(FREEZE_MANAGER_ROLE, freezeManager);
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IFreezable
function freeze(address account) external onlyRole(FREEZE_MANAGER_ROLE) {
_freeze(_getFreezableStorageLocation(), account);
}
/// @inheritdoc IFreezable
function freezeAccounts(address[] calldata accounts) external onlyRole(FREEZE_MANAGER_ROLE) {
FreezableStorageStruct storage $ = _getFreezableStorageLocation();
for (uint256 i; i < accounts.length; ++i) {
_freeze($, accounts[i]);
}
}
/// @inheritdoc IFreezable
function unfreeze(address account) external onlyRole(FREEZE_MANAGER_ROLE) {
_unfreeze(_getFreezableStorageLocation(), account);
}
/// @inheritdoc IFreezable
function unfreezeAccounts(address[] calldata accounts) external onlyRole(FREEZE_MANAGER_ROLE) {
FreezableStorageStruct storage $ = _getFreezableStorageLocation();
for (uint256 i; i < accounts.length; ++i) {
_unfreeze($, accounts[i]);
}
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IFreezable
function isFrozen(address account) public view returns (bool) {
return _getFreezableStorageLocation().isFrozen[account];
}
/* ============ Internal Interactive Functions ============ */
/**
* @notice Internal function that freezes an account.
* @param $ The storage location of the freezable contract.
* @param account The account to freeze.
*/
function _freeze(FreezableStorageStruct storage $, address account) internal {
_revertIfFrozen($, account);
$.isFrozen[account] = true;
emit Frozen(account, block.timestamp);
}
/**
* @notice Internal function that unfreezes an account.
* @param $ The storage location of the freezable contract.
* @param account The account to unfreeze.
*/
function _unfreeze(FreezableStorageStruct storage $, address account) internal {
_revertIfNotFrozen($, account);
$.isFrozen[account] = false;
emit Unfrozen(account, block.timestamp);
}
/* ============ Internal View/Pure Functions ============ */
/**
* @notice Internal function that reverts if an account is frozen.
* @param $ The storage location of the freezable contract.
* @param account The account to check.
*/
function _revertIfFrozen(FreezableStorageStruct storage $, address account) internal view {
if ($.isFrozen[account]) revert AccountFrozen(account);
}
/**
* @notice Internal function that reverts if an account is frozen.
* @param account The account to check.
*/
function _revertIfFrozen(address account) internal view {
if (_getFreezableStorageLocation().isFrozen[account]) revert AccountFrozen(account);
}
/**
* @notice Internal function that reverts if an account is not frozen.
* @param $ The storage location of the freezable contract.
* @param account The account to check.
*/
function _revertIfNotFrozen(FreezableStorageStruct storage $, address account) internal view {
if (!$.isFrozen[account]) revert AccountNotFrozen(account);
}
/**
* @notice Internal function that reverts if an account is not frozen.
* @param account The account to check.
*/
function _revertIfNotFrozen(address account) internal view {
if (!_getFreezableStorageLocation().isFrozen[account]) revert AccountNotFrozen(account);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { ERC20ExtendedUpgradeable } from "../lib/common/src/ERC20ExtendedUpgradeable.sol";
import { IERC20 } from "../lib/common/src/interfaces/IERC20.sol";
import { IMTokenLike } from "./interfaces/IMTokenLike.sol";
import { IMExtension } from "./interfaces/IMExtension.sol";
import { ISwapFacility } from "./swap/interfaces/ISwapFacility.sol";
/**
* @title MExtension
* @notice Upgradeable ERC20 Token contract for wrapping M into a non-rebasing token.
* @author M0 Labs
*/
abstract contract MExtension is IMExtension, ERC20ExtendedUpgradeable {
/* ============ Variables ============ */
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
/// @inheritdoc IMExtension
address public immutable mToken;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
/// @inheritdoc IMExtension
address public immutable swapFacility;
/* ============ Modifiers ============ */
/// @dev Modifier to check if caller is SwapFacility.
modifier onlySwapFacility() {
if (msg.sender != swapFacility) revert NotSwapFacility();
_;
}
/* ============ Constructor ============ */
/**
* @custom:oz-upgrades-unsafe-allow constructor
* @notice Constructs MExtension Implementation contract
* @dev Sets immutable storage.
* @param mToken_ The address of $M token.
* @param swapFacility_ The address of Swap Facility.
*/
constructor(address mToken_, address swapFacility_) {
_disableInitializers();
if ((mToken = mToken_) == address(0)) revert ZeroMToken();
if ((swapFacility = swapFacility_) == address(0)) revert ZeroSwapFacility();
}
/* ============ Initializer ============ */
/**
* @notice Initializes the generic M extension token.
* @param name The name of the token (e.g. "HALO USD").
* @param symbol The symbol of the token (e.g. "HUSD").
*/
function __MExtension_init(string memory name, string memory symbol) internal onlyInitializing {
__ERC20ExtendedUpgradeable_init(name, symbol, 6);
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IMExtension
function wrap(address recipient, uint256 amount) external onlySwapFacility {
// NOTE: `msg.sender` is always SwapFacility contract.
// `ISwapFacility.msgSender()` is used to ensure that the original caller is passed to `_beforeWrap`.
_wrap(ISwapFacility(msg.sender).msgSender(), recipient, amount);
}
/// @inheritdoc IMExtension
function unwrap(address /* recipient */, uint256 amount) external onlySwapFacility {
// NOTE: `msg.sender` is always SwapFacility contract.
// `ISwapFacility.msgSender()` is used to ensure that the original caller is passed to `_beforeUnwrap`.
// NOTE: `recipient` is not used in this function as the $M is always sent to SwapFacility contract.
_unwrap(ISwapFacility(msg.sender).msgSender(), amount);
}
/// @inheritdoc IMExtension
function enableEarning() external virtual {
if (isEarningEnabled()) revert EarningIsEnabled();
emit EarningEnabled(currentIndex());
IMTokenLike(mToken).startEarning();
}
/// @inheritdoc IMExtension
function disableEarning() external virtual {
if (!isEarningEnabled()) revert EarningIsDisabled();
emit EarningDisabled(currentIndex());
IMTokenLike(mToken).stopEarning(address(this));
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IMExtension
function currentIndex() public view virtual returns (uint128) {
return IMTokenLike(mToken).currentIndex();
}
/// @inheritdoc IMExtension
function isEarningEnabled() public view virtual returns (bool) {
return IMTokenLike(mToken).isEarning(address(this));
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256);
/* ============ Hooks For Internal Interactive Functions ============ */
/**
* @dev Hook called before approval of M Extension token.
* @param account The sender's address.
* @param spender The spender address.
* @param amount The amount to be approved.
*/
function _beforeApprove(address account, address spender, uint256 amount) internal virtual {}
/**
* @dev Hook called before wrapping M into M Extension token.
* @param account The account from which M is deposited.
* @param recipient The account receiving the minted M Extension token.
* @param amount The amount of M deposited.
*/
function _beforeWrap(address account, address recipient, uint256 amount) internal virtual {}
/**
* @dev Hook called before unwrapping M Extension token.
* @param account The account from which M Extension token is burned.
* @param amount The amount of M Extension token burned.
*/
function _beforeUnwrap(address account, uint256 amount) internal virtual {}
/**
* @dev Hook called before transferring M Extension token.
* @param sender The sender's address.
* @param recipient The recipient's address.
* @param amount The amount to be transferred.
*/
function _beforeTransfer(address sender, address recipient, uint256 amount) internal virtual {}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Approve `spender` to spend `amount` of tokens from `account`.
* @param account The address approving the allowance.
* @param spender The address approved to spend the tokens.
* @param amount The amount of tokens being approved for spending.
*/
function _approve(address account, address spender, uint256 amount) internal override {
// NOTE: Add extension-specific checks before approval.
_beforeApprove(account, spender, amount);
super._approve(account, spender, amount);
}
/**
* @dev Wraps `amount` M from `account` into M Extension for `recipient`.
* @param account The original caller of SwapFacility functions.
* @param recipient The account receiving the minted M Extension token.
* @param amount The amount of M deposited.
*/
function _wrap(address account, address recipient, uint256 amount) internal {
_revertIfInvalidRecipient(recipient);
_revertIfInsufficientAmount(amount);
// NOTE: Add extension-specific checks before wrapping.
_beforeWrap(account, recipient, amount);
// NOTE: `msg.sender` is always SwapFacility contract.
// NOTE: The behavior of `IMTokenLike.transferFrom` is known, so its return can be ignored.
IMTokenLike(mToken).transferFrom(msg.sender, address(this), amount);
// NOTE: This method is overridden by the inheriting M Extension contract.
// NOTE: Mints precise amount of $M Extension token to `recipient`.
// Option 1: $M transfer from an $M earner to another $M earner ($M Extension in earning state): rounds up → rounds up,
// 0, 1, or XX extra wei may be locked in M Extension compared to the minted amount of $M Extension token.
// Option 2: $M transfer from an $M non-earner to an $M earner ($M Extension in earning state): precise $M transfer → rounds down,
// 0, -1, or -XX wei may be locked in $M Extension compared to the minted amount of $M Extension token.
//
_mint(recipient, amount);
}
/**
* @dev Unwraps `amount` M Extension token from `account` into $M and transfers to SwapFacility.
* @param account The original caller of SwapFacility functions.
* @param amount The amount of M Extension token burned.
*/
function _unwrap(address account, uint256 amount) internal {
_revertIfInsufficientAmount(amount);
// NOTE: Add extension-specific checks before unwrapping.
_beforeUnwrap(account, amount);
_revertIfInsufficientBalance(msg.sender, amount);
// NOTE: This method will be overridden by the inheriting M Extension contract.
// NOTE: Computes the actual decrease in the $M balance of the $M Extension contract.
// Option 1: $M transfer from an $M earner ($M Extension in earning state) to another $M earner: round up → rounds up.
// Option 2: $M transfer from an $M earner ($M Extension in earning state) to an $M non-earner: round up → precise $M transfer.
// In both cases, 0, 1, or XX extra wei may be deducted from the $M Extension contract's $M balance compared to the burned amount of $M Extension token.
// NOTE: Always burn from SwapFacility as it is the only contract that can call this function.
_burn(msg.sender, amount);
// NOTE: The behavior of `IMTokenLike.transfer` is known, so its return can be ignored.
// NOTE: `msg.sender` is always SwapFacility contract.
IMTokenLike(mToken).transfer(msg.sender, amount);
}
/**
* @dev Mints `amount` tokens to `recipient`.
* @param recipient The address to which the tokens will be minted.
* @param amount The amount of tokens to mint.
*/
function _mint(address recipient, uint256 amount) internal virtual;
/**
* @dev Burns `amount` tokens from `account`.
* @param account The address from which the tokens will be burned.
* @param amount The amount of tokens to burn.
*/
function _burn(address account, uint256 amount) internal virtual;
/**
* @dev Internal balance update function that needs to be implemented by the inheriting contract.
* @param sender The sender's address.
* @param recipient The recipient's address.
* @param amount The amount to be transferred.
*/
function _update(address sender, address recipient, uint256 amount) internal virtual;
/**
* @dev Internal ERC20 transfer function.
* @param sender The sender's address.
* @param recipient The recipient's address.
* @param amount The amount to be transferred.
*/
function _transfer(address sender, address recipient, uint256 amount) internal override {
_revertIfInvalidRecipient(recipient);
// NOTE: Add extension-specific checks before transfers.
_beforeTransfer(sender, recipient, amount);
emit Transfer(sender, recipient, amount);
if (amount == 0) return;
_revertIfInsufficientBalance(sender, amount);
// NOTE: This method will be overridden by the inheriting M Extension contract.
_update(sender, recipient, amount);
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the M Token balance of `account`.
* @param account The account being queried.
* @return balance The M Token balance of the account.
*/
function _mBalanceOf(address account) internal view returns (uint256) {
return IMTokenLike(mToken).balanceOf(account);
}
/**
* @dev Reverts if `recipient` is address(0).
* @param recipient Address of a recipient.
*/
function _revertIfInvalidRecipient(address recipient) internal pure {
if (recipient == address(0)) revert InvalidRecipient(recipient);
}
/**
* @dev Reverts if `amount` is equal to 0.
* @param amount Amount of token.
*/
function _revertIfInsufficientAmount(uint256 amount) internal pure {
if (amount == 0) revert InsufficientAmount(amount);
}
/**
* @dev Reverts if `account` balance is below `amount`.
* @param account Address of an account.
* @param amount Amount to transfer or burn.
*/
function _revertIfInsufficientBalance(address account, uint256 amount) internal view {
uint256 balance = balanceOf(account);
if (balance < amount) revert InsufficientBalance(account, balance, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title Freezable interface.
* @author M0 Labs
*/
interface IFreezable {
/* ============ Events ============ */
/**
* @notice Emitted when an account is frozen.
* @param account The address of the frozen account.
* @param timestamp The timestamp at which the account was frozen.
*/
event Frozen(address indexed account, uint256 timestamp);
/**
* @notice Emitted when an account is unfrozen.
* @param account The address of the unfrozen account.
* @param timestamp The timestamp at which the account was unfrozen.
*/
event Unfrozen(address indexed account, uint256 timestamp);
/* ============ Errors ============ */
/**
* @notice Emitted when a frozen account attempts to interact with the contract.
* @param account The address of the frozen account.
*/
error AccountFrozen(address account);
/**
* @notice Emitted when trying to unfreeze a non-frozen account.
* @param account The address of the account that is not frozen.
*/
error AccountNotFrozen(address account);
/// @notice Emitted if no freeze manager is set.
error ZeroFreezeManager();
/* ============ Interactive Functions ============ */
/**
* @notice Freezes an account.
* @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
* @dev SHOULD revert if the account is already frozen.
* @param account The address of the account to freeze.
*/
function freeze(address account) external;
/**
* @notice Freezes multiple accounts.
* @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
* @dev SHOULD revert if any of the accounts are already frozen.
* @param accounts The list of addresses to freeze.
*/
function freezeAccounts(address[] calldata accounts) external;
/**
* @notice Unfreezes an account.
* @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
* @dev SHOULD revert if the account is not frozen.
* @param account The address of the account to unfreeze.
*/
function unfreeze(address account) external;
/**
* @notice Unfreezes multiple accounts.
* @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
* @dev SHOULD revert if any of the accounts are not frozen.
* @param accounts The list of addresses to unfreeze.
*/
function unfreezeAccounts(address[] calldata accounts) external;
/* ============ View/Pure Functions ============ */
/// @notice The role that can manage the freezelist.
function FREEZE_MANAGER_ROLE() external view returns (bytes32);
/**
* @notice Returns whether an account is frozen or not.
* @param account The address of the account to check.
* @return True if the account is frozen, false otherwise.
*/
function isFrozen(address account) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { ERC3009Upgradeable } from "./ERC3009Upgradeable.sol";
import { IERC20 } from "./interfaces/IERC20.sol";
import { IERC20Extended } from "./interfaces/IERC20Extended.sol";
abstract contract ERC20ExtendedUpgradeableStorageLayout {
/// @custom:storage-location erc7201:M0.storage.ERC20Extended
struct ERC20ExtendedStorageStruct {
mapping(address account => mapping(address spender => uint256 allowance)) allowance;
uint8 decimals;
string symbol;
}
// keccak256(abi.encode(uint256(keccak256("M0.storage.ERC20Extended")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _ERC20_EXTENDED_STORAGE_LOCATION =
0xcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100;
function _getERC20ExtendedStorageLocation() internal pure returns (ERC20ExtendedStorageStruct storage $) {
assembly {
$.slot := _ERC20_EXTENDED_STORAGE_LOCATION
}
}
}
/**
* @title An upgradeable ERC20 token extended with EIP-2612 permits for signed approvals
* (via EIP-712 and with EIP-1271 and EIP-5267 compatibility).
* @author M0 Labs
*/
abstract contract ERC20ExtendedUpgradeable is
ERC20ExtendedUpgradeableStorageLayout,
ERC3009Upgradeable,
IERC20Extended
{
/* ============ Variables ============ */
/**
* @inheritdoc IERC20Extended
* @dev Keeping this constant, despite `permit` parameter name differences, to ensure max EIP-2612 compatibility.
* keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
*/
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/* ============ Initializer ============ */
function __ERC20ExtendedUpgradeable_init(
string memory name_,
string memory symbol_,
uint8 decimals_
) internal onlyInitializing {
__ERC3009Upgradeable_init(name_);
ERC20ExtendedStorageStruct storage $ = _getERC20ExtendedStorageLocation();
$.decimals = decimals_;
$.symbol = symbol_;
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IERC20
function approve(address spender_, uint256 amount_) external returns (bool) {
_approve(msg.sender, spender_, amount_);
return true;
}
/// @inheritdoc IERC20Extended
function permit(
address owner_,
address spender_,
uint256 value_,
uint256 deadline_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external {
_revertIfInvalidSignature(owner_, _permitAndGetDigest(owner_, spender_, value_, deadline_), v_, r_, s_);
}
/// @inheritdoc IERC20Extended
function permit(
address owner_,
address spender_,
uint256 value_,
uint256 deadline_,
bytes memory signature_
) external {
_revertIfInvalidSignature(owner_, _permitAndGetDigest(owner_, spender_, value_, deadline_), signature_);
}
/// @inheritdoc IERC20
function transfer(address recipient_, uint256 amount_) external returns (bool) {
_transfer(msg.sender, recipient_, amount_);
return true;
}
/// @inheritdoc IERC20
function transferFrom(address sender_, address recipient_, uint256 amount_) external returns (bool) {
ERC20ExtendedStorageStruct storage $ = _getERC20ExtendedStorageLocation();
uint256 spenderAllowance_ = $.allowance[sender_][msg.sender]; // Cache `spenderAllowance_` to stack.
if (spenderAllowance_ != type(uint256).max) {
if (spenderAllowance_ < amount_) revert InsufficientAllowance(msg.sender, spenderAllowance_, amount_);
unchecked {
_setAllowance($, sender_, msg.sender, spenderAllowance_ - amount_);
}
}
_transfer(sender_, recipient_, amount_);
return true;
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IERC20
function allowance(address account, address spender) public view returns (uint256) {
return _getERC20ExtendedStorageLocation().allowance[account][spender];
}
/// @inheritdoc IERC20
function decimals() external view virtual returns (uint8) {
return _getERC20ExtendedStorageLocation().decimals;
}
/// @inheritdoc IERC20
function name() external view virtual returns (string memory) {
return _getERC712ExtendedStorageLocation().name;
}
/// @inheritdoc IERC20
function symbol() external view virtual returns (string memory) {
return _getERC20ExtendedStorageLocation().symbol;
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Approve `spender_` to spend `amount_` of tokens from `account_`.
* @param account_ The address approving the allowance.
* @param spender_ The address approved to spend the tokens.
* @param amount_ The amount of tokens being approved for spending.
*/
function _approve(address account_, address spender_, uint256 amount_) internal virtual {
_setAllowance(_getERC20ExtendedStorageLocation(), account_, spender_, amount_);
emit Approval(account_, spender_, amount_);
}
/**
* @dev Set the `amount_` of tokens `spender_` is allowed to spend from `account_`.
* @param $ ERC20Extended storage location.
* @param account_ The address for which the allowance is set.
* @param spender_ The address allowed to spend the tokens.
* @param amount_ The amount of tokens being allowed for spending.
*/
function _setAllowance(
ERC20ExtendedStorageStruct storage $,
address account_,
address spender_,
uint256 amount_
) internal virtual {
$.allowance[account_][spender_] = amount_;
}
/**
* @dev Performs the approval based on the permit info, validates the deadline, and returns the digest.
* @param owner_ The address of the account approving the allowance.
* @param spender_ The address of the account being allowed to spend the tokens.
* @param amount_ The amount of tokens being approved for spending.
* @param deadline_ The deadline by which the signature must be used.
* @return digest_ The EIP-712 digest of the permit.
*/
function _permitAndGetDigest(
address owner_,
address spender_,
uint256 amount_,
uint256 deadline_
) internal virtual returns (bytes32) {
_revertIfExpired(deadline_);
_approve(owner_, spender_, amount_);
unchecked {
// Nonce realistically cannot overflow.
return
_getDigest(
keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner_,
spender_,
amount_,
_getStatefulERC712ExtendedStorageLocation().nonces[owner_]++,
deadline_
)
)
);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title Subset of M Token interface required for source contracts.
* @author M0 Labs
*/
interface IMTokenLike {
/* ============ Custom Errors ============ */
/// @notice Emitted when calling `stopEarning` for an account approved as earner by TTG.
error IsApprovedEarner();
/// @notice Emitted when calling `startEarning` for an account not approved as earner by TTG.
error NotApprovedEarner();
/* ============ Interactive Functions ============ */
/**
* @notice Allows a calling account to approve `spender` to spend up to `amount` of its token balance.
* @dev MUST emit an `Approval` event.
* @param spender The address of the account being allowed to spend up to the allowed amount.
* @param amount The amount of the allowance being approved.
* @return Whether or not the approval was successful.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
* @param owner The address of the account who's token balance is being approved to be spent by `spender`.
* @param spender The address of an account allowed to spend on behalf of `owner`.
* @param value The amount of the allowance being approved.
* @param deadline The last timestamp where the signature is still valid.
* @param v An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param r An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param s An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
* @param owner The address of the account who's token balance is being approved to be spent by `spender`.
* @param spender The address of an account allowed to spend on behalf of `owner`.
* @param value The amount of the allowance being approved.
* @param deadline The last timestamp where the signature is still valid.
* @param signature An arbitrary signature (EIP-712).
*/
function permit(address owner, address spender, uint256 value, uint256 deadline, bytes memory signature) external;
/**
* @notice Allows a calling account to transfer `amount` tokens to `recipient`.
* @param recipient The address of the recipient who's token balance will be incremented.
* @param amount The amount of tokens being transferred.
* @return success Whether or not the transfer was successful.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @notice Allows a calling account to transfer `amount` tokens from `sender`, with allowance, to a `recipient`.
* @param sender The address of the sender who's token balance will be decremented.
* @param recipient The address of the recipient who's token balance will be incremented.
* @param amount The amount of tokens being transferred.
* @return success Whether or not the transfer was successful.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/// @notice Starts earning for caller if allowed by the Registrar.
function startEarning() external;
/**
* @notice Stops earning for `account`.
* @dev MUST revert if `account` is an approved earner in TTG Registrar.
* @param account The account to stop earning for.
*/
function stopEarning(address account) external;
/* ============ View/Pure Functions ============ */
/**
* @notice Checks if account is an earner.
* @param account The account to check.
* @return earning True if account is an earner, false otherwise.
*/
function isEarning(address account) external view returns (bool);
/**
* @notice Returns the token balance of `account`.
* @param account The address of some account.
* @return balance The token balance of `account`.
*/
function balanceOf(address account) external view returns (uint256);
/// @notice The current index that would be written to storage if `updateIndex` is called.
function currentIndex() external view returns (uint128);
/// @notice The current value of earner rate in basis points.
function earnerRate() external view returns (uint32);
/// @notice Returns the EIP712 domain separator used in the encoding of a signed digest.
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice Returns the EIP712 typehash used in the encoding of the digest for the permit function.
function PERMIT_TYPEHASH() external view returns (bytes32);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.sol";
/**
* @title M Extension interface extending Extended ERC20,
* includes additional enable/disable earnings and index logic.
* @author M0 Labs
*/
interface IMExtension is IERC20Extended {
/* ============ Events ============ */
/**
* @notice Emitted when M extension earning is enabled.
* @param index The index at the moment earning is enabled.
*/
event EarningEnabled(uint128 index);
/**
* @notice Emitted when M extension earning is disabled.
* @param index The index at the moment earning is disabled.
*/
event EarningDisabled(uint128 index);
/* ============ Custom Errors ============ */
/// @notice Emitted when performing an operation that is not allowed when earning is disabled.
error EarningIsDisabled();
/// @notice Emitted when performing an operation that is not allowed when earning is enabled.
error EarningIsEnabled();
/**
* @notice Emitted when there is insufficient balance to decrement from `account`.
* @param account The account with insufficient balance.
* @param balance The balance of the account.
* @param amount The amount to decrement.
*/
error InsufficientBalance(address account, uint256 balance, uint256 amount);
/// @notice Emitted in constructor if M Token is 0x0.
error ZeroMToken();
/// @notice Emitted in constructor if Swap Facility is 0x0.
error ZeroSwapFacility();
/// @notice Emitted in `wrap` and `unwrap` functions if the caller is not the Swap Facility.
error NotSwapFacility();
/* ============ Interactive Functions ============ */
/**
* @notice Enables earning of extension token if allowed by the TTG Registrar and if it has never been done.
* @dev SHOULD be virtual to allow extensions to override it.
*/
function enableEarning() external;
/**
* @notice Disables earning of extension token if disallowed by the TTG Registrar and if it has never been done.
* @dev SHOULD be virtual to allow extensions to override it.
*/
function disableEarning() external;
/**
* @notice Wraps `amount` M from the caller into extension token for `recipient`.
* @dev Can only be called by the SwapFacility.
* @param recipient The account receiving the minted M extension token.
* @param amount The amount of M extension token minted.
*/
function wrap(address recipient, uint256 amount) external;
/**
* @notice Unwraps `amount` extension token from the caller into M for `recipient`.
* @dev Can only be called by the SwapFacility.
* @param recipient The account receiving the withdrawn M,
* it will always be the SwapFacility (keep `recipient` for backward compatibility).
* @param amount The amount of M extension token burned.
*/
function unwrap(address recipient, uint256 amount) external;
/* ============ View/Pure Functions ============ */
/// @notice The address of the M Token contract.
function mToken() external view returns (address);
/// @notice The address of the SwapFacility contract.
function swapFacility() external view returns (address);
/**
* @notice Whether M extension earning is enabled.
* @dev SHOULD be virtual to allow extensions to override it.
*/
function isEarningEnabled() external view returns (bool);
/**
* @notice Returns the current index for M extension earnings.
* @dev SHOULD be virtual to allow extensions to override it.
*/
function currentIndex() external view returns (uint128);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title Swap Facility interface.
* @author M0 Labs
*/
interface ISwapFacility {
/* ============ Events ============ */
/**
* @notice Emitted when $M Extension is swapped for another $M Extension.
* @param extensionIn The address of the input $M Extension.
* @param extensionOut The address of the output $M Extension.
* @param amount The amount swapped.
* @param recipient The address to receive the output $M Extension token.
*/
event Swapped(address indexed extensionIn, address indexed extensionOut, uint256 amount, address indexed recipient);
/**
* @notice Emitted when $M token is swapped for $M Extension.
* @param extensionOut The address of the output $M Extension.
* @param amount The amount swapped.
* @param recipient The address to receive the output $M Extension token.
*/
event SwappedInM(address indexed extensionOut, uint256 amount, address indexed recipient);
/**
* @notice Emitted when $M Extension is swapped for $M token.
* @param extensionIn The address of the input $M Extension.
* @param amount The amount swapped.
* @param recipient The address to receive the $M token.
*/
event SwappedOutM(address indexed extensionIn, uint256 amount, address indexed recipient);
/**
* @notice Emitted when an $M Extension is set as permissioned or not.
* @param extension The address of an $M Extension.
* @param allowed True if the extension is allowed, false otherwise.
*/
event PermissionedExtensionSet(address indexed extension, bool allowed);
/**
* @notice Emitted when a `swapper` is allowed or not to swap the permissioned `extension` from/to M.
* @param extension The address of an $M extension.
* @param swapper The address of the swapper.
* @param allowed True if the swapper is allowed, false otherwise.
*/
event PermissionedMSwapperSet(address indexed extension, address indexed swapper, bool allowed);
/* ============ Custom Errors ============ */
/// @notice Thrown in the constructor if $M Token is 0x0.
error ZeroMToken();
/// @notice Thrown in the constructor if Registrar is 0x0.
error ZeroRegistrar();
/// @notice Thrown in `setPermissionedMSwapper()` if the $M extension is 0x0.
error ZeroExtension();
/// @notice Thrown in `setPermissionedMSwapper()` if the swapper is 0x0.
error ZeroSwapper();
/// @notice Thrown in `swap` and `swapM` functions if the extension is not TTG approved earner.
error NotApprovedExtension(address extension);
/// @notice Thrown in `swapInM` and `swapOutM` functions if `swapper` is not approved to swap a permissioned `extension`.
error NotApprovedPermissionedSwapper(address extension, address swapper);
/// @notice Thrown in `swapInM` and `swapOutM` functions if `swapper` is not approved to swap the `extension`.
error NotApprovedSwapper(address extension, address swapper);
/// @notice Thrown in `swap` function if the extension is permissioned.
error PermissionedExtension(address extension);
/* ============ Interactive Functions ============ */
/**
* @notice Swaps one $M Extension to another.
* @param extensionIn The address of the $M Extension to swap from.
* @param extensionOut The address of the $M Extension to swap to.
* @param amount The amount to swap.
* @param recipient The address to receive the swapped $M Extension tokens.
*/
function swap(address extensionIn, address extensionOut, uint256 amount, address recipient) external;
/**
* @notice Swaps one $M Extension to another using permit.
* @param extensionIn The address of the $M Extension to swap from.
* @param extensionOut The address of the $M Extension to swap to.
* @param amount The amount to swap.
* @param recipient The address to receive the swapped $M Extension tokens.
* @param deadline The last timestamp where the signature is still valid.
* @param v An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param r An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param s An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
*/
function swapWithPermit(
address extensionIn,
address extensionOut,
uint256 amount,
address recipient,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Swaps one $M Extension to another using permit.
* @param extensionIn The address of the $M Extension to swap from.
* @param extensionOut The address of the $M Extension to swap to.
* @param amount The amount to swap.
* @param recipient The address to receive the swapped $M Extension tokens.
* @param deadline The last timestamp where the signature is still valid.
* @param signature An arbitrary signature (EIP-712).
*/
function swapWithPermit(
address extensionIn,
address extensionOut,
uint256 amount,
address recipient,
uint256 deadline,
bytes calldata signature
) external;
/**
* @notice Swaps $M token to $M Extension.
* @param extensionOut The address of the M Extension to swap to.
* @param amount The amount of $M token to swap.
* @param recipient The address to receive the swapped $M Extension tokens.
*/
function swapInM(address extensionOut, uint256 amount, address recipient) external;
/**
* @notice Swaps $M token to $M Extension using permit.
* @param extensionOut The address of the M Extension to swap to.
* @param amount The amount of $M token to swap.
* @param recipient The address to receive the swapped $M Extension tokens.
* @param deadline The last timestamp where the signature is still valid.
* @param v An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param r An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param s An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
*/
function swapInMWithPermit(
address extensionOut,
uint256 amount,
address recipient,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Swaps $M token to $M Extension using permit.
* @param extensionOut The address of the M Extension to swap to.
* @param amount The amount of $M token to swap.
* @param recipient The address to receive the swapped $M Extension tokens.
* @param deadline The last timestamp where the signature is still valid.
* @param signature An arbitrary signature (EIP-712).
*/
function swapInMWithPermit(
address extensionOut,
uint256 amount,
address recipient,
uint256 deadline,
bytes calldata signature
) external;
/**
* @notice Swaps $M Extension to $M token.
* @param extensionIn The address of the $M Extension to swap from.
* @param amount The amount of $M Extension tokens to swap.
* @param recipient The address to receive $M tokens.
*/
function swapOutM(address extensionIn, uint256 amount, address recipient) external;
/**
* @notice Swaps $M Extension to $M token using permit.
* @param extensionIn The address of the $M Extension to swap from.
* @param amount The amount of $M Extension tokens to swap.
* @param recipient The address to receive $M tokens.
* @param deadline The last timestamp where the signature is still valid.
* @param v An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param r An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param s An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
*/
function swapOutMWithPermit(
address extensionIn,
uint256 amount,
address recipient,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Swaps $M Extension to $M token using permit.
* @param extensionIn The address of the $M Extension to swap from.
* @param amount The amount of $M Extension tokens to swap.
* @param recipient The address to receive $M tokens.
* @param deadline The last timestamp where the signature is still valid.
* @param signature An arbitrary signature (EIP-712).
*/
function swapOutMWithPermit(
address extensionIn,
uint256 amount,
address recipient,
uint256 deadline,
bytes calldata signature
) external;
/**
* @notice Sets whether the `extension` is permissioned.
* @dev MUST only be callable by an address with the `DEFAULT_ADMIN_ROLE` role.
* @param extension The address of an $M Extension.
* @param permissioned True if the extension is permissioned, false otherwise.
*/
function setPermissionedExtension(address extension, bool permissioned) external;
/**
* @notice Sets whether `swapper` is allowed to swap the permissioned `extension` from/to M.
* @dev MUST only be callable by an address with the `DEFAULT_ADMIN_ROLE` role.
* @param extension The address of an extension to set permission for.
* @param swapper The address of the swapper to set permission for.
* @param allowed True if the swapper is allowed, false otherwise.
*/
function setPermissionedMSwapper(address extension, address swapper, bool allowed) external;
/* ============ View/Pure Functions ============ */
/// @notice The address of the $M Token contract.
function mToken() external view returns (address mToken);
/// @notice The address of the Registrar.
function registrar() external view returns (address registrar);
/**
* @notice Returns the address that called `swap` or `swapM`
* @dev Must be used instead of `msg.sender` in $M Extensions contracts to get the original sender.
*/
function msgSender() external view returns (address msgSender);
/**
* @notice Checks if the extension is permissioned.
* @param extension The extension address to check.
* @return true if allowed, false otherwise.
*/
function isPermissionedExtension(address extension) external view returns (bool);
/**
* @notice Checks if `swapper` is allowed to swap the permissioned extension from/to M.
* @param extension The $M extension address.
* @param swapper The swapper address to check.
* @return true if allowed, false otherwise.
*/
function isPermissionedMSwapper(address extension, address swapper) external view returns (bool);
/**
* @notice Checks if `swapper` is allowed to swap the permissionless (common) extension from/to M.
* @param swapper The swapper address to check.
* @return true if allowed, false otherwise.
*/
function isMSwapper(address swapper) external view returns (bool);
/// @notice The parameter name in the Registrar that defines the earners list.
function EARNERS_LIST_NAME() external pure returns (bytes32);
/// @notice The parameter name in the Registrar that defines whether to ignore the earners list.
function EARNERS_LIST_IGNORED_KEY() external pure returns (bytes32);
/// @notice Swapper role for permissioned extensions.
function M_SWAPPER_ROLE() external pure returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted to signal this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { Initializable } from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import { IERC3009 } from "./interfaces/IERC3009.sol";
import { StatefulERC712Upgradeable } from "./StatefulERC712Upgradeable.sol";
abstract contract ERC3009UpgradeableStorageLayout {
/// @custom:storage-location erc7201:M0.storage.ERC3009
struct ERC3009StorageStruct {
mapping(address authorizer => mapping(bytes32 nonce => bool isNonceUsed)) authorizationState;
}
// keccak256(abi.encode(uint256(keccak256("M0.storage.ERC3009")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _ERC3009_STORAGE_LOCATION =
0x1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a4888600;
function _getERC3009StorageLocation() internal pure returns (ERC3009StorageStruct storage $) {
assembly {
$.slot := _ERC3009_STORAGE_LOCATION
}
}
}
/**
* @title ERC3009 implementation allowing the transfer of fungible assets via a signed authorization.
* @author M0 Labs
* @dev Inherits from ERC712ExtendedUpgradeable and StatefulERC712Upgradeable.
*/
abstract contract ERC3009Upgradeable is IERC3009, ERC3009UpgradeableStorageLayout, StatefulERC712Upgradeable {
/* ============ Variables ============ */
// solhint-disable-next-line max-line-length
/// @dev keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
/// @inheritdoc IERC3009
bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
// solhint-disable-next-line max-line-length
/// @dev keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
/// @inheritdoc IERC3009
bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH =
0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
/**
* @inheritdoc IERC3009
* @dev keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
*/
bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH =
0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
/* ============ Initializer ============ */
/**
* @notice Initializes the ERC3009Upgradeable contract.
* @param name_ The name of the contract.
*/
function __ERC3009Upgradeable_init(string memory name_) internal onlyInitializing {
__StatefulERC712ExtendedUpgradeable_init(name_);
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IERC3009
function transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes memory signature_
) external {
_revertIfInvalidSignature(
from_,
_getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
signature_
);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes32 r_,
bytes32 vs_
) external {
_revertIfInvalidSignature(
from_,
_getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
r_,
vs_
);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external {
_revertIfInvalidSignature(
from_,
_getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
v_,
r_,
s_
);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes memory signature_
) external {
_revertIfInvalidSignature(
from_,
_getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
signature_
);
_receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes32 r_,
bytes32 vs_
) external {
_revertIfInvalidSignature(
from_,
_getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
r_,
vs_
);
_receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external {
_revertIfInvalidSignature(
from_,
_getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
v_,
r_,
s_
);
_receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function cancelAuthorization(address authorizer_, bytes32 nonce_, bytes memory signature_) external {
_revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), signature_);
_cancelAuthorization(authorizer_, nonce_);
}
/// @inheritdoc IERC3009
function cancelAuthorization(address authorizer_, bytes32 nonce_, bytes32 r_, bytes32 vs_) external {
_revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), r_, vs_);
_cancelAuthorization(authorizer_, nonce_);
}
/// @inheritdoc IERC3009
function cancelAuthorization(address authorizer_, bytes32 nonce_, uint8 v_, bytes32 r_, bytes32 s_) external {
_revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), v_, r_, s_);
_cancelAuthorization(authorizer_, nonce_);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IERC3009
function authorizationState(address authorizer, bytes32 nonce) public view returns (bool) {
return _getERC3009StorageLocation().authorizationState[authorizer][nonce];
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Common transfer function used by `transferWithAuthorization` and `_receiveWithAuthorization`.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
*/
function _transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal {
if (block.timestamp <= validAfter_) revert AuthorizationNotYetValid(block.timestamp, validAfter_);
if (block.timestamp >= validBefore_) revert AuthorizationExpired(block.timestamp, validBefore_);
_revertIfAuthorizationAlreadyUsed(from_, nonce_);
_getERC3009StorageLocation().authorizationState[from_][nonce_] = true;
emit AuthorizationUsed(from_, nonce_);
_transfer(from_, to_, value_);
}
/**
* @dev Common receive function used by `receiveWithAuthorization`.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
*/
function _receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal {
if (msg.sender != to_) revert CallerMustBePayee(msg.sender, to_);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/**
* @dev Common cancel function used by `cancelAuthorization`.
* @param authorizer_ Authorizer's address.
* @param nonce_ Nonce of the authorization.
*/
function _cancelAuthorization(address authorizer_, bytes32 nonce_) internal {
_revertIfAuthorizationAlreadyUsed(authorizer_, nonce_);
_getERC3009StorageLocation().authorizationState[authorizer_][nonce_] = true;
emit AuthorizationCanceled(authorizer_, nonce_);
}
/**
* @dev Internal ERC20 transfer function that needs to be implemented by the inheriting contract.
* @param sender_ The sender's address.
* @param recipient_ The recipient's address.
* @param amount_ The amount to be transferred.
*/
function _transfer(address sender_, address recipient_, uint256 amount_) internal virtual;
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the internal EIP-712 digest of a transferWithAuthorization call.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
* @return The internal EIP-712 digest.
*/
function _getTransferWithAuthorizationDigest(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal view returns (bytes32) {
return
_getDigest(
keccak256(
abi.encode(
TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
from_,
to_,
value_,
validAfter_,
validBefore_,
nonce_
)
)
);
}
/**
* @dev Returns the internal EIP-712 digest of a receiveWithAuthorization call.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
* @return The internal EIP-712 digest.
*/
function _getReceiveWithAuthorizationDigest(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal view returns (bytes32) {
return
_getDigest(
keccak256(
abi.encode(
RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
from_,
to_,
value_,
validAfter_,
validBefore_,
nonce_
)
)
);
}
/**
* @dev Returns the internal EIP-712 digest of a cancelAuthorization call.
* @param authorizer_ Authorizer's address.
* @param nonce_ Nonce of the authorization.
* @return The internal EIP-712 digest.
*/
function _getCancelAuthorizationDigest(address authorizer_, bytes32 nonce_) internal view returns (bytes32) {
return _getDigest(keccak256(abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer_, nonce_)));
}
/**
* @dev Reverts if the authorization is already used.
* @param authorizer_ The authorizer's address.
* @param nonce_ The nonce of the authorization.
*/
function _revertIfAuthorizationAlreadyUsed(address authorizer_, bytes32 nonce_) internal view {
if (authorizationState(authorizer_, nonce_)) revert AuthorizationAlreadyUsed(authorizer_, nonce_);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IERC20 } from "./IERC20.sol";
import { IERC3009 } from "./IERC3009.sol";
/**
* @title An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712
* and with EIP-1271 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
* @author M^0 Labs
* @dev The additional interface as defined by EIP-2612: https://eips.ethereum.org/EIPS/eip-2612
*/
interface IERC20Extended is IERC20, IERC3009 {
/* ============ Custom Errors ============ */
/**
* @notice Revert message when spender's allowance is not sufficient.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @notice Revert message emitted when the transferred amount is insufficient.
* @param amount Amount transferred.
*/
error InsufficientAmount(uint256 amount);
/**
* @notice Revert message emitted when the recipient of a token is invalid.
* @param recipient Address of the invalid recipient.
*/
error InvalidRecipient(address recipient);
/* ============ Interactive Functions ============ */
/**
* @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
* @param owner The address of the account who's token balance is being approved to be spent by `spender`.
* @param spender The address of an account allowed to spend on behalf of `owner`.
* @param value The amount of the allowance being approved.
* @param deadline The last timestamp where the signature is still valid.
* @param v An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param r An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param s An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
* @param owner The address of the account who's token balance is being approved to be spent by `spender`.
* @param spender The address of an account allowed to spend on behalf of `owner`.
* @param value The amount of the allowance being approved.
* @param deadline The last timestamp where the signature is still valid.
* @param signature An arbitrary signature (EIP-712).
*/
function permit(address owner, address spender, uint256 value, uint256 deadline, bytes memory signature) external;
/* ============ View/Pure Functions ============ */
/// @notice Returns the EIP712 typehash used in the encoding of the digest for the permit function.
function PERMIT_TYPEHASH() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IStatefulERC712 } from "./IStatefulERC712.sol";
/**
* @title Transfer via signed authorization following EIP-3009 standard.
* @author M^0 Labs
* @dev The interface as defined by EIP-3009: https://eips.ethereum.org/EIPS/eip-3009
*/
interface IERC3009 is IStatefulERC712 {
/* ============ Events ============ */
/**
* @notice Emitted when an authorization has been canceled.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the canceled authorization.
*/
event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce);
/**
* @notice Emitted when an authorization has been used.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the used authorization.
*/
event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
/* ============ Custom Errors ============ */
/**
* @notice Emitted when an authorization has already been used.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the used authorization.
*/
error AuthorizationAlreadyUsed(address authorizer, bytes32 nonce);
/**
* @notice Emitted when an authorization is expired.
* @param timestamp Timestamp at which the transaction was submitted.
* @param validBefore Timestamp before which the authorization would have been valid.
*/
error AuthorizationExpired(uint256 timestamp, uint256 validBefore);
/**
* @notice Emitted when an authorization is not yet valid.
* @param timestamp Timestamp at which the transaction was submitted.
* @param validAfter Timestamp after which the authorization will be valid.
*/
error AuthorizationNotYetValid(uint256 timestamp, uint256 validAfter);
/**
* @notice Emitted when the caller of `receiveWithAuthorization` is not the payee.
* @param caller Caller's address.
* @param payee Payee's address.
*/
error CallerMustBePayee(address caller, address payee);
/* ============ Interactive Functions ============ */
/**
* @notice Execute a transfer with a signed authorization.
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
*/
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) external;
/**
* @notice Execute a transfer with a signed authorization.
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
*/
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes32 r,
bytes32 vs
) external;
/**
* @notice Execute a transfer with a signed authorization.
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param v v of the signature.
* @param r r of the signature.
* @param s s of the signature.
*/
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Receive a transfer with a signed authorization from the payer.
* @dev This has an additional check to ensure that the payee's address matches
* the caller of this function to prevent front-running attacks.
* (See security considerations)
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
*/
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) external;
/**
* @notice Receive a transfer with a signed authorization from the payer.
* @dev This has an additional check to ensure that the payee's address matches
* the caller of this function to prevent front-running attacks.
* (See security considerations)
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
*/
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes32 r,
bytes32 vs
) external;
/**
* @notice Receive a transfer with a signed authorization from the payer.
* @dev This has an additional check to ensure that the payee's address matches
* the caller of this function to prevent front-running attacks.
* (See security considerations)
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param v v of the signature.
* @param r r of the signature.
* @param s s of the signature.
*/
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Attempt to cancel an authorization.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
*/
function cancelAuthorization(address authorizer, bytes32 nonce, bytes memory signature) external;
/**
* @notice Attempt to cancel an authorization.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
*/
function cancelAuthorization(address authorizer, bytes32 nonce, bytes32 r, bytes32 vs) external;
/**
* @notice Attempt to cancel an authorization.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @param v v of the signature.
* @param r r of the signature.
* @param s s of the signature.
*/
function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external;
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the state of an authorization.
* @dev Nonces are randomly generated 32-byte data unique to the authorizer's address
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @return True if the nonce is used.
*/
function authorizationState(address authorizer, bytes32 nonce) external view returns (bool);
/// @notice Returns `transferWithAuthorization` typehash.
function TRANSFER_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
/// @notice Returns `receiveWithAuthorization` typehash.
function RECEIVE_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
/// @notice Returns `cancelAuthorization` typehash.
function CANCEL_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IStatefulERC712 } from "./interfaces/IStatefulERC712.sol";
import { ERC712ExtendedUpgradeable } from "./ERC712ExtendedUpgradeable.sol";
abstract contract StatefulERC712ExtendedUpgradeableStorageLayout {
/// @custom:storage-location erc7201:M0.storage.StatefulERC712Extended
struct StatefulERC712ExtendedStorageStruct {
mapping(address account => uint256 nonce) nonces; // Nonces for all signatures.
}
// keccak256(abi.encode(uint256(keccak256("M0.storage.StatefulERC712Extended")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _STATEFUL_ERC712_EXTENDED_STORAGE_LOCATION =
0x1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00;
function _getStatefulERC712ExtendedStorageLocation()
internal
pure
returns (StatefulERC712ExtendedStorageStruct storage $)
{
assembly {
$.slot := _STATEFUL_ERC712_EXTENDED_STORAGE_LOCATION
}
}
}
/**
* @title Stateful and upgradeable extension for EIP-712 typed structured data hashing and signing with nonces.
* @author M0 Labs
* @dev An abstract implementation to satisfy stateful EIP-712 with nonces.
*/
abstract contract StatefulERC712Upgradeable is
StatefulERC712ExtendedUpgradeableStorageLayout,
IStatefulERC712,
ERC712ExtendedUpgradeable
{
/* ============ Initializer ============ */
/**
* @notice Initializes the StatefulERC712Upgradeable contract.
* @param name The name of the contract.
*/
function __StatefulERC712ExtendedUpgradeable_init(string memory name) internal onlyInitializing {
__ERC712ExtendedUpgradeable_init(name);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IStatefulERC712
function nonces(address account) external view returns (uint256) {
return _getStatefulERC712ExtendedStorageLocation().nonces[account];
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IERC712Extended } from "./IERC712Extended.sol";
/**
* @title Stateful Extension for EIP-712 typed structured data hashing and signing with nonces.
* @author M^0 Labs
*/
interface IStatefulERC712 is IERC712Extended {
/* ============ Custom Errors ============ */
/**
* @notice Revert message when a signing account's nonce is not the expected current nonce.
* @param nonce The nonce used in the signature.
* @param expectedNonce The expected nonce to be used in a signature by the signing account.
*/
error InvalidAccountNonce(uint256 nonce, uint256 expectedNonce);
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the next nonce to be used in a signature by `account`.
* @param account The address of some account.
* @return nonce The next nonce to be used in a signature by `account`.
*/
function nonces(address account) external view returns (uint256 nonce);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { Initializable } from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import { IERC712 } from "./interfaces/IERC712.sol";
import { IERC712Extended } from "./interfaces/IERC712Extended.sol";
import { SignatureChecker } from "./libs/SignatureChecker.sol";
abstract contract ERC712ExtendedUpgradeableStorageLayout {
/// @custom:storage-location erc7201:M0.storage.ERC712Extended
struct ERC712ExtendedStorageStruct {
uint256 initialChainId;
bytes32 initialDomainSeparator;
string name;
}
// keccak256(abi.encode(uint256(keccak256("M0.storage.ERC712Extended")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _ERC712_EXTENDED_STORAGE_LOCATION =
0x103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea100;
function _getERC712ExtendedStorageLocation() internal pure returns (ERC712ExtendedStorageStruct storage $) {
assembly {
$.slot := _ERC712_EXTENDED_STORAGE_LOCATION
}
}
}
/**
* @title Typed structured data hashing and signing via EIP-712, extended by EIP-5267.
* @author M0 Labs
* @dev An abstract implementation to satisfy EIP-712: https://eips.ethereum.org/EIPS/eip-712
*/
abstract contract ERC712ExtendedUpgradeable is ERC712ExtendedUpgradeableStorageLayout, IERC712Extended, Initializable {
/* ============ Variables ============ */
/// @dev keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
bytes32 internal constant _EIP712_DOMAIN_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev keccak256("1")
bytes32 internal constant _EIP712_VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/* ============ Initializer ============ */
/**
* @notice Initializes the ERC712ExtendedUpgradeable contract.
* @param name_ The name of the contract.
*/
function __ERC712ExtendedUpgradeable_init(string memory name_) internal onlyInitializing {
ERC712ExtendedStorageStruct storage $ = _getERC712ExtendedStorageLocation();
$.name = name_;
$.initialChainId = block.chainid;
$.initialDomainSeparator = _getDomainSeparator();
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IERC712Extended
function eip712Domain()
external
view
virtual
returns (
bytes1 fields_,
string memory name_,
string memory version_,
uint256 chainId_,
address verifyingContract_,
bytes32 salt_,
uint256[] memory extensions_
)
{
return (
hex"0f", // 01111
_getERC712ExtendedStorageLocation().name,
"1",
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/// @inheritdoc IERC712
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
ERC712ExtendedStorageStruct storage $ = _getERC712ExtendedStorageLocation();
return block.chainid == $.initialChainId ? $.initialDomainSeparator : _getDomainSeparator();
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Computes the EIP-712 domain separator.
* @return The EIP-712 domain separator.
*/
function _getDomainSeparator() internal view returns (bytes32) {
return
keccak256(
abi.encode(
_EIP712_DOMAIN_HASH,
keccak256(bytes(_getERC712ExtendedStorageLocation().name)),
_EIP712_VERSION_HASH,
block.chainid,
address(this)
)
);
}
/**
* @dev Returns the digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
* @param internalDigest_ The internal digest.
* @return The digest to be signed.
*/
function _getDigest(bytes32 internalDigest_) internal view returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), internalDigest_));
}
/**
* @dev Revert if the signature is expired.
* @param expiry_ Timestamp at which the signature expires or max uint256 for no expiry.
*/
function _revertIfExpired(uint256 expiry_) internal view {
if (block.timestamp > expiry_) revert SignatureExpired(expiry_, block.timestamp);
}
/**
* @dev Revert if the signature is invalid.
* @dev We first validate if the signature is a valid ECDSA signature and return early if it is the case.
* Then, we validate if it is a valid ERC-1271 signature, and return early if it is the case.
* If not, we revert with the error from the ECDSA signature validation.
* @param signer_ The signer of the signature.
* @param digest_ The digest that was signed.
* @param signature_ The signature.
*/
function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes memory signature_) internal view {
SignatureChecker.Error error_ = SignatureChecker.validateECDSASignature(signer_, digest_, signature_);
if (error_ == SignatureChecker.Error.NoError) return;
if (SignatureChecker.isValidERC1271Signature(signer_, digest_, signature_)) return;
_revertIfError(error_);
}
/**
* @dev Returns the signer of a signed digest, via EIP-712, and reverts if the signature is invalid.
* @param digest_ The digest that was signed.
* @param v_ v of the signature.
* @param r_ r of the signature.
* @param s_ s of the signature.
* @return signer_ The signer of the digest.
*/
function _getSignerAndRevertIfInvalidSignature(
bytes32 digest_,
uint8 v_,
bytes32 r_,
bytes32 s_
) internal pure returns (address signer_) {
SignatureChecker.Error error_;
(error_, signer_) = SignatureChecker.recoverECDSASigner(digest_, v_, r_, s_);
_revertIfError(error_);
}
/**
* @dev Revert if the signature is invalid.
* @param signer_ The signer of the signature.
* @param digest_ The digest that was signed.
* @param r_ An ECDSA/secp256k1 signature parameter.
* @param vs_ An ECDSA/secp256k1 short signature parameter.
*/
function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes32 r_, bytes32 vs_) internal pure {
_revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, r_, vs_));
}
/**
* @dev Revert if the signature is invalid.
* @param signer_ The signer of the signature.
* @param digest_ The digest that was signed.
* @param v_ v of the signature.
* @param r_ r of the signature.
* @param s_ s of the signature.
*/
function _revertIfInvalidSignature(
address signer_,
bytes32 digest_,
uint8 v_,
bytes32 r_,
bytes32 s_
) internal pure {
_revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, v_, r_, s_));
}
/**
* @dev Revert if error.
* @param error_ The SignatureChecker Error enum.
*/
function _revertIfError(SignatureChecker.Error error_) private pure {
if (error_ == SignatureChecker.Error.NoError) return;
if (error_ == SignatureChecker.Error.InvalidSignature) revert InvalidSignature();
if (error_ == SignatureChecker.Error.InvalidSignatureLength) revert InvalidSignatureLength();
if (error_ == SignatureChecker.Error.InvalidSignatureS) revert InvalidSignatureS();
if (error_ == SignatureChecker.Error.InvalidSignatureV) revert InvalidSignatureV();
if (error_ == SignatureChecker.Error.SignerMismatch) revert SignerMismatch();
revert InvalidSignature();
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IERC712 } from "./IERC712.sol";
/**
* @title EIP-712 extended by EIP-5267.
* @author M^0 Labs
* @dev The additional interface as defined by EIP-5267: https://eips.ethereum.org/EIPS/eip-5267
*/
interface IERC712Extended is IERC712 {
/* ============ Events ============ */
/// @notice MAY be emitted to signal that the domain could have changed.
event EIP712DomainChanged();
/* ============ View/Pure Functions ============ */
/// @notice Returns the fields and values that describe the domain separator used by this contract for EIP-712.
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
/**
* @title Typed structured data hashing and signing via EIP-712.
* @author M^0 Labs
* @dev The interface as defined by EIP-712: https://eips.ethereum.org/EIPS/eip-712
*/
interface IERC712 {
/* ============ Custom Errors ============ */
/// @notice Revert message when an invalid signature is detected.
error InvalidSignature();
/// @notice Revert message when a signature with invalid length is detected.
error InvalidSignatureLength();
/// @notice Revert message when the S portion of a signature is invalid.
error InvalidSignatureS();
/// @notice Revert message when the V portion of a signature is invalid.
error InvalidSignatureV();
/**
* @notice Revert message when a signature is being used beyond its deadline (i.e. expiry).
* @param deadline The last timestamp where the signature is still valid.
* @param timestamp The current timestamp.
*/
error SignatureExpired(uint256 deadline, uint256 timestamp);
/// @notice Revert message when a recovered signer does not match the account being purported to have signed.
error SignerMismatch();
/* ============ View/Pure Functions ============ */
/// @notice Returns the EIP712 domain separator used in the encoding of a signed digest.
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IERC1271 } from "../interfaces/IERC1271.sol";
/**
* @title A library to handle ECDSA/secp256k1 and ERC1271 signatures, individually or in arbitrarily in combination.
* @author M^0 Labs
*/
library SignatureChecker {
/* ============ Enums ============ */
/**
* @notice An enum representing the possible errors that can be emitted during signature validation.
* @param NoError No error occurred during signature validation.
* @param InvalidSignature The signature is invalid.
* @param InvalidSignatureLength The signature length is invalid.
* @param InvalidSignatureS The signature parameter S is invalid.
* @param InvalidSignatureV The signature parameter V is invalid.
* @param SignerMismatch The signer does not match the recovered signer.
*/
enum Error {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV,
SignerMismatch
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns whether a signature is valid (ECDSA/secp256k1 or ERC1271) for a signer and digest.
* @dev Signatures must not be used as unique identifiers since the `ecrecover` EVM opcode
* allows for malleable (non-unique) signatures.
* See https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array signature.
* @return Whether the signature is valid or not.
*/
function isValidSignature(address signer, bytes32 digest, bytes memory signature) internal view returns (bool) {
return isValidECDSASignature(signer, digest, signature) || isValidERC1271Signature(signer, digest, signature);
}
/**
* @dev Returns whether an ERC1271 signature is valid for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ERC1271 signature.
* @return Whether the signature is valid or not.
*/
function isValidERC1271Signature(
address signer,
bytes32 digest,
bytes memory signature
) internal view returns (bool) {
(bool success_, bytes memory result_) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (digest, signature))
);
return
success_ &&
result_.length >= 32 &&
abi.decode(result_, (bytes32)) == bytes32(IERC1271.isValidSignature.selector);
}
/**
* @dev Decodes an ECDSA/secp256k1 signature from a byte array to standard v, r, and s parameters.
* @param signature A byte array ECDSA/secp256k1 signature.
* @return v An ECDSA/secp256k1 signature parameter.
* @return r An ECDSA/secp256k1 signature parameter.
* @return s An ECDSA/secp256k1 signature parameter.
*/
function decodeECDSASignature(bytes memory signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
// ecrecover takes the signature parameters, and they can be decoded using assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
}
/**
* @dev Decodes an ECDSA/secp256k1 short signature as defined by EIP2098
* from a byte array to standard v, r, and s parameters.
* @param signature A byte array ECDSA/secp256k1 short signature.
* @return r An ECDSA/secp256k1 signature parameter.
* @return vs An ECDSA/secp256k1 short signature parameter.
*/
function decodeShortECDSASignature(bytes memory signature) internal pure returns (bytes32 r, bytes32 vs) {
// ecrecover takes the signature parameters, and they can be decoded using assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
}
/**
* @dev Returns whether an ECDSA/secp256k1 signature is valid for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
* @return Whether the signature is valid or not.
*/
function isValidECDSASignature(
address signer,
bytes32 digest,
bytes memory signature
) internal pure returns (bool) {
if (signature.length == 64) {
(bytes32 r, bytes32 vs) = decodeShortECDSASignature(signature);
return isValidECDSASignature(signer, digest, r, vs);
}
return validateECDSASignature(signer, digest, signature) == Error.NoError;
}
/**
* @dev Returns whether an ECDSA/secp256k1 short signature is valid for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return Whether the signature is valid or not.
*/
function isValidECDSASignature(address signer, bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (bool) {
return validateECDSASignature(signer, digest, r, vs) == Error.NoError;
}
/**
* @dev Returns the signer of an ECDSA/secp256k1 signature for some digest.
* @param digest The hash of the data that was signed.
* @param signature A byte array ECDSA/secp256k1 signature.
* @return An error, if any, that occurred during the signer recovery.
* @return The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(bytes32 digest, bytes memory signature) internal pure returns (Error, address) {
if (signature.length != 65) return (Error.InvalidSignatureLength, address(0));
(uint8 v, bytes32 r, bytes32 s) = decodeECDSASignature(signature);
return recoverECDSASigner(digest, v, r, s);
}
/**
* @dev Returns the signer of an ECDSA/secp256k1 short signature for some digest.
* @dev See https://eips.ethereum.org/EIPS/eip-2098
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return An error, if any, that occurred during the signer recovery.
* @return The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (Error, address) {
unchecked {
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
return recoverECDSASigner(digest, v, r, s);
}
}
/**
* @dev Returns the signer of an ECDSA/secp256k1 signature for some digest.
* @param digest The hash of the data that was signed.
* @param v An ECDSA/secp256k1 signature parameter.
* @param r An ECDSA/secp256k1 signature parameter.
* @param s An ECDSA/secp256k1 signature parameter.
* @return An error, if any, that occurred during the signer recovery.
* @return signer The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (Error, address signer) {
// Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}.
if (uint256(s) > uint256(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0))
return (Error.InvalidSignatureS, address(0));
if (v != 27 && v != 28) return (Error.InvalidSignatureV, address(0));
signer = ecrecover(digest, v, r, s);
return (signer == address(0)) ? (Error.InvalidSignature, address(0)) : (Error.NoError, signer);
}
/**
* @dev Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ERC1271 signature.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
bytes memory signature
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, signature);
return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
}
/**
* @dev Returns an error, if any, in validating an ECDSA/secp256k1 short signature for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
bytes32 r,
bytes32 vs
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, r, vs);
return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
}
/**
* @dev Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param v An ECDSA/secp256k1 signature parameter.
* @param r An ECDSA/secp256k1 signature parameter.
* @param s An ECDSA/secp256k1 signature parameter.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, v, r, s);
return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
}
/**
* @dev Returns an error if `signer` is not `recoveredSigner`.
* @param signer The address of the some signer.
* @param recoveredSigner The address of the some recoveredSigner.
* @return An error if `signer` is not `recoveredSigner`.
*/
function validateRecoveredSigner(address signer, address recoveredSigner) internal pure returns (Error) {
return (signer == recoveredSigner) ? Error.NoError : Error.SignerMismatch;
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
/**
* @title Standard Signature Validation Method for Contracts via EIP-1271.
* @author M^0 Labs
* @dev The interface as defined by EIP-1271: https://eips.ethereum.org/EIPS/eip-1271
*/
interface IERC1271 {
/**
* @dev Returns a specific magic value if the provided signature is valid for the provided digest.
* @param digest Hash of the data purported to have been signed.
* @param signature Signature byte array associated with the digest.
* @return magicValue Magic value 0x1626ba7e if the signature is valid.
*/
function isValidSignature(bytes32 digest, bytes memory signature) external view returns (bytes4 magicValue);
}{
"remappings": [
"@ensdomains/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/node_modules/@ensdomains/",
"@openzeppelin/contracts-upgradeable/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@uniswap/v4-core/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/",
"common/=lib/evm-m-extensions/lib/common/src/",
"ds-test/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"evm-m-extensions/=lib/evm-m-extensions/",
"forge-gas-snapshot/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"hardhat/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/node_modules/hardhat/",
"openzeppelin-contracts-upgradeable/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/evm-m-extensions/lib/openzeppelin-foundry-upgrades/src/",
"permit2/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/permit2/",
"solmate/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/lib/solmate/",
"uniswap-v4-periphery/=lib/evm-m-extensions/lib/uniswap-v4-periphery/",
"v4-core/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/src/",
"wrapped-m-token/=lib/evm-m-extensions/lib/wrapped-m-token/"
],
"optimizer": {
"enabled": true,
"runs": 999999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"mToken","type":"address"},{"internalType":"address","name":"swapFacility","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AccountFrozen","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AccountNotFrozen","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationAlreadyUsed","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"}],"name":"AuthorizationExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"}],"name":"AuthorizationNotYetValid","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"payee","type":"address"}],"name":"CallerMustBePayee","type":"error"},{"inputs":[],"name":"EarningIsDisabled","type":"error"},{"inputs":[],"name":"EarningIsEnabled","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientAmount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expectedNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSignatureLength","type":"error"},{"inputs":[],"name":"InvalidSignatureS","type":"error"},{"inputs":[],"name":"InvalidSignatureV","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotSwapFacility","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"SignatureExpired","type":"error"},{"inputs":[],"name":"SignerMismatch","type":"error"},{"inputs":[],"name":"ZeroAdmin","type":"error"},{"inputs":[],"name":"ZeroForcedTransferManager","type":"error"},{"inputs":[],"name":"ZeroFreezeManager","type":"error"},{"inputs":[],"name":"ZeroMToken","type":"error"},{"inputs":[],"name":"ZeroPauser","type":"error"},{"inputs":[],"name":"ZeroSwapFacility","type":"error"},{"inputs":[],"name":"ZeroYieldRecipient","type":"error"},{"inputs":[],"name":"ZeroYieldRecipientManager","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","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":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationUsed","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"index","type":"uint128"}],"name":"EarningDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"index","type":"uint128"}],"name":"EarningEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"frozenAccount","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"forcedTransferManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ForcedTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Frozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Unfrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"yield","type":"uint256"}],"name":"YieldClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldRecipient","type":"address"}],"name":"YieldRecipientSet","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FORCED_TRANSFER_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FREEZE_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECEIVE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"YIELD_RECIPIENT_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"authorizationState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disableEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields_","type":"bytes1"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"version_","type":"string"},{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"address","name":"verifyingContract_","type":"address"},{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"internalType":"uint256[]","name":"extensions_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"frozenAccount","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"forceTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"frozenAccounts","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"forceTransfers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"freeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"freezeAccounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldRecipient","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"freezeManager","type":"address"},{"internalType":"address","name":"yieldRecipientManager","type":"address"},{"internalType":"address","name":"pauser","type":"address"},{"internalType":"address","name":"forcedTransferManager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"yieldRecipient_","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"freezeManager","type":"address"},{"internalType":"address","name":"yieldRecipientManager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isEarningEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setYieldRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapFacility","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unfreeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"unfreezeAccounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unwrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"wrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"yield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c060405234801561001057600080fd5b50604051614feb380380614feb83398101604081905261002f9161016c565b8181818161003b61009e565b6001600160a01b03821660808190526100675760405163b01d5e2b60e01b815260040160405180910390fd5b6001600160a01b03811660a081905261009357604051636880ffc960e11b815260040160405180910390fd5b50505050505061019f565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100ee5760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161461014d5780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b80516001600160a01b038116811461016757600080fd5b919050565b6000806040838503121561017f57600080fd5b61018883610150565b915061019660208401610150565b90509250929050565b60805160a051614de8610203600039600081816108b2015281816110c901526117cc01526000818161092401528181610ea901528181611674015281816118cd015281816119db015281816122140152818161269c015261303d0152614de86000f3fe608060405234801561001057600080fd5b50600436106103c55760003560e01c806388b7ab63116101ff578063c9144ddb1161011a578063d9169487116100ad578063e58398361161007c578063e583983614610aa4578063e63ab1e914610afc578063e94a010214610b23578063ef55bec614610b8857600080fd5b8063d9169487146109f2578063dd62ed3e14610a19578063e3ee160e14610a7e578063e56f2fe414610a9157600080fd5b8063cf092995116100e9578063cf092995146109a6578063d505accf146109b9578063d547741f146109cc578063d7a49f0b146109df57600080fd5b8063c9144ddb14610946578063c967891a1461094e578063cc2a9a5b14610956578063cc4c5b641461096957600080fd5b8063a8afc01f11610192578063ae06b7e411610161578063ae06b7e4146108ad578063b7b72899146108f9578063bf376c7a1461090c578063c3b6f9391461091f57600080fd5b8063a8afc01f1461086c578063a9059cbb14610874578063aad1202914610887578063ace150a51461089a57600080fd5b80639fd5a6cf116101ce5780639fd5a6cf14610817578063a08cb48b1461082a578063a0cc6a681461083d578063a217fddf1461086457600080fd5b806388b7ab63146107845780638d1fdf2f1461079757806391d14854146107aa57806395d89b411461080f57600080fd5b806336568abe116102ef5780635a049a70116102825780637ecebe00116102515780637ecebe00146106e55780637f2eecc31461073a5780638456cb591461076157806384b0196e1461076957600080fd5b80635a049a701461062c5780635c975abb1461063f57806363f156491461066957806370a082311461069057600080fd5b80634259dff9116102be5780634259dff9146105cc57806345c8b1a6146105f357806345cf012d14610606578063532992c51461061957600080fd5b806336568abe1461059657806339f47693146105a95780633f4ba83a146105bc578063406cf229146105c457600080fd5b806326987b601161036757806330adf81f1161033657806330adf81f14610520578063313ce5671461054757806333bebb771461057b5780633644e5151461058e57600080fd5b806326987b60146104b557806328593984146104de5780632cfd442d146104e65780632f2ff15d1461050d57600080fd5b8063170e2070116103a3578063170e20701461041a57806318160ddd1461042f57806323b872dd14610460578063248a9ca31461047357600080fd5b806301ffc9a7146103ca57806306fdde03146103f2578063095ea7b314610407575b600080fd5b6103dd6103d8366004614145565b610b9b565b60405190151581526020015b60405180910390f35b6103fa610c34565b6040516103e991906141f5565b6103dd61041536600461422a565b610ce9565b61042d61042836600461429b565b610cff565b005b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af100545b6040519081526020016103e9565b6103dd61046e3660046142dd565b610d94565b61045261048136600461431e565b60009081527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604090206001015490565b6104bd610ea5565b6040516fffffffffffffffffffffffffffffffff90911681526020016103e9565b610452610f3b565b6104527fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c81565b61042d61051b366004614337565b610f8e565b6104527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1015460405160ff90911681526020016103e9565b61042d6105893660046142dd565b610fd8565b61045261100d565b61042d6105a4366004614337565b611053565b61042d6105b736600461422a565b6110b1565b61042d61119c565b6104526111d1565b6104527f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca81565b61042d610601366004614367565b611276565b61042d610614366004614367565b6112ca565b61042d610627366004614384565b611306565b61042d61063a3660046143d0565b611325565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166103dd565b6104527f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3581565b61045261069e366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604090205490565b6104526106f3366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602052604090205490565b6104527fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b61042d611345565b610771611377565b6040516103e99796959493929190614420565b61042d6107923660046145c4565b6114d6565b61042d6107a5366004614367565b611505565b6103dd6107b8366004614337565b60009182527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b6103fa611559565b61042d61082536600461464d565b611581565b61042d6108383660046146c3565b611591565b6104527f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b610452600081565b61042d6115bb565b6103dd61088236600461422a565b6116e1565b61042d61089536600461429b565b6116ee565b61042d6108a83660046146c3565b61177c565b6108d47f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103e9565b61042d61090736600461472e565b61179c565b61042d61091a36600461422a565b6117b4565b6108d47f000000000000000000000000000000000000000000000000000000000000000081565b6103dd61189c565b61042d61194d565b61042d610964366004614787565b611a41565b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff166108d4565b61042d6109b43660046145c4565b611d1a565b61042d6109c7366004614809565b611d3a565b61042d6109da366004614337565b611d4a565b61042d6109ed366004614878565b611d8e565b6104527f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b610452610a2736600461491e565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1006020908152604080832093909416825291909152205490565b61042d610a8c36600461494c565b611e80565b61042d610a9f3660046149d1565b611eab565b6103dd610ab2366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff1690565b6104527f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6103dd610b3136600461422a565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a488860060209081526040808320938352929052205460ff1690565b61042d610b9636600461494c565b612010565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610c2e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60607f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1005b6002018054610c6690614a47565b80601f0160208091040260200160405190810160405280929190818152602001828054610c9290614a47565b8015610cdf5780601f10610cb457610100808354040283529160200191610cdf565b820191906000526020600020905b815481529060010190602001808311610cc257829003601f168201915b5050505050905090565b6000610cf6338484612030565b50600192915050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b35610d2981612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d57610d8582868684818110610d6b57610d6b614a9a565b9050602002016020810190610d809190614367565b612050565b600101610d4d565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602081815260408084203385529091528220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e8e5783811015610e5a576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018590526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616600090815260208381526040808320338452909152902084820390555b610e998686866120dc565b50600195945050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166326987b606040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614ac9565b905090565b600080610f47306121cc565b90506000610f737fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1005490565b9050808211610f83576000610f87565b8082035b9250505090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154610fc881612046565b610fd28383612281565b50505050565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c61100281612046565b610fd28484846123a2565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1008054600091904614611047576110426124a6565b61104d565b80600101545b91505090565b73ffffffffffffffffffffffffffffffffffffffff811633146110a2576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ac8282612562565b505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611120576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561116e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111929190614afb565b82612640565b5050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6111c681612046565b6111ce61271e565b50565b60006111db6127bb565b60006111e5610f3b565b9050806000036111f757600091505090565b6040518181527fd1c22369a95f91ae16576036bba6372736ba109f257ad94dccb89e141762e2659060200160405180910390a161127161126b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff1690565b826127e5565b919050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b356112a081612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612050565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6112f481612046565b6112fc6111d1565b5061119882612893565b61131b8461131486866129ba565b8484612a36565b610fd28484612a4a565b61133b8561133387876129ba565b858585612af8565b610d8d8585612a4a565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61136f81612046565b6111ce612b08565b6000606080828080837f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea10060020146306000806040519080825280602002602001820160405280156113d2578160200160208202803683370190505b507f0f00000000000000000000000000000000000000000000000000000000000000949392919084805461140590614a47565b80601f016020809104026020016040519081016040528092919081815260200182805461143190614a47565b801561147e5780601f106114535761010080835404028352916020019161147e565b820191906000526020600020905b81548152906001019060200180831161146157829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6114ee876114e8898989898989612b81565b83612c09565b6114fc878787878787612c56565b50505050505050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561152f81612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612cd9565b60607fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100610c58565b610d8d856114e887878787612d63565b6115a3886113148a8a8a8a8a8a612b81565b6115b1888888888888612c56565b5050505050505050565b6115c361189c565b6115f9576040517fb019ea3500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce611622610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a16040517f81399be40000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906381399be490602401600060405180830381600087803b1580156116cd57600080fd5b505af1158015610fd2573d6000803e3d6000fd5b6000610cf63384846120dc565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561171881612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d576117748286868481811061175a5761175a614a9a565b905060200201602081019061176f9190614367565b612cd9565b60010161173c565b61178e886113148a8a8a8a8a8a612e2f565b6115b1888888888888612eac565b6117aa836114e885856129ba565b6110ac8383612a4a565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611823576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611871573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118959190614afb565b8383612fe5565b6040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906384af270f90602401602060405180830381865afa158015611929573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614b18565b61195561189c565b1561198c576040517f7a42736800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a186119b5610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a17f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a36e40fc6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156116cd57600080fd5b6000611a4b6130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611a785750825b905060008267ffffffffffffffff166001148015611a955750303b155b905081158015611aa3575080155b15611ada576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611b3b5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b73ffffffffffffffffffffffffffffffffffffffff8716611b88576040517f042d717b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616611bd5576040517f354368a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c4d6040518060400160405280600c81526020017f4d6574614d61736b2055534400000000000000000000000000000000000000008152506040518060400160405280600481526020017f6d555344000000000000000000000000000000000000000000000000000000008152508d8d8d8d6130f3565b611c556131e7565b611c7f7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a88612281565b50611caa7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c87612281565b508315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2906020015b60405180910390a15b5050505050505050505050565b611d2c876114e8898989898989612e2f565b6114fc878787878787612eac565b6114fc8761133389898989612d63565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154611d8481612046565b610fd28383612562565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c611db881612046565b8584141580611dc75750858214155b15611dfe576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b868110156115b157611e78888883818110611e1e57611e1e614a9a565b9050602002016020810190611e339190614367565b878784818110611e4557611e45614a9a565b9050602002016020810190611e5a9190614367565b868685818110611e6c57611e6c614a9a565b905060200201356123a2565b600101611e01565b611e92896113338b8b8b8b8b8b612e2f565b611ea0898989898989612eac565b505050505050505050565b6000611eb56130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611ee25750825b905060008267ffffffffffffffff166001148015611eff5750303b155b905081158015611f0d575080155b15611f44576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611fa55784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b611fb38b8b8b8b8b8b6130f3565b8315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001611d04565b612022896113338b8b8b8b8b8b612b81565b611ea0898989898989612c56565b61203b8383836131f1565b6110ac838383613226565b6111ce81336132ab565b61205a8282613352565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590514281527f07d647ad688e085159820c1d8d030e5765cdc5274d4ee4065c6066b388a2ef5991015b60405180910390a25050565b6120e5826133c9565b6120f083838361342e565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161214f91815260200190565b60405180910390a38060000361216457505050565b61216e8382613441565b73ffffffffffffffffffffffffffffffffffffffff80841660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604080822080548590039055918416815220805482019055505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa15801561225d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2e9190614b3a565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff166123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556123343390565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610c2e565b6000915050610c2e565b6123ab826133c9565b6123b4836134e6565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161241391815260200190565b60405180910390a33373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f47cea260e2dfb95ed2ab59ad44fe2ac9cddb432afb828d2a1475936b5a2b829a8460405161249191815260200190565b60405180910390a48060000361216457505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1006002016040516124fa9190614b53565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff16156123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610c2e565b6126498161357c565b61265382826135b9565b61265d3382613441565b61266733826135cb565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af11580156126fa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ac9190614b18565b612726613671565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a150565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6111ce81612046565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602090815260408083208054860190557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054860181559051858152909392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff81166128e0576040517f5dfd400c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af101547fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1009073ffffffffffffffffffffffffffffffffffffffff90811690831603612948575050565b6001810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040517f77f12a3c9f87d4602fe59bb8d2b68c7b516e0cacba414a53e74ea75d435dc18d90600090a25050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff84169181019190915260608101829052600090612a2f906080015b604051602081830303815290604052805190602001206136cc565b9392505050565b610fd2612a458585858561372e565b61376c565b612a548282613931565b73ffffffffffffffffffffffffffffffffffffffff821660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610d8d612a4586868686866139da565b612b10613a25565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833612790565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b979650505050505050565b6000612c16848484613a81565b90506000816005811115612c2c57612c2c614be6565b03612c375750505050565b612c42848484613ac8565b15612c4d5750505050565b610fd28161376c565b3373ffffffffffffffffffffffffffffffffffffffff861614612cc3576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff86166024820152604401610e51565b612cd1868686868686612eac565b505050505050565b612ce38282613c15565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f68e0d8c112165d0949ce87205b719ed7d98c7401866c34a159f7c67c6f5620e7906120d09042815260200190565b6000612d6e82613c8d565b612d79858585612030565b73ffffffffffffffffffffffffffffffffffffffff80861660008181527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052612e269060e001612a14565b95945050505050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b824211612eee576040517f498ff9a200000000000000000000000000000000000000000000000000000000815242600482015260248101849052604401610e51565b814210612f30576040517fb3fcd33e00000000000000000000000000000000000000000000000000000000815242600482015260248101839052604401610e51565b612f3a8682613931565b73ffffffffffffffffffffffffffffffffffffffff861660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3612cd18686866120dc565b612fee826133c9565b612ff78161357c565b613002838383613cd0565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af115801561309b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bf9190614b18565b506110ac82826127e5565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c2e565b6130fb613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613148576040517f09d50edf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316613195576040517f7289db0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61319f8686613d21565b6131a882613d35565b6131b184612893565b6131bc600084612281565b506114fc7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca82612281565b6131ef613ce3565b565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0061321c8185613c15565b610fd28184613c15565b73ffffffffffffffffffffffffffffffffffffffff83811660008181527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101612886565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16611198576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff16611198576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b73ffffffffffffffffffffffffffffffffffffffff81166111ce576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b613436613a25565b6110ac838383613db4565b73ffffffffffffffffffffffffffffffffffffffff821660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1026020526040902054818110156110ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024810182905260448101839052606401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff166111ce576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b806000036111ce576040517f77b8dde300000000000000000000000000000000000000000000000000000000815260048101829052602401610e51565b6135c1613a25565b6111988282613de9565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af10260209081526040808320805486900390557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054869003815590518581529093917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101612886565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166131ef576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006136d661100d565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b600080600061373e868686613e13565b9092509050600082600581111561375757613757614be6565b146137625781612bfe565b612bfe8782613e5b565b600081600581111561378057613780614be6565b036137885750565b600181600581111561379c5761379c614be6565b036137d3576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028160058111156137e7576137e7614be6565b0361381e576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381600581111561383257613832614be6565b03613869576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561387d5761387d614be6565b036138b4576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058160058111156138c8576138c8614be6565b036138ff576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832084845290915290205460ff1615611198576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260248101829052604401610e51565b60008060006139eb87878787613ea0565b90925090506000826005811115613a0457613a04614be6565b14613a0f5781613a19565b613a198882613e5b565b98975050505050505050565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff16156131ef576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000613a908585613fb5565b90925090506000826005811115613aa957613aa9614be6565b14613ab45781613abe565b613abe8682613e5b565b9695505050505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff168585604051602401613af7929190614c15565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251613b789190614c36565b600060405180830381855afa9150503d8060008114613bb3576040519150601f19603f3d011682016040523d82523d6000602084013e613bb8565b606091505b5091509150818015613bcc57506020815110155b8015613abe575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090613c0a9083016020908101908401614b3a565b149695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff1615611198576040517f4f2a367e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b804211156111ce576040517ff88f049000000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610e51565b613cd8613a25565b6110ac8383836131f1565b613ceb613ffc565b6131ef576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613d29613ce3565b6111988282600661401b565b613d3d613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613d8a576040517f6cbbd1d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111987f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3582612281565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00613ddf8133613c15565b61321c8185613c15565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083613c15565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416613e4d87838884613ea0565b935093505050935093915050565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614613e97576005612a2f565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613ed75750600390506000613fac565b8460ff16601b14158015613eef57508460ff16601c14155b15613f005750600490506000613fac565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613f53573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615613fa257600081613fa7565b600160005b915091505b94509492505050565b6000808251604114613fcd5750600290506000613ff5565b60208301516040840151606085015160001a9190613fed87848484613ea0565b945094505050505b9250929050565b60006140066130ca565b5468010000000000000000900460ff16919050565b614023613ce3565b61402c836140c4565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da10180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff83161790557fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1007fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da102610d8d8482614c99565b6140cc613ce3565b6111ce816140d8613ce3565b6111ce816140e4613ce3565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1007f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1026141308382614c99565b5046815561413c6124a6565b60019091015550565b60006020828403121561415757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114612a2f57600080fd5b60005b838110156141a257818101518382015260200161418a565b50506000910152565b600081518084526141c3816020860160208601614187565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612a2f60208301846141ab565b73ffffffffffffffffffffffffffffffffffffffff811681146111ce57600080fd5b6000806040838503121561423d57600080fd5b823561424881614208565b946020939093013593505050565b60008083601f84011261426857600080fd5b50813567ffffffffffffffff81111561428057600080fd5b6020830191508360208260051b8501011115613ff557600080fd5b600080602083850312156142ae57600080fd5b823567ffffffffffffffff8111156142c557600080fd5b6142d185828601614256565b90969095509350505050565b6000806000606084860312156142f257600080fd5b83356142fd81614208565b9250602084013561430d81614208565b929592945050506040919091013590565b60006020828403121561433057600080fd5b5035919050565b6000806040838503121561434a57600080fd5b82359150602083013561435c81614208565b809150509250929050565b60006020828403121561437957600080fd5b8135612a2f81614208565b6000806000806080858703121561439a57600080fd5b84356143a581614208565b966020860135965060408601359560600135945092505050565b803560ff8116811461127157600080fd5b600080600080600060a086880312156143e857600080fd5b85356143f381614208565b945060208601359350614408604087016143bf565b94979396509394606081013594506080013592915050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061445b60e08301896141ab565b828103604084015261446d81896141ab565b6060840188905273ffffffffffffffffffffffffffffffffffffffff8716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b818110156144d05783518352602093840193909201916001016144b2565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261452157600080fd5b81356020830160008067ffffffffffffffff841115614542576145426144e1565b506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85018116603f0116810181811067ffffffffffffffff8211171561458f5761458f6144e1565b6040528381529050808284018710156145a757600080fd5b838360208301376000602085830101528094505050505092915050565b600080600080600080600060e0888a0312156145df57600080fd5b87356145ea81614208565b965060208801356145fa81614208565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff81111561463257600080fd5b61463e8a828b01614510565b91505092959891949750929550565b600080600080600060a0868803121561466557600080fd5b853561467081614208565b9450602086013561468081614208565b93506040860135925060608601359150608086013567ffffffffffffffff8111156146aa57600080fd5b6146b688828901614510565b9150509295509295909350565b600080600080600080600080610100898b0312156146e057600080fd5b88356146eb81614208565b975060208901356146fb81614208565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b60008060006060848603121561474357600080fd5b833561474e81614208565b925060208401359150604084013567ffffffffffffffff81111561477157600080fd5b61477d86828701614510565b9150509250925092565b60008060008060008060c087890312156147a057600080fd5b86356147ab81614208565b955060208701356147bb81614208565b945060408701356147cb81614208565b935060608701356147db81614208565b925060808701356147eb81614208565b915060a08701356147fb81614208565b809150509295509295509295565b600080600080600080600060e0888a03121561482457600080fd5b873561482f81614208565b9650602088013561483f81614208565b9550604088013594506060880135935061485b608089016143bf565b9699959850939692959460a0840135945060c09093013592915050565b6000806000806000806060878903121561489157600080fd5b863567ffffffffffffffff8111156148a857600080fd5b6148b489828a01614256565b909750955050602087013567ffffffffffffffff8111156148d457600080fd5b6148e089828a01614256565b909550935050604087013567ffffffffffffffff81111561490057600080fd5b61490c89828a01614256565b979a9699509497509295939492505050565b6000806040838503121561493157600080fd5b823561493c81614208565b9150602083013561435c81614208565b60008060008060008060008060006101208a8c03121561496b57600080fd5b893561497681614208565b985060208a013561498681614208565b975060408a0135965060608a0135955060808a0135945060a08a013593506149b060c08b016143bf565b989b979a50959894979396929550929360e081013593506101000135919050565b60008060008060008060c087890312156149ea57600080fd5b863567ffffffffffffffff811115614a0157600080fd5b614a0d89828a01614510565b965050602087013567ffffffffffffffff811115614a2a57600080fd5b614a3689828a01614510565b95505060408701356147cb81614208565b600181811c90821680614a5b57607f821691505b602082108103614a94577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614adb57600080fd5b81516fffffffffffffffffffffffffffffffff81168114612a2f57600080fd5b600060208284031215614b0d57600080fd5b8151612a2f81614208565b600060208284031215614b2a57600080fd5b81518015158114612a2f57600080fd5b600060208284031215614b4c57600080fd5b5051919050565b6000808354614b6181614a47565b600182168015614b785760018114614bab57614bdb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083168652811515820286019350614bdb565b86600052602060002060005b83811015614bd357815488820152600190910190602001614bb7565b505081860193505b509195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000614c2e60408301846141ab565b949350505050565b60008251614c48818460208701614187565b9190910192915050565b601f8211156110ac57806000526020600020601f840160051c81016020851015614c795750805b601f840160051c820191505b81811015610d8d5760008155600101614c85565b815167ffffffffffffffff811115614cb357614cb36144e1565b614cc781614cc18454614a47565b84614c52565b6020601f821160018114614d195760008315614ce35750848201515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b178455610d8d565b6000848152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08516915b82811015614d675787850151825560209485019460019092019101614d47565b5084821015614da357868401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b60f8161c191681555b50505050600190811b0190555056fea26469706673582212207ed056e91645947340e29d61720bf8d2aa1bfe6e139bd66c9e46b7c2b88cfada64736f6c634300081a0033000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103c55760003560e01c806388b7ab63116101ff578063c9144ddb1161011a578063d9169487116100ad578063e58398361161007c578063e583983614610aa4578063e63ab1e914610afc578063e94a010214610b23578063ef55bec614610b8857600080fd5b8063d9169487146109f2578063dd62ed3e14610a19578063e3ee160e14610a7e578063e56f2fe414610a9157600080fd5b8063cf092995116100e9578063cf092995146109a6578063d505accf146109b9578063d547741f146109cc578063d7a49f0b146109df57600080fd5b8063c9144ddb14610946578063c967891a1461094e578063cc2a9a5b14610956578063cc4c5b641461096957600080fd5b8063a8afc01f11610192578063ae06b7e411610161578063ae06b7e4146108ad578063b7b72899146108f9578063bf376c7a1461090c578063c3b6f9391461091f57600080fd5b8063a8afc01f1461086c578063a9059cbb14610874578063aad1202914610887578063ace150a51461089a57600080fd5b80639fd5a6cf116101ce5780639fd5a6cf14610817578063a08cb48b1461082a578063a0cc6a681461083d578063a217fddf1461086457600080fd5b806388b7ab63146107845780638d1fdf2f1461079757806391d14854146107aa57806395d89b411461080f57600080fd5b806336568abe116102ef5780635a049a70116102825780637ecebe00116102515780637ecebe00146106e55780637f2eecc31461073a5780638456cb591461076157806384b0196e1461076957600080fd5b80635a049a701461062c5780635c975abb1461063f57806363f156491461066957806370a082311461069057600080fd5b80634259dff9116102be5780634259dff9146105cc57806345c8b1a6146105f357806345cf012d14610606578063532992c51461061957600080fd5b806336568abe1461059657806339f47693146105a95780633f4ba83a146105bc578063406cf229146105c457600080fd5b806326987b601161036757806330adf81f1161033657806330adf81f14610520578063313ce5671461054757806333bebb771461057b5780633644e5151461058e57600080fd5b806326987b60146104b557806328593984146104de5780632cfd442d146104e65780632f2ff15d1461050d57600080fd5b8063170e2070116103a3578063170e20701461041a57806318160ddd1461042f57806323b872dd14610460578063248a9ca31461047357600080fd5b806301ffc9a7146103ca57806306fdde03146103f2578063095ea7b314610407575b600080fd5b6103dd6103d8366004614145565b610b9b565b60405190151581526020015b60405180910390f35b6103fa610c34565b6040516103e991906141f5565b6103dd61041536600461422a565b610ce9565b61042d61042836600461429b565b610cff565b005b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af100545b6040519081526020016103e9565b6103dd61046e3660046142dd565b610d94565b61045261048136600461431e565b60009081527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604090206001015490565b6104bd610ea5565b6040516fffffffffffffffffffffffffffffffff90911681526020016103e9565b610452610f3b565b6104527fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c81565b61042d61051b366004614337565b610f8e565b6104527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1015460405160ff90911681526020016103e9565b61042d6105893660046142dd565b610fd8565b61045261100d565b61042d6105a4366004614337565b611053565b61042d6105b736600461422a565b6110b1565b61042d61119c565b6104526111d1565b6104527f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca81565b61042d610601366004614367565b611276565b61042d610614366004614367565b6112ca565b61042d610627366004614384565b611306565b61042d61063a3660046143d0565b611325565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166103dd565b6104527f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3581565b61045261069e366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604090205490565b6104526106f3366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602052604090205490565b6104527fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b61042d611345565b610771611377565b6040516103e99796959493929190614420565b61042d6107923660046145c4565b6114d6565b61042d6107a5366004614367565b611505565b6103dd6107b8366004614337565b60009182527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b6103fa611559565b61042d61082536600461464d565b611581565b61042d6108383660046146c3565b611591565b6104527f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b610452600081565b61042d6115bb565b6103dd61088236600461422a565b6116e1565b61042d61089536600461429b565b6116ee565b61042d6108a83660046146c3565b61177c565b6108d47f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f627881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103e9565b61042d61090736600461472e565b61179c565b61042d61091a36600461422a565b6117b4565b6108d47f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b81565b6103dd61189c565b61042d61194d565b61042d610964366004614787565b611a41565b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff166108d4565b61042d6109b43660046145c4565b611d1a565b61042d6109c7366004614809565b611d3a565b61042d6109da366004614337565b611d4a565b61042d6109ed366004614878565b611d8e565b6104527f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b610452610a2736600461491e565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1006020908152604080832093909416825291909152205490565b61042d610a8c36600461494c565b611e80565b61042d610a9f3660046149d1565b611eab565b6103dd610ab2366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff1690565b6104527f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6103dd610b3136600461422a565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a488860060209081526040808320938352929052205460ff1690565b61042d610b9636600461494c565b612010565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610c2e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60607f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1005b6002018054610c6690614a47565b80601f0160208091040260200160405190810160405280929190818152602001828054610c9290614a47565b8015610cdf5780601f10610cb457610100808354040283529160200191610cdf565b820191906000526020600020905b815481529060010190602001808311610cc257829003601f168201915b5050505050905090565b6000610cf6338484612030565b50600192915050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b35610d2981612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d57610d8582868684818110610d6b57610d6b614a9a565b9050602002016020810190610d809190614367565b612050565b600101610d4d565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602081815260408084203385529091528220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e8e5783811015610e5a576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018590526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616600090815260208381526040808320338452909152902084820390555b610e998686866120dc565b50600195945050505050565b60007f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff166326987b606040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614ac9565b905090565b600080610f47306121cc565b90506000610f737fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1005490565b9050808211610f83576000610f87565b8082035b9250505090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154610fc881612046565b610fd28383612281565b50505050565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c61100281612046565b610fd28484846123a2565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1008054600091904614611047576110426124a6565b61104d565b80600101545b91505090565b73ffffffffffffffffffffffffffffffffffffffff811633146110a2576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ac8282612562565b505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f62781614611120576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561116e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111929190614afb565b82612640565b5050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6111c681612046565b6111ce61271e565b50565b60006111db6127bb565b60006111e5610f3b565b9050806000036111f757600091505090565b6040518181527fd1c22369a95f91ae16576036bba6372736ba109f257ad94dccb89e141762e2659060200160405180910390a161127161126b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff1690565b826127e5565b919050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b356112a081612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612050565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6112f481612046565b6112fc6111d1565b5061119882612893565b61131b8461131486866129ba565b8484612a36565b610fd28484612a4a565b61133b8561133387876129ba565b858585612af8565b610d8d8585612a4a565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61136f81612046565b6111ce612b08565b6000606080828080837f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea10060020146306000806040519080825280602002602001820160405280156113d2578160200160208202803683370190505b507f0f00000000000000000000000000000000000000000000000000000000000000949392919084805461140590614a47565b80601f016020809104026020016040519081016040528092919081815260200182805461143190614a47565b801561147e5780601f106114535761010080835404028352916020019161147e565b820191906000526020600020905b81548152906001019060200180831161146157829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6114ee876114e8898989898989612b81565b83612c09565b6114fc878787878787612c56565b50505050505050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561152f81612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612cd9565b60607fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100610c58565b610d8d856114e887878787612d63565b6115a3886113148a8a8a8a8a8a612b81565b6115b1888888888888612c56565b5050505050505050565b6115c361189c565b6115f9576040517fb019ea3500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce611622610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a16040517f81399be40000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff16906381399be490602401600060405180830381600087803b1580156116cd57600080fd5b505af1158015610fd2573d6000803e3d6000fd5b6000610cf63384846120dc565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561171881612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d576117748286868481811061175a5761175a614a9a565b905060200201602081019061176f9190614367565b612cd9565b60010161173c565b61178e886113148a8a8a8a8a8a612e2f565b6115b1888888888888612eac565b6117aa836114e885856129ba565b6110ac8383612a4a565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f62781614611823576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611871573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118959190614afb565b8383612fe5565b6040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff16906384af270f90602401602060405180830381865afa158015611929573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614b18565b61195561189c565b1561198c576040517f7a42736800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a186119b5610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a17f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff1663a36e40fc6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156116cd57600080fd5b6000611a4b6130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611a785750825b905060008267ffffffffffffffff166001148015611a955750303b155b905081158015611aa3575080155b15611ada576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611b3b5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b73ffffffffffffffffffffffffffffffffffffffff8716611b88576040517f042d717b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616611bd5576040517f354368a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c4d6040518060400160405280600c81526020017f4d6574614d61736b2055534400000000000000000000000000000000000000008152506040518060400160405280600481526020017f6d555344000000000000000000000000000000000000000000000000000000008152508d8d8d8d6130f3565b611c556131e7565b611c7f7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a88612281565b50611caa7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c87612281565b508315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2906020015b60405180910390a15b5050505050505050505050565b611d2c876114e8898989898989612e2f565b6114fc878787878787612eac565b6114fc8761133389898989612d63565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154611d8481612046565b610fd28383612562565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c611db881612046565b8584141580611dc75750858214155b15611dfe576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b868110156115b157611e78888883818110611e1e57611e1e614a9a565b9050602002016020810190611e339190614367565b878784818110611e4557611e45614a9a565b9050602002016020810190611e5a9190614367565b868685818110611e6c57611e6c614a9a565b905060200201356123a2565b600101611e01565b611e92896113338b8b8b8b8b8b612e2f565b611ea0898989898989612eac565b505050505050505050565b6000611eb56130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611ee25750825b905060008267ffffffffffffffff166001148015611eff5750303b155b905081158015611f0d575080155b15611f44576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611fa55784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b611fb38b8b8b8b8b8b6130f3565b8315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001611d04565b612022896113338b8b8b8b8b8b612b81565b611ea0898989898989612c56565b61203b8383836131f1565b6110ac838383613226565b6111ce81336132ab565b61205a8282613352565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590514281527f07d647ad688e085159820c1d8d030e5765cdc5274d4ee4065c6066b388a2ef5991015b60405180910390a25050565b6120e5826133c9565b6120f083838361342e565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161214f91815260200190565b60405180910390a38060000361216457505050565b61216e8382613441565b73ffffffffffffffffffffffffffffffffffffffff80841660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604080822080548590039055918416815220805482019055505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b909116906370a0823190602401602060405180830381865afa15801561225d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2e9190614b3a565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff166123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556123343390565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610c2e565b6000915050610c2e565b6123ab826133c9565b6123b4836134e6565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161241391815260200190565b60405180910390a33373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f47cea260e2dfb95ed2ab59ad44fe2ac9cddb432afb828d2a1475936b5a2b829a8460405161249191815260200190565b60405180910390a48060000361216457505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1006002016040516124fa9190614b53565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff16156123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610c2e565b6126498161357c565b61265382826135b9565b61265d3382613441565b61266733826135cb565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018290527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af11580156126fa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ac9190614b18565b612726613671565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a150565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6111ce81612046565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602090815260408083208054860190557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054860181559051858152909392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff81166128e0576040517f5dfd400c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af101547fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1009073ffffffffffffffffffffffffffffffffffffffff90811690831603612948575050565b6001810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040517f77f12a3c9f87d4602fe59bb8d2b68c7b516e0cacba414a53e74ea75d435dc18d90600090a25050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff84169181019190915260608101829052600090612a2f906080015b604051602081830303815290604052805190602001206136cc565b9392505050565b610fd2612a458585858561372e565b61376c565b612a548282613931565b73ffffffffffffffffffffffffffffffffffffffff821660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610d8d612a4586868686866139da565b612b10613a25565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833612790565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b979650505050505050565b6000612c16848484613a81565b90506000816005811115612c2c57612c2c614be6565b03612c375750505050565b612c42848484613ac8565b15612c4d5750505050565b610fd28161376c565b3373ffffffffffffffffffffffffffffffffffffffff861614612cc3576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff86166024820152604401610e51565b612cd1868686868686612eac565b505050505050565b612ce38282613c15565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f68e0d8c112165d0949ce87205b719ed7d98c7401866c34a159f7c67c6f5620e7906120d09042815260200190565b6000612d6e82613c8d565b612d79858585612030565b73ffffffffffffffffffffffffffffffffffffffff80861660008181527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052612e269060e001612a14565b95945050505050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b824211612eee576040517f498ff9a200000000000000000000000000000000000000000000000000000000815242600482015260248101849052604401610e51565b814210612f30576040517fb3fcd33e00000000000000000000000000000000000000000000000000000000815242600482015260248101839052604401610e51565b612f3a8682613931565b73ffffffffffffffffffffffffffffffffffffffff861660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3612cd18686866120dc565b612fee826133c9565b612ff78161357c565b613002838383613cd0565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018290527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af115801561309b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bf9190614b18565b506110ac82826127e5565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c2e565b6130fb613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613148576040517f09d50edf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316613195576040517f7289db0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61319f8686613d21565b6131a882613d35565b6131b184612893565b6131bc600084612281565b506114fc7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca82612281565b6131ef613ce3565b565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0061321c8185613c15565b610fd28184613c15565b73ffffffffffffffffffffffffffffffffffffffff83811660008181527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101612886565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16611198576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff16611198576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b73ffffffffffffffffffffffffffffffffffffffff81166111ce576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b613436613a25565b6110ac838383613db4565b73ffffffffffffffffffffffffffffffffffffffff821660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1026020526040902054818110156110ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024810182905260448101839052606401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff166111ce576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b806000036111ce576040517f77b8dde300000000000000000000000000000000000000000000000000000000815260048101829052602401610e51565b6135c1613a25565b6111988282613de9565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af10260209081526040808320805486900390557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054869003815590518581529093917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101612886565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166131ef576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006136d661100d565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b600080600061373e868686613e13565b9092509050600082600581111561375757613757614be6565b146137625781612bfe565b612bfe8782613e5b565b600081600581111561378057613780614be6565b036137885750565b600181600581111561379c5761379c614be6565b036137d3576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028160058111156137e7576137e7614be6565b0361381e576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381600581111561383257613832614be6565b03613869576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561387d5761387d614be6565b036138b4576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058160058111156138c8576138c8614be6565b036138ff576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832084845290915290205460ff1615611198576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260248101829052604401610e51565b60008060006139eb87878787613ea0565b90925090506000826005811115613a0457613a04614be6565b14613a0f5781613a19565b613a198882613e5b565b98975050505050505050565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff16156131ef576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000613a908585613fb5565b90925090506000826005811115613aa957613aa9614be6565b14613ab45781613abe565b613abe8682613e5b565b9695505050505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff168585604051602401613af7929190614c15565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251613b789190614c36565b600060405180830381855afa9150503d8060008114613bb3576040519150601f19603f3d011682016040523d82523d6000602084013e613bb8565b606091505b5091509150818015613bcc57506020815110155b8015613abe575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090613c0a9083016020908101908401614b3a565b149695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff1615611198576040517f4f2a367e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b804211156111ce576040517ff88f049000000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610e51565b613cd8613a25565b6110ac8383836131f1565b613ceb613ffc565b6131ef576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613d29613ce3565b6111988282600661401b565b613d3d613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613d8a576040517f6cbbd1d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111987f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3582612281565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00613ddf8133613c15565b61321c8185613c15565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083613c15565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416613e4d87838884613ea0565b935093505050935093915050565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614613e97576005612a2f565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613ed75750600390506000613fac565b8460ff16601b14158015613eef57508460ff16601c14155b15613f005750600490506000613fac565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613f53573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615613fa257600081613fa7565b600160005b915091505b94509492505050565b6000808251604114613fcd5750600290506000613ff5565b60208301516040840151606085015160001a9190613fed87848484613ea0565b945094505050505b9250929050565b60006140066130ca565b5468010000000000000000900460ff16919050565b614023613ce3565b61402c836140c4565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da10180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff83161790557fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1007fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da102610d8d8482614c99565b6140cc613ce3565b6111ce816140d8613ce3565b6111ce816140e4613ce3565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1007f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1026141308382614c99565b5046815561413c6124a6565b60019091015550565b60006020828403121561415757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114612a2f57600080fd5b60005b838110156141a257818101518382015260200161418a565b50506000910152565b600081518084526141c3816020860160208601614187565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612a2f60208301846141ab565b73ffffffffffffffffffffffffffffffffffffffff811681146111ce57600080fd5b6000806040838503121561423d57600080fd5b823561424881614208565b946020939093013593505050565b60008083601f84011261426857600080fd5b50813567ffffffffffffffff81111561428057600080fd5b6020830191508360208260051b8501011115613ff557600080fd5b600080602083850312156142ae57600080fd5b823567ffffffffffffffff8111156142c557600080fd5b6142d185828601614256565b90969095509350505050565b6000806000606084860312156142f257600080fd5b83356142fd81614208565b9250602084013561430d81614208565b929592945050506040919091013590565b60006020828403121561433057600080fd5b5035919050565b6000806040838503121561434a57600080fd5b82359150602083013561435c81614208565b809150509250929050565b60006020828403121561437957600080fd5b8135612a2f81614208565b6000806000806080858703121561439a57600080fd5b84356143a581614208565b966020860135965060408601359560600135945092505050565b803560ff8116811461127157600080fd5b600080600080600060a086880312156143e857600080fd5b85356143f381614208565b945060208601359350614408604087016143bf565b94979396509394606081013594506080013592915050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061445b60e08301896141ab565b828103604084015261446d81896141ab565b6060840188905273ffffffffffffffffffffffffffffffffffffffff8716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b818110156144d05783518352602093840193909201916001016144b2565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261452157600080fd5b81356020830160008067ffffffffffffffff841115614542576145426144e1565b506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85018116603f0116810181811067ffffffffffffffff8211171561458f5761458f6144e1565b6040528381529050808284018710156145a757600080fd5b838360208301376000602085830101528094505050505092915050565b600080600080600080600060e0888a0312156145df57600080fd5b87356145ea81614208565b965060208801356145fa81614208565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff81111561463257600080fd5b61463e8a828b01614510565b91505092959891949750929550565b600080600080600060a0868803121561466557600080fd5b853561467081614208565b9450602086013561468081614208565b93506040860135925060608601359150608086013567ffffffffffffffff8111156146aa57600080fd5b6146b688828901614510565b9150509295509295909350565b600080600080600080600080610100898b0312156146e057600080fd5b88356146eb81614208565b975060208901356146fb81614208565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b60008060006060848603121561474357600080fd5b833561474e81614208565b925060208401359150604084013567ffffffffffffffff81111561477157600080fd5b61477d86828701614510565b9150509250925092565b60008060008060008060c087890312156147a057600080fd5b86356147ab81614208565b955060208701356147bb81614208565b945060408701356147cb81614208565b935060608701356147db81614208565b925060808701356147eb81614208565b915060a08701356147fb81614208565b809150509295509295509295565b600080600080600080600060e0888a03121561482457600080fd5b873561482f81614208565b9650602088013561483f81614208565b9550604088013594506060880135935061485b608089016143bf565b9699959850939692959460a0840135945060c09093013592915050565b6000806000806000806060878903121561489157600080fd5b863567ffffffffffffffff8111156148a857600080fd5b6148b489828a01614256565b909750955050602087013567ffffffffffffffff8111156148d457600080fd5b6148e089828a01614256565b909550935050604087013567ffffffffffffffff81111561490057600080fd5b61490c89828a01614256565b979a9699509497509295939492505050565b6000806040838503121561493157600080fd5b823561493c81614208565b9150602083013561435c81614208565b60008060008060008060008060006101208a8c03121561496b57600080fd5b893561497681614208565b985060208a013561498681614208565b975060408a0135965060608a0135955060808a0135945060a08a013593506149b060c08b016143bf565b989b979a50959894979396929550929360e081013593506101000135919050565b60008060008060008060c087890312156149ea57600080fd5b863567ffffffffffffffff811115614a0157600080fd5b614a0d89828a01614510565b965050602087013567ffffffffffffffff811115614a2a57600080fd5b614a3689828a01614510565b95505060408701356147cb81614208565b600181811c90821680614a5b57607f821691505b602082108103614a94577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614adb57600080fd5b81516fffffffffffffffffffffffffffffffff81168114612a2f57600080fd5b600060208284031215614b0d57600080fd5b8151612a2f81614208565b600060208284031215614b2a57600080fd5b81518015158114612a2f57600080fd5b600060208284031215614b4c57600080fd5b5051919050565b6000808354614b6181614a47565b600182168015614b785760018114614bab57614bdb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083168652811515820286019350614bdb565b86600052602060002060005b83811015614bd357815488820152600190910190602001614bb7565b505081860193505b509195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000614c2e60408301846141ab565b949350505050565b60008251614c48818460208701614187565b9190910192915050565b601f8211156110ac57806000526020600020601f840160051c81016020851015614c795750805b601f840160051c820191505b81811015610d8d5760008155600101614c85565b815167ffffffffffffffff811115614cb357614cb36144e1565b614cc781614cc18454614a47565b84614c52565b6020601f821160018114614d195760008315614ce35750848201515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b178455610d8d565b6000848152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08516915b82811015614d675787850151825560209485019460019092019101614d47565b5084821015614da357868401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b60f8161c191681555b50505050600190811b0190555056fea26469706673582212207ed056e91645947340e29d61720bf8d2aa1bfe6e139bd66c9e46b7c2b88cfada64736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278
-----Decoded View---------------
Arg [0] : mToken (address): 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b
Arg [1] : swapFacility (address): 0xB6807116b3B1B321a390594e31ECD6e0076f6278
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b
Arg [1] : 000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.