Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
WrappedMToken
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 1500 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { UIntMath } from "../lib/common/src/libs/UIntMath.sol";
import { IERC20 } from "../lib/common/src/interfaces/IERC20.sol";
import { ERC20Extended } from "../lib/common/src/ERC20Extended.sol";
import { IndexingMath } from "./libs/IndexingMath.sol";
import { IMTokenLike } from "./interfaces/IMTokenLike.sol";
import { IRegistrarLike } from "./interfaces/IRegistrarLike.sol";
import { IWrappedMToken } from "./interfaces/IWrappedMToken.sol";
import { Migratable } from "./Migratable.sol";
/*
██╗ ██╗██████╗ █████╗ ██████╗ ██████╗ ███████╗██████╗ ███╗ ███╗ ████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗
██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗ ████╗ ████║ ╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║
██║ █╗ ██║██████╔╝███████║██████╔╝██████╔╝█████╗ ██║ ██║ ██╔████╔██║ ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║
██║███╗██║██╔══██╗██╔══██║██╔═══╝ ██╔═══╝ ██╔══╝ ██║ ██║ ██║╚██╔╝██║ ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║
╚███╔███╔╝██║ ██║██║ ██║██║ ██║ ███████╗██████╔╝ ██║ ╚═╝ ██║ ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║
╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝
*/
/**
* @title ERC20 Token contract for wrapping M into a non-rebasing token with claimable yields.
* @author M^0 Labs
*/
contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
struct Account {
bool isEarning;
uint240 balance;
uint128 lastIndex;
}
/* ============ Variables ============ */
/// @dev Registrar key holding value of whether the earners list can be ignored or not.
bytes32 internal constant _EARNERS_LIST_IGNORED = "earners_list_ignored";
/// @dev Registrar key of earners list.
bytes32 internal constant _EARNERS_LIST = "earners";
/// @dev Registrar key prefix to determine the override recipient of an account's accrued yield.
bytes32 internal constant _CLAIM_OVERRIDE_RECIPIENT_PREFIX = "wm_claim_override_recipient";
/// @dev Registrar key prefix to determine the migrator contract.
bytes32 internal constant _MIGRATOR_V1_PREFIX = "wm_migrator_v1";
/// @inheritdoc IWrappedMToken
address public immutable migrationAdmin;
/// @inheritdoc IWrappedMToken
address public immutable mToken;
/// @inheritdoc IWrappedMToken
address public immutable registrar;
/// @inheritdoc IWrappedMToken
address public immutable excessDestination;
/// @inheritdoc IWrappedMToken
uint112 public principalOfTotalEarningSupply;
/// @inheritdoc IWrappedMToken
uint240 public totalEarningSupply;
/// @inheritdoc IWrappedMToken
uint240 public totalNonEarningSupply;
/// @dev Mapping of accounts to their respective `AccountInfo` structs.
mapping(address account => Account balance) internal _accounts;
/// @dev Array of indices at which earning was enabled or disabled.
uint128[] internal _enableDisableEarningIndices;
/* ============ Constructor ============ */
/**
* @dev Constructs the contract given an M Token address and migration admin.
* Note that a proxy will not need to initialize since there are no mutable storage values affected.
* @param mToken_ The address of an M Token.
* @param registrar_ The address of a Registrar.
* @param excessDestination_ The address of an excess destination.
* @param migrationAdmin_ The address of a migration admin.
*/
constructor(
address mToken_,
address registrar_,
address excessDestination_,
address migrationAdmin_
) ERC20Extended("WrappedM by M^0", "wM", 6) {
if ((mToken = mToken_) == address(0)) revert ZeroMToken();
if ((registrar = registrar_) == address(0)) revert ZeroRegistrar();
if ((excessDestination = excessDestination_) == address(0)) revert ZeroExcessDestination();
if ((migrationAdmin = migrationAdmin_) == address(0)) revert ZeroMigrationAdmin();
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IWrappedMToken
function wrap(address recipient_, uint256 amount_) external returns (uint240 wrapped_) {
return _wrap(msg.sender, recipient_, UIntMath.safe240(amount_));
}
/// @inheritdoc IWrappedMToken
function wrap(address recipient_) external returns (uint240 wrapped_) {
return _wrap(msg.sender, recipient_, UIntMath.safe240(IMTokenLike(mToken).balanceOf(msg.sender)));
}
/// @inheritdoc IWrappedMToken
function unwrap(address recipient_, uint256 amount_) external returns (uint240 unwrapped_) {
return _unwrap(msg.sender, recipient_, UIntMath.safe240(amount_));
}
/// @inheritdoc IWrappedMToken
function unwrap(address recipient_) external returns (uint240 unwrapped_) {
return _unwrap(msg.sender, recipient_, uint240(balanceWithYieldOf(msg.sender)));
}
/// @inheritdoc IWrappedMToken
function claimFor(address account_) external returns (uint240 yield_) {
return _claim(account_, currentIndex());
}
/// @inheritdoc IWrappedMToken
function claimExcess() external returns (uint240 excess_) {
emit ExcessClaimed(excess_ = excess());
IMTokenLike(mToken).transfer(excessDestination, excess_);
}
/// @inheritdoc IWrappedMToken
function enableEarning() external {
_revertIfNotApprovedEarner(address(this));
if (isEarningEnabled()) revert EarningIsEnabled();
// NOTE: This is a temporary measure to prevent re-enabling earning after it has been disabled.
// This line will be removed in the future.
if (wasEarningEnabled()) revert EarningCannotBeReenabled();
uint128 currentMIndex_ = _currentMIndex();
_enableDisableEarningIndices.push(currentMIndex_);
IMTokenLike(mToken).startEarning();
emit EarningEnabled(currentMIndex_);
}
/// @inheritdoc IWrappedMToken
function disableEarning() external {
_revertIfApprovedEarner(address(this));
if (!isEarningEnabled()) revert EarningIsDisabled();
uint128 currentMIndex_ = _currentMIndex();
_enableDisableEarningIndices.push(currentMIndex_);
IMTokenLike(mToken).stopEarning();
emit EarningDisabled(currentMIndex_);
}
/// @inheritdoc IWrappedMToken
function startEarningFor(address account_) external {
_revertIfNotApprovedEarner(account_);
if (!isEarningEnabled()) revert EarningIsDisabled();
Account storage accountInfo_ = _accounts[account_];
if (accountInfo_.isEarning) return;
// NOTE: Use `currentIndex()` if/when upgrading to support `startEarningFor` while earning is disabled.
uint128 currentIndex_ = _currentMIndex();
accountInfo_.isEarning = true;
accountInfo_.lastIndex = currentIndex_;
uint240 balance_ = accountInfo_.balance;
_addTotalEarningSupply(balance_, currentIndex_);
unchecked {
totalNonEarningSupply -= balance_;
}
emit StartedEarning(account_);
}
/// @inheritdoc IWrappedMToken
function stopEarningFor(address account_) external {
_revertIfApprovedEarner(account_);
uint128 currentIndex_ = currentIndex();
_claim(account_, currentIndex_);
Account storage accountInfo_ = _accounts[account_];
if (!accountInfo_.isEarning) return;
accountInfo_.isEarning = false;
accountInfo_.lastIndex = 0;
uint240 balance_ = accountInfo_.balance;
_subtractTotalEarningSupply(balance_, currentIndex_);
unchecked {
totalNonEarningSupply += balance_;
}
emit StoppedEarning(account_);
}
/* ============ Temporary Admin Migration ============ */
/// @inheritdoc IWrappedMToken
function migrate(address migrator_) external {
if (msg.sender != migrationAdmin) revert UnauthorizedMigration();
_migrate(migrator_);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IWrappedMToken
function accruedYieldOf(address account_) public view returns (uint240 yield_) {
Account storage accountInfo_ = _accounts[account_];
if (!accountInfo_.isEarning) return 0;
return _getAccruedYield(accountInfo_.balance, accountInfo_.lastIndex, currentIndex());
}
/// @inheritdoc IERC20
function balanceOf(address account_) public view returns (uint256 balance_) {
return _accounts[account_].balance;
}
/// @inheritdoc IWrappedMToken
function balanceWithYieldOf(address account_) public view returns (uint256 balance_) {
return balanceOf(account_) + accruedYieldOf(account_);
}
/// @inheritdoc IWrappedMToken
function lastIndexOf(address account_) public view returns (uint128 lastIndex_) {
return _accounts[account_].lastIndex;
}
/// @inheritdoc IWrappedMToken
function claimOverrideRecipientFor(address account_) public view returns (address recipient_) {
return
address(
uint160(
uint256(
IRegistrarLike(registrar).get(keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_PREFIX, account_)))
)
)
);
}
/// @inheritdoc IWrappedMToken
function currentIndex() public view returns (uint128 index_) {
return isEarningEnabled() ? _currentMIndex() : _lastDisableEarningIndex();
}
/// @inheritdoc IWrappedMToken
function isEarning(address account_) external view returns (bool isEarning_) {
return _accounts[account_].isEarning;
}
/// @inheritdoc IWrappedMToken
function isEarningEnabled() public view returns (bool isEnabled_) {
return _enableDisableEarningIndices.length % 2 == 1;
}
/// @inheritdoc IWrappedMToken
function wasEarningEnabled() public view returns (bool wasEarning_) {
return _enableDisableEarningIndices.length != 0;
}
/// @inheritdoc IWrappedMToken
function excess() public view returns (uint240 excess_) {
unchecked {
uint128 currentIndex_ = currentIndex();
uint240 balance_ = uint240(IMTokenLike(mToken).balanceOf(address(this)));
uint240 earmarked_ = totalNonEarningSupply + _projectedEarningSupply(currentIndex_);
return balance_ > earmarked_ ? _getSafeTransferableM(balance_ - earmarked_, currentIndex_) : 0;
}
}
/// @inheritdoc IWrappedMToken
function totalAccruedYield() external view returns (uint240 yield_) {
uint240 projectedEarningSupply_ = _projectedEarningSupply(currentIndex());
uint240 earningSupply_ = totalEarningSupply;
unchecked {
return projectedEarningSupply_ <= earningSupply_ ? 0 : projectedEarningSupply_ - earningSupply_;
}
}
/// @inheritdoc IERC20
function totalSupply() external view returns (uint256 totalSupply_) {
return totalEarningSupply + totalNonEarningSupply;
}
/* ============ 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_, uint240 amount_) internal {
_revertIfInsufficientAmount(amount_);
_revertIfInvalidRecipient(recipient_);
if (_accounts[recipient_].isEarning) {
uint128 currentIndex_ = currentIndex();
_claim(recipient_, currentIndex_);
// NOTE: Additional principal may end up being rounded to 0 and this will not `_revertIfInsufficientAmount`.
_addEarningAmount(recipient_, amount_, currentIndex_);
} else {
_addNonEarningAmount(recipient_, 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_, uint240 amount_) internal {
_revertIfInsufficientAmount(amount_);
if (_accounts[account_].isEarning) {
uint128 currentIndex_ = currentIndex();
_claim(account_, currentIndex_);
// NOTE: Subtracted principal may end up being rounded to 0 and this will not `_revertIfInsufficientAmount`.
_subtractEarningAmount(account_, amount_, currentIndex_);
} else {
_subtractNonEarningAmount(account_, amount_);
}
emit Transfer(account_, address(0), amount_);
}
/**
* @dev Increments the token balance of `account_` by `amount_`, assuming non-earning status.
* @param account_ The address whose account balance will be incremented.
* @param amount_ The present amount of tokens to increment by.
*/
function _addNonEarningAmount(address account_, uint240 amount_) internal {
// NOTE: Can be `unchecked` because the max amount of wrappable M is never greater than `type(uint240).max`.
unchecked {
_accounts[account_].balance += amount_;
totalNonEarningSupply += amount_;
}
}
/**
* @dev Decrements the token balance of `account_` by `amount_`, assuming non-earning status.
* @param account_ The address whose account balance will be decremented.
* @param amount_ The present amount of tokens to decrement by.
*/
function _subtractNonEarningAmount(address account_, uint240 amount_) internal {
unchecked {
Account storage accountInfo_ = _accounts[account_];
uint240 balance_ = accountInfo_.balance;
if (balance_ < amount_) revert InsufficientBalance(account_, balance_, amount_);
accountInfo_.balance = balance_ - amount_;
totalNonEarningSupply -= amount_;
}
}
/**
* @dev Increments the token balance of `account_` by `amount_`, assuming earning status and updated index.
* @param account_ The address whose account balance will be incremented.
* @param amount_ The present amount of tokens to increment by.
* @param currentIndex_ The current index to use to compute the principal amount.
*/
function _addEarningAmount(address account_, uint240 amount_, uint128 currentIndex_) internal {
// NOTE: Can be `unchecked` because the max amount of wrappable M is never greater than `type(uint240).max`.
unchecked {
_accounts[account_].balance += amount_;
_addTotalEarningSupply(amount_, currentIndex_);
}
}
/**
* @dev Decrements the token balance of `account_` by `amount_`, assuming earning status and updated index.
* @param account_ The address whose account balance will be decremented.
* @param amount_ The present amount of tokens to decrement by.
* @param currentIndex_ The current index to use to compute the principal amount.
*/
function _subtractEarningAmount(address account_, uint240 amount_, uint128 currentIndex_) internal {
unchecked {
Account storage accountInfo_ = _accounts[account_];
uint240 balance_ = accountInfo_.balance;
if (balance_ < amount_) revert InsufficientBalance(account_, balance_, amount_);
accountInfo_.balance = balance_ - amount_;
_subtractTotalEarningSupply(amount_, currentIndex_);
}
}
/**
* @dev Claims accrued yield for `account_` given a `currentIndex_`.
* @param account_ The address to claim accrued yield for.
* @param currentIndex_ The current index to accrue until.
* @return yield_ The accrued yield that was claimed.
*/
function _claim(address account_, uint128 currentIndex_) internal returns (uint240 yield_) {
Account storage accountInfo_ = _accounts[account_];
if (!accountInfo_.isEarning) return 0;
uint128 index_ = accountInfo_.lastIndex;
if (currentIndex_ == index_) return 0;
uint240 startingBalance_ = accountInfo_.balance;
yield_ = _getAccruedYield(startingBalance_, index_, currentIndex_);
accountInfo_.lastIndex = currentIndex_;
unchecked {
if (yield_ == 0) return 0;
accountInfo_.balance = startingBalance_ + yield_;
// Update the total earning supply to account for the yield, but the principal has not changed.
totalEarningSupply += yield_;
}
address claimOverrideRecipient_ = claimOverrideRecipientFor(account_);
address claimRecipient_ = claimOverrideRecipient_ == address(0) ? account_ : claimOverrideRecipient_;
// Emit the appropriate `Claimed` and `Transfer` events, depending on the claim override recipient
emit Claimed(account_, claimRecipient_, yield_);
emit Transfer(address(0), account_, yield_);
if (claimRecipient_ != account_) {
// NOTE: Watch out for a long chain of earning claim override recipients.
_transfer(account_, claimRecipient_, yield_, currentIndex_);
}
}
/**
* @dev Transfers `amount_` tokens from `sender_` to `recipient_` given some current index.
* @param sender_ The sender's address.
* @param recipient_ The recipient's address.
* @param amount_ The amount to be transferred.
* @param currentIndex_ The current index.
*/
function _transfer(address sender_, address recipient_, uint240 amount_, uint128 currentIndex_) internal {
_revertIfInvalidRecipient(recipient_);
// Claims for both the sender and recipient are required before transferring since add an subtract functions
// assume accounts' balances are up-to-date with the current index.
_claim(sender_, currentIndex_);
_claim(recipient_, currentIndex_);
emit Transfer(sender_, recipient_, amount_);
Account storage senderAccountInfo_ = _accounts[sender_];
Account storage recipientAccountInfo_ = _accounts[recipient_];
// If the sender and recipient are both earning or both non-earning, update their balances without affecting
// the total earning and non-earning supply storage variables.
if (senderAccountInfo_.isEarning == recipientAccountInfo_.isEarning) {
uint240 senderBalance_ = senderAccountInfo_.balance;
if (senderBalance_ < amount_) revert InsufficientBalance(sender_, senderBalance_, amount_);
unchecked {
senderAccountInfo_.balance = senderBalance_ - amount_;
recipientAccountInfo_.balance += amount_;
}
return;
}
senderAccountInfo_.isEarning
? _subtractEarningAmount(sender_, amount_, currentIndex_)
: _subtractNonEarningAmount(sender_, amount_);
recipientAccountInfo_.isEarning
? _addEarningAmount(recipient_, amount_, currentIndex_)
: _addNonEarningAmount(recipient_, amount_);
}
/**
* @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 override {
_transfer(sender_, recipient_, UIntMath.safe240(amount_), currentIndex());
}
/**
* @dev Increments total earning supply by `amount_` tokens.
* @param amount_ The present amount of tokens to increment total earning supply by.
* @param currentIndex_ The current index used to compute the principal amount.
*/
function _addTotalEarningSupply(uint240 amount_, uint128 currentIndex_) internal {
unchecked {
// Increment the total earning supply and principal proportionally.
totalEarningSupply += amount_;
principalOfTotalEarningSupply += IndexingMath.getPrincipalAmountRoundedUp(amount_, currentIndex_);
}
}
/**
* @dev Decrements total earning supply by `amount_` tokens.
* @param amount_ The present amount of tokens to decrement total earning supply by.
* @param currentIndex_ The current index used to compute the principal amount.
*/
function _subtractTotalEarningSupply(uint240 amount_, uint128 currentIndex_) internal {
if (amount_ >= totalEarningSupply) {
totalEarningSupply = 0;
principalOfTotalEarningSupply = 0;
return;
}
unchecked {
uint112 principal_ = IndexingMath.getPrincipalAmountRoundedDown(amount_, currentIndex_);
principalOfTotalEarningSupply -= (
principal_ > principalOfTotalEarningSupply ? principalOfTotalEarningSupply : principal_
);
totalEarningSupply -= amount_;
}
}
/**
* @dev Wraps `amount` M from `account_` into wM for `recipient`.
* @param account_ The account from which M is deposited.
* @param recipient_ The account receiving the minted wM.
* @param amount_ The amount of M deposited.
* @return wrapped_ The amount of wM minted.
*/
function _wrap(address account_, address recipient_, uint240 amount_) internal returns (uint240 wrapped_) {
uint256 startingBalance_ = IMTokenLike(mToken).balanceOf(address(this));
// NOTE: The behavior of `IMTokenLike.transferFrom` is known, so its return can be ignored.
IMTokenLike(mToken).transferFrom(account_, address(this), amount_);
// NOTE: When this WrappedMToken contract is earning, any amount of M sent to it is converted to a principal
// amount at the MToken contract, which when represented as a present amount, may be a rounding error
// amount less than `amount_`. In order to capture the real increase in M, the difference between the
// starting and ending M balance is minted as WrappedM.
_mint(recipient_, wrapped_ = UIntMath.safe240(IMTokenLike(mToken).balanceOf(address(this)) - startingBalance_));
}
/**
* @dev Unwraps `amount` wM from `account_` into M for `recipient`.
* @param account_ The account from which WM is burned.
* @param recipient_ The account receiving the withdrawn M.
* @param amount_ The amount of wM burned.
* @return unwrapped_ The amount of M withdrawn.
*/
function _unwrap(address account_, address recipient_, uint240 amount_) internal returns (uint240 unwrapped_) {
_burn(account_, amount_);
uint256 startingBalance_ = IMTokenLike(mToken).balanceOf(address(this));
// NOTE: The behavior of `IMTokenLike.transfer` is known, so its return can be ignored.
IMTokenLike(mToken).transfer(recipient_, _getSafeTransferableM(amount_, currentIndex()));
// NOTE: When this WrappedMToken contract is earning, any amount of M sent from it is converted to a principal
// amount at the MToken contract, which when represented as a present amount, may be a rounding error
// amount more than `amount_`. In order to capture the real decrease in M, the difference between the
// ending and starting M balance is returned.
return UIntMath.safe240(startingBalance_ - IMTokenLike(mToken).balanceOf(address(this)));
}
/* ============ Internal View/Pure Functions ============ */
/// @dev Returns the current index of the M Token.
function _currentMIndex() internal view returns (uint128 index_) {
return IMTokenLike(mToken).currentIndex();
}
/// @dev Returns the earning index from the last `disableEarning` call.
function _lastDisableEarningIndex() internal view returns (uint128 index_) {
return wasEarningEnabled() ? _unsafeAccess(_enableDisableEarningIndices, 1) : 0;
}
/**
* @dev Compute the yield given an account's balance, last index, and the current index.
* @param balance_ The token balance of an earning account.
* @param lastIndex_ The index of ast interaction for the account.
* @param currentIndex_ The current index.
* @return yield_ The yield accrued since the last interaction.
*/
function _getAccruedYield(
uint240 balance_,
uint128 lastIndex_,
uint128 currentIndex_
) internal pure returns (uint240 yield_) {
uint240 balanceWithYield_ = IndexingMath.getPresentAmountRoundedDown(
IndexingMath.getPrincipalAmountRoundedDown(balance_, lastIndex_),
currentIndex_
);
unchecked {
return (balanceWithYield_ <= balance_) ? 0 : balanceWithYield_ - balance_;
}
}
/**
* @dev Compute the adjusted amount of M that can safely be transferred out given the current index.
* @param amount_ Some amount to be transferred out of the wrapper.
* @param currentIndex_ The current index.
* @return safeAmount_ The adjusted amount that can safely be transferred out.
*/
function _getSafeTransferableM(uint240 amount_, uint128 currentIndex_) internal view returns (uint240 safeAmount_) {
// If the wrapper is earning, adjust `amount_` to ensure it's M balance decrement is limited to `amount_`.
return
IMTokenLike(mToken).isEarning(address(this))
? IndexingMath.getPresentAmountRoundedDown(
IndexingMath.getPrincipalAmountRoundedDown(amount_, currentIndex_),
currentIndex_
)
: amount_;
}
/// @dev Returns the address of the contract to use as a migrator, if any.
function _getMigrator() internal view override returns (address migrator_) {
return
address(
uint160(
// NOTE: A subsequent implementation should use a unique migrator prefix.
uint256(IRegistrarLike(registrar).get(keccak256(abi.encode(_MIGRATOR_V1_PREFIX, address(this)))))
)
);
}
/**
* @dev Returns whether `account_` is a TTG-approved earner.
* @param account_ The account being queried.
* @return isApproved_ True if the account_ is a TTG-approved earner, false otherwise.
*/
function _isApprovedEarner(address account_) internal view returns (bool isApproved_) {
return
IRegistrarLike(registrar).get(_EARNERS_LIST_IGNORED) != bytes32(0) ||
IRegistrarLike(registrar).listContains(_EARNERS_LIST, account_);
}
/**
* @dev Returns the projected total earning supply if all accrued yield was claimed at this moment.
* @param currentIndex_ The current index.
* @return supply_ The projected total earning supply.
*/
function _projectedEarningSupply(uint128 currentIndex_) internal view returns (uint240 supply_) {
return IndexingMath.getPresentAmountRoundedDown(principalOfTotalEarningSupply, currentIndex_);
}
/**
* @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 `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 `account_` is an approved earner.
* @param account_ Address of an account.
*/
function _revertIfApprovedEarner(address account_) internal view {
if (_isApprovedEarner(account_)) revert IsApprovedEarner();
}
/**
* @dev Reverts if `account_` is not an approved earner.
* @param account_ Address of an account.
*/
function _revertIfNotApprovedEarner(address account_) internal view {
if (!_isApprovedEarner(account_)) revert NotApprovedEarner();
}
/**
* @dev Reads the uint128 value at some index of an array of uint128 values whose storage pointer is given,
* assuming the index is valid, without wasting gas checking for out-of-bounds errors.
* @param array_ The storage pointer of an array of uint128 values.
* @param i_ The index of the array to read.
*/
function _unsafeAccess(uint128[] storage array_, uint256 i_) internal view returns (uint128 value_) {
assembly {
mstore(0, array_.slot)
value_ := sload(add(keccak256(0, 0x20), div(i_, 2)))
// Since uint128 values take up either the top half or bottom half of a slot, shift the result accordingly.
if eq(mod(i_, 2), 1) {
value_ := shr(128, value_)
}
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
/**
* @title Library to perform safe math operations on uint types
* @author M^0 Labs
*/
library UIntMath {
/* ============ Custom Errors ============ */
/// @notice Emitted when a passed value is greater than the maximum value of uint16.
error InvalidUInt16();
/// @notice Emitted when a passed value is greater than the maximum value of uint40.
error InvalidUInt40();
/// @notice Emitted when a passed value is greater than the maximum value of uint48.
error InvalidUInt48();
/// @notice Emitted when a passed value is greater than the maximum value of uint112.
error InvalidUInt112();
/// @notice Emitted when a passed value is greater than the maximum value of uint128.
error InvalidUInt128();
/// @notice Emitted when a passed value is greater than the maximum value of uint240.
error InvalidUInt240();
/* ============ Internal View/Pure Functions ============ */
/**
* @notice Casts a uint256 value to a uint16, ensuring that it is less than or equal to the maximum uint16 value.
* @param n The value to cast.
* @return The value casted to uint16.
*/
function safe16(uint256 n) internal pure returns (uint16) {
if (n > type(uint16).max) revert InvalidUInt16();
return uint16(n);
}
/**
* @notice Casts a uint256 value to a uint40, ensuring that it is less than or equal to the maximum uint40 value.
* @param n The value to cast.
* @return The value casted to uint40.
*/
function safe40(uint256 n) internal pure returns (uint40) {
if (n > type(uint40).max) revert InvalidUInt40();
return uint40(n);
}
/**
* @notice Casts a uint256 value to a uint48, ensuring that it is less than or equal to the maximum uint48 value.
* @param n The value to cast.
* @return The value casted to uint48.
*/
function safe48(uint256 n) internal pure returns (uint48) {
if (n > type(uint48).max) revert InvalidUInt48();
return uint48(n);
}
/**
* @notice Casts a uint256 value to a uint112, ensuring that it is less than or equal to the maximum uint112 value.
* @param n The value to cast.
* @return The value casted to uint112.
*/
function safe112(uint256 n) internal pure returns (uint112) {
if (n > type(uint112).max) revert InvalidUInt112();
return uint112(n);
}
/**
* @notice Casts a uint256 value to a uint128, ensuring that it is less than or equal to the maximum uint128 value.
* @param n The value to cast.
* @return The value casted to uint128.
*/
function safe128(uint256 n) internal pure returns (uint128) {
if (n > type(uint128).max) revert InvalidUInt128();
return uint128(n);
}
/**
* @notice Casts a uint256 value to a uint240, ensuring that it is less than or equal to the maximum uint240 value.
* @param n The value to cast.
* @return The value casted to uint240.
*/
function safe240(uint256 n) internal pure returns (uint240) {
if (n > type(uint240).max) revert InvalidUInt240();
return uint240(n);
}
/**
* @notice Limits a uint256 value to the maximum uint32 value.
* @param n The value to bound.
* @return The value limited to within uint32 bounds.
*/
function bound32(uint256 n) internal pure returns (uint32) {
return uint32(min256(n, uint256(type(uint32).max)));
}
/**
* @notice Limits a uint256 value to the maximum uint112 value.
* @param n The value to bound.
* @return The value limited to within uint112 bounds.
*/
function bound112(uint256 n) internal pure returns (uint112) {
return uint112(min256(n, uint256(type(uint112).max)));
}
/**
* @notice Limits a uint256 value to the maximum uint128 value.
* @param n The value to bound.
* @return The value limited to within uint128 bounds.
*/
function bound128(uint256 n) internal pure returns (uint128) {
return uint128(min256(n, uint256(type(uint128).max)));
}
/**
* @notice Limits a uint256 value to the maximum uint240 value.
* @param n The value to bound.
* @return The value limited to within uint240 bounds.
*/
function bound240(uint256 n) internal pure returns (uint240) {
return uint240(min256(n, uint256(type(uint240).max)));
}
/**
* @notice Compares two uint32 values and returns the larger one.
* @param a Value to compare.
* @param b Value to compare.
* @return The larger value.
*/
function max32(uint32 a, uint32 b) internal pure returns (uint32) {
return a > b ? a : b;
}
/**
* @notice Compares two uint40 values and returns the larger one.
* @param a Value to compare.
* @param b Value to compare.
* @return The larger value.
*/
function max40(uint40 a, uint40 b) internal pure returns (uint40) {
return a > b ? a : b;
}
/**
* @notice Compares two uint32 values and returns the lesser one.
* @param a Value to compare.
* @param b Value to compare.
* @return The lesser value.
*/
function min32(uint32 a, uint32 b) internal pure returns (uint32) {
return a < b ? a : b;
}
/**
* @notice Compares two uint40 values and returns the lesser one.
* @param a Value to compare.
* @param b Value to compare.
* @return The lesser value.
*/
function min40(uint40 a, uint40 b) internal pure returns (uint40) {
return a < b ? a : b;
}
/**
* @notice Compares two uint240 values and returns the lesser one.
* @param a Value to compare.
* @param b Value to compare.
* @return The lesser value.
*/
function min240(uint240 a, uint240 b) internal pure returns (uint240) {
return a < b ? a : b;
}
/**
* @notice Compares two uint112 values and returns the lesser one.
* @param a Value to compare.
* @param b Value to compare.
* @return The lesser value.
*/
function min112(uint112 a, uint112 b) internal pure returns (uint112) {
return a < b ? a : b;
}
/**
* @notice Compares two uint256 values and returns the lesser one.
* @param a Value to compare.
* @param b Value to compare.
* @return The lesser value.
*/
function min256(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}// 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: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IERC20 } from "./interfaces/IERC20.sol";
import { IERC20Extended } from "./interfaces/IERC20Extended.sol";
import { Bytes32String } from "./libs/Bytes32String.sol";
import { ERC3009 } from "./ERC3009.sol";
/**
* @title An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712 and with EIP-1271
* and EIP-5267 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
* @author M^0 Labs
*/
abstract contract ERC20Extended is IERC20Extended, ERC3009 {
/* ============ 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;
/// @inheritdoc IERC20
uint8 public immutable decimals;
/// @dev The symbol of the token (stored as a bytes32 instead of a string in order to be immutable).
bytes32 internal immutable _symbol;
/// @inheritdoc IERC20
mapping(address account => mapping(address spender => uint256 allowance)) public allowance;
/* ============ Constructor ============ */
/**
* @notice Constructs the ERC20Extended contract.
* @param name_ The name of the token.
* @param symbol_ The symbol of the token.
* @param decimals_ The number of decimals the token uses.
*/
constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC3009(name_) {
_symbol = Bytes32String.toBytes32(symbol_);
decimals = decimals_;
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IERC20
function approve(address spender_, uint256 amount_) external returns (bool success_) {
_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 success_) {
_transfer(msg.sender, recipient_, amount_);
return true;
}
/// @inheritdoc IERC20
function transferFrom(address sender_, address recipient_, uint256 amount_) external returns (bool success_) {
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 name() external view returns (string memory) {
return Bytes32String.toString(_name);
}
/// @inheritdoc IERC20
function symbol() external view returns (string memory) {
return Bytes32String.toString(_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(account_, spender_, amount_);
emit Approval(account_, spender_, amount_);
}
/**
* @dev Set the `amount_` of tokens `spender_` is allowed to spend from `account_`.
* @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(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 digest_) {
_revertIfExpired(deadline_);
_approve(owner_, spender_, amount_);
unchecked {
// Nonce realistically cannot overflow.
return
_getDigest(
keccak256(abi.encode(PERMIT_TYPEHASH, owner_, spender_, amount_, nonces[owner_]++, deadline_))
);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { UIntMath } from "../../lib/common/src/libs/UIntMath.sol";
/**
* @title Helper library for indexing math functions.
* @author M^0 Labs
*/
library IndexingMath {
/* ============ Variables ============ */
/// @notice The scaling of indexes for exponent math.
uint56 internal constant EXP_SCALED_ONE = 1e12;
/* ============ Custom Errors ============ */
/// @notice Emitted when a division by zero occurs.
error DivisionByZero();
/* ============ Internal View/Pure Functions ============ */
/**
* @notice Helper function to calculate `(x * EXP_SCALED_ONE) / y`, rounded down.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function divide240By128Down(uint240 x_, uint128 y_) internal pure returns (uint112) {
if (y_ == 0) revert DivisionByZero();
unchecked {
// NOTE: While `uint256(x) * EXP_SCALED_ONE` can technically overflow, these divide/multiply functions are
// only used for the purpose of principal/present amount calculations for continuous indexing, and
// so for an `x` to be large enough to overflow this, it would have to be a possible result of
// `multiply112By128Down` or `multiply112By128Up`, which would already satisfy
// `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
return UIntMath.safe112((uint256(x_) * EXP_SCALED_ONE) / y_);
}
}
/**
* @notice Helper function to calculate `(x * EXP_SCALED_ONE) / y`, rounded up.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function divide240By128Up(uint240 x_, uint128 y_) internal pure returns (uint112) {
if (y_ == 0) revert DivisionByZero();
unchecked {
// NOTE: While `uint256(x) * EXP_SCALED_ONE` can technically overflow, these divide/multiply functions are
// only used for the purpose of principal/present amount calculations for continuous indexing, and
// so for an `x` to be large enough to overflow this, it would have to be a possible result of
// `multiply112By128Down` or `multiply112By128Up`, which would already satisfy
// `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
return UIntMath.safe112(((uint256(x_) * EXP_SCALED_ONE) + y_ - 1) / y_);
}
}
/**
* @notice Helper function to calculate `(x * y) / EXP_SCALED_ONE`, rounded down.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function multiply112By128Down(uint112 x_, uint128 y_) internal pure returns (uint240) {
unchecked {
return uint240((uint256(x_) * y_) / EXP_SCALED_ONE);
}
}
/**
* @dev Returns the present amount (rounded down) given the principal amount and an index.
* @param principalAmount_ The principal amount.
* @param index_ An index.
* @return The present amount rounded down.
*/
function getPresentAmountRoundedDown(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) {
return multiply112By128Down(principalAmount_, index_);
}
/**
* @dev Returns the principal amount given the present amount, using the current index.
* @param presentAmount_ The present amount.
* @param index_ An index.
* @return The principal amount rounded down.
*/
function getPrincipalAmountRoundedDown(uint240 presentAmount_, uint128 index_) internal pure returns (uint112) {
return divide240By128Down(presentAmount_, index_);
}
/**
* @dev Returns the principal amount given the present amount, using the current index.
* @param presentAmount_ The present amount.
* @param index_ An index.
* @return The principal amount rounded up.
*/
function getPrincipalAmountRoundedUp(uint240 presentAmount_, uint128 index_) internal pure returns (uint112) {
return divide240By128Up(presentAmount_, index_);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title Subset of M Token interface required for source contracts.
* @author M^0 Labs
*/
interface IMTokenLike {
/* ============ Interactive Functions ============ */
/**
* @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 success);
/**
* @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 success);
/// @notice Starts earning for caller if allowed by TTG.
function startEarning() external;
/// @notice Stops earning for caller.
function stopEarning() 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 earning);
/**
* @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 balance);
/// @notice The current index that would be written to storage if `updateIndex` is called.
function currentIndex() external view returns (uint128 currentIndex);
/// @notice The address of the TTG Registrar contract.
function ttgRegistrar() external view returns (address ttgRegistrar);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title Subset of Registrar interface required for source contracts.
* @author M^0 Labs
*/
interface IRegistrarLike {
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the value of `key`.
* @param key Some key.
* @return value Some value.
*/
function get(bytes32 key) external view returns (bytes32 value);
/**
* @notice Returns whether `list` contains `account` or not.
* @param list The key for some list.
* @param account The address of some account.
* @return contains Whether `list` contains `account` or not.
*/
function listContains(bytes32 list, address account) external view returns (bool contains);
/// @notice Returns the address of the Vault.
function vault() external view returns (address vault);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.sol";
import { IMigratable } from "./IMigratable.sol";
/**
* @title Wrapped M Token interface extending Extended ERC20.
* @author M^0 Labs
*/
interface IWrappedMToken is IMigratable, IERC20Extended {
/* ============ Events ============ */
/**
* @notice Emitted when some yield is claim for `account` to `recipient`.
* @param account The account under which yield was generated.
* @param recipient The account that received the yield.
* @param yield The amount of yield claimed.
*/
event Claimed(address indexed account, address indexed recipient, uint240 yield);
/**
* @notice Emitted when earning is enabled for the entire wrapper.
* @param index The index at the moment earning is enabled.
*/
event EarningEnabled(uint128 index);
/**
* @notice Emitted when earning is disabled for the entire wrapper.
* @param index The index at the moment earning is disabled.
*/
event EarningDisabled(uint128 index);
/**
* @notice Emitted when the wrapper's excess M is claimed.
* @param excess The amount of excess M claimed.
*/
event ExcessClaimed(uint240 excess);
/**
* @notice Emitted when `account` starts being an wM earner.
* @param account The account that started earning.
*/
event StartedEarning(address indexed account);
/**
* @notice Emitted when `account` stops being an wM earner.
* @param account The account that stopped earning.
*/
event StoppedEarning(address indexed account);
/* ============ 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 trying to enable earning after it has been explicitly disabled.
error EarningCannotBeReenabled();
/// @notice Emitted when calling `stopEarning` for an account approved as earner by TTG.
error IsApprovedEarner();
/**
* @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, uint240 balance, uint240 amount);
/// @notice Emitted when calling `startEarning` for an account not approved as earner by TTG.
error NotApprovedEarner();
/// @notice Emitted when the non-governance migrate function is called by a account other than the migration admin.
error UnauthorizedMigration();
/// @notice Emitted in constructor if Excess Destination is 0x0.
error ZeroExcessDestination();
/// @notice Emitted in constructor if M Token is 0x0.
error ZeroMToken();
/// @notice Emitted in constructor if Migration Admin is 0x0.
error ZeroMigrationAdmin();
/// @notice Emitted in constructor if Registrar is 0x0.
error ZeroRegistrar();
/* ============ Interactive Functions ============ */
/**
* @notice Wraps `amount` M from the caller into wM for `recipient`.
* @param recipient The account receiving the minted wM.
* @param amount The amount of M deposited.
* @return wrapped The amount of wM minted.
*/
function wrap(address recipient, uint256 amount) external returns (uint240 wrapped);
/**
* @notice Wraps all the M from the caller into wM for `recipient`.
* @param recipient The account receiving the minted wM.
* @return wrapped The amount of wM minted.
*/
function wrap(address recipient) external returns (uint240 wrapped);
/**
* @notice Unwraps `amount` wM from the caller into M for `recipient`.
* @param recipient The account receiving the withdrawn M.
* @param amount The amount of wM burned.
* @return unwrapped The amount of M withdrawn.
*/
function unwrap(address recipient, uint256 amount) external returns (uint240 unwrapped);
/**
* @notice Unwraps all the wM from the caller into M for `recipient`.
* @param recipient The account receiving the withdrawn M.
* @return unwrapped The amount of M withdrawn.
*/
function unwrap(address recipient) external returns (uint240 unwrapped);
/**
* @notice Claims any claimable yield for `account`.
* @param account The account under which yield was generated.
* @return yield The amount of yield claimed.
*/
function claimFor(address account) external returns (uint240 yield);
/**
* @notice Claims any excess M of the wrapper.
* @return excess The amount of excess claimed.
*/
function claimExcess() external returns (uint240 excess);
/// @notice Enables earning for the wrapper if allowed by TTG and if it has never been done.
function enableEarning() external;
/// @notice Disables earning for the wrapper if disallowed by TTG and if it has never been done.
function disableEarning() external;
/**
* @notice Starts earning for `account` if allowed by TTG.
* @param account The account to start earning for.
*/
function startEarningFor(address account) external;
/**
* @notice Stops earning for `account` if disallowed by TTG.
* @param account The account to stop earning for.
*/
function stopEarningFor(address account) external;
/* ============ Temporary Admin Migration ============ */
/**
* @notice Performs an arbitrarily defined migration.
* @param migrator The address of a migrator contract.
*/
function migrate(address migrator) external;
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the yield accrued for `account`, which is claimable.
* @param account The account being queried.
* @return yield The amount of yield that is claimable.
*/
function accruedYieldOf(address account) external view returns (uint240 yield);
/**
* @notice Returns the token balance of `account` including any accrued yield.
* @param account The address of some account.
* @return balance The token balance of `account` including any accrued yield.
*/
function balanceWithYieldOf(address account) external view returns (uint256 balance);
/**
* @notice Returns the last index of `account`.
* @param account The address of some account.
* @return lastIndex The last index of `account`, 0 if the account is not earning.
*/
function lastIndexOf(address account) external view returns (uint128 lastIndex);
/**
* @notice Returns the recipient to override as the destination for an account's claim of yield.
* @param account The account being queried.
* @return recipient The address of the recipient, if any, to override as the destination of claimed yield.
*/
function claimOverrideRecipientFor(address account) external view returns (address recipient);
/// @notice The current index of the wrapper's earning mechanism.
function currentIndex() external view returns (uint128 index);
/// @notice The current excess M of the wrapper that is not earmarked for account balances or accrued yield.
function excess() external view returns (uint240 excess);
/**
* @notice Returns whether `account` is a wM earner.
* @param account The account being queried.
* @return isEarning true if the account has started earning.
*/
function isEarning(address account) external view returns (bool isEarning);
/// @notice Whether earning is enabled for the entire wrapper.
function isEarningEnabled() external view returns (bool isEnabled);
/// @notice Whether earning has been enabled at least once or not.
function wasEarningEnabled() external view returns (bool wasEnabled);
/// @notice The account that can bypass TTG and call the `migrate(address migrator)` function.
function migrationAdmin() external view returns (address migrationAdmin);
/// @notice The address of the M Token contract.
function mToken() external view returns (address mToken);
/// @notice The address of the TTG registrar.
function registrar() external view returns (address registrar);
/// @notice The portion of total supply that is not earning yield.
function totalNonEarningSupply() external view returns (uint240 totalSupply);
/// @notice The accrued yield of the portion of total supply that is earning yield.
function totalAccruedYield() external view returns (uint240 yield);
/// @notice The portion of total supply that is earning yield.
function totalEarningSupply() external view returns (uint240 totalSupply);
/// @notice The principal of totalEarningSupply to help compute totalAccruedYield(), and thus excess().
function principalOfTotalEarningSupply() external view returns (uint112 principalOfTotalEarningSupply);
/// @notice The address where excess is claimed to.
function excessDestination() external view returns (address excessDestination);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IMigratable } from "./interfaces/IMigratable.sol";
/**
* @title Abstract implementation for exposing the ability to migrate a contract, extending ERC-1967.
* @author M^0 Labs
*/
abstract contract Migratable is IMigratable {
/* ============ Variables ============ */
/// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`.
uint256 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/* ============ Interactive Functions ============ */
/// @inheritdoc IMigratable
function migrate() external {
_migrate(_getMigrator());
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IMigratable
function implementation() public view returns (address implementation_) {
assembly {
implementation_ := sload(_IMPLEMENTATION_SLOT)
}
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Performs an arbitrary migration by delegate-calling `migrator_`.
* @param migrator_ The address of a migrator contract.
*/
function _migrate(address migrator_) internal {
if (migrator_ == address(0)) revert ZeroMigrator();
if (migrator_.code.length == 0) revert InvalidMigrator();
address oldImplementation_ = implementation();
(bool success_, ) = migrator_.delegatecall("");
if (!success_) revert MigrationFailed();
address newImplementation_ = implementation();
emit Migrated(migrator_, oldImplementation_, newImplementation_);
// NOTE: Redundant event emitted to conform to the EIP-1967 standard.
emit Upgraded(newImplementation_);
}
/* ============ Internal View/Pure Functions ============ */
/// @dev Returns the address of a migrator contract.
function _getMigrator() internal view virtual returns (address migrator_);
}// 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: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
/**
* @title A library to convert between string and bytes32 (assuming 32 characters or less).
* @author M^0 Labs
*/
library Bytes32String {
function toBytes32(string memory input) internal pure returns (bytes32) {
return bytes32(abi.encodePacked(input));
}
function toString(bytes32 input) internal pure returns (string memory) {
uint256 length;
while (length < 32 && uint8(input[length]) != 0) {
++length;
}
bytes memory name = new bytes(length);
for (uint256 index; index < length; ++index) {
name[index] = input[index];
}
return string(name);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
import { IERC3009 } from "./interfaces/IERC3009.sol";
import { StatefulERC712 } from "./StatefulERC712.sol";
/**
* @title ERC3009 implementation allowing the transfer of fungible assets via a signed authorization.
* @author M^0 Labs
* @dev Inherits from ERC712 and StatefulERC712.
*/
abstract contract ERC3009 is IERC3009, StatefulERC712 {
/* ============ 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;
/// @inheritdoc IERC3009
mapping(address authorizer => mapping(bytes32 nonce => bool isNonceUsed)) public authorizationState;
/* ============ Constructor ============ */
/**
* @notice Construct the ERC3009 contract.
* @param name_ The name of the contract.
*/
constructor(string memory name_) StatefulERC712(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_);
}
/* ============ 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_);
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_);
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: BUSL-1.1
pragma solidity 0.8.26;
/**
* @title Interface for exposing the ability to migrate a contract, extending the ERC-1967 interface.
* @author M^0 Labs
*/
interface IMigratable {
/* ============ Events ============ */
/**
* @notice Emitted when a migration to a new implementation is performed.
* @param migrator The address that performed the migration.
* @param oldImplementation The address of the old implementation.
* @param newImplementation The address of the new implementation.
*/
event Migrated(address indexed migrator, address indexed oldImplementation, address indexed newImplementation);
/**
* @notice Emitted when the implementation address for the proxy is changed.
* @param implementation The address of the new implementation for the proxy.
*/
event Upgraded(address indexed implementation);
/// @notice Emitted when calling `stopEarning` for an account approved as earner by TTG.
error InvalidMigrator();
/// @notice Emitted when the delegatecall to a migrator fails.
error MigrationFailed();
/// @notice Emitted when the zero address is passed as a migrator.
error ZeroMigrator();
/* ============ Interactive Functions ============ */
/// @notice Performs an arbitrarily defined migration.
function migrate() external;
/* ============ View/Pure Functions ============ */
/// @notice Returns the address of the current implementation contract.
function implementation() external view returns (address implementation);
}// 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 { ERC712Extended } from "./ERC712Extended.sol";
/**
* @title Stateful Extension for EIP-712 typed structured data hashing and signing with nonces.
* @author M^0 Labs
* @dev An abstract implementation to satisfy stateful EIP-712 with nonces.
*/
abstract contract StatefulERC712 is IStatefulERC712, ERC712Extended {
/// @inheritdoc IStatefulERC712
mapping(address account => uint256 nonce) public nonces; // Nonces for all signatures.
/**
* @notice Construct the StatefulERC712 contract.
* @param name_ The name of the contract.
*/
constructor(string memory name_) ERC712Extended(name_) {}
}// 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 { IERC712 } from "./interfaces/IERC712.sol";
import { IERC712Extended } from "./interfaces/IERC712Extended.sol";
import { Bytes32String } from "./libs/Bytes32String.sol";
import { SignatureChecker } from "./libs/SignatureChecker.sol";
/**
* @title Typed structured data hashing and signing via EIP-712, extended by EIP-5267.
* @author M^0 Labs
* @dev An abstract implementation to satisfy EIP-712: https://eips.ethereum.org/EIPS/eip-712
*/
abstract contract ERC712Extended is IERC712Extended {
/* ============ 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;
/// @dev Initial Chain ID set at deployment.
uint256 internal immutable _INITIAL_CHAIN_ID;
/// @dev Initial EIP-712 domain separator set at deployment.
bytes32 internal immutable _INITIAL_DOMAIN_SEPARATOR;
/// @dev The name of the contract (stored as a bytes32 instead of a string in order to be immutable).
bytes32 internal immutable _name;
/* ============ Constructor ============ */
/**
* @notice Constructs the EIP-712 domain separator.
* @param name_ The name of the contract.
*/
constructor(string memory name_) {
_name = Bytes32String.toBytes32(name_);
_INITIAL_CHAIN_ID = block.chainid;
_INITIAL_DOMAIN_SEPARATOR = _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
Bytes32String.toString(_name),
"1",
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/// @inheritdoc IERC712
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == _INITIAL_CHAIN_ID ? _INITIAL_DOMAIN_SEPARATOR : _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(Bytes32String.toString(_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": [
"common/=lib/common/src/",
"ds-test/=lib/native-token-transfers/evm/lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"native-token-transfers/=lib/native-token-transfers/",
"openzeppelin-contracts/=lib/native-token-transfers/evm/lib/openzeppelin-contracts/",
"protocol/=lib/protocol/",
"solidity-bytes-utils/=lib/native-token-transfers/evm/lib/solidity-bytes-utils/contracts/",
"ttg/=lib/ttg/",
"wormhole-solidity-sdk/=lib/native-token-transfers/evm/lib/wormhole-solidity-sdk/src/",
"wrapped-m-token/=lib/wrapped-m-token/"
],
"optimizer": {
"enabled": true,
"runs": 1500
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {
"lib/native-token-transfers/evm/src/libraries/TransceiverStructs.sol": {
"TransceiverStructs": "0x188B1E9E77B04E1699A78D929568Ed5BB15F962C"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"mToken_","type":"address"},{"internalType":"address","name":"registrar_","type":"address"},{"internalType":"address","name":"excessDestination_","type":"address"},{"internalType":"address","name":"migrationAdmin_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"DivisionByZero","type":"error"},{"inputs":[],"name":"EarningCannotBeReenabled","type":"error"},{"inputs":[],"name":"EarningIsDisabled","type":"error"},{"inputs":[],"name":"EarningIsEnabled","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":"uint240","name":"balance","type":"uint240"},{"internalType":"uint240","name":"amount","type":"uint240"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expectedNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidMigrator","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":"InvalidUInt112","type":"error"},{"inputs":[],"name":"InvalidUInt240","type":"error"},{"inputs":[],"name":"IsApprovedEarner","type":"error"},{"inputs":[],"name":"MigrationFailed","type":"error"},{"inputs":[],"name":"NotApprovedEarner","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":"UnauthorizedMigration","type":"error"},{"inputs":[],"name":"ZeroExcessDestination","type":"error"},{"inputs":[],"name":"ZeroMToken","type":"error"},{"inputs":[],"name":"ZeroMigrationAdmin","type":"error"},{"inputs":[],"name":"ZeroMigrator","type":"error"},{"inputs":[],"name":"ZeroRegistrar","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":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint240","name":"yield","type":"uint240"}],"name":"Claimed","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":false,"internalType":"uint240","name":"excess","type":"uint240"}],"name":"ExcessClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"migrator","type":"address"},{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Migrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"StartedEarning","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"StoppedEarning","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":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","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":"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":[{"internalType":"address","name":"account_","type":"address"}],"name":"accruedYieldOf","outputs":[{"internalType":"uint240","name":"yield_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"allowance","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":"success_","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":"isNonceUsed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"balanceWithYieldOf","outputs":[{"internalType":"uint256","name":"balance_","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":"claimExcess","outputs":[{"internalType":"uint240","name":"excess_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"claimFor","outputs":[{"internalType":"uint240","name":"yield_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"claimOverrideRecipientFor","outputs":[{"internalType":"address","name":"recipient_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint128","name":"index_","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":[],"name":"excess","outputs":[{"internalType":"uint240","name":"excess_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"excessDestination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"isEarning","outputs":[{"internalType":"bool","name":"isEarning_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isEarningEnabled","outputs":[{"internalType":"bool","name":"isEnabled_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"lastIndexOf","outputs":[{"internalType":"uint128","name":"lastIndex_","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_","type":"address"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrationAdmin","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":"nonce","type":"uint256"}],"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":[],"name":"principalOfTotalEarningSupply","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","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":[],"name":"registrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"startEarningFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"stopEarningFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAccruedYield","outputs":[{"internalType":"uint240","name":"yield_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEarningSupply","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNonEarningSupply","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"totalSupply_","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":"success_","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":"success_","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":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"unwrap","outputs":[{"internalType":"uint240","name":"unwrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"}],"name":"unwrap","outputs":[{"internalType":"uint240","name":"unwrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wasEarningEnabled","outputs":[{"internalType":"bool","name":"wasEarning_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"}],"name":"wrap","outputs":[{"internalType":"uint240","name":"wrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"wrap","outputs":[{"internalType":"uint240","name":"wrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101a0806040523461035757608081613cf68038038091610020828561035b565b8339810103126103575761003381610392565b9061004060208201610392565b90610059606061005260408401610392565b9201610392565b9060409283519461006a858761035b565b600f86526e0577261707065644d206279204d5e3608c1b60208701526100aa855196610096878961035b565b6002885261774d60f01b60208901526103c1565b958660c052466080525f5b6020811080610348575b156100e8575f1981146100d4576001016100b5565b634e487b7160e01b5f52601160045260245ffd5b876100f2826103a6565b906100ff8951928361035b565b82825261010b836103a6565b602083019390601f19013685375f5b81811061030f57505050906101a79291519020875160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8352898201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815261019c60c08261035b565b51902060a0526103c1565b61010052600660e0526101408190526001600160a01b031615610300576101608190526001600160a01b0316156102f1576101808190526001600160a01b0316156102e2576101208190526001600160a01b0316156102d357516138ea908161040c823960805181611b80015260a05181611ba6015260c05181818161050601528181610bce0152611bcd015260e051816107dd01526101005181611095015261012051818181610a4801526115b101526101405181818161041e01528181610f0f01528181611216015281816113fe01528181611499015281816119ea01528181611f25015281816124a70152818161259101526126830152610160518181816107430152818161101a01528181611e5d0152612fd2015261018051818181610ec501526118690152f35b63ea0b51bb60e01b5f5260045ffd5b63c956307160e01b5f5260045ffd5b6379a6314960e01b5f5260045ffd5b63b01d5e2b60e01b5f5260045ffd5b60208110156103345783518110156103345780836001921a602082870101530161011a565b634e487b7160e01b5f52603260045260245ffd5b156103345787811a15156100bf565b5f80fd5b601f909101601f19168101906001600160401b0382119082101761037e57604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361035757565b6001600160401b03811161037e57601f01601f191660200190565b6040516103ed6020828180820195805191829101875e81015f838201520301601f19810183528261035b565b51905190602081106103fd575090565b5f199060200360031b1b169056fe60806040526004361015610011575f80fd5b5f3560e01c8063023276f0146103b457806306fdde03146103af578063095ea7b3146103aa578063174f6ce9146103a557806318160ddd146103a05780631ae2379c1461039b57806323b872dd1461039657806326987b6014610391578063281b229d1461038c5780632b20e397146103875780632c7861631461038257806330adf81f1461037d578063313ce567146103785780633644e5151461037357806339f476931461036e5780634c57a8fa14610369578063532992c51461036457806356d59ed21461035f5780635a049a701461035a5780635c60da1b146103555780635cf993841461035057806370a082311461034b57806374df25581461034657806374edaa201461034157806375f26e631461033c5780637c3ab944146103375780637ecebe00146103325780637f2eecc31461032d57806384af270f1461032857806384b0196e1461032357806388b7ab631461031e5780638a4a1017146103195780638a75f238146103145780638d78bf7e1461030f5780638fd3ab801461030a57806395d89b41146103055780639fd5a6cf14610300578063a08cb48b146102fb578063a0cc6a68146102f6578063a8afc01f146102f1578063a9059cbb146102ec578063ace150a5146102e7578063b7b72899146102e2578063bf376c7a146102dd578063c3b6f939146102d8578063c9144ddb146102d3578063c967891a146102ce578063ce5494bb146102c9578063cf092995146102c4578063d33627e5146102bf578063d505accf146102ba578063d9169487146102b5578063dd62ed3e146102b0578063ddeae033146102ab578063e3ee160e146102a6578063e94a0102146102a1578063e9ef148a1461029c5763ef55bec614610297575f80fd5b61188d565b61184a565b6117ff565b6117d4565b61174a565b6116f1565b6116b7565b611666565b611632565b611608565b61158e565b61144c565b611422565b6113df565b6113b6565b611369565b611339565b611303565b6111c9565b61118f565b611164565b6110b9565b61107d565b610f8b565b610e63565b610e3d565b610e1e565b610de9565b610bb6565b610b79565b610b3f565b610b08565b610ac4565b610a8d565b610a6c565b610a29565b6109e5565b6109c2565b61097d565b610935565b6108b7565b610879565b610853565b610823565b610801565b6107c4565b61078a565b610767565b610724565b6106fe565b6106d3565b61060d565b6105e2565b610592565b61056f565b61053e565b6104ee565b6103e9565b600435906001600160a01b03821682036103cf57565b5f80fd5b602435906001600160a01b03821682036103cf57565b346103cf5760203660031901126103cf576104026103b9565b6040516370a0823160e01b8152336004820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156104bb5761046761046e92610488945f9161048c575b50611eb6565b9033611f05565b6040516001600160f01b0390911681529081906020820190565b0390f35b6104ae915060203d6020116104b4575b6104a68183610cfa565b8101906118b8565b5f610461565b503d61049c565b6118c7565b5f9103126103cf57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346103cf575f3660031901126103cf5761048861052a7f00000000000000000000000000000000000000000000000000000000000000006120ba565b6040519182916020835260208301906104ca565b346103cf5760403660031901126103cf5761056461055a6103b9565b6024359033612124565b602060405160018152f35b346103cf5760203660031901126103cf5761059061058b6103b9565b6118d2565b005b346103cf575f3660031901126103cf576001600160f01b03600454166001600160f01b0360055416016001600160f01b0381116105dd576020906001600160f01b0360405191168152f35b6119b3565b346103cf575f3660031901126103cf5760206105fc6119c7565b6001600160f01b0360405191168152f35b346103cf5760603660031901126103cf576106266103b9565b61062e6103d3565b604435906001600160a01b0383165f52600260205261066060405f20336001600160a01b03165f5260205260405f2090565b549260018401610681575b610675935061253f565b60405160018152602090f35b82841061069d57610698836106759503338361250d565b61066b565b7f192b9e4e000000000000000000000000000000000000000000000000000000005f52336004526024849052604483905260645ffd5b346103cf575f3660031901126103cf5760206106ed611ada565b6001600160801b0360405191168152f35b346103cf575f3660031901126103cf5760206001600160f01b0360055416604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf5760203660031901126103cf5760206105fc6107856103b9565b611b2d565b346103cf575f3660031901126103cf5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b346103cf575f3660031901126103cf57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf575f3660031901126103cf57602061081b611b7d565b604051908152f35b346103cf5760403660031901126103cf5760206105fc6108416103b9565b61084c602435611eb6565b903361265b565b346103cf575f3660031901126103cf5760206001600160701b0360035416604051908152f35b346103cf5760803660031901126103cf576105906108956103b9565b602435906108b26064356044356108ac85856127c2565b8461281c565b612891565b346103cf575f3660031901126103cf576001600160f01b0364e8d4a510006108dd611ada565b6001600160801b036001600160701b036003541691160204166001600160f01b0360045416808211155f146109255750505f5b6040516001600160f01b039091168152602090f35b6001600160f01b03910316610910565b346103cf5760a03660031901126103cf5761094e6103b9565b6024356044359160ff831683036103cf576108b2610590936064356084359161097786866127c2565b856128ec565b346103cf575f3660031901126103cf5760207f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b0360405191168152f35b346103cf5760203660031901126103cf57602061081b6109e06103b9565b611c6f565b346103cf5760203660031901126103cf576001600160a01b03610a066103b9565b165f52600660205260206001600160f01b0360405f205460081c16604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf5760203660031901126103cf57610590610a886103b9565b611ca8565b346103cf5760203660031901126103cf5760206105fc610aab6103b9565b6001600160f01b03610abc33611c6f565b16903361265b565b346103cf5760203660031901126103cf576001600160a01b03610ae56103b9565b165f52600660205260206001600160801b03600160405f20015416604051908152f35b346103cf5760203660031901126103cf576001600160a01b03610b296103b9565b165f525f602052602060405f2054604051908152f35b346103cf575f3660031901126103cf5760206040517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88152f35b346103cf5760203660031901126103cf576001600160a01b03610b9a6103b9565b165f526006602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf57610c81610bf27f00000000000000000000000000000000000000000000000000000000000000006120ba565b6040516020610c018183610cfa565b5f8252601f1981013682840137610c8f604051610c1f604082610cfa565b600181527f3100000000000000000000000000000000000000000000000000000000000000838201526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e08588015260e08701906104ca565b9085820360408701526104ca565b4660608501526001600160a01b03301660808501525f60a085015283810360c08501528180845192838152019301915f5b828110610ccf57505050500390f35b835185528695509381019392810192600101610cc0565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117610d1c57604052565b610ce6565b67ffffffffffffffff8111610d1c57601f01601f191660200190565b81601f820112156103cf57803590610d5482610d21565b92610d626040519485610cfa565b828452602083830101116103cf57815f926020809301838601378301015290565b60e06003198201126103cf576004356001600160a01b03811681036103cf57916024356001600160a01b03811681036103cf579160443591606435916084359160a4359160c4359067ffffffffffffffff82116103cf57610de691600401610d3d565b90565b346103cf57610590610e19610dfd36610d83565b610e13828486888a8c989c9d979d9b999b6129df565b83612a56565b612b22565b346103cf575f3660031901126103cf5760206007541515604051908152f35b346103cf575f3660031901126103cf5760206001600160f01b0360045416604051908152f35b346103cf575f3660031901126103cf57610e7b6119c7565b7f49ec4048a361156a5338ff734a59a750f44cb5753bbe0886c6f39289bec2a01460206040516001600160f01b0384168152a160405163a9059cbb60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160f01b038216602482015290602082806044810103815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af19182156104bb5761048892610f5c575b506040516001600160f01b0390911681529081906020820190565b610f7d9060203d602011610f84575b610f758183610cfa565b810190611d7f565b505f610f41565b503d610f6b565b346103cf575f3660031901126103cf57604080517f776d5f6d69677261746f725f76310000000000000000000000000000000000006020828101918252309383019390935261100e9291610fec81606081015b03601f198101835282610cfa565b5190206040518093819263023aa9ab60e61b8352600483019190602083019252565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104bb57610590916001600160a01b03915f9161105e575b5016612ba4565b611077915060203d6020116104b4576104a68183610cfa565b5f611057565b346103cf575f3660031901126103cf5761048861052a7f00000000000000000000000000000000000000000000000000000000000000006120ba565b346103cf5760a03660031901126103cf576110d26103b9565b6110da6103d3565b6044359160843560643567ffffffffffffffff82116103cf576105909461110861110f933690600401610d3d565b9484612ce7565b90612a56565b6101009060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c4359060e43590565b346103cf57610590610e1961117836611115565b906108ac838587898b8d999d9e989e9c9a9c6129df565b346103cf575f3660031901126103cf5760206040517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678152f35b346103cf575f3660031901126103cf576111e23061216b565b6111f76111f3600180600754161490565b1590565b6112db5761120361255b565b61120c81611dab565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b156103cf575f80926004604051809581937f9f8495f90000000000000000000000000000000000000000000000000000000083525af19081156104bb577fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce926112bc926112c1575b506040516001600160801b0390911681529081906020820190565b0390a1005b806112cf5f6112d593610cfa565b806104c0565b5f6112a1565b7fb019ea35000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760403660031901126103cf5761056461131f6103b9565b61132a602435611eb6565b611332611ada565b91336130cf565b346103cf5761059061136461134d36611115565b906108ac838587898b8d999d9e989e9c9a9c612dae565b612e1b565b346103cf5760603660031901126103cf576113826103b9565b6024356044359167ffffffffffffffff83116103cf576108b26113ac610590943690600401610d3d565b610e1384846127c2565b346103cf5760403660031901126103cf5760206105fc6113d46103b9565b610467602435611eb6565b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf575f3660031901126103cf576020611442600180600754161490565b6040519015158152f35b346103cf575f3660031901126103cf57611465306128f8565b611473600180600754161490565b6115665760075461153e5761148661255b565b61148f81611dab565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b156103cf575f80926004604051809581937fa36e40fc0000000000000000000000000000000000000000000000000000000083525af19081156104bb577f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a18926112bc926112c157506040516001600160801b0390911681529081906020820190565b7f437b381f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7a427368000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760203660031901126103cf576115a76103b9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036115e05761059090612ba4565b7f51e2625e000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5761059061136461161c36610d83565b610e13828486888a8c989c9d979d9b999b612dae565b346103cf5760203660031901126103cf5760206116556116506103b9565b611e01565b6001600160a01b0360405191168152f35b346103cf5760e03660031901126103cf5761167f6103b9565b6116876103d3565b604435606435926084359060ff821682036103cf576116b16105909560a4359460c4359684612ce7565b906128ec565b346103cf575f3660031901126103cf5760206040517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298152f35b346103cf5760403660031901126103cf57602061174161170f6103b9565b6001600160a01b0361171f6103d3565b91165f526002835260405f20906001600160a01b03165f5260205260405f2090565b54604051908152f35b346103cf5760203660031901126103cf5760206105fc6117686103b9565b611770611ada565b906121a2565b6101209060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c43560ff811681036103cf579060e435906101043590565b346103cf576105906113646117e836611776565b916109778486888a8c989e988e9a9e9d9b9d612dae565b346103cf5760403660031901126103cf576118186103b9565b6001600160a01b0360243591165f52600160205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf57610590610e196118a136611776565b916109778486888a8c989e988e9a9e9d9b9d6129df565b908160209103126103cf575190565b6040513d5f823e3d90fd5b6118db8161216b565b6001600160a01b036118eb611ada565b916118f683826121a2565b501690815f52600660205260405f2060ff815416156119ae57611988916119516001600160f01b0361196c9360ff198154168155600181016fffffffffffffffffffffffffffffffff1981541690555460081c169182612393565b6001600160f01b0360055416906001600160f01b0391011690565b6001600160f01b03166001600160f01b03196005541617600555565b7f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d5f80a2565b505050565b634e487b7160e01b5f52601160045260245ffd5b6119cf611ada565b6040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa80156104bb576001600160f01b03915f91611abb575b501690611a8e611a426005546001600160f01b031690565b611a8183611a586001600160701b036003541690565b64e8d4a51000906001600160701b036001600160801b036001600160f01b039416911602041690565b016001600160f01b031690565b916001600160f01b038316811115611ab457610de6926001600160f01b0391031661246a565b5050505f90565b611ad4915060203d6020116104b4576104a68183610cfa565b5f611a2a565b611ae8600180600754161490565b15611af557610de661255b565b60075415611b295760075f527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885460801c90565b5f90565b6001600160a01b03165f52600660205260405f20805460ff811615611b77576001600160801b036001610de6930154166001600160f01b03611b6d611ada565b9260081c16612603565b50505f90565b467f000000000000000000000000000000000000000000000000000000000000000003611bc8577f000000000000000000000000000000000000000000000000000000000000000090565b611bf17f00000000000000000000000000000000000000000000000000000000000000006120ba565b6020815191012060405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a08152611c6960c082610cfa565b51902090565b6001600160a01b0381165f5260066020526001600160f01b03611c9b8160405f205460081c1692611b2d565b1681018091116105dd5790565b611cb1816128f8565b611cbf600180600754161490565b156112db576001600160a01b0316805f52600660205260405f2060ff815416611d7b5761196c611d5591611d3a6001600160f01b03611cfc61255b565b835460ff19166001178455926001810180546fffffffffffffffffffffffffffffffff19166001600160801b0386161790555460081c169182612930565b6001600160f01b0360055416906001600160f01b0391031690565b7f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c5f80a2565b5050565b908160209103126103cf575180151581036103cf5790565b634e487b7160e01b5f52603260045260245ffd5b6007549068010000000000000000821015610d1c576001820180600755821015611dfc5760075f5260205f208260011c01916001600160801b0380608085549360071b169316831b921b1916179055565b611d97565b604080517f776d5f636c61696d5f6f766572726964655f726563697069656e74000000000060208281019182526001600160a01b0390941682840152918152611e51929190610fec606082610cfa565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa80156104bb576001600160a01b03915f91611e9957501690565b611eb2915060203d6020116104b4576104a68183610cfa565b1690565b6001600160f01b038111611ed0576001600160f01b031690565b7f2a49c10d000000000000000000000000000000000000000000000000000000005f5260045ffd5b919082039182116105dd57565b6040516370a0823160e01b81523060048201529193926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691602084602481865afa9384156104bb575f9461204f575b506040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201523060248201526001600160f01b03909116604482015260208180606481015b03815f865af180156104bb57612032575b506040516370a0823160e01b815230600482015290602090829060249082905afa9081156104bb576120119261200a92612005925f91612013575b50611ef8565b611eb6565b8093612efe565b565b61202c915060203d6020116104b4576104a68183610cfa565b5f611fff565b61204a9060203d602011610f8457610f758183610cfa565b611fc4565b60209194509161206e611fb393833d85116104b4576104a68183610cfa565b94915091611f5d565b9061208182610d21565b61208e6040519182610cfa565b828152809261209f601f1991610d21565b0190602036910137565b908151811015611dfc570160200190565b5f5b6020811080612115575b156120db575f1981146105dd576001016120bc565b906120e582612077565b915f5b8181106120f55750505090565b6020811015611dfc5780836001921a61210e82876120a9565b53016120e8565b15611dfc5781811a15156120c6565b919060206001600160a01b03807f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259361215e86828961250d565b60405195865216941692a3565b61217490612f95565b61217a57565b7f8b198077000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190916121c0816001600160a01b03165f52600660205260405f2090565b8054939060ff85161561238b5760018101946121e386546001600160801b031690565b906001600160801b0382166001600160801b03851614612381578361222081612219612245946001600160f01b039060081c1690565b9485612603565b97906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b6001600160f01b03861615612378578154610100600160f81b03191690860160081b610100600160f81b03161790556122a961228d85611a816004546001600160f01b031690565b6001600160f01b03166001600160f01b03196004541617600455565b836122b383611e01565b6001600160a01b0381166123735750825b6001600160a01b0384166001600160a01b03821680827f4f3dc01c04a512d8b0b3b29dd31108ec4a93696f6506c2e8e609aa77953da7596040518061231989829190916001600160f01b036020820193169052565b0390a36040516001600160f01b038516815282905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a303612361575b50505050565b61236a936130cf565b5f80838161235b565b6122c4565b505f9450505050565b505f955050505050565b505f93505050565b906004546001600160f01b0381166001600160f01b038416101561244257508161241b6123f86123c961228d9461201196613736565b6001600160701b0360035416906001600160701b0382166001600160701b038216115f1461243457505f919050565b6001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b6004546001600160f01b0316036001600160f01b031690565b90036001600160701b031690565b6001600160f01b0319166004555050600380546dffffffffffffffffffffffffffff19169055565b906040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104bb575f916124ee575b50156124ea57611a5881610de693613736565b5090565b612507915060203d602011610f8457610f758183610cfa565b5f6124d7565b906001600160a01b0361253c92165f52600260205260405f20906001600160a01b03165f5260205260405f2090565b55565b9061254c61201193611eb6565b90612555611ada565b926130cf565b6040517f26987b600000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104bb575f916125c4575090565b90506020813d6020116125fb575b816125df60209383610cfa565b810103126103cf57516001600160801b03811681036103cf5790565b3d91506125d2565b9164e8d4a51000906001600160801b036001600160701b0361262d6001600160f01b039587613736565b169116020416906001600160f01b0381168211155f1461264d5750505f90565b6001600160f01b0391031690565b82612665916132a2565b6040516370a0823160e01b8152306004820152916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190602084602481865afa9384156104bb575f94612796575b5061270c916126d56020926126cf611ada565b9061246a565b60405163a9059cbb60e01b81526001600160a01b0390921660048301526001600160f01b0316602482015291829081906044820190565b03815f865af180156104bb57612779575b506040516370a0823160e01b815230600482015290602090829060249082905afa80156104bb57610de692612005925f926127585750611ef8565b61277291925060203d6020116104b4576104a68183610cfa565b905f611fff565b6127919060203d602011610f8457610f758183610cfa565b61271d565b6020919450916126d56127b861270c94843d86116104b4576104a68183610cfa565b95925050916126bc565b610de691604051906001600160a01b0360208301937f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298552166040830152606082015260608152612814608082610cfa565b519020613323565b9290916128559260ff601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851694821c01169061377b565b600682101561287d576120119282612876576128719250613760565b613371565b5050613371565b634e487b7160e01b5f52602160045260245ffd5b6001600160a01b03906128a48382613499565b16805f52600160205260405f20825f5260205260405f20600160ff198254161790557f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d815f80a3565b9391612855939161377b565b61290190612f95565b1561290857565b7fdd93dca8000000000000000000000000000000000000000000000000000000005f5260045ffd5b906001600160801b03906004546001600160f01b03196001600160f01b038581841601169116176004551680156129b757612011916001600160701b039161298d916001600160f01b031664e8d4a510000281015f190104613872565b816003541601166001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b7f23d359a3000000000000000000000000000000000000000000000000000000005f5260045ffd5b939092610de695926001600160a01b03604051958160208801987fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b6006111561287d57565b91612a618183613843565b600682101561287d5781612b1b57612a7a915084613760565b925b612a8584612a4c565b831561235b575f92610fde612ac085946040519283916020830195630b135d3f60e11b875260248401526040604484015260648301906104ca565b51915afa612acc612b75565b81612b0d575b81612ae9575b50612ae65761201190613371565b50565b630b135d3f60e11b915080602080612b06935183010191016118b8565b145f612ad8565b905060208151101590612ad2565b5092612a7c565b9594939291906001600160a01b03811696873303612b4557612011969750612e1b565b877f1c5939f3000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b3d15612b9f573d90612b8682610d21565b91612b946040519384610cfa565b82523d5f602084013e565b606090565b6001600160a01b038116908115612cbf57803b15612c97575f8080807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54945af4612bed612b75565b5015612c6f576001600160a01b0391827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc541692839216907fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a5f80a47fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2565b7fa27bfda2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f8d1e7cf4000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f0d626a32000000000000000000000000000000000000000000000000000000005f5260045ffd5b939293844211612d7e57936001600160a01b0385612d0a8585610de69899612124565b1692835f525f60205260405f20908154916001830190556001600160a01b036040519460208601967f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988526040870152166060850152608084015260a083015260c082015260c0815261281460e082610cfa565b847ff88f0490000000000000000000000000000000000000000000000000000000005f526004524260245260445ffd5b939092610de695926001600160a01b03604051958160208801987f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b9390929594919580421115612ecf575080421015612ea05750612011939481612e4761254c9386613499565b6001600160a01b038516805f52600160205260405f20825f52602052612e7760405f20600160ff19825416179055565b7f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a55f80a3611eb6565b7fb3fcd33e000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f498ff9a2000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f926001600160f01b03851690612f37826134f2565b612f4081613526565b6001600160a01b038116958686526006845260ff6040872054168614612f875790612f7e91612f6d611ada565b91612f7883826121a2565b506135cf565b604051908152a3565b612f9091613563565b612f7e565b60405163023aa9ab60e61b81527f6561726e6572735f6c6973745f69676e6f72656400000000000000000000000060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190602081602481865afa9081156104bb575f916130b0575b50159182159261301a57505090565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081527f6561726e6572730000000000000000000000000000000000000000000000000060048201526001600160a01b03929092166024830152909150602090829060449082905afa9081156104bb575f91613097575090565b610de6915060203d602011610f8457610f758183610cfa565b6130c9915060203d6020116104b4576104a68183610cfa565b5f61300b565b929190926130dc84613526565b6130e683826121a2565b506130f183856121a2565b506040516001600160f01b03831681526001600160a01b0385811691908316907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3613152816001600160a01b03165f52600660205260405f2090565b9061316e856001600160a01b03165f52600660205260405f2090565b91805461317b8160ff1690565b61318e613189865460ff1690565b151590565b901515146131e757509084846131a96131be95945460ff1690565b156131d7576131b792613696565b5460ff1690565b156131cc57612011926135cf565b905061201191613563565b90506131e29161361d565b6131b7565b9295509293509060081c6001600160f01b0316906001600160f01b0384166001600160f01b0383161061327357508154610100600160f81b0319169083900360081b610100600160f81b0316179055612011919061325290825460081c6001600160f01b0316611a81565b610100600160f81b0319610100600160f81b0383549260081b169116179055565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b0390811660245290911660445260645ffd5b907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f936001600160f01b038416906132dc826134f2565b6001600160a01b038116948587526006845260ff604088205416871461331a5790612f7e91613309611ada565b9161331483826121a2565b50613696565b612f909161361d565b61332b611b7d565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611c69606282610cfa565b9061337b82612a4c565b8115613495575061338b81612a4c565b600181146134865761339c81612a4c565b6002811461345e576133ad81612a4c565b60038114613436576133be81612a4c565b6004811461340e57806133d2600592612a4c565b146133e657638baa579f60e01b5f5260045ffd5b7f10c74b03000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fff551e89000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbf4bf5b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4be6321b000000000000000000000000000000000000000000000000000000005f5260045ffd5b638baa579f60e01b5f5260045ffd5b9050565b6001600160a01b0316805f52600160205260405f20825f5260205260ff60405f2054166134c4575050565b7fd309466d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b80156134fb5750565b7f77b8dde3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b031680156135385750565b7f17858bbe000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b03165f5260066020526135ad60405f206001600160f01b038381835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b6001600160f01b03196001600160f01b03600554928184160116911617600555565b6001600160a01b03165f9081526006602052604090208054610100600160f81b03198116600891821c6001600160f01b0316840190911b610100600160f81b03161790556120119190612930565b6001600160a01b0381165f52600660205260405f20906001600160f01b03825460081c16906001600160f01b038416821061327357508154610100600160f81b0319169083900360081b610100600160f81b03161790556001600160f01b03196001600160f01b03600554928184160316911617600555565b9291906001600160a01b0384165f52600660205260405f20936001600160f01b03855460081c16906001600160f01b0383168210613709575061201193946001600160f01b0383613704930316610100600160f81b0319610100600160f81b0383549260081b169116179055565b612393565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b039081166024521660445260645ffd5b906001600160801b031680156129b75764e8d4a510006001600160f01b03610de693160204613872565b6001600160a01b03908116911603613776575f90565b600590565b9291927f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116138385760ff8216601b811415908161382c575b50613821576137e85f93602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa156104bb575f516001600160a01b03811661380e57506001905f90565b5f906001600160a01b03169091565b9091565b505050506004905f90565b601c915014155f6137b5565b505050506003905f90565b9060418151036138695761381d91602082015190606060408401519301515f1a9061377b565b50506002905f90565b6001600160701b03811161388c576001600160701b031690565b7fca21dbd1000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220306a1ac6055685688633af84c0cf55b801c07e0727b7b7e4bd25a407d8b0017164736f6c634300081a0033000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c8063023276f0146103b457806306fdde03146103af578063095ea7b3146103aa578063174f6ce9146103a557806318160ddd146103a05780631ae2379c1461039b57806323b872dd1461039657806326987b6014610391578063281b229d1461038c5780632b20e397146103875780632c7861631461038257806330adf81f1461037d578063313ce567146103785780633644e5151461037357806339f476931461036e5780634c57a8fa14610369578063532992c51461036457806356d59ed21461035f5780635a049a701461035a5780635c60da1b146103555780635cf993841461035057806370a082311461034b57806374df25581461034657806374edaa201461034157806375f26e631461033c5780637c3ab944146103375780637ecebe00146103325780637f2eecc31461032d57806384af270f1461032857806384b0196e1461032357806388b7ab631461031e5780638a4a1017146103195780638a75f238146103145780638d78bf7e1461030f5780638fd3ab801461030a57806395d89b41146103055780639fd5a6cf14610300578063a08cb48b146102fb578063a0cc6a68146102f6578063a8afc01f146102f1578063a9059cbb146102ec578063ace150a5146102e7578063b7b72899146102e2578063bf376c7a146102dd578063c3b6f939146102d8578063c9144ddb146102d3578063c967891a146102ce578063ce5494bb146102c9578063cf092995146102c4578063d33627e5146102bf578063d505accf146102ba578063d9169487146102b5578063dd62ed3e146102b0578063ddeae033146102ab578063e3ee160e146102a6578063e94a0102146102a1578063e9ef148a1461029c5763ef55bec614610297575f80fd5b61188d565b61184a565b6117ff565b6117d4565b61174a565b6116f1565b6116b7565b611666565b611632565b611608565b61158e565b61144c565b611422565b6113df565b6113b6565b611369565b611339565b611303565b6111c9565b61118f565b611164565b6110b9565b61107d565b610f8b565b610e63565b610e3d565b610e1e565b610de9565b610bb6565b610b79565b610b3f565b610b08565b610ac4565b610a8d565b610a6c565b610a29565b6109e5565b6109c2565b61097d565b610935565b6108b7565b610879565b610853565b610823565b610801565b6107c4565b61078a565b610767565b610724565b6106fe565b6106d3565b61060d565b6105e2565b610592565b61056f565b61053e565b6104ee565b6103e9565b600435906001600160a01b03821682036103cf57565b5f80fd5b602435906001600160a01b03821682036103cf57565b346103cf5760203660031901126103cf576104026103b9565b6040516370a0823160e01b8152336004820152906020826024817f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b03165afa9081156104bb5761046761046e92610488945f9161048c575b50611eb6565b9033611f05565b6040516001600160f01b0390911681529081906020820190565b0390f35b6104ae915060203d6020116104b4575b6104a68183610cfa565b8101906118b8565b5f610461565b503d61049c565b6118c7565b5f9103126103cf57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346103cf575f3660031901126103cf5761048861052a7f577261707065644d206279204d5e3000000000000000000000000000000000006120ba565b6040519182916020835260208301906104ca565b346103cf5760403660031901126103cf5761056461055a6103b9565b6024359033612124565b602060405160018152f35b346103cf5760203660031901126103cf5761059061058b6103b9565b6118d2565b005b346103cf575f3660031901126103cf576001600160f01b03600454166001600160f01b0360055416016001600160f01b0381116105dd576020906001600160f01b0360405191168152f35b6119b3565b346103cf575f3660031901126103cf5760206105fc6119c7565b6001600160f01b0360405191168152f35b346103cf5760603660031901126103cf576106266103b9565b61062e6103d3565b604435906001600160a01b0383165f52600260205261066060405f20336001600160a01b03165f5260205260405f2090565b549260018401610681575b610675935061253f565b60405160018152602090f35b82841061069d57610698836106759503338361250d565b61066b565b7f192b9e4e000000000000000000000000000000000000000000000000000000005f52336004526024849052604483905260645ffd5b346103cf575f3660031901126103cf5760206106ed611ada565b6001600160801b0360405191168152f35b346103cf575f3660031901126103cf5760206001600160f01b0360055416604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c168152f35b346103cf5760203660031901126103cf5760206105fc6107856103b9565b611b2d565b346103cf575f3660031901126103cf5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b346103cf575f3660031901126103cf57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000006168152f35b346103cf575f3660031901126103cf57602061081b611b7d565b604051908152f35b346103cf5760403660031901126103cf5760206105fc6108416103b9565b61084c602435611eb6565b903361265b565b346103cf575f3660031901126103cf5760206001600160701b0360035416604051908152f35b346103cf5760803660031901126103cf576105906108956103b9565b602435906108b26064356044356108ac85856127c2565b8461281c565b612891565b346103cf575f3660031901126103cf576001600160f01b0364e8d4a510006108dd611ada565b6001600160801b036001600160701b036003541691160204166001600160f01b0360045416808211155f146109255750505f5b6040516001600160f01b039091168152602090f35b6001600160f01b03910316610910565b346103cf5760a03660031901126103cf5761094e6103b9565b6024356044359160ff831683036103cf576108b2610590936064356084359161097786866127c2565b856128ec565b346103cf575f3660031901126103cf5760207f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b0360405191168152f35b346103cf5760203660031901126103cf57602061081b6109e06103b9565b611c6f565b346103cf5760203660031901126103cf576001600160a01b03610a066103b9565b165f52600660205260206001600160f01b0360405f205460081c16604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1168152f35b346103cf5760203660031901126103cf57610590610a886103b9565b611ca8565b346103cf5760203660031901126103cf5760206105fc610aab6103b9565b6001600160f01b03610abc33611c6f565b16903361265b565b346103cf5760203660031901126103cf576001600160a01b03610ae56103b9565b165f52600660205260206001600160801b03600160405f20015416604051908152f35b346103cf5760203660031901126103cf576001600160a01b03610b296103b9565b165f525f602052602060405f2054604051908152f35b346103cf575f3660031901126103cf5760206040517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88152f35b346103cf5760203660031901126103cf576001600160a01b03610b9a6103b9565b165f526006602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf57610c81610bf27f577261707065644d206279204d5e3000000000000000000000000000000000006120ba565b6040516020610c018183610cfa565b5f8252601f1981013682840137610c8f604051610c1f604082610cfa565b600181527f3100000000000000000000000000000000000000000000000000000000000000838201526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e08588015260e08701906104ca565b9085820360408701526104ca565b4660608501526001600160a01b03301660808501525f60a085015283810360c08501528180845192838152019301915f5b828110610ccf57505050500390f35b835185528695509381019392810192600101610cc0565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117610d1c57604052565b610ce6565b67ffffffffffffffff8111610d1c57601f01601f191660200190565b81601f820112156103cf57803590610d5482610d21565b92610d626040519485610cfa565b828452602083830101116103cf57815f926020809301838601378301015290565b60e06003198201126103cf576004356001600160a01b03811681036103cf57916024356001600160a01b03811681036103cf579160443591606435916084359160a4359160c4359067ffffffffffffffff82116103cf57610de691600401610d3d565b90565b346103cf57610590610e19610dfd36610d83565b610e13828486888a8c989c9d979d9b999b6129df565b83612a56565b612b22565b346103cf575f3660031901126103cf5760206007541515604051908152f35b346103cf575f3660031901126103cf5760206001600160f01b0360045416604051908152f35b346103cf575f3660031901126103cf57610e7b6119c7565b7f49ec4048a361156a5338ff734a59a750f44cb5753bbe0886c6f39289bec2a01460206040516001600160f01b0384168152a160405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde1660048201526001600160f01b038216602482015290602082806044810103815f6001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b165af19182156104bb5761048892610f5c575b506040516001600160f01b0390911681529081906020820190565b610f7d9060203d602011610f84575b610f758183610cfa565b810190611d7f565b505f610f41565b503d610f6b565b346103cf575f3660031901126103cf57604080517f776d5f6d69677261746f725f76310000000000000000000000000000000000006020828101918252309383019390935261100e9291610fec81606081015b03601f198101835282610cfa565b5190206040518093819263023aa9ab60e61b8352600483019190602083019252565b03816001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c165afa9081156104bb57610590916001600160a01b03915f9161105e575b5016612ba4565b611077915060203d6020116104b4576104a68183610cfa565b5f611057565b346103cf575f3660031901126103cf5761048861052a7f774d0000000000000000000000000000000000000000000000000000000000006120ba565b346103cf5760a03660031901126103cf576110d26103b9565b6110da6103d3565b6044359160843560643567ffffffffffffffff82116103cf576105909461110861110f933690600401610d3d565b9484612ce7565b90612a56565b6101009060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c4359060e43590565b346103cf57610590610e1961117836611115565b906108ac838587898b8d999d9e989e9c9a9c6129df565b346103cf575f3660031901126103cf5760206040517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678152f35b346103cf575f3660031901126103cf576111e23061216b565b6111f76111f3600180600754161490565b1590565b6112db5761120361255b565b61120c81611dab565b6001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b1690813b156103cf575f80926004604051809581937f9f8495f90000000000000000000000000000000000000000000000000000000083525af19081156104bb577fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce926112bc926112c1575b506040516001600160801b0390911681529081906020820190565b0390a1005b806112cf5f6112d593610cfa565b806104c0565b5f6112a1565b7fb019ea35000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760403660031901126103cf5761056461131f6103b9565b61132a602435611eb6565b611332611ada565b91336130cf565b346103cf5761059061136461134d36611115565b906108ac838587898b8d999d9e989e9c9a9c612dae565b612e1b565b346103cf5760603660031901126103cf576113826103b9565b6024356044359167ffffffffffffffff83116103cf576108b26113ac610590943690600401610d3d565b610e1384846127c2565b346103cf5760403660031901126103cf5760206105fc6113d46103b9565b610467602435611eb6565b346103cf575f3660031901126103cf5760206040516001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b168152f35b346103cf575f3660031901126103cf576020611442600180600754161490565b6040519015158152f35b346103cf575f3660031901126103cf57611465306128f8565b611473600180600754161490565b6115665760075461153e5761148661255b565b61148f81611dab565b6001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b1690813b156103cf575f80926004604051809581937fa36e40fc0000000000000000000000000000000000000000000000000000000083525af19081156104bb577f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a18926112bc926112c157506040516001600160801b0390911681529081906020820190565b7f437b381f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7a427368000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760203660031901126103cf576115a76103b9565b6001600160a01b037f000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd11633036115e05761059090612ba4565b7f51e2625e000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5761059061136461161c36610d83565b610e13828486888a8c989c9d979d9b999b612dae565b346103cf5760203660031901126103cf5760206116556116506103b9565b611e01565b6001600160a01b0360405191168152f35b346103cf5760e03660031901126103cf5761167f6103b9565b6116876103d3565b604435606435926084359060ff821682036103cf576116b16105909560a4359460c4359684612ce7565b906128ec565b346103cf575f3660031901126103cf5760206040517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298152f35b346103cf5760403660031901126103cf57602061174161170f6103b9565b6001600160a01b0361171f6103d3565b91165f526002835260405f20906001600160a01b03165f5260205260405f2090565b54604051908152f35b346103cf5760203660031901126103cf5760206105fc6117686103b9565b611770611ada565b906121a2565b6101209060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c43560ff811681036103cf579060e435906101043590565b346103cf576105906113646117e836611776565b916109778486888a8c989e988e9a9e9d9b9d612dae565b346103cf5760403660031901126103cf576118186103b9565b6001600160a01b0360243591165f52600160205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde168152f35b346103cf57610590610e196118a136611776565b916109778486888a8c989e988e9a9e9d9b9d6129df565b908160209103126103cf575190565b6040513d5f823e3d90fd5b6118db8161216b565b6001600160a01b036118eb611ada565b916118f683826121a2565b501690815f52600660205260405f2060ff815416156119ae57611988916119516001600160f01b0361196c9360ff198154168155600181016fffffffffffffffffffffffffffffffff1981541690555460081c169182612393565b6001600160f01b0360055416906001600160f01b0391011690565b6001600160f01b03166001600160f01b03196005541617600555565b7f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d5f80a2565b505050565b634e487b7160e01b5f52601160045260245ffd5b6119cf611ada565b6040516370a0823160e01b81523060048201526020816024817f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b03165afa80156104bb576001600160f01b03915f91611abb575b501690611a8e611a426005546001600160f01b031690565b611a8183611a586001600160701b036003541690565b64e8d4a51000906001600160701b036001600160801b036001600160f01b039416911602041690565b016001600160f01b031690565b916001600160f01b038316811115611ab457610de6926001600160f01b0391031661246a565b5050505f90565b611ad4915060203d6020116104b4576104a68183610cfa565b5f611a2a565b611ae8600180600754161490565b15611af557610de661255b565b60075415611b295760075f527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885460801c90565b5f90565b6001600160a01b03165f52600660205260405f20805460ff811615611b77576001600160801b036001610de6930154166001600160f01b03611b6d611ada565b9260081c16612603565b50505f90565b467f000000000000000000000000000000000000000000000000000000000000a4b103611bc8577f3027ba94c91fce0fc24681fb321a3428aa60b6207230bedee4912d3609c52ff990565b611bf17f577261707065644d206279204d5e3000000000000000000000000000000000006120ba565b6020815191012060405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a08152611c6960c082610cfa565b51902090565b6001600160a01b0381165f5260066020526001600160f01b03611c9b8160405f205460081c1692611b2d565b1681018091116105dd5790565b611cb1816128f8565b611cbf600180600754161490565b156112db576001600160a01b0316805f52600660205260405f2060ff815416611d7b5761196c611d5591611d3a6001600160f01b03611cfc61255b565b835460ff19166001178455926001810180546fffffffffffffffffffffffffffffffff19166001600160801b0386161790555460081c169182612930565b6001600160f01b0360055416906001600160f01b0391031690565b7f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c5f80a2565b5050565b908160209103126103cf575180151581036103cf5790565b634e487b7160e01b5f52603260045260245ffd5b6007549068010000000000000000821015610d1c576001820180600755821015611dfc5760075f5260205f208260011c01916001600160801b0380608085549360071b169316831b921b1916179055565b611d97565b604080517f776d5f636c61696d5f6f766572726964655f726563697069656e74000000000060208281019182526001600160a01b0390941682840152918152611e51929190610fec606082610cfa565b03816001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c165afa80156104bb576001600160a01b03915f91611e9957501690565b611eb2915060203d6020116104b4576104a68183610cfa565b1690565b6001600160f01b038111611ed0576001600160f01b031690565b7f2a49c10d000000000000000000000000000000000000000000000000000000005f5260045ffd5b919082039182116105dd57565b6040516370a0823160e01b81523060048201529193926001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b1691602084602481865afa9384156104bb575f9461204f575b506040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201523060248201526001600160f01b03909116604482015260208180606481015b03815f865af180156104bb57612032575b506040516370a0823160e01b815230600482015290602090829060249082905afa9081156104bb576120119261200a92612005925f91612013575b50611ef8565b611eb6565b8093612efe565b565b61202c915060203d6020116104b4576104a68183610cfa565b5f611fff565b61204a9060203d602011610f8457610f758183610cfa565b611fc4565b60209194509161206e611fb393833d85116104b4576104a68183610cfa565b94915091611f5d565b9061208182610d21565b61208e6040519182610cfa565b828152809261209f601f1991610d21565b0190602036910137565b908151811015611dfc570160200190565b5f5b6020811080612115575b156120db575f1981146105dd576001016120bc565b906120e582612077565b915f5b8181106120f55750505090565b6020811015611dfc5780836001921a61210e82876120a9565b53016120e8565b15611dfc5781811a15156120c6565b919060206001600160a01b03807f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259361215e86828961250d565b60405195865216941692a3565b61217490612f95565b61217a57565b7f8b198077000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190916121c0816001600160a01b03165f52600660205260405f2090565b8054939060ff85161561238b5760018101946121e386546001600160801b031690565b906001600160801b0382166001600160801b03851614612381578361222081612219612245946001600160f01b039060081c1690565b9485612603565b97906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b6001600160f01b03861615612378578154610100600160f81b03191690860160081b610100600160f81b03161790556122a961228d85611a816004546001600160f01b031690565b6001600160f01b03166001600160f01b03196004541617600455565b836122b383611e01565b6001600160a01b0381166123735750825b6001600160a01b0384166001600160a01b03821680827f4f3dc01c04a512d8b0b3b29dd31108ec4a93696f6506c2e8e609aa77953da7596040518061231989829190916001600160f01b036020820193169052565b0390a36040516001600160f01b038516815282905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a303612361575b50505050565b61236a936130cf565b5f80838161235b565b6122c4565b505f9450505050565b505f955050505050565b505f93505050565b906004546001600160f01b0381166001600160f01b038416101561244257508161241b6123f86123c961228d9461201196613736565b6001600160701b0360035416906001600160701b0382166001600160701b038216115f1461243457505f919050565b6001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b6004546001600160f01b0316036001600160f01b031690565b90036001600160701b031690565b6001600160f01b0319166004555050600380546dffffffffffffffffffffffffffff19169055565b906040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b165afa9081156104bb575f916124ee575b50156124ea57611a5881610de693613736565b5090565b612507915060203d602011610f8457610f758183610cfa565b5f6124d7565b906001600160a01b0361253c92165f52600260205260405f20906001600160a01b03165f5260205260405f2090565b55565b9061254c61201193611eb6565b90612555611ada565b926130cf565b6040517f26987b600000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b165afa9081156104bb575f916125c4575090565b90506020813d6020116125fb575b816125df60209383610cfa565b810103126103cf57516001600160801b03811681036103cf5790565b3d91506125d2565b9164e8d4a51000906001600160801b036001600160701b0361262d6001600160f01b039587613736565b169116020416906001600160f01b0381168211155f1461264d5750505f90565b6001600160f01b0391031690565b82612665916132a2565b6040516370a0823160e01b8152306004820152916001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b169190602084602481865afa9384156104bb575f94612796575b5061270c916126d56020926126cf611ada565b9061246a565b60405163a9059cbb60e01b81526001600160a01b0390921660048301526001600160f01b0316602482015291829081906044820190565b03815f865af180156104bb57612779575b506040516370a0823160e01b815230600482015290602090829060249082905afa80156104bb57610de692612005925f926127585750611ef8565b61277291925060203d6020116104b4576104a68183610cfa565b905f611fff565b6127919060203d602011610f8457610f758183610cfa565b61271d565b6020919450916126d56127b861270c94843d86116104b4576104a68183610cfa565b95925050916126bc565b610de691604051906001600160a01b0360208301937f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298552166040830152606082015260608152612814608082610cfa565b519020613323565b9290916128559260ff601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851694821c01169061377b565b600682101561287d576120119282612876576128719250613760565b613371565b5050613371565b634e487b7160e01b5f52602160045260245ffd5b6001600160a01b03906128a48382613499565b16805f52600160205260405f20825f5260205260405f20600160ff198254161790557f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d815f80a3565b9391612855939161377b565b61290190612f95565b1561290857565b7fdd93dca8000000000000000000000000000000000000000000000000000000005f5260045ffd5b906001600160801b03906004546001600160f01b03196001600160f01b038581841601169116176004551680156129b757612011916001600160701b039161298d916001600160f01b031664e8d4a510000281015f190104613872565b816003541601166001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b7f23d359a3000000000000000000000000000000000000000000000000000000005f5260045ffd5b939092610de695926001600160a01b03604051958160208801987fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b6006111561287d57565b91612a618183613843565b600682101561287d5781612b1b57612a7a915084613760565b925b612a8584612a4c565b831561235b575f92610fde612ac085946040519283916020830195630b135d3f60e11b875260248401526040604484015260648301906104ca565b51915afa612acc612b75565b81612b0d575b81612ae9575b50612ae65761201190613371565b50565b630b135d3f60e11b915080602080612b06935183010191016118b8565b145f612ad8565b905060208151101590612ad2565b5092612a7c565b9594939291906001600160a01b03811696873303612b4557612011969750612e1b565b877f1c5939f3000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b3d15612b9f573d90612b8682610d21565b91612b946040519384610cfa565b82523d5f602084013e565b606090565b6001600160a01b038116908115612cbf57803b15612c97575f8080807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54945af4612bed612b75565b5015612c6f576001600160a01b0391827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc541692839216907fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a5f80a47fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2565b7fa27bfda2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f8d1e7cf4000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f0d626a32000000000000000000000000000000000000000000000000000000005f5260045ffd5b939293844211612d7e57936001600160a01b0385612d0a8585610de69899612124565b1692835f525f60205260405f20908154916001830190556001600160a01b036040519460208601967f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988526040870152166060850152608084015260a083015260c082015260c0815261281460e082610cfa565b847ff88f0490000000000000000000000000000000000000000000000000000000005f526004524260245260445ffd5b939092610de695926001600160a01b03604051958160208801987f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b9390929594919580421115612ecf575080421015612ea05750612011939481612e4761254c9386613499565b6001600160a01b038516805f52600160205260405f20825f52602052612e7760405f20600160ff19825416179055565b7f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a55f80a3611eb6565b7fb3fcd33e000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f498ff9a2000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f926001600160f01b03851690612f37826134f2565b612f4081613526565b6001600160a01b038116958686526006845260ff6040872054168614612f875790612f7e91612f6d611ada565b91612f7883826121a2565b506135cf565b604051908152a3565b612f9091613563565b612f7e565b60405163023aa9ab60e61b81527f6561726e6572735f6c6973745f69676e6f72656400000000000000000000000060048201526001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c169190602081602481865afa9081156104bb575f916130b0575b50159182159261301a57505090565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081527f6561726e6572730000000000000000000000000000000000000000000000000060048201526001600160a01b03929092166024830152909150602090829060449082905afa9081156104bb575f91613097575090565b610de6915060203d602011610f8457610f758183610cfa565b6130c9915060203d6020116104b4576104a68183610cfa565b5f61300b565b929190926130dc84613526565b6130e683826121a2565b506130f183856121a2565b506040516001600160f01b03831681526001600160a01b0385811691908316907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3613152816001600160a01b03165f52600660205260405f2090565b9061316e856001600160a01b03165f52600660205260405f2090565b91805461317b8160ff1690565b61318e613189865460ff1690565b151590565b901515146131e757509084846131a96131be95945460ff1690565b156131d7576131b792613696565b5460ff1690565b156131cc57612011926135cf565b905061201191613563565b90506131e29161361d565b6131b7565b9295509293509060081c6001600160f01b0316906001600160f01b0384166001600160f01b0383161061327357508154610100600160f81b0319169083900360081b610100600160f81b0316179055612011919061325290825460081c6001600160f01b0316611a81565b610100600160f81b0319610100600160f81b0383549260081b169116179055565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b0390811660245290911660445260645ffd5b907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f936001600160f01b038416906132dc826134f2565b6001600160a01b038116948587526006845260ff604088205416871461331a5790612f7e91613309611ada565b9161331483826121a2565b50613696565b612f909161361d565b61332b611b7d565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611c69606282610cfa565b9061337b82612a4c565b8115613495575061338b81612a4c565b600181146134865761339c81612a4c565b6002811461345e576133ad81612a4c565b60038114613436576133be81612a4c565b6004811461340e57806133d2600592612a4c565b146133e657638baa579f60e01b5f5260045ffd5b7f10c74b03000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fff551e89000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbf4bf5b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4be6321b000000000000000000000000000000000000000000000000000000005f5260045ffd5b638baa579f60e01b5f5260045ffd5b9050565b6001600160a01b0316805f52600160205260405f20825f5260205260ff60405f2054166134c4575050565b7fd309466d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b80156134fb5750565b7f77b8dde3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b031680156135385750565b7f17858bbe000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b03165f5260066020526135ad60405f206001600160f01b038381835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b6001600160f01b03196001600160f01b03600554928184160116911617600555565b6001600160a01b03165f9081526006602052604090208054610100600160f81b03198116600891821c6001600160f01b0316840190911b610100600160f81b03161790556120119190612930565b6001600160a01b0381165f52600660205260405f20906001600160f01b03825460081c16906001600160f01b038416821061327357508154610100600160f81b0319169083900360081b610100600160f81b03161790556001600160f01b03196001600160f01b03600554928184160316911617600555565b9291906001600160a01b0384165f52600660205260405f20936001600160f01b03855460081c16906001600160f01b0383168210613709575061201193946001600160f01b0383613704930316610100600160f81b0319610100600160f81b0383549260081b169116179055565b612393565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b039081166024521660445260645ffd5b906001600160801b031680156129b75764e8d4a510006001600160f01b03610de693160204613872565b6001600160a01b03908116911603613776575f90565b600590565b9291927f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116138385760ff8216601b811415908161382c575b50613821576137e85f93602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa156104bb575f516001600160a01b03811661380e57506001905f90565b5f906001600160a01b03169091565b9091565b505050506004905f90565b601c915014155f6137b5565b505050506003905f90565b9060418151036138695761381d91602082015190606060408401519301515f1a9061377b565b50506002905f90565b6001600160701b03811161388c576001600160701b031690565b7fca21dbd1000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220306a1ac6055685688633af84c0cf55b801c07e0727b7b7e4bd25a407d8b0017164736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1
-----Decoded View---------------
Arg [0] : mToken_ (address): 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b
Arg [1] : registrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c
Arg [2] : excessDestination_ (address): 0x3349e443068F76666789C4f76F00D9c4F38A4DdE
Arg [3] : migrationAdmin_ (address): 0xdcf79C332cB3Fe9d39A830a5f8de7cE6b1BD6fD1
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b
Arg [1] : 000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c
Arg [2] : 0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde
Arg [3] : 000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.